diff options
author | Igor Pashev <pashev.igor@gmail.com> | 2013-05-18 00:00:40 +0400 |
---|---|---|
committer | Igor Pashev <pashev.igor@gmail.com> | 2013-05-18 00:00:40 +0400 |
commit | 213d4f4f0d053d779dd43aa3ed1b6e256569ce16 (patch) | |
tree | 613b497a9c3f9cb385b8c35bb8b77252621f9f93 | |
download | iscsit-213d4f4f0d053d779dd43aa3ed1b6e256569ce16.tar.gz |
Initial illumos sourcesillumos
-rw-r--r-- | usr/src/cmd/iscsitsvc/iscsi-target.xml | 110 | ||||
-rw-r--r-- | usr/src/cmd/iscsitsvc/iscsitsvc.c | 249 | ||||
-rw-r--r-- | usr/src/cmd/itadm/itadm.c | 2215 | ||||
-rw-r--r-- | usr/src/common/cmdparse/cmdparse.c | 681 | ||||
-rw-r--r-- | usr/src/common/cmdparse/cmdparse.h | 222 | ||||
-rw-r--r-- | usr/src/common/iscsi/base64.c | 255 | ||||
-rw-r--r-- | usr/src/common/iscsit/iscsit_common.c | 1588 | ||||
-rw-r--r-- | usr/src/lib/libiscsit/common/libiscsit.c | 2044 | ||||
-rw-r--r-- | usr/src/lib/libiscsit/common/libiscsit.h | 545 | ||||
-rw-r--r-- | usr/src/man/man1m/itadm.1m | 943 | ||||
-rw-r--r-- | usr/src/man/man3iscsit/it_config_load.3iscsit | 182 | ||||
-rw-r--r-- | usr/src/man/man3iscsit/it_ini_create.3iscsit | 188 | ||||
-rw-r--r-- | usr/src/man/man3iscsit/it_portal_create.3iscsit | 139 | ||||
-rw-r--r-- | usr/src/man/man3iscsit/it_tgt_create.3iscsit | 291 | ||||
-rw-r--r-- | usr/src/man/man3iscsit/it_tpg_create.3iscsit | 175 | ||||
-rw-r--r-- | usr/src/man/man3lib/libiscsit.3lib | 87 | ||||
-rw-r--r-- | usr/src/uts/common/sys/iscsit/iscsit_common.h | 516 |
17 files changed, 10430 insertions, 0 deletions
diff --git a/usr/src/cmd/iscsitsvc/iscsi-target.xml b/usr/src/cmd/iscsitsvc/iscsi-target.xml new file mode 100644 index 0000000..4598d27 --- /dev/null +++ b/usr/src/cmd/iscsitsvc/iscsi-target.xml @@ -0,0 +1,110 @@ +<?xml version='1.0'?> +<!DOCTYPE service_bundle SYSTEM '/usr/share/lib/xml/dtd/service_bundle.dtd.1'> + +<!-- + +CDDL HEADER START + +The contents of this file are subject to the terms of the +Common Development and Distribution License (the "License"). +You may not use this file except in compliance with the License. + +You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +or http://www.opensolaris.org/os/licensing. +See the License for the specific language governing permissions +and limitations under the License. + +When distributing Covered Code, include this CDDL HEADER in each +file and include the License file at usr/src/OPENSOLARIS.LICENSE. +If applicable, add the following below this CDDL HEADER, with the +fields enclosed by brackets "[]" replaced with your own identifying +information: Portions Copyright [yyyy] [name of copyright owner] + +CDDL HEADER END + +Copyright 2008 Sun Microsystems, Inc. All rights reserved. +Use is subject to license terms. + +Service manifests for iSCSI Target +--> + +<!-- + network/iscsi/target - Export iSCSI target services + +--> + +<service_bundle type='manifest' name='SUNWiscsitr:iscsi-target'> + +<service + name='network/iscsi/target' + type='service' + version='1'> + + <create_default_instance enabled='false' /> + + <single_instance/> + + <dependency name = 'network' + grouping='require_any' + restart_on='error' + type='service'> + <service_fmri value='svc:/milestone/network'/> + </dependency> + + <dependency name = 'stmf' + grouping='require_all' + restart_on='none' + type='service'> + <service_fmri value='svc:/system/stmf:default'/> + </dependency> + + <exec_method + type='method' + name='start' + exec='/lib/svc/method/iscsi-target start' + timeout_seconds='600'> + <method_context> + <method_credential + user='root' + group='root' + privileges='basic,sys_devices' + /> + </method_context> + </exec_method> + + <exec_method + type='method' + name='stop' + exec='/lib/svc/method/iscsi-target stop' + timeout_seconds='600'> + <method_context> + <method_credential + user='root' + group='root' + privileges='basic,sys_devices' + /> + </method_context> + </exec_method> + + <property_group name='startd' type='framework'> + <propval name='duration' type='astring' + value='transient' /> + </property_group> + + <stability value='Evolving' /> + + <template> + <common_name> + <loctext xml:lang='C'> + iscsi target + </loctext> + </common_name> + <documentation> + <manpage title='itadm' section='1M' + manpath='/usr/share/man' /> + </documentation> + </template> + +</service> + +</service_bundle> diff --git a/usr/src/cmd/iscsitsvc/iscsitsvc.c b/usr/src/cmd/iscsitsvc/iscsitsvc.c new file mode 100644 index 0000000..0c147d2 --- /dev/null +++ b/usr/src/cmd/iscsitsvc/iscsitsvc.c @@ -0,0 +1,249 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <strings.h> +#include <sys/types.h> +#include <unistd.h> +#include <stropts.h> +#include <libintl.h> +#include <errno.h> +#include <time.h> +#include <string.h> +#include <assert.h> +#include <getopt.h> +#include <cmdparse.h> +#include <signal.h> +#include <pthread.h> +#include <fcntl.h> +#include <locale.h> +#include <sys/systeminfo.h> + +#include <libiscsit.h> +#include <sys/iscsit/iscsit_common.h> + +static int it_enable(int, char **, cmdOptions_t *, void *); +static int it_disable(int, char **, cmdOptions_t *, void *); + +/* + * MAJOR - This should only change when there is an incompatible change made + * to the interfaces or the output. + * + * MINOR - This should change whenever there is a new command or new feature + * with no incompatible change. + */ +#define VERSION_STRING_MAJOR "1" +#define VERSION_STRING_MINOR "0" +#define VERSION_STRING_MAX_LEN 10 + +/* 10 ms sleep in nanoseconds */ +#define TEN_MS_NANOSLEEP 10000000 + +/* tables set up based on cmdparse instructions */ + +/* add new options here */ +optionTbl_t longOptions[] = { + {NULL, 0, 0, 0} +}; + +/* + * Add new subcommands here + */ +subCommandProps_t subcommands[] = { + {"start", it_enable, NULL, NULL, NULL, OPERAND_NONE, NULL}, + {"stop", it_disable, NULL, NULL, NULL, OPERAND_NONE, NULL}, + {NULL, 0, NULL, NULL, 0, NULL, 0, NULL} +}; + +/* globals */ +char *cmdName; + +/* + * Opens the iSCSI Target Node + * + * fd - Return the iscsit file descriptor + */ +static int +it_open(int *fd) +{ + + int ret = ITADM_SUCCESS; + + *fd = open(ISCSIT_NODE, O_RDONLY); + if (*fd < 0) { + if (errno == EPERM) { + (void) fprintf(stdout, "open failed: EPERM"); + ret = ITADM_PERM; + } else { + (void) fprintf(stdout, "open failed: INVALID"); + ret = ITADM_INVALID; + } + } + + return (ret); +} + +/* + * Enables the iSCSI Target + */ +/*ARGSUSED*/ +static int +it_enable(int operandLen, char *operands[], cmdOptions_t *options, + void *args) +{ + int ret; + int fd; + char buf[256]; + uint32_t *buflenp; + char *fqhnp; + iscsit_hostinfo_t hostinfo; + + (void) fprintf(stdout, "%s: %s\n", cmdName, + gettext("Requesting to enable iscsi target")); + + bzero(buf, 256); + bzero(hostinfo.fqhn, sizeof (hostinfo.fqhn)); + + /* Open the iscsi target node */ + if ((ret = it_open(&fd)) != ITADM_SUCCESS) { + (void) fprintf(stdout, "Unable to open device %s", ISCSIT_NODE); + return (ret); + } + + (void) fprintf(stdout, "it_enable [fd=%d]\n", fd); + /* enable the iscsi target */ + buflenp = (uint32_t *)((void *)&buf); + *buflenp = strlen("target_name") + 1; + (void) strncpy(buf + sizeof (uint32_t), "target_name", + 256 - sizeof (uint32_t)); + + fqhnp = &hostinfo.fqhn[0]; + + ret = sysinfo(SI_HOSTNAME, fqhnp, 256); + + if ((ret != -1) && (ret < sizeof (hostinfo.fqhn))) { + fqhnp += ret; + hostinfo.length = ret; + hostinfo.fqhn[ret-1] = '.'; + hostinfo.length += sysinfo(SI_SRPC_DOMAIN, fqhnp, + sizeof (hostinfo.fqhn) - ret); + } + + (void) fprintf(stdout, "it_enable: fqhn = '%s'\n", hostinfo.fqhn); + + if ((ret = ioctl(fd, ISCSIT_IOC_ENABLE_SVC, &hostinfo)) != 0) { + (void) fprintf(stdout, "Unable to issue ioctl: %d", errno); + return (ret); + } + return (ITADM_SUCCESS); +} + + +/* + * Disable the iSCSI target + */ +/* ARGSUSED */ +static int +it_disable(int operandLen, char *operands[], cmdOptions_t *options, + void *args) +{ + int ret; + int fd; + + (void) fprintf(stdout, "%s: %s\n", cmdName, + gettext("Requesting to disable iscsi target")); + + /* Open the iscsi target node */ + if ((ret = it_open(&fd)) != ITADM_SUCCESS) { + return (ret); + } + + /* disable the iSCSI target */ + if ((ret = ioctl(fd, ISCSIT_IOC_DISABLE_SVC, NULL)) != 0) { + return (ret); + } + return (ITADM_SUCCESS); +} + +/* + * input: + * execFullName - exec name of program (argv[0]) + * + * copied from usr/src/cmd/zoneadm/zoneadm.c in OS/Net + * (changed name to lowerCamelCase to keep consistent with this file) + * + * Returns: + * command name portion of execFullName + */ +static char * +getExecBasename(char *execFullname) +{ + char *lastSlash, *execBasename; + + /* guard against '/' at end of command invocation */ + for (;;) { + lastSlash = strrchr(execFullname, '/'); + if (lastSlash == NULL) { + execBasename = execFullname; + break; + } else { + execBasename = lastSlash + 1; + if (*execBasename == '\0') { + *lastSlash = '\0'; + continue; + } + break; + } + } + return (execBasename); +} + +int +main(int argc, char *argv[]) +{ + synTables_t synTables; + char versionString[VERSION_STRING_MAX_LEN]; + int ret; + int funcRet; + void *subcommandArgs = NULL; + + (void) setlocale(LC_ALL, ""); + /* set global command name */ + cmdName = getExecBasename(argv[0]); + + (void) snprintf(versionString, VERSION_STRING_MAX_LEN, "%s.%s", + VERSION_STRING_MAJOR, VERSION_STRING_MINOR); + synTables.versionString = versionString; + synTables.longOptionTbl = &longOptions[0]; + synTables.subCommandPropsTbl = &subcommands[0]; + + ret = cmdParse(argc, argv, synTables, subcommandArgs, &funcRet); + if (ret != 0) { + return (ret); + } + + return (funcRet); +} /* end main */ 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"); +} diff --git a/usr/src/common/cmdparse/cmdparse.c b/usr/src/common/cmdparse/cmdparse.c new file mode 100644 index 0000000..7a9e575 --- /dev/null +++ b/usr/src/common/cmdparse/cmdparse.c @@ -0,0 +1,681 @@ +/* + * 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 <stdlib.h> +#include <stdio.h> +#include <sys/types.h> +#include <unistd.h> +#include <libintl.h> +#include <errno.h> +#include <string.h> +#include <assert.h> +#include <getopt.h> +#include <cmdparse.h> + + +/* Usage types */ +#define GENERAL_USAGE 1 +#define DETAIL_USAGE 2 + +/* printable ascii character set len */ +#define MAXOPTIONS (uint_t)('~' - '!' + 1) + +/* + * MAXOPTIONSTRING is the max length of the options string used in getopt and + * will be the printable character set + ':' for each character, + * providing for options with arguments. e.g. "t:Cs:hglr:" + */ +#define MAXOPTIONSTRING MAXOPTIONS * 2 + +/* standard command options table to support -?, -V */ +struct option standardCmdOptions[] = { + {"help", no_argument, NULL, '?'}, + {"version", no_argument, NULL, 'V'}, + {NULL, 0, NULL, 0} +}; + +/* standard subcommand options table to support -? */ +struct option standardSubCmdOptions[] = { + {"help", no_argument, NULL, '?'}, + {NULL, 0, NULL, 0} +}; + +/* forward declarations */ +static int getSubcommandProps(char *, subCommandProps_t **); +static char *getExecBasename(char *); +static void usage(uint_t); +static void subUsage(uint_t, subCommandProps_t *); +static char *getLongOption(int); +static char *getOptionArgDesc(int); + +/* global data */ +static struct option *_longOptions; +static subCommandProps_t *_subCommandProps; +static optionTbl_t *_clientOptionTbl; +static char *commandName; + + +/* + * input: + * subCommand - subcommand value + * output: + * subCommandProps - pointer to subCommandProps_t structure allocated by caller + * + * On successful return, subCommandProps contains the properties for the value + * in subCommand. On failure, the contents of subCommandProps is unspecified. + * + * Returns: + * zero on success + * non-zero on failure + * + */ +static int +getSubcommandProps(char *subCommand, subCommandProps_t **subCommandProps) +{ + subCommandProps_t *sp; + int len; + + for (sp = _subCommandProps; sp->name; sp++) { + len = strlen(subCommand); + if (len == strlen(sp->name) && + strncasecmp(subCommand, sp->name, len) == 0) { + *subCommandProps = sp; + return (0); + } + } + return (1); +} + +/* + * input: + * shortOption - short option character for which to return the + * associated long option string + * + * Returns: + * on success, long option name + * on failure, NULL + */ +static char * +getLongOption(int shortOption) +{ + struct option *op; + for (op = _longOptions; op->name; op++) { + if (shortOption == op->val) { + return (op->name); + } + } + return (NULL); +} + +/* + * input + * shortOption - short option character for which to return the + * option argument + * Returns: + * on success, argument string + * on failure, NULL + */ +static char * +getOptionArgDesc(int shortOption) +{ + optionTbl_t *op; + for (op = _clientOptionTbl; op->name; op++) { + if (op->val == shortOption && + op->has_arg == required_argument) { + return (op->argDesc); + } + } + return (NULL); +} + + +/* + * Print usage for a subcommand. + * + * input: + * usage type - GENERAL_USAGE, DETAIL_USAGE + * subcommand - pointer to subCommandProps_t structure + * + * Returns: + * none + * + */ +static void +subUsage(uint_t usageType, subCommandProps_t *subcommand) +{ + int i; + char *optionArgDesc; + char *longOpt; + + if (usageType == GENERAL_USAGE) { + (void) printf("%s:\t%s %s [", gettext("Usage"), commandName, + subcommand->name); + for (i = 0; standardSubCmdOptions[i].name; i++) { + (void) printf("-%c", standardSubCmdOptions[i].val); + if (standardSubCmdOptions[i+1].name) + (void) printf(","); + } + (void) fprintf(stdout, "]\n"); + return; + } + + /* print subcommand usage */ + (void) printf("\n%s:\t%s %s ", gettext("Usage"), commandName, + subcommand->name); + + /* print options if applicable */ + if (subcommand->optionString != NULL) { + if (subcommand->required) { + (void) printf("%s", gettext("<")); + } else { + (void) printf("%s", gettext("[")); + } + (void) printf("%s", gettext("OPTIONS")); + if (subcommand->required) { + (void) printf("%s ", gettext(">")); + } else { + (void) printf("%s ", gettext("]")); + } + } + + /* print operand requirements */ + if (!(subcommand->operand & OPERAND_NONE) && + !(subcommand->operand & OPERAND_MANDATORY)) { + (void) printf(gettext("[")); + } + + if (subcommand->operand & OPERAND_MANDATORY) { + (void) printf(gettext("<")); + } + + if (!(subcommand->operand & OPERAND_NONE)) { + assert(subcommand->operandDefinition); + (void) printf("%s", subcommand->operandDefinition); + } + + if (subcommand->operand & OPERAND_MULTIPLE) { + (void) printf(gettext(" ...")); + } + + if (subcommand->operand & OPERAND_MANDATORY) { + (void) printf(gettext(">")); + } + + if (!(subcommand->operand & OPERAND_NONE) && + !(subcommand->operand & OPERAND_MANDATORY)) { + (void) printf(gettext("]")); + } + + /* print options for subcommand */ + if (subcommand->optionString != NULL) { + (void) printf("\n\t%s:", gettext("OPTIONS")); + for (i = 0; i < strlen(subcommand->optionString); i++) { + assert((longOpt = getLongOption( + subcommand->optionString[i])) != NULL); + (void) printf("\n\t\t-%c, --%s ", + subcommand->optionString[i], + longOpt); + optionArgDesc = + getOptionArgDesc(subcommand->optionString[i]); + if (optionArgDesc != NULL) { + (void) printf("<%s>", optionArgDesc); + } + if (subcommand->exclusive && + strchr(subcommand->exclusive, + subcommand->optionString[i])) { + (void) printf(" (%s)", gettext("exclusive")); + } + } + } + (void) fprintf(stdout, "\n"); + if (subcommand->helpText) { + (void) printf("%s\n", subcommand->helpText); + } +} + +/* + * input: + * type of usage statement to print + * + * Returns: + * return value of subUsage + */ +static void +usage(uint_t usageType) +{ + int i; + subCommandProps_t *sp; + + /* print general command usage */ + (void) printf("%s:\t%s ", gettext("Usage"), commandName); + + for (i = 0; standardCmdOptions[i].name; i++) { + (void) printf("-%c", standardCmdOptions[i].val); + if (standardCmdOptions[i+1].name) + (void) printf(","); + } + + if (usageType == GENERAL_USAGE) { + for (i = 0; standardSubCmdOptions[i].name; i++) { + (void) printf(",--%s", standardSubCmdOptions[i].name); + if (standardSubCmdOptions[i+1].name) + (void) printf(","); + } + } + + (void) fprintf(stdout, "\n"); + + + /* print all subcommand usage */ + for (sp = _subCommandProps; sp->name; sp++) { + subUsage(usageType, sp); + } +} + +/* + * input: + * execFullName - exec name of program (argv[0]) + * + * Returns: + * command name portion of execFullName + */ +static char * +getExecBasename(char *execFullname) +{ + char *lastSlash, *execBasename; + + /* guard against '/' at end of command invocation */ + for (;;) { + lastSlash = strrchr(execFullname, '/'); + if (lastSlash == NULL) { + execBasename = execFullname; + break; + } else { + execBasename = lastSlash + 1; + if (*execBasename == '\0') { + *lastSlash = '\0'; + continue; + } + break; + } + } + return (execBasename); +} + +/* + * cmdParse is a parser that checks syntax of the input command against + * various rules tables. + * + * It provides usage feedback based upon the passed rules tables by calling + * two usage functions, usage, subUsage + * + * When syntax is successfully validated, the associated function is called + * using the subcommands table functions. + * + * Syntax is as follows: + * command subcommand [<options>] [<operand>] + * + * There are two standard short and long options assumed: + * -?, --help Provides usage on a command or subcommand + * and stops further processing of the arguments + * + * -V, --version Provides version information on the command + * and stops further processing of the arguments + * + * These options are loaded by this function. + * + * input: + * argc, argv from main + * syntax rules tables (synTables_t structure) + * callArgs - void * passed by caller to be passed to subcommand function + * + * output: + * funcRet - pointer to int that holds subcommand function return value + * + * Returns: + * + * zero on successful syntax parse and function call + * + * 1 on unsuccessful syntax parse (no function has been called) + * This could be due to a version or help call or simply a + * general usage call. + * + * -1 check errno, call failed + * + * This module is not MT-safe. + * + */ +int +cmdParse(int argc, char *argv[], synTables_t synTable, void *callArgs, + int *funcRet) +{ + int getoptargc; + char **getoptargv; + int opt; + int operInd; + int i, j; + int len; + int requiredOptionCnt = 0, requiredOptionEntered = 0; + char *availOptions; + char *versionString; + char optionStringAll[MAXOPTIONSTRING + 1]; + subCommandProps_t *subcommand; + cmdOptions_t cmdOptions[MAXOPTIONS + 1]; + optionTbl_t *optionTbl; + struct option *lp; + struct option intLongOpt[MAXOPTIONS + 1]; + + /* + * Check for NULLs on mandatory input arguments + * + * Note: longOptionTbl can be NULL in the case + * where there is no caller defined options + * + */ + assert(synTable.versionString); + assert(synTable.subCommandPropsTbl); + assert(funcRet); + + versionString = synTable.versionString; + + /* set global command name */ + commandName = getExecBasename(argv[0]); + + /* Set unbuffered output */ + setbuf(stdout, NULL); + + /* load globals */ + _subCommandProps = synTable.subCommandPropsTbl; + _clientOptionTbl = synTable.longOptionTbl; + + /* There must be at least two arguments */ + if (argc < 2) { + usage(GENERAL_USAGE); + return (1); + } + + (void) memset(&intLongOpt[0], 0, sizeof (intLongOpt)); + + /* + * load standard subcommand options to internal long options table + * Two separate getopt_long(3C) tables are used. + */ + for (i = 0; standardSubCmdOptions[i].name; i++) { + intLongOpt[i].name = standardSubCmdOptions[i].name; + intLongOpt[i].has_arg = standardSubCmdOptions[i].has_arg; + intLongOpt[i].flag = standardSubCmdOptions[i].flag; + intLongOpt[i].val = standardSubCmdOptions[i].val; + } + + /* + * copy caller's long options into internal long options table + * We do this for two reasons: + * 1) We need to use the getopt_long option structure internally + * 2) We need to prepend the table with the standard option + * for all subcommands (currently -?) + */ + for (optionTbl = synTable.longOptionTbl; + optionTbl && optionTbl->name; optionTbl++, i++) { + if (i > MAXOPTIONS - 1) { + /* option table too long */ + assert(0); + } + intLongOpt[i].name = optionTbl->name; + intLongOpt[i].has_arg = optionTbl->has_arg; + intLongOpt[i].flag = NULL; + intLongOpt[i].val = optionTbl->val; + } + + /* set option table global */ + _longOptions = &intLongOpt[0]; + + + /* + * Check for help/version request immediately following command + * '+' in option string ensures POSIX compliance in getopt_long() + * which means that processing will stop at first non-option + * argument. + */ + while ((opt = getopt_long(argc, argv, "+?V", standardCmdOptions, + NULL)) != EOF) { + switch (opt) { + case '?': + /* + * getopt can return a '?' when no + * option letters match string. Check for + * the 'real' '?' in optopt. + */ + if (optopt == '?') { + usage(DETAIL_USAGE); + exit(0); + } else { + usage(GENERAL_USAGE); + return (1); + } + break; + case 'V': + (void) fprintf(stdout, "%s: %s %s\n", + commandName, gettext("Version"), + versionString); + exit(0); + break; + default: + break; + } + } + + /* + * subcommand is always in the second argument. If there is no + * recognized subcommand in the second argument, print error, + * general usage and then return. + */ + if (getSubcommandProps(argv[1], &subcommand) != 0) { + (void) printf("%s: %s\n", commandName, + gettext("invalid subcommand")); + usage(GENERAL_USAGE); + return (1); + } + + getoptargv = argv; + getoptargv++; + getoptargc = argc; + getoptargc -= 1; + + (void) memset(optionStringAll, 0, sizeof (optionStringAll)); + (void) memset(&cmdOptions[0], 0, sizeof (cmdOptions)); + + j = 0; + /* + * Build optionStringAll from long options table + */ + for (lp = _longOptions; lp->name; lp++, j++) { + /* sanity check on string length */ + if (j + 1 >= sizeof (optionStringAll)) { + /* option table too long */ + assert(0); + } + optionStringAll[j] = lp->val; + if (lp->has_arg == required_argument) { + optionStringAll[++j] = ':'; + } + } + + i = 0; + /* + * Run getopt for all arguments against all possible options + * Store all options/option arguments in an array for retrieval + * later. + * + * Once all options are retrieved, a validity check against + * subcommand table is performed. + */ + while ((opt = getopt_long(getoptargc, getoptargv, optionStringAll, + _longOptions, NULL)) != EOF) { + switch (opt) { + case '?': + subUsage(DETAIL_USAGE, subcommand); + /* + * getopt can return a '?' when no + * option letters match string. Check for + * the 'real' '?' in optopt. + */ + if (optopt == '?') { + exit(0); + } else { + exit(1); + } + default: + cmdOptions[i].optval = opt; + if (optarg) { + len = strlen(optarg); + if (len > sizeof (cmdOptions[i].optarg) + - 1) { + (void) printf("%s: %s\n", + commandName, + gettext("option too long")); + errno = EINVAL; + return (-1); + } + (void) strncpy(cmdOptions[i].optarg, + optarg, len); + } + i++; + break; + } + } + + /* + * increment past last option + */ + operInd = optind + 1; + + /* + * Check validity of given options, if any were given + */ + + /* get option string for this subcommand */ + availOptions = subcommand->optionString; + + /* Get count of required options */ + if (subcommand->required) { + requiredOptionCnt = strlen(subcommand->required); + } + + if (cmdOptions[0].optval != 0) { /* options were input */ + if (availOptions == NULL) { /* no options permitted */ + (void) printf("%s: %s\n", commandName, + gettext("no options permitted")); + subUsage(DETAIL_USAGE, subcommand); + return (1); + } + for (i = 0; cmdOptions[i].optval; i++) { + /* is the option in the available option string? */ + if (!(strchr(availOptions, cmdOptions[i].optval))) { + (void) printf("%s: '-%c': %s\n", commandName, + cmdOptions[i].optval, + gettext("invalid option")); + subUsage(DETAIL_USAGE, subcommand); + return (1); + /* increment required options entered */ + } else if (subcommand->required && + (strchr(subcommand->required, + cmdOptions[i].optval))) { + requiredOptionEntered++; + /* Check for exclusive options */ + } else if (cmdOptions[1].optval != 0 && + subcommand->exclusive && + strchr(subcommand->exclusive, + cmdOptions[i].optval)) { + (void) printf("%s: '-%c': %s\n", + commandName, cmdOptions[i].optval, + gettext("is an exclusive option")); + subUsage(DETAIL_USAGE, subcommand); + return (1); + } + } + } else { /* no options were input */ + if (availOptions != NULL && subcommand->required) { + (void) printf("%s: %s\n", commandName, + gettext("at least one option required")); + subUsage(DETAIL_USAGE, subcommand); + return (1); + } + } + + /* Were all required options entered? */ + if (requiredOptionEntered != requiredOptionCnt) { + (void) printf("%s: %s: %s\n", commandName, + gettext("Following option(s) required"), + subcommand->required); + subUsage(DETAIL_USAGE, subcommand); + return (1); + } + + + /* + * If there are no operands, + * check to see if this is okay + */ + if ((operInd == argc) && + (subcommand->operand & OPERAND_MANDATORY)) { + (void) printf("%s: %s %s\n", commandName, subcommand->name, + gettext("requires an operand")); + subUsage(DETAIL_USAGE, subcommand); + return (1); + } + + /* + * If there are more operands, + * check to see if this is okay + */ + if ((argc > operInd) && + (subcommand->operand & OPERAND_NONE)) { + (void) fprintf(stderr, "%s: %s %s\n", commandName, + subcommand->name, gettext("takes no operands")); + subUsage(DETAIL_USAGE, subcommand); + return (1); + } + + /* + * If there is more than one more operand, + * check to see if this is okay + */ + if ((argc > operInd) && ((argc - operInd) != 1) && + (subcommand->operand & OPERAND_SINGLE)) { + (void) printf("%s: %s %s\n", commandName, + subcommand->name, gettext("accepts only a single operand")); + subUsage(DETAIL_USAGE, subcommand); + return (1); + } + + /* Finished syntax checks */ + + + /* Call appropriate function */ + *funcRet = subcommand->handler(argc - operInd, &argv[operInd], + &cmdOptions[0], callArgs); + + return (0); +} diff --git a/usr/src/common/cmdparse/cmdparse.h b/usr/src/common/cmdparse/cmdparse.h new file mode 100644 index 0000000..47dab55 --- /dev/null +++ b/usr/src/common/cmdparse/cmdparse.h @@ -0,0 +1,222 @@ +/* + * 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 2010 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _CMDPARSE_H +#define _CMDPARSE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <getopt.h> + +#define SUBCOMMAND_BASE 1 + +/* bit defines for operand macros */ +#define OPERAND_SINGLE 0x2 +#define OPERAND_MULTIPLE 0x4 +#define OPERAND_MANDATORY 0x8 +#define OPERAND_OPTIONAL 0x10 + +/* maximum length of an option argument */ +#define MAXOPTARGLEN 512 + + +/* Following are used to express operand requirements */ +#define OPERAND_NONE 0x1 +#define OPERAND_MANDATORY_SINGLE (OPERAND_MANDATORY | OPERAND_SINGLE) +#define OPERAND_OPTIONAL_SINGLE (OPERAND_OPTIONAL | OPERAND_SINGLE) +#define OPERAND_MANDATORY_MULTIPLE (OPERAND_MANDATORY | OPERAND_MULTIPLE) +#define OPERAND_OPTIONAL_MULTIPLE (OPERAND_OPTIONAL | OPERAND_MULTIPLE) + +/* subcommands must have a single bit on and must have exclusive values */ +#define SUBCOMMAND(x) (SUBCOMMAND_BASE << x) + +/* + * This structure is passed into the caller's callback function and + * will contain a list of all options entered and their associated + * option arguments if applicable + */ +typedef struct _cmdOptions { + int optval; + char optarg[MAXOPTARGLEN + 1]; +} cmdOptions_t; + +/* + * subcommand callback function + * + * argc - number of arguments in argv + * argv - operand arguments + * options - options entered on command line + * callData - pointer to caller data to be passed to subcommand function + */ +typedef int (*handler_t)(int argc, char *argv[], cmdOptions_t *options, + void *callData); + +/* + * list of subcommands and associated properties + * + * name -> subcommand name + * handler -> function to call on successful syntax check + * optionString -> short options that are valid + * required -> Does it require at least one option? + * exclusive -> short options that are required to be exclusively entered + * operand -> Type of operand input. Can be: + * + * NO_OPERAND + * OPERAND_MANDATORY_SINGLE + * OPERAND_OPTIONAL_SINGLE + * OPERAND_MANDATORY_MULTIPLE + * OPERAND_OPTIONAL_MULTIPLE + * + * operandDefinition -> char * definition of the operand + * + * The long options table specifies whether an option argument is required. + * + * + * EXAMPLE: + * + * Based on "list-target" entry below: + * + * "list-target" is expected as the subcommand input + * listTarget is the function to be called on success + * "list-target" accepts -i, -s, -t and -l + * "list-target" requires the option 'i'. + * "list-target" has no exclusive options + * "list-target" may have one or more operands + * "list-target" operand description is "target-name" + * + * + * optionRules_t optionRules[] = { + * {"list-target", listTarget, "istl", "i", NULL, + * OPERAND_OPTIONAL_MULTIPLE, "target-name"}, + * {"modify-target", modifyTarget, "t", "t", NULL, + * OPERAND_MANDATORY_MULTIPLE, "target-name"}, + * {"enable", enable, NULL, NULL, NULL, NO_OPERAND, NULL}, + * {NULL, 0, 0, NULL, 0, NULL} + * }; + */ +typedef struct _subCommandProps { + char *name; + handler_t handler; + char *optionString; + char *required; + char *exclusive; + int operand; + char *operandDefinition; + char *helpText; + uint8_t reserved[60]; +} subCommandProps_t; + + + +#define required_arg required_argument +#define no_arg no_argument + +/* + * Add short options and long options here + * + * name -> long option name + * has_arg -> required_arg, no_arg + * val -> short option character + * argDesc -> description of option argument + * + * Note: This structure may not be used if your CLI has no + * options. However, -?, --help and -V, --version will still be supported + * as they are standard for every CLI. + * + * EXAMPLE: + * + * optionTbl_t options[] = { + * {"filename", arg_required, 'f', "out-filename"}, + * {NULL, 0, 0} + * }; + * + */ +typedef struct _optionTbl { + char *name; + int has_arg; + int val; + char *argDesc; +} optionTbl_t; + +/* + * After tables are set, assign them to this structure + * for passing into cmdparse() + */ +typedef struct _synTables { + char *versionString; + optionTbl_t *longOptionTbl; + subCommandProps_t *subCommandPropsTbl; +} synTables_t; + +/* + * cmdParse is a parser that checks syntax of the input command against + * rules and property tables. + * + * When syntax is successfully validated, the function associated with the + * subcommand is called using the subcommands table functions. + * + * Syntax for the command is as follows: + * + * command [options] subcommand [<options>] [<operand ...>] + * + * + * There are two standard short and long options assumed: + * -?, --help Provides usage on a command or subcommand + * and stops further processing of the arguments + * + * -V, --version Provides version information on the command + * and stops further processing of the arguments + * + * These options are loaded by this function. + * + * input: + * argc, argv from main + * syntax rules tables (synTables_t structure) + * callArgs - void * passed by caller to be passed to subcommand function + * + * output: + * funcRet - pointer to int that holds subcommand function return value + * + * Returns: + * + * zero on successful syntax parse and function call + * + * 1 on unsuccessful syntax parse (no function has been called) + * This could be due to a version or help call or simply a + * general usage call. + * + * -1 check errno, call failed + * + */ +int cmdParse(int numOperands, char *operands[], synTables_t synTables, + void *callerArgs, int *funcRet); + +#ifdef __cplusplus +} +#endif + +#endif /* _CMDPARSE_H */ diff --git a/usr/src/common/iscsi/base64.c b/usr/src/common/iscsi/base64.c new file mode 100644 index 0000000..f4f3364 --- /dev/null +++ b/usr/src/common/iscsi/base64.c @@ -0,0 +1,255 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include <sys/types.h> +#ifdef _KERNEL +#include <sys/sunddi.h> +#include <sys/errno.h> +#else +#include <string.h> +#include <errno.h> +#endif /* _KERNEL */ + +/* + * base64 decoding table (from uudecode.c) + */ +/* BEGIN CSTYLED */ + static char base64_decode_tab[] = { + '\377', '\377', '\377', '\377', '\377', '\377', '\377', '\377', + '\377', '\377', '\377', '\377', '\377', '\377', '\377', '\377', + '\377', '\377', '\377', '\377', '\377', '\377', '\377', '\377', + '\377', '\377', '\377', '\377', '\377', '\377', '\377', '\377', + '\377', '\377', '\377', '\377', '\377', '\377', '\377', '\377', + '\377', '\377', '\377', 62, '\377', '\377', '\377', 63, + 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, '\377', '\377', '\377', '\377', '\377', '\377', + '\377', 0, 1, 2, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, + 23, 24, 25, '\377', '\377', '\377', '\377', '\377', + '\377', 26, 27, 28, 29, 30, 31, 32, + 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, '\377', '\377', '\377', '\377', '\377' +}; +/* END CSTYLED */ + +/* true if the character is in the base64 encoding table */ +#define validbase64(c) (('A' <= (c) && (c) <= 'Z') || \ + ('a' <= (c) && (c) <= 'z') || \ + ('0' <= (c) && (c) <= '9') || \ + (c) == '+' || (c) == '/') + +static int +outdec64(unsigned char *out, unsigned char *chr, int num) +{ + + unsigned char char1, char2, char3, char4; + unsigned char *outptr = out; + int rc = 0; + + switch (num) { + case 0: + case 1: /* these are impossible */ + default: + break; + case 2: /* 2 base64 bytes == 1 decoded byte */ + char1 = base64_decode_tab[chr[0]] & 0xFF; + char2 = base64_decode_tab[chr[1]] & 0xFF; + *(outptr++) = ((char1 << 2) & 0xFC) | + ((char2 >> 4) & 0x03); + rc = 1; + break; + case 3: /* 3 base64 bytes == 2 decoded bytes */ + char1 = base64_decode_tab[chr[0]] & 0xFF; + char2 = base64_decode_tab[chr[1]] & 0xFF; + char3 = base64_decode_tab[chr[2]] & 0xFF; + *(outptr++) = ((char1 << 2) & 0xFC) | + ((char2 >> 4) & 0x03); + *(outptr++) = ((char2 << 4) & 0xF0) | + ((char3 >> 2) & 0x0F); + rc = 2; + break; + case 4: /* 4 base64 bytes == 3 decoded bytes */ + char1 = base64_decode_tab[chr[0]] & 0xFF; + char2 = base64_decode_tab[chr[1]] & 0xFF; + char3 = base64_decode_tab[chr[2]] & 0xFF; + char4 = base64_decode_tab[chr[3]] & 0xFF; + *(outptr++) = ((char1 << 2) & 0xFC) | + ((char2 >> 4) & 0x03); + *(outptr++) = ((char2 << 4) & 0xF0) | + ((char3 >> 2) & 0x0F); + *(outptr++) = ((char3 << 6) & 0xC0) | + (char4 & 0x3F); + rc = 3; + break; + } + return (rc); +} + +#define BUFSIZE 12 + +int +iscsi_base64_str_to_binary(char *hstr, int hstr_len, + uint8_t *binary, int binary_buf_len, int *out_len) +{ + char *iptr; + uint8_t tmp_out[BUFSIZE]; + int octets, endseen, numbase64chars; + unsigned char chr[4], curchr; + + /* + * base64 decode algorith, adapted from uudecode.c + * + * A valid base64 string is a multiple of 4 bytes in length + */ + if ((hstr_len % 4) != 0) + return (EINVAL); + + endseen = numbase64chars = 0; + *out_len = 0; + iptr = hstr; + + while (((curchr = *(iptr++)) != NULL) && + (((uintptr_t)iptr - (uintptr_t)hstr) <= hstr_len)) { + /* decode chars */ + if (curchr == '=') /* if end */ + endseen++; + + if (validbase64(curchr)) + chr[numbase64chars++] = curchr; + /* + * if we've gathered 4 base64 octets + * we need to decode and output them + */ + if (numbase64chars == 4) { + octets = outdec64(tmp_out, chr, 4); + numbase64chars = 0; + + if (*out_len + octets > binary_buf_len) + return (E2BIG); + + (void) memcpy(binary + *out_len, tmp_out, octets); + *out_len += octets; + } + + /* + * handle any remaining base64 octets at end + */ + if (endseen && numbase64chars > 0) { + octets = outdec64(tmp_out, chr, numbase64chars); + numbase64chars = 0; + if (*out_len + octets > binary_buf_len) + return (E2BIG); + + (void) memcpy(binary + *out_len, tmp_out, octets); + *out_len += octets; + } + } + + return (0); +} + + +static char base64_encode_tab[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz0123456789+/"; + +#define ENC(c) base64_encode_tab[(c) & 0x3f] + +#define BASE64_BUF_HAS_ROOM(bytes_needed) \ + ((optr + (bytes_needed)) <= base64_str_buf + base64_buf_len) + +int +iscsi_binary_to_base64_str(uint8_t *in_buf, int in_buf_len, + char *base64_str_buf, int base64_buf_len) +{ + uint8_t *iptr; + char *optr; + int in_bytes_remaining; + + /* base64 encode algorith, adapted from uuencode.c */ + iptr = in_buf; + optr = base64_str_buf; + + /* + * read must be a multiple of 3 bytes for + * this algorithm to work, and also must + * be small enough that read_size * (4/3) + * will always be 76 bytes or less, since + * base64 lines can be no longer than that + */ + while (iptr + 3 <= in_buf + in_buf_len) { + if (!BASE64_BUF_HAS_ROOM(4)) + return (E2BIG); + + *(optr++) = ENC(*iptr >> 2); + *(optr++) = ENC((*iptr << 4) & 060 | + (*(iptr + 1) >> 4) & 017); + *(optr++) = ENC((*(iptr + 1) << 2) + & 074 | (*(iptr + 2) >> 6) & 03); + *(optr++) = ENC(*(iptr + 2) & 077); + + iptr += 3; + } + + /* need output padding ? */ + in_bytes_remaining = ((uintptr_t)in_buf + in_buf_len) - (uintptr_t)iptr; + /* ASSERT(in_bytes_remaining < 3); */ + switch (in_bytes_remaining) { + case 0: + /* no-op - 24 bits of data encoded */ + if (!BASE64_BUF_HAS_ROOM(1)) + return (E2BIG); + *(optr++) = '\0'; + break; + case 1: + /* 8 bits encoded - pad with 2 '=' */ + if (!BASE64_BUF_HAS_ROOM(5)) + return (E2BIG); + *(optr++) = ENC((*iptr & 0xFC) >> 2); + *(optr++) = ENC((*iptr & 0x03) << 4); + *(optr++) = '='; + *(optr++) = '='; + *(optr++) = '\0'; + break; + case 2: + /* 16 bits encoded - pad with 1 '=' */ + if (!BASE64_BUF_HAS_ROOM(5)) + return (E2BIG); + *(optr++) = ENC((*iptr & 0xFC) >> 2); + *(optr++) = ENC(((*iptr & 0x03) << 4) | + ((*(iptr + 1) & 0xF0) >> 4)); + *(optr++) = ENC((*(iptr + 1) & 0x0F) << 2); + *(optr++) = '='; + *(optr++) = '\0'; + break; + default: + /* impossible */ + break; + } + + return (0); +} diff --git a/usr/src/common/iscsit/iscsit_common.c b/usr/src/common/iscsit/iscsit_common.c new file mode 100644 index 0000000..7b2b18e --- /dev/null +++ b/usr/src/common/iscsit/iscsit_common.c @@ -0,0 +1,1588 @@ +/* + * 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 <sys/time.h> + +#if defined(_KERNEL) +#include <sys/ddi.h> +#include <sys/types.h> +#include <sys/sunddi.h> +#include <sys/socket.h> +#include <inet/tcp.h> +#else +#include <stdio.h> +#include <strings.h> +#include <stdlib.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#endif + +#include <sys/iscsit/iscsit_common.h> +#include <sys/iscsi_protocol.h> +#include <sys/iscsit/isns_protocol.h> + +void * +iscsit_zalloc(size_t size) +{ +#if defined(_KERNEL) + return (kmem_zalloc(size, KM_SLEEP)); +#else + return (calloc(1, size)); +#endif +} + +void +iscsit_free(void *buf, size_t size) /* ARGSUSED */ +{ +#if defined(_KERNEL) + kmem_free(buf, size); +#else + free(buf); +#endif +} + +/* + * default_port should be the port to be used, if not specified + * as part of the supplied string 'arg'. + */ + +#define NI_MAXHOST 1025 +#define NI_MAXSERV 32 + + +struct sockaddr_storage * +it_common_convert_sa(char *arg, struct sockaddr_storage *buf, + uint32_t default_port) +{ + /* Why does addrbuf need to be this big!??! XXX */ + char addrbuf[NI_MAXHOST + NI_MAXSERV + 1]; + char *addr_str; + char *port_str; +#ifndef _KERNEL + char *errchr; +#endif + long tmp_port = 0; + sa_family_t af; + + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; + struct sockaddr_storage *sa = buf; + + if (!arg || !buf) { + return (NULL); + } + + bzero(buf, sizeof (struct sockaddr_storage)); + + /* don't modify the passed-in string */ + (void) strlcpy(addrbuf, arg, sizeof (addrbuf)); + + addr_str = addrbuf; + + if (*addr_str == '[') { + /* + * An IPv6 address must be inside square brackets + */ + port_str = strchr(addr_str, ']'); + if (!port_str) { + /* No closing bracket */ + return (NULL); + } + + /* strip off the square brackets so we can convert */ + addr_str++; + *port_str = '\0'; + port_str++; + + if (*port_str == ':') { + /* TCP port to follow */ + port_str++; + } else if (*port_str == '\0') { + /* No port specified */ + port_str = NULL; + } else { + /* malformed */ + return (NULL); + } + af = AF_INET6; + } else { + port_str = strchr(addr_str, ':'); + if (port_str) { + *port_str = '\0'; + port_str++; + } + af = AF_INET; + } + + if (port_str) { +#if defined(_KERNEL) + if (ddi_strtol(port_str, NULL, 10, &tmp_port) != 0) { + return (NULL); + } +#else + tmp_port = strtol(port_str, &errchr, 10); +#endif + if (tmp_port < 0 || tmp_port > 65535) { + return (NULL); + } + } else { + tmp_port = default_port; + } + + sa->ss_family = af; + + sin = (struct sockaddr_in *)sa; + if (af == AF_INET) { + if (inet_pton(af, addr_str, + (void *)&(sin->sin_addr.s_addr)) != 1) { + return (NULL); + } + /* + * intet_pton does not seem to convert to network + * order in kernel. This is a workaround until the + * inet_pton works or we have our own inet_pton function. + */ +#ifdef _KERNEL + sin->sin_addr.s_addr = ntohl((uint32_t)sin->sin_addr.s_addr); +#endif + sin->sin_port = htons(tmp_port); + } else { + sin6 = (struct sockaddr_in6 *)sa; + if (inet_pton(af, addr_str, + (void *)&(sin6->sin6_addr.s6_addr)) != 1) { + return (NULL); + } + sin6->sin6_port = htons(tmp_port); + } + + /* successful */ + return (sa); +} + + +/* Functions to convert iSCSI target structures to/from nvlists. */ + +#ifndef _KERNEL +int +it_config_to_nv(it_config_t *cfg, nvlist_t **nvl) +{ + int ret; + nvlist_t *nv; + nvlist_t *lnv = NULL; + + if (!nvl) { + return (EINVAL); + } + + *nvl = NULL; + + ret = nvlist_alloc(&nv, NV_UNIQUE_NAME_TYPE, 0); + if (ret != 0) { + return (ret); + } + + /* if there's no config, store an empty list */ + if (!cfg) { + *nvl = nv; + return (0); + } + + ret = nvlist_add_uint32(nv, "cfgVersion", cfg->config_version); + if (ret == 0) { + ret = it_tgtlist_to_nv(cfg->config_tgt_list, &lnv); + } + + if ((ret == 0) && (lnv != NULL)) { + ret = nvlist_add_nvlist(nv, "targetList", lnv); + nvlist_free(lnv); + lnv = NULL; + } + + if (ret == 0) { + ret = it_tpglist_to_nv(cfg->config_tpg_list, &lnv); + } + + if ((ret == 0) && (lnv != NULL)) { + ret = nvlist_add_nvlist(nv, "tpgList", lnv); + nvlist_free(lnv); + lnv = NULL; + } + + if (ret == 0) { + ret = it_inilist_to_nv(cfg->config_ini_list, &lnv); + } + + if ((ret == 0) && (lnv != NULL)) { + ret = nvlist_add_nvlist(nv, "iniList", lnv); + nvlist_free(lnv); + lnv = NULL; + } + + if (ret == 0) { + ret = nvlist_add_nvlist(nv, "globalProperties", + cfg->config_global_properties); + } + + if (ret == 0) { + *nvl = nv; + } else { + nvlist_free(nv); + } + + return (ret); +} +#endif /* !_KERNEL */ + +/* + * nvlist version of config is 3 list-of-list, + 1 proplist. arrays + * are interesting, but lists-of-lists are more useful when doing + * individual lookups when we later add support for it. Also, no + * need to store name in individual struct representation. + */ +int +it_nv_to_config(nvlist_t *nvl, it_config_t **cfg) +{ + int ret; + uint32_t intval; + nvlist_t *listval; + it_config_t *tmpcfg; + + if (!cfg) { + return (EINVAL); + } + + /* initialize output */ + *cfg = NULL; + + tmpcfg = iscsit_zalloc(sizeof (it_config_t)); + if (tmpcfg == NULL) { + return (ENOMEM); + } + + if (!nvl) { + /* nothing to decode, but return the empty cfg struct */ + ret = nvlist_alloc(&tmpcfg->config_global_properties, + NV_UNIQUE_NAME, 0); + if (ret != 0) { + iscsit_free(tmpcfg, sizeof (it_config_t)); + return (ret); + } + *cfg = tmpcfg; + return (0); + } + + ret = nvlist_lookup_uint32(nvl, "cfgVersion", &intval); + if (ret != 0) { + iscsit_free(tmpcfg, sizeof (it_config_t)); + return (ret); + } + + tmpcfg->config_version = intval; + + ret = nvlist_lookup_nvlist(nvl, "targetList", &listval); + if (ret == 0) { + /* decode list of it_tgt_t */ + ret = it_nv_to_tgtlist(listval, &(tmpcfg->config_tgt_count), + &(tmpcfg->config_tgt_list)); + } + + ret = nvlist_lookup_nvlist(nvl, "tpgList", &listval); + if (ret == 0) { + /* decode list of it_tpg_t */ + ret = it_nv_to_tpglist(listval, &(tmpcfg->config_tpg_count), + &(tmpcfg->config_tpg_list)); + } + + ret = nvlist_lookup_nvlist(nvl, "iniList", &listval); + if (ret == 0) { + /* decode list of initiators */ + ret = it_nv_to_inilist(listval, &(tmpcfg->config_ini_count), + &(tmpcfg->config_ini_list)); + } + + ret = nvlist_lookup_nvlist(nvl, "globalProperties", &listval); + if (ret == 0) { + /* + * don't depend on the original nvlist staying in-scope, + * duplicate the nvlist + */ + ret = nvlist_dup(listval, &(tmpcfg->config_global_properties), + 0); + } else if (ret == ENOENT) { + /* + * No global properties defined, make an empty list + */ + ret = nvlist_alloc(&tmpcfg->config_global_properties, + NV_UNIQUE_NAME, 0); + } + + if (ret == 0) { + char **isnsArray = NULL; + uint32_t numisns = 0; + + /* + * decode the list of iSNS server information to make + * references from the kernel simpler. + */ + if (tmpcfg->config_global_properties) { + ret = nvlist_lookup_string_array( + tmpcfg->config_global_properties, + PROP_ISNS_SERVER, + &isnsArray, &numisns); + if (ret == 0) { + ret = it_array_to_portallist(isnsArray, + numisns, ISNS_DEFAULT_SERVER_PORT, + &tmpcfg->config_isns_svr_list, + &tmpcfg->config_isns_svr_count); + } else if (ret == ENOENT) { + /* It's OK if we don't have any iSNS servers */ + ret = 0; + } + } + } + + if (ret == 0) { + *cfg = tmpcfg; + } else { + it_config_free_cmn(tmpcfg); + } + + return (ret); +} + +it_tgt_t * +it_tgt_lookup(it_config_t *cfg, char *tgt_name) +{ + it_tgt_t *cfg_tgt = NULL; + + for (cfg_tgt = cfg->config_tgt_list; + cfg_tgt != NULL; + cfg_tgt = cfg_tgt->tgt_next) { + if (strncmp(cfg_tgt->tgt_name, tgt_name, + MAX_ISCSI_NODENAMELEN) == 0) { + return (cfg_tgt); + } + } + + return (NULL); +} + +int +it_nv_to_tgtlist(nvlist_t *nvl, uint32_t *count, it_tgt_t **tgtlist) +{ + int ret = 0; + it_tgt_t *tgt; + it_tgt_t *prev = NULL; + nvpair_t *nvp = NULL; + nvlist_t *nvt; + char *name; + + if (!tgtlist || !count) { + return (EINVAL); + } + + *tgtlist = NULL; + *count = 0; + + if (!nvl) { + /* nothing to do */ + return (0); + } + + while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { + name = nvpair_name(nvp); + + ret = nvpair_value_nvlist(nvp, &nvt); + if (ret != 0) { + /* invalid entry? */ + continue; + } + + ret = it_nv_to_tgt(nvt, name, &tgt); + if (ret != 0) { + break; + } + + (*count)++; + + if (*tgtlist == NULL) { + *tgtlist = tgt; + } else { + prev->tgt_next = tgt; + } + prev = tgt; + } + + if (ret != 0) { + it_tgt_free_cmn(*tgtlist); + *tgtlist = NULL; + } + + return (ret); +} + +int +it_tgtlist_to_nv(it_tgt_t *tgtlist, nvlist_t **nvl) +{ + int ret; + it_tgt_t *tgtp = tgtlist; + nvlist_t *pnv = NULL; + nvlist_t *tnv; + + if (!nvl) { + return (EINVAL); + } + + if (!tgtlist) { + /* nothing to do */ + return (0); + } + + /* create the target list if required */ + if (*nvl == NULL) { + ret = nvlist_alloc(&pnv, NV_UNIQUE_NAME, 0); + if (ret != 0) { + return (ret); + } + *nvl = pnv; + } + + while (tgtp) { + ret = it_tgt_to_nv(tgtp, &tnv); + + if (ret != 0) { + break; + } + + ret = nvlist_add_nvlist(*nvl, tgtp->tgt_name, tnv); + + if (ret != 0) { + break; + } + + nvlist_free(tnv); + + tgtp = tgtp->tgt_next; + } + + if (ret != 0) { + if (pnv) { + nvlist_free(pnv); + *nvl = NULL; + } + } + + return (ret); +} + +int +it_tgt_to_nv(it_tgt_t *tgt, nvlist_t **nvl) +{ + int ret; + nvlist_t *tnv = NULL; + + if (!nvl) { + return (EINVAL); + } + + if (!tgt) { + /* nothing to do */ + return (0); + } + + ret = nvlist_alloc(nvl, NV_UNIQUE_NAME, 0); + if (ret != 0) { + return (ret); + } + + if (tgt->tgt_properties) { + ret = nvlist_add_nvlist(*nvl, "properties", + tgt->tgt_properties); + } + + if (ret == 0) { + ret = nvlist_add_uint64(*nvl, "generation", + tgt->tgt_generation); + } + + if (ret == 0) { + ret = it_tpgtlist_to_nv(tgt->tgt_tpgt_list, &tnv); + } + + if ((ret == 0) && tnv) { + ret = nvlist_add_nvlist(*nvl, "tpgtList", tnv); + nvlist_free(tnv); + } + + if (ret != 0) { + nvlist_free(*nvl); + *nvl = NULL; + } + + return (ret); +} + +int +it_nv_to_tgt(nvlist_t *nvl, char *name, it_tgt_t **tgt) +{ + int ret; + it_tgt_t *ttgt; + nvlist_t *listval; + uint32_t intval; + + if (!nvl || !tgt || !name) { + return (EINVAL); + } + + *tgt = NULL; + + ttgt = iscsit_zalloc(sizeof (it_tgt_t)); + if (!ttgt) { + return (ENOMEM); + } + + (void) strlcpy(ttgt->tgt_name, name, sizeof (ttgt->tgt_name)); + + ret = nvlist_lookup_nvlist(nvl, "properties", &listval); + if (ret == 0) { + /* duplicate list so it does not go out of context */ + ret = nvlist_dup(listval, &(ttgt->tgt_properties), 0); + } else if (ret == ENOENT) { + ret = 0; + } + + if (ret == 0) { + ret = nvlist_lookup_uint64(nvl, "generation", + &(ttgt->tgt_generation)); + } else if (ret == ENOENT) { + ret = 0; + } + + if (ret == 0) { + ret = nvlist_lookup_nvlist(nvl, "tpgtList", &listval); + } + + if (ret == 0) { + ret = it_nv_to_tpgtlist(listval, &intval, + &(ttgt->tgt_tpgt_list)); + ttgt->tgt_tpgt_count = intval; + } else if (ret == ENOENT) { + ret = 0; + } + + if (ret == 0) { + *tgt = ttgt; + } else { + it_tgt_free_cmn(ttgt); + } + + return (ret); +} + +int +it_tpgt_to_nv(it_tpgt_t *tpgt, nvlist_t **nvl) +{ + int ret; + + if (!nvl) { + return (EINVAL); + } + + if (!tpgt) { + /* nothing to do */ + return (0); + } + + ret = nvlist_alloc(nvl, NV_UNIQUE_NAME, 0); + if (ret != 0) { + return (ret); + } + + ret = nvlist_add_uint16(*nvl, "tag", tpgt->tpgt_tag); + if (ret == 0) { + ret = nvlist_add_uint64(*nvl, "generation", + tpgt->tpgt_generation); + } + + if (ret != 0) { + nvlist_free(*nvl); + *nvl = NULL; + } + + return (ret); +} + +int +it_nv_to_tpgt(nvlist_t *nvl, char *name, it_tpgt_t **tpgt) +{ + int ret; + it_tpgt_t *ptr; + + if (!tpgt || !name) { + return (EINVAL); + } + + *tpgt = NULL; + + if (!nvl) { + return (0); + } + + ptr = iscsit_zalloc(sizeof (it_tpgt_t)); + if (!ptr) { + return (ENOMEM); + } + + (void) strlcpy(ptr->tpgt_tpg_name, name, sizeof (ptr->tpgt_tpg_name)); + + ret = nvlist_lookup_uint16(nvl, "tag", &(ptr->tpgt_tag)); + if (ret == 0) { + ret = nvlist_lookup_uint64(nvl, "generation", + &(ptr->tpgt_generation)); + } + + if (ret == 0) { + *tpgt = ptr; + } else { + iscsit_free(ptr, sizeof (it_tpgt_t)); + } + + return (ret); +} + +int +it_tpgtlist_to_nv(it_tpgt_t *tpgtlist, nvlist_t **nvl) +{ + int ret; + nvlist_t *pnv = NULL; + nvlist_t *tnv; + it_tpgt_t *ptr = tpgtlist; + + if (!nvl) { + return (EINVAL); + } + + if (!tpgtlist) { + /* nothing to do */ + return (0); + } + + /* create the target list if required */ + if (*nvl == NULL) { + ret = nvlist_alloc(&pnv, NV_UNIQUE_NAME, 0); + if (ret != 0) { + return (ret); + } + *nvl = pnv; + } + + while (ptr) { + ret = it_tpgt_to_nv(ptr, &tnv); + + if (ret != 0) { + break; + } + + ret = nvlist_add_nvlist(*nvl, ptr->tpgt_tpg_name, tnv); + + if (ret != 0) { + break; + } + + nvlist_free(tnv); + + ptr = ptr->tpgt_next; + } + + if (ret != 0) { + if (pnv) { + nvlist_free(pnv); + *nvl = NULL; + } + } + + return (ret); +} + +int +it_nv_to_tpgtlist(nvlist_t *nvl, uint32_t *count, it_tpgt_t **tpgtlist) +{ + int ret = 0; + it_tpgt_t *tpgt; + it_tpgt_t *prev = NULL; + nvpair_t *nvp = NULL; + nvlist_t *nvt; + char *name; + + if (!tpgtlist || !count) { + return (EINVAL); + } + + *tpgtlist = NULL; + *count = 0; + + if (!nvl) { + /* nothing to do */ + return (0); + } + + while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { + name = nvpair_name(nvp); + + ret = nvpair_value_nvlist(nvp, &nvt); + if (ret != 0) { + /* invalid entry? */ + continue; + } + + ret = it_nv_to_tpgt(nvt, name, &tpgt); + if (ret != 0) { + break; + } + + (*count)++; + + if (*tpgtlist == NULL) { + *tpgtlist = tpgt; + } else { + prev->tpgt_next = tpgt; + } + + prev = tpgt; + } + + if (ret != 0) { + it_tpgt_free_cmn(*tpgtlist); + *tpgtlist = NULL; + } + + return (ret); +} + +#ifndef _KERNEL +int +it_tpg_to_nv(it_tpg_t *tpg, nvlist_t **nvl) +{ + int ret; + char **portalArray = NULL; + int i; + it_portal_t *ptr; + + if (!nvl) { + return (EINVAL); + } + + if (!tpg) { + /* nothing to do */ + return (0); + } + + ret = nvlist_alloc(nvl, NV_UNIQUE_NAME, 0); + if (ret != 0) { + return (ret); + } + + ret = nvlist_add_uint64(*nvl, "generation", tpg->tpg_generation); + + if ((ret == 0) && tpg->tpg_portal_list) { + /* add the portals */ + portalArray = iscsit_zalloc(tpg->tpg_portal_count * + sizeof (it_portal_t)); + if (portalArray == NULL) { + nvlist_free(*nvl); + *nvl = NULL; + return (ENOMEM); + } + + i = 0; + ptr = tpg->tpg_portal_list; + + while (ptr && (i < tpg->tpg_portal_count)) { + ret = sockaddr_to_str(&(ptr->portal_addr), + &(portalArray[i])); + if (ret != 0) { + break; + } + ptr = ptr->portal_next; + i++; + } + } + + if ((ret == 0) && portalArray) { + ret = nvlist_add_string_array(*nvl, "portalList", + portalArray, i); + } + + + if (portalArray) { + while (--i >= 0) { + if (portalArray[i]) { + iscsit_free(portalArray[i], + strlen(portalArray[i] + 1)); + } + } + iscsit_free(portalArray, + tpg->tpg_portal_count * sizeof (it_portal_t)); + } + + if (ret != 0) { + nvlist_free(*nvl); + *nvl = NULL; + } + + return (ret); +} +#endif /* !_KERNEL */ + +int +it_nv_to_tpg(nvlist_t *nvl, char *name, it_tpg_t **tpg) +{ + int ret; + it_tpg_t *ptpg; + char **portalArray = NULL; + uint32_t count = 0; + + if (!name || !tpg) { + return (EINVAL); + } + + *tpg = NULL; + + ptpg = iscsit_zalloc(sizeof (it_tpg_t)); + if (ptpg == NULL) { + return (ENOMEM); + } + + (void) strlcpy(ptpg->tpg_name, name, sizeof (ptpg->tpg_name)); + + ret = nvlist_lookup_uint64(nvl, "generation", + &(ptpg->tpg_generation)); + + if (ret == 0) { + ret = nvlist_lookup_string_array(nvl, "portalList", + &portalArray, &count); + } + + if (ret == 0) { + /* set the portals */ + ret = it_array_to_portallist(portalArray, count, + ISCSI_LISTEN_PORT, &ptpg->tpg_portal_list, + &ptpg->tpg_portal_count); + } else if (ret == ENOENT) { + ret = 0; + } + + if (ret == 0) { + *tpg = ptpg; + } else { + it_tpg_free_cmn(ptpg); + } + + return (ret); +} + + + + +#ifndef _KERNEL +int +it_tpglist_to_nv(it_tpg_t *tpglist, nvlist_t **nvl) +{ + int ret; + nvlist_t *pnv = NULL; + nvlist_t *tnv; + it_tpg_t *ptr = tpglist; + + if (!nvl) { + return (EINVAL); + } + + if (!tpglist) { + /* nothing to do */ + return (0); + } + + /* create the target portal group list if required */ + if (*nvl == NULL) { + ret = nvlist_alloc(&pnv, NV_UNIQUE_NAME, 0); + if (ret != 0) { + return (ret); + } + *nvl = pnv; + } + + while (ptr) { + ret = it_tpg_to_nv(ptr, &tnv); + + if (ret != 0) { + break; + } + + ret = nvlist_add_nvlist(*nvl, ptr->tpg_name, tnv); + + if (ret != 0) { + break; + } + + nvlist_free(tnv); + + ptr = ptr->tpg_next; + } + + if (ret != 0) { + if (pnv) { + nvlist_free(pnv); + *nvl = NULL; + } + } + + return (ret); +} +#endif /* !_KERNEL */ + +it_tpg_t * +it_tpg_lookup(it_config_t *cfg, char *tpg_name) +{ + it_tpg_t *cfg_tpg = NULL; + + for (cfg_tpg = cfg->config_tpg_list; + cfg_tpg != NULL; + cfg_tpg = cfg_tpg->tpg_next) { + if (strncmp(&cfg_tpg->tpg_name[0], tpg_name, + MAX_TPG_NAMELEN) == 0) { + return (cfg_tpg); + } + } + + return (NULL); +} + +int +it_sa_compare(struct sockaddr_storage *sa1, struct sockaddr_storage *sa2) +{ + struct sockaddr_in *sin1, *sin2; + struct sockaddr_in6 *sin6_1, *sin6_2; + + /* + * XXX - should we check here for IPv4 addrs mapped to v6? + * see also iscsit_is_v4_mapped in iscsit_login.c + */ + + if (sa1->ss_family != sa2->ss_family) { + return (1); + } + + /* + * sockaddr_in has padding which may not be initialized. + * be more specific in the comparison, and don't trust the + * caller has fully initialized the structure. + */ + if (sa1->ss_family == AF_INET) { + sin1 = (struct sockaddr_in *)sa1; + sin2 = (struct sockaddr_in *)sa2; + if ((bcmp(&sin1->sin_addr, &sin2->sin_addr, + sizeof (struct in_addr)) == 0) && + (sin1->sin_port == sin2->sin_port)) { + return (0); + } + } else if (sa1->ss_family == AF_INET6) { + sin6_1 = (struct sockaddr_in6 *)sa1; + sin6_2 = (struct sockaddr_in6 *)sa2; + if (bcmp(sin6_1, sin6_2, sizeof (struct sockaddr_in6)) == 0) { + return (0); + } + } + + return (1); +} + +it_portal_t * +it_portal_lookup(it_tpg_t *tpg, struct sockaddr_storage *sa) +{ + it_portal_t *cfg_portal; + + for (cfg_portal = tpg->tpg_portal_list; + cfg_portal != NULL; + cfg_portal = cfg_portal->portal_next) { + if (it_sa_compare(sa, &cfg_portal->portal_addr) == 0) + return (cfg_portal); + } + + return (NULL); +} + +it_portal_t * +it_sns_svr_lookup(it_config_t *cfg, struct sockaddr_storage *sa) +{ + it_portal_t *cfg_portal; + + for (cfg_portal = cfg->config_isns_svr_list; + cfg_portal != NULL; + cfg_portal = cfg_portal->portal_next) { + if (it_sa_compare(sa, &cfg_portal->portal_addr) == 0) + return (cfg_portal); + } + + return (NULL); +} + +int +it_nv_to_tpglist(nvlist_t *nvl, uint32_t *count, it_tpg_t **tpglist) +{ + int ret = 0; + it_tpg_t *tpg; + it_tpg_t *prev = NULL; + nvpair_t *nvp = NULL; + nvlist_t *nvt; + char *name; + + if (!tpglist || !count) { + return (EINVAL); + } + + *tpglist = NULL; + *count = 0; + + if (!nvl) { + /* nothing to do */ + return (0); + } + + while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { + name = nvpair_name(nvp); + + ret = nvpair_value_nvlist(nvp, &nvt); + if (ret != 0) { + /* invalid entry? */ + continue; + } + + ret = it_nv_to_tpg(nvt, name, &tpg); + if (ret != 0) { + break; + } + + (*count)++; + + if (*tpglist == NULL) { + *tpglist = tpg; + } else { + prev->tpg_next = tpg; + } + prev = tpg; + } + + if (ret != 0) { + it_tpg_free_cmn(*tpglist); + *tpglist = NULL; + } + + return (ret); +} + +int +it_ini_to_nv(it_ini_t *ini, nvlist_t **nvl) +{ + int ret; + + if (!nvl) { + return (EINVAL); + } + + if (!ini) { + return (0); + } + + ret = nvlist_alloc(nvl, NV_UNIQUE_NAME, 0); + if (ret != 0) { + return (ret); + } + + if (ini->ini_properties) { + ret = nvlist_add_nvlist(*nvl, "properties", + ini->ini_properties); + } + + if (ret == 0) { + ret = nvlist_add_uint64(*nvl, "generation", + ini->ini_generation); + } else if (ret == ENOENT) { + ret = 0; + } + + if (ret != 0) { + nvlist_free(*nvl); + *nvl = NULL; + } + + return (ret); +} + +int +it_nv_to_ini(nvlist_t *nvl, char *name, it_ini_t **ini) +{ + int ret; + it_ini_t *inip; + nvlist_t *listval; + + if (!name || !ini) { + return (EINVAL); + } + + *ini = NULL; + + if (!nvl) { + return (0); + } + + inip = iscsit_zalloc(sizeof (it_ini_t)); + if (!inip) { + return (ENOMEM); + } + + (void) strlcpy(inip->ini_name, name, sizeof (inip->ini_name)); + + ret = nvlist_lookup_nvlist(nvl, "properties", &listval); + if (ret == 0) { + ret = nvlist_dup(listval, &(inip->ini_properties), 0); + } else if (ret == ENOENT) { + ret = 0; + } + + if (ret == 0) { + ret = nvlist_lookup_uint64(nvl, "generation", + &(inip->ini_generation)); + } + + if (ret == 0) { + *ini = inip; + } else { + it_ini_free_cmn(inip); + } + + return (ret); +} + +int +it_inilist_to_nv(it_ini_t *inilist, nvlist_t **nvl) +{ + int ret; + nvlist_t *pnv = NULL; + nvlist_t *tnv; + it_ini_t *ptr = inilist; + + if (!nvl) { + return (EINVAL); + } + + if (!inilist) { + return (0); + } + + /* create the target list if required */ + if (*nvl == NULL) { + ret = nvlist_alloc(&pnv, NV_UNIQUE_NAME, 0); + if (ret != 0) { + return (ret); + } + *nvl = pnv; + } + + while (ptr) { + ret = it_ini_to_nv(ptr, &tnv); + + if (ret != 0) { + break; + } + + ret = nvlist_add_nvlist(*nvl, ptr->ini_name, tnv); + + if (ret != 0) { + break; + } + + nvlist_free(tnv); + + ptr = ptr->ini_next; + } + + if (ret != 0) { + if (pnv) { + nvlist_free(pnv); + *nvl = NULL; + } + } + + return (ret); +} + +int +it_nv_to_inilist(nvlist_t *nvl, uint32_t *count, it_ini_t **inilist) +{ + int ret = 0; + it_ini_t *inip; + it_ini_t *prev = NULL; + nvpair_t *nvp = NULL; + nvlist_t *nvt; + char *name; + + if (!inilist || !count) { + return (EINVAL); + } + + *inilist = NULL; + *count = 0; + + if (!nvl) { + /* nothing to do */ + return (0); + } + + while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { + name = nvpair_name(nvp); + + ret = nvpair_value_nvlist(nvp, &nvt); + if (ret != 0) { + /* invalid entry? */ + continue; + } + + ret = it_nv_to_ini(nvt, name, &inip); + if (ret != 0) { + break; + } + + (*count)++; + + if (*inilist == NULL) { + *inilist = inip; + } else { + prev->ini_next = inip; + } + prev = inip; + } + + if (ret != 0) { + it_ini_free_cmn(*inilist); + *inilist = NULL; + } + + return (ret); +} + +/* + * Convert a sockaddr to the string representation, suitable for + * storing in an nvlist or printing out in a list. + */ +#ifndef _KERNEL +int +sockaddr_to_str(struct sockaddr_storage *sa, char **addr) +{ + int ret; + char buf[INET6_ADDRSTRLEN + 7]; /* addr : port */ + char pbuf[7]; + const char *bufp; + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; + uint16_t port; + + if (!sa || !addr) { + return (EINVAL); + } + + buf[0] = '\0'; + + if (sa->ss_family == AF_INET) { + sin = (struct sockaddr_in *)sa; + bufp = inet_ntop(AF_INET, + (const void *)&(sin->sin_addr.s_addr), + buf, sizeof (buf)); + if (bufp == NULL) { + ret = errno; + return (ret); + } + port = ntohs(sin->sin_port); + } else if (sa->ss_family == AF_INET6) { + (void) strlcat(buf, "[", sizeof (buf)); + sin6 = (struct sockaddr_in6 *)sa; + bufp = inet_ntop(AF_INET6, + (const void *)&sin6->sin6_addr.s6_addr, + &buf[1], (sizeof (buf) - 1)); + if (bufp == NULL) { + ret = errno; + return (ret); + } + (void) strlcat(buf, "]", sizeof (buf)); + port = ntohs(sin6->sin6_port); + } else { + return (EINVAL); + } + + + (void) snprintf(pbuf, sizeof (pbuf), ":%u", port); + (void) strlcat(buf, pbuf, sizeof (buf)); + + *addr = strdup(buf); + if (*addr == NULL) { + return (ENOMEM); + } + + return (0); +} +#endif /* !_KERNEL */ + +int +it_array_to_portallist(char **arr, uint32_t count, uint32_t default_port, + it_portal_t **portallist, uint32_t *list_count) +{ + int ret = 0; + int i; + it_portal_t *portal; + it_portal_t *prev = NULL; + it_portal_t *tmp; + + if (!arr || !portallist || !list_count) { + return (EINVAL); + } + + *list_count = 0; + *portallist = NULL; + + for (i = 0; i < count; i++) { + if (!arr[i]) { + /* should never happen */ + continue; + } + portal = iscsit_zalloc(sizeof (it_portal_t)); + if (!portal) { + ret = ENOMEM; + break; + } + if (it_common_convert_sa(arr[i], + &(portal->portal_addr), default_port) == NULL) { + iscsit_free(portal, sizeof (it_portal_t)); + ret = EINVAL; + break; + } + + /* make sure no duplicates */ + tmp = *portallist; + while (tmp) { + if (it_sa_compare(&(tmp->portal_addr), + &(portal->portal_addr)) == 0) { + iscsit_free(portal, sizeof (it_portal_t)); + portal = NULL; + break; + } + tmp = tmp->portal_next; + } + + if (!portal) { + continue; + } + + /* + * The first time through the loop, *portallist == NULL + * because we assigned it to NULL above. Subsequently + * prev will have been set. Therefor it's OK to put + * lint override before prev->portal_next assignment. + */ + if (*portallist == NULL) { + *portallist = portal; + } else { + prev->portal_next = portal; + } + + prev = portal; + (*list_count)++; + } + + return (ret); +} + +/* + * Function: it_config_free_cmn() + * + * Free any resources associated with the it_config_t structure. + * + * Parameters: + * cfg A C representation of the current iSCSI configuration + */ +void +it_config_free_cmn(it_config_t *cfg) +{ + if (!cfg) { + return; + } + + if (cfg->config_tgt_list) { + it_tgt_free_cmn(cfg->config_tgt_list); + } + + if (cfg->config_tpg_list) { + it_tpg_free_cmn(cfg->config_tpg_list); + } + + if (cfg->config_ini_list) { + it_ini_free_cmn(cfg->config_ini_list); + } + + if (cfg->config_global_properties) { + nvlist_free(cfg->config_global_properties); + } + + if (cfg->config_isns_svr_list) { + it_portal_t *pp = cfg->config_isns_svr_list; + it_portal_t *pp_next; + + while (pp) { + pp_next = pp->portal_next; + iscsit_free(pp, sizeof (it_portal_t)); + pp = pp_next; + } + } + + iscsit_free(cfg, sizeof (it_config_t)); +} + +/* + * Function: it_tgt_free_cmn() + * + * Frees an it_tgt_t structure. If tgt_next is not NULL, frees + * all structures in the list. + */ +void +it_tgt_free_cmn(it_tgt_t *tgt) +{ + it_tgt_t *tgtp = tgt; + it_tgt_t *next; + + if (!tgt) { + return; + } + + while (tgtp) { + next = tgtp->tgt_next; + + if (tgtp->tgt_tpgt_list) { + it_tpgt_free_cmn(tgtp->tgt_tpgt_list); + } + + if (tgtp->tgt_properties) { + nvlist_free(tgtp->tgt_properties); + } + + iscsit_free(tgtp, sizeof (it_tgt_t)); + + tgtp = next; + } +} + +/* + * Function: it_tpgt_free_cmn() + * + * Deallocates resources of an it_tpgt_t structure. If tpgt->next + * is not NULL, frees all members of the list. + */ +void +it_tpgt_free_cmn(it_tpgt_t *tpgt) +{ + it_tpgt_t *tpgtp = tpgt; + it_tpgt_t *next; + + if (!tpgt) { + return; + } + + while (tpgtp) { + next = tpgtp->tpgt_next; + + iscsit_free(tpgtp, sizeof (it_tpgt_t)); + + tpgtp = next; + } +} + +/* + * Function: it_tpg_free_cmn() + * + * Deallocates resources associated with an it_tpg_t structure. + * If tpg->next is not NULL, frees all members of the list. + */ +void +it_tpg_free_cmn(it_tpg_t *tpg) +{ + it_tpg_t *tpgp = tpg; + it_tpg_t *next; + it_portal_t *portalp; + it_portal_t *pnext; + + while (tpgp) { + next = tpgp->tpg_next; + + portalp = tpgp->tpg_portal_list; + + while (portalp) { + pnext = portalp->portal_next; + iscsit_free(portalp, sizeof (it_portal_t)); + portalp = pnext; + } + + iscsit_free(tpgp, sizeof (it_tpg_t)); + + tpgp = next; + } +} + +/* + * Function: it_ini_free_cmn() + * + * Deallocates resources of an it_ini_t structure. If ini->next is + * not NULL, frees all members of the list. + */ +void +it_ini_free_cmn(it_ini_t *ini) +{ + it_ini_t *inip = ini; + it_ini_t *next; + + if (!ini) { + return; + } + + while (inip) { + next = inip->ini_next; + + if (inip->ini_properties) { + nvlist_free(inip->ini_properties); + } + + iscsit_free(inip, sizeof (it_ini_t)); + + inip = next; + } +} diff --git a/usr/src/lib/libiscsit/common/libiscsit.c b/usr/src/lib/libiscsit/common/libiscsit.c new file mode 100644 index 0000000..c45b9b1 --- /dev/null +++ b/usr/src/lib/libiscsit/common/libiscsit.c @@ -0,0 +1,2044 @@ +/* + * 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. + */ +/* + * Copyright 2011 Nexenta Systems, Inc. All rights reserved. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <ctype.h> +#include <fcntl.h> +#include <uuid/uuid.h> +#include <errno.h> +#include <unistd.h> +#include <strings.h> +#include <libintl.h> +#include <libscf.h> + +#include <libstmf.h> +#include <libiscsit.h> +#include <sys/iscsi_protocol.h> +#include <sys/iscsit/isns_protocol.h> + +/* From iscsitgtd */ +#define TARGET_NAME_VERS 2 + +/* this should be defined someplace central... */ +#define ISCSI_NAME_LEN_MAX 223 + +/* max length of a base64 encoded secret */ +#define MAX_BASE64_LEN 341 + +/* Default RADIUS server port */ +#define DEFAULT_RADIUS_PORT 1812 + +/* The iscsit SMF service FMRI */ +#define ISCSIT_FMRI "svc:/network/iscsi/target:default" +/* + * The kernel reserves target portal group tag value 1 as the default. + */ +#define ISCSIT_DEFAULT_TPGT 1 +#define MAXTAG 0xffff + +/* helper for property list validation */ +#define PROPERR(lst, key, value) { \ + if (lst) { \ + (void) nvlist_add_string(lst, key, value); \ + } \ +} + +/* helper function declarations */ +static int +it_iqn_generate(char *iqn_buf, int iqn_buf_len, char *opt_iqn_suffix); + +static int +it_val_pass(char *name, char *val, nvlist_t *e); + +/* consider making validate funcs public */ +static int +it_validate_configprops(nvlist_t *nvl, nvlist_t *errs); + +static int +it_validate_tgtprops(nvlist_t *nvl, nvlist_t *errs); + +static int +it_validate_iniprops(nvlist_t *nvl, nvlist_t *errs); + +static boolean_t +is_iscsit_enabled(void); + +static void +iqnstr(char *s); + +static void +euistr(char *s); + +/* + * Function: it_config_load() + * + * Allocate and create an it_config_t structure representing the + * current iSCSI configuration. This structure is compiled using + * the 'provider' data returned by stmfGetProviderData(). If there + * is no provider data associated with iscsit, the it_config_t + * structure will be set to a default configuration. + * + * Parameters: + * cfg A C representation of the current iSCSI configuration + * + * Return Values: + * 0 Success + * ENOMEM Could not allocate resources + * EINVAL Invalid parameter + */ +int +it_config_load(it_config_t **cfg) +{ + int ret = 0; + nvlist_t *cfg_nv = NULL; + it_config_t *newcfg = NULL; + uint64_t stmf_token = 0; + + if (!cfg) { + return (EINVAL); + } + + *cfg = NULL; + + ret = stmfGetProviderDataProt(ISCSIT_MODNAME, &cfg_nv, + STMF_PORT_PROVIDER_TYPE, &stmf_token); + + if ((ret == STMF_STATUS_SUCCESS) || + (ret == STMF_ERROR_NOT_FOUND)) { + /* + * If not initialized yet, return empty it_config_t + * Else, convert nvlist to struct + */ + ret = it_nv_to_config(cfg_nv, &newcfg); + } + + if (ret == 0) { + newcfg->stmf_token = stmf_token; + *cfg = newcfg; + } + + if (cfg_nv) { + nvlist_free(cfg_nv); + } + + return (ret); +} + +/* + * Function: it_config_commit() + * + * Informs the iscsit service that the configuration has changed and + * commits the new configuration to persistent store by calling + * stmfSetProviderData. This function can be called multiple times + * during a configuration sequence if necessary. + * + * Parameters: + * cfg A C representation of the current iSCSI configuration + * + * Return Values: + * 0 Success + * ENOMEM Could not allocate resources + * EINVAL Invalid it_config_t structure + * TBD ioctl() failed + * TBD could not save config to STMF + */ +int +it_config_commit(it_config_t *cfg) +{ + int ret; + nvlist_t *cfgnv = NULL; + char *packednv = NULL; + int iscsit_fd = -1; + size_t pnv_size; + iscsit_ioc_set_config_t iop; + it_tgt_t *tgtp; + + if (!cfg) { + return (EINVAL); + } + + ret = it_config_to_nv(cfg, &cfgnv); + if (ret == 0) { + ret = nvlist_size(cfgnv, &pnv_size, NV_ENCODE_NATIVE); + } + + /* + * If the iscsit service is enabled, send the changes to the + * kernel first. Kernel will be the final sanity check before + * the config is saved persistently. + * + * This somewhat leaves open the simultaneous-change hole + * that STMF was trying to solve, but is a better sanity + * check and allows for graceful handling of target renames. + */ + if ((ret == 0) && is_iscsit_enabled()) { + packednv = malloc(pnv_size); + if (!packednv) { + ret = ENOMEM; + } else { + ret = nvlist_pack(cfgnv, &packednv, &pnv_size, + NV_ENCODE_NATIVE, 0); + } + + if (ret == 0) { + iscsit_fd = open(ISCSIT_NODE, O_RDWR|O_EXCL); + if (iscsit_fd != -1) { + iop.set_cfg_vers = ISCSIT_API_VERS0; + iop.set_cfg_pnvlist = packednv; + iop.set_cfg_pnvlist_len = pnv_size; + if ((ioctl(iscsit_fd, ISCSIT_IOC_SET_CONFIG, + &iop)) != 0) { + ret = errno; + } + + (void) close(iscsit_fd); + } else { + ret = errno; + } + } + + if (packednv != NULL) { + free(packednv); + } + } + + /* + * Before saving the config persistently, remove any + * PROP_OLD_TARGET_NAME entries. This is only interesting to + * the active service. + */ + if (ret == 0) { + boolean_t changed = B_FALSE; + + tgtp = cfg->config_tgt_list; + for (; tgtp != NULL; tgtp = tgtp->tgt_next) { + if (!tgtp->tgt_properties) { + continue; + } + if (nvlist_exists(tgtp->tgt_properties, + PROP_OLD_TARGET_NAME)) { + (void) nvlist_remove_all(tgtp->tgt_properties, + PROP_OLD_TARGET_NAME); + changed = B_TRUE; + } + } + + if (changed) { + /* rebuild the config nvlist */ + nvlist_free(cfgnv); + cfgnv = NULL; + ret = it_config_to_nv(cfg, &cfgnv); + } + } + + /* + * stmfGetProviderDataProt() checks to ensure + * that the config data hasn't changed since we fetched it. + * + * The kernel now has a version we need to save persistently. + * CLI will 'do the right thing' and warn the user if it + * gets STMF_ERROR_PROV_DATA_STALE. We'll try once to revert + * the kernel to the persistently saved data, but ultimately, + * it's up to the administrator to validate things are as they + * want them to be. + */ + if (ret == 0) { + ret = stmfSetProviderDataProt(ISCSIT_MODNAME, cfgnv, + STMF_PORT_PROVIDER_TYPE, &(cfg->stmf_token)); + + if (ret == STMF_STATUS_SUCCESS) { + ret = 0; + } else if (ret == STMF_ERROR_NOMEM) { + ret = ENOMEM; + } else if (ret == STMF_ERROR_PROV_DATA_STALE) { + int st; + it_config_t *rcfg = NULL; + + st = it_config_load(&rcfg); + if (st == 0) { + (void) it_config_commit(rcfg); + it_config_free(rcfg); + } + } + } + + if (cfgnv) { + nvlist_free(cfgnv); + } + + return (ret); +} + +/* + * Function: it_config_setprop() + * + * Validate the provided property list and set the global properties + * for iSCSI Target. If errlist is not NULL, returns detailed + * errors for each property that failed. The format for errorlist + * is key = property, value = error string. + * + * Parameters: + * + * cfg The current iSCSI configuration obtained from + * it_config_load() + * proplist nvlist_t containing properties for this target. + * errlist (optional) nvlist_t of errors encountered when + * validating the properties. + * + * Return Values: + * 0 Success + * EINVAL Invalid property + * + */ +int +it_config_setprop(it_config_t *cfg, nvlist_t *proplist, nvlist_t **errlist) +{ + int ret; + nvlist_t *errs = NULL; + it_portal_t *isns = NULL; + it_portal_t *pnext = NULL; + it_portal_t *newisnslist = NULL; + char **arr; + uint32_t count; + uint32_t newcount; + nvlist_t *cprops = NULL; + char *val = NULL; + + if (!cfg || !proplist) { + return (EINVAL); + } + + if (errlist) { + (void) nvlist_alloc(&errs, 0, 0); + *errlist = errs; + } + + /* + * copy the existing properties, merge, then validate + * the merged properties before committing them. + */ + if (cfg->config_global_properties) { + ret = nvlist_dup(cfg->config_global_properties, &cprops, 0); + } else { + ret = nvlist_alloc(&cprops, NV_UNIQUE_NAME, 0); + } + + if (ret != 0) { + return (ret); + } + + ret = nvlist_merge(cprops, proplist, 0); + if (ret != 0) { + nvlist_free(cprops); + return (ret); + } + + /* + * base64 encode the radius secret, if it's changed. + */ + val = NULL; + (void) nvlist_lookup_string(proplist, PROP_RADIUS_SECRET, &val); + if (val) { + char bsecret[MAX_BASE64_LEN]; + + ret = it_val_pass(PROP_RADIUS_SECRET, val, errs); + + if (ret == 0) { + (void) memset(bsecret, 0, MAX_BASE64_LEN); + + ret = iscsi_binary_to_base64_str((uint8_t *)val, + strlen(val), bsecret, MAX_BASE64_LEN); + + if (ret == 0) { + /* replace the value in the nvlist */ + ret = nvlist_add_string(cprops, + PROP_RADIUS_SECRET, bsecret); + } + } + } + + if (ret != 0) { + nvlist_free(cprops); + return (ret); + } + + /* see if we need to remove the radius server setting */ + val = NULL; + (void) nvlist_lookup_string(cprops, PROP_RADIUS_SERVER, &val); + if (val && (strcasecmp(val, "none") == 0)) { + (void) nvlist_remove_all(cprops, PROP_RADIUS_SERVER); + } + + /* and/or remove the alias */ + val = NULL; + (void) nvlist_lookup_string(cprops, PROP_ALIAS, &val); + if (val && (strcasecmp(val, "none") == 0)) { + (void) nvlist_remove_all(cprops, PROP_ALIAS); + } + + ret = it_validate_configprops(cprops, errs); + if (ret != 0) { + if (cprops) { + nvlist_free(cprops); + } + return (ret); + } + + /* + * Update iSNS server list, if exists in provided property list. + */ + ret = nvlist_lookup_string_array(proplist, PROP_ISNS_SERVER, + &arr, &count); + + if (ret == 0) { + /* special case: if "none", remove all defined */ + if (strcasecmp(arr[0], "none") != 0) { + ret = it_array_to_portallist(arr, count, + ISNS_DEFAULT_SERVER_PORT, &newisnslist, &newcount); + } else { + newisnslist = NULL; + newcount = 0; + (void) nvlist_remove_all(cprops, PROP_ISNS_SERVER); + } + + if (ret == 0) { + isns = cfg->config_isns_svr_list; + while (isns) { + pnext = isns->portal_next; + free(isns); + isns = pnext; + } + + cfg->config_isns_svr_list = newisnslist; + cfg->config_isns_svr_count = newcount; + + /* + * Replace the array in the nvlist to ensure + * duplicates are properly removed & port numbers + * are added. + */ + if (newcount > 0) { + int i = 0; + char **newarray; + + newarray = malloc(sizeof (char *) * newcount); + if (newarray == NULL) { + ret = ENOMEM; + } else { + for (isns = newisnslist; isns != NULL; + isns = isns->portal_next) { + (void) sockaddr_to_str( + &(isns->portal_addr), + &(newarray[i++])); + } + (void) nvlist_add_string_array(cprops, + PROP_ISNS_SERVER, newarray, + newcount); + + for (i = 0; i < newcount; i++) { + if (newarray[i]) { + free(newarray[i]); + } + } + free(newarray); + } + } + } + } else if (ret == ENOENT) { + /* not an error */ + ret = 0; + } + + if (ret == 0) { + /* replace the global properties list */ + nvlist_free(cfg->config_global_properties); + cfg->config_global_properties = cprops; + } else { + if (cprops) { + nvlist_free(cprops); + } + } + + return (ret); +} + +/* + * Function: it_config_free() + * + * Free any resources associated with the it_config_t structure. + * + * Parameters: + * cfg A C representation of the current iSCSI configuration + */ +void +it_config_free(it_config_t *cfg) +{ + it_config_free_cmn(cfg); +} + +/* + * Function: it_tgt_create() + * + * Allocate and create an it_tgt_t structure representing a new iSCSI + * target node. If tgt_name is NULL, then a unique target node name will + * be generated automatically. Otherwise, the value of tgt_name will be + * used as the target node name. The new it_tgt_t structure is added to + * the target list (cfg_tgt_list) in the configuration structure, and the + * new target will not be instantiated until the modified configuration + * is committed by calling it_config_commit(). + * + * Parameters: + * cfg The current iSCSI configuration obtained from + * it_config_load() + * tgt Pointer to an iSCSI target structure + * tgt_name The target node name for the target to be created. + * The name must be in either IQN or EUI format. If + * this value is NULL, a node name will be generated + * automatically in IQN format. + * + * Return Values: + * 0 Success + * ENOMEM Could not allocated resources + * EINVAL Invalid parameter + * EFAULT Invalid iSCSI name specified + * E2BIG Too many already exist + */ +int +it_tgt_create(it_config_t *cfg, it_tgt_t **tgt, char *tgt_name) +{ + int ret = 0; + it_tgt_t *ptr; + it_tgt_t *cfgtgt; + char *namep; + char buf[ISCSI_NAME_LEN_MAX + 1]; + + if (!cfg || !tgt) { + return (EINVAL); + } + + if (!tgt_name) { + /* generate a name */ + ret = it_iqn_generate(buf, sizeof (buf), NULL); + if (ret != 0) { + return (ret); + } + } else { + /* validate the passed-in name */ + if (!validate_iscsi_name(tgt_name)) { + return (EFAULT); + } + (void) strlcpy(buf, tgt_name, sizeof (buf)); + canonical_iscsi_name(buf); + } + namep = buf; + + /* make sure this name isn't already on the list */ + cfgtgt = cfg->config_tgt_list; + while (cfgtgt != NULL) { + if (strcasecmp(namep, cfgtgt->tgt_name) == 0) { + return (EEXIST); + } + cfgtgt = cfgtgt->tgt_next; + } + + /* Too many targets? */ + if (cfg->config_tgt_count >= MAX_TARGETS) { + return (E2BIG); + } + + ptr = calloc(1, sizeof (it_tgt_t)); + if (ptr == NULL) { + return (ENOMEM); + } + + (void) strlcpy(ptr->tgt_name, namep, sizeof (ptr->tgt_name)); + ptr->tgt_generation = 1; + ptr->tgt_next = cfg->config_tgt_list; + cfg->config_tgt_list = ptr; + cfg->config_tgt_count++; + + *tgt = ptr; + + return (0); +} + +/* + * Function: it_tgt_setprop() + * + * Validate the provided property list and set the properties for + * the specified target. If errlist is not NULL, returns detailed + * errors for each property that failed. The format for errorlist + * is key = property, value = error string. + * + * Parameters: + * + * cfg The current iSCSI configuration obtained from + * it_config_load() + * tgt Pointer to an iSCSI target structure + * proplist nvlist_t containing properties for this target. + * errlist (optional) nvlist_t of errors encountered when + * validating the properties. + * + * Return Values: + * 0 Success + * EINVAL Invalid property + * + */ +int +it_tgt_setprop(it_config_t *cfg, it_tgt_t *tgt, nvlist_t *proplist, + nvlist_t **errlist) +{ + int ret; + nvlist_t *errs = NULL; + nvlist_t *tprops = NULL; + char *val = NULL; + + if (!cfg || !tgt || !proplist) { + return (EINVAL); + } + + /* verify the target name in case the target node is renamed */ + if (!validate_iscsi_name(tgt->tgt_name)) { + return (EINVAL); + } + canonical_iscsi_name(tgt->tgt_name); + + if (errlist) { + (void) nvlist_alloc(&errs, 0, 0); + *errlist = errs; + } + + /* + * copy the existing properties, merge, then validate + * the merged properties before committing them. + */ + if (tgt->tgt_properties) { + ret = nvlist_dup(tgt->tgt_properties, &tprops, 0); + } else { + ret = nvlist_alloc(&tprops, NV_UNIQUE_NAME, 0); + } + + if (ret != 0) { + return (ret); + } + + ret = nvlist_merge(tprops, proplist, 0); + if (ret != 0) { + nvlist_free(tprops); + return (ret); + } + + /* unset chap username or alias if requested */ + val = NULL; + (void) nvlist_lookup_string(proplist, PROP_TARGET_CHAP_USER, &val); + if (val && (strcasecmp(val, "none") == 0)) { + (void) nvlist_remove_all(tprops, PROP_TARGET_CHAP_USER); + } + + val = NULL; + (void) nvlist_lookup_string(proplist, PROP_ALIAS, &val); + if (val && (strcasecmp(val, "none") == 0)) { + (void) nvlist_remove_all(tprops, PROP_ALIAS); + } + + /* base64 encode the CHAP secret, if it's changed */ + val = NULL; + (void) nvlist_lookup_string(proplist, PROP_TARGET_CHAP_SECRET, &val); + if (val) { + char bsecret[MAX_BASE64_LEN]; + + ret = it_val_pass(PROP_TARGET_CHAP_SECRET, val, errs); + + if (ret == 0) { + (void) memset(bsecret, 0, MAX_BASE64_LEN); + + ret = iscsi_binary_to_base64_str((uint8_t *)val, + strlen(val), bsecret, MAX_BASE64_LEN); + + if (ret == 0) { + /* replace the value in the nvlist */ + ret = nvlist_add_string(tprops, + PROP_TARGET_CHAP_SECRET, bsecret); + } + } + } + + if (ret == 0) { + ret = it_validate_tgtprops(tprops, errs); + } + + if (ret != 0) { + if (tprops) { + nvlist_free(tprops); + } + return (ret); + } + + if (tgt->tgt_properties) { + nvlist_free(tgt->tgt_properties); + } + tgt->tgt_properties = tprops; + + return (0); +} + + +/* + * Function: it_tgt_delete() + * + * Delete target represented by 'tgt', where 'tgt' is an existing + * it_tgt_structure within the configuration 'cfg'. The target removal + * will not take effect until the modified configuration is committed + * by calling it_config_commit(). + * + * Parameters: + * cfg The current iSCSI configuration obtained from + * it_config_load() + * tgt Pointer to an iSCSI target structure + * + * force Set the target to offline before removing it from + * the config. If not specified, the operation will + * fail if the target is determined to be online. + * Return Values: + * 0 Success + * EBUSY Target is online + */ +int +it_tgt_delete(it_config_t *cfg, it_tgt_t *tgt, boolean_t force) +{ + int ret; + it_tgt_t *ptgt; + it_tgt_t *prev = NULL; + stmfDevid devid; + stmfTargetProperties props; + + if (!cfg || !tgt) { + return (0); + } + + ptgt = cfg->config_tgt_list; + while (ptgt != NULL) { + if (strcasecmp(tgt->tgt_name, ptgt->tgt_name) == 0) { + break; + } + prev = ptgt; + ptgt = ptgt->tgt_next; + } + + if (!ptgt) { + return (0); + } + + /* + * check to see if this target is offline. If it is not, + * and the 'force' flag is TRUE, tell STMF to offline it + * before removing from the configuration. + */ + ret = stmfDevidFromIscsiName(ptgt->tgt_name, &devid); + if (ret != STMF_STATUS_SUCCESS) { + /* can't happen? */ + return (EINVAL); + } + + ret = stmfGetTargetProperties(&devid, &props); + if (ret == STMF_STATUS_SUCCESS) { + /* + * only other return is STMF_ERROR_NOT_FOUND, which + * means we don't have to offline it. + */ + if (props.status == STMF_TARGET_PORT_ONLINE) { + if (!force) { + return (EBUSY); + } + ret = stmfOfflineTarget(&devid); + if (ret != 0) { + return (EBUSY); + } + } + } + + if (prev) { + prev->tgt_next = ptgt->tgt_next; + } else { + /* first one on the list */ + cfg->config_tgt_list = ptgt->tgt_next; + } + + ptgt->tgt_next = NULL; /* Only free this target */ + + cfg->config_tgt_count--; + it_tgt_free(ptgt); + + return (0); +} + +/* + * Function: it_tgt_free() + * + * Frees an it_tgt_t structure. If tgt_next is not NULL, frees + * all structures in the list. + */ +void +it_tgt_free(it_tgt_t *tgt) +{ + it_tgt_free_cmn(tgt); +} + +/* + * Function: it_tpgt_create() + * + * Allocate and create an it_tpgt_t structure representing a new iSCSI + * target portal group tag. The new it_tpgt_t structure is added to the + * target tpgt list (tgt_tpgt_list) in the it_tgt_t structure. The new + * target portal group tag will not be instantiated until the modified + * configuration is committed by calling it_config_commit(). + * + * Parameters: + * cfg The current iSCSI configuration obtained from + * it_config_load() + * tgt Pointer to the iSCSI target structure associated + * with the target portal group tag + * tpgt Pointer to a target portal group tag structure + * tpg_name The name of the TPG to be associated with this TPGT + * tpgt_tag 16-bit numerical identifier for this TPGT. If + * tpgt_tag is '0', this function will choose the + * tag number. If tpgt_tag is >0, and the requested + * tag is determined to be in use, another value + * will be chosen. + * + * Return Values: + * 0 Success + * ENOMEM Could not allocate resources + * EINVAL Invalid parameter + * EEXIST Specified tag name is already used. + * E2BIG No available tag numbers + */ +int +it_tpgt_create(it_config_t *cfg, it_tgt_t *tgt, it_tpgt_t **tpgt, + char *tpg_name, uint16_t tpgt_tag) +{ + it_tpgt_t *ptr = NULL; + it_tpgt_t *cfgt; + char tagid_used[MAXTAG + 1]; + uint16_t tagid = ISCSIT_DEFAULT_TPGT; + + if (!cfg || !tgt || !tpgt || !tpg_name) { + return (EINVAL); + } + + (void) memset(&(tagid_used[0]), 0, sizeof (tagid_used)); + + /* + * Make sure this name and/or tag isn't already on the list + * At the same time, capture all tag ids in use for this target + * + * About tag numbering -- since tag numbers are used by + * the iSCSI protocol, we should be careful about reusing + * them too quickly. Start with a value greater than the + * highest one currently defined. If current == MAXTAG, + * just find an unused tag. + */ + cfgt = tgt->tgt_tpgt_list; + while (cfgt != NULL) { + tagid_used[cfgt->tpgt_tag] = 1; + + if (strcmp(tpg_name, cfgt->tpgt_tpg_name) == 0) { + return (EEXIST); + } + + if (cfgt->tpgt_tag > tagid) { + tagid = cfgt->tpgt_tag; + } + + cfgt = cfgt->tpgt_next; + } + + if ((tpgt_tag > ISCSIT_DEFAULT_TPGT) && (tpgt_tag < MAXTAG) && + (tagid_used[tpgt_tag] == 0)) { + /* ok to use requested */ + tagid = tpgt_tag; + } else if (tagid == MAXTAG) { + /* + * The highest value is used, find an available id. + */ + tagid = ISCSIT_DEFAULT_TPGT + 1; + for (; tagid < MAXTAG; tagid++) { + if (tagid_used[tagid] == 0) { + break; + } + } + if (tagid >= MAXTAG) { + return (E2BIG); + } + } else { + /* next available ID */ + tagid++; + } + + ptr = calloc(1, sizeof (it_tpgt_t)); + if (!ptr) { + return (ENOMEM); + } + + (void) strlcpy(ptr->tpgt_tpg_name, tpg_name, + sizeof (ptr->tpgt_tpg_name)); + ptr->tpgt_generation = 1; + ptr->tpgt_tag = tagid; + + ptr->tpgt_next = tgt->tgt_tpgt_list; + tgt->tgt_tpgt_list = ptr; + tgt->tgt_tpgt_count++; + tgt->tgt_generation++; + + *tpgt = ptr; + + return (0); +} + +/* + * Function: it_tpgt_delete() + * + * Delete the target portal group tag represented by 'tpgt', where + * 'tpgt' is an existing is_tpgt_t structure within the target 'tgt'. + * The target portal group tag removal will not take effect until the + * modified configuration is committed by calling it_config_commit(). + * + * Parameters: + * cfg The current iSCSI configuration obtained from + * it_config_load() + * tgt Pointer to the iSCSI target structure associated + * with the target portal group tag + * tpgt Pointer to a target portal group tag structure + */ +void +it_tpgt_delete(it_config_t *cfg, it_tgt_t *tgt, it_tpgt_t *tpgt) +{ + it_tpgt_t *ptr; + it_tpgt_t *prev = NULL; + + if (!cfg || !tgt || !tpgt) { + return; + } + + ptr = tgt->tgt_tpgt_list; + while (ptr) { + if (ptr->tpgt_tag == tpgt->tpgt_tag) { + break; + } + prev = ptr; + ptr = ptr->tpgt_next; + } + + if (!ptr) { + return; + } + + if (prev) { + prev->tpgt_next = ptr->tpgt_next; + } else { + tgt->tgt_tpgt_list = ptr->tpgt_next; + } + ptr->tpgt_next = NULL; + + tgt->tgt_tpgt_count--; + tgt->tgt_generation++; + + it_tpgt_free(ptr); +} + +/* + * Function: it_tpgt_free() + * + * Deallocates resources of an it_tpgt_t structure. If tpgt->next + * is not NULL, frees all members of the list. + */ +void +it_tpgt_free(it_tpgt_t *tpgt) +{ + it_tpgt_free_cmn(tpgt); +} + +/* + * Function: it_tpg_create() + * + * Allocate and create an it_tpg_t structure representing a new iSCSI + * target portal group. The new it_tpg_t structure is added to the global + * tpg list (cfg_tgt_list) in the it_config_t structure. The new target + * portal group will not be instantiated until the modified configuration + * is committed by calling it_config_commit(). + * + * Parameters: + * cfg The current iSCSI configuration obtained from + * it_config_load() + * tpg Pointer to the it_tpg_t structure representing + * the target portal group + * tpg_name Identifier for the target portal group + * portal_ip_port A string containing an appropriatedly formatted + * IP address:port. Both IPv4 and IPv6 addresses are + * permitted. This value becomes the first portal in + * the TPG -- applications can add additional values + * using it_portal_create() before committing the TPG. + * Return Values: + * 0 Success + * ENOMEM Cannot allocate resources + * EINVAL Invalid parameter + * EEXIST Requested portal in use by another target portal + * group + */ +int +it_tpg_create(it_config_t *cfg, it_tpg_t **tpg, char *tpg_name, + char *portal_ip_port) +{ + int ret; + it_tpg_t *ptr; + it_portal_t *portal = NULL; + + if (!cfg || !tpg || !tpg_name || !portal_ip_port) { + return (EINVAL); + } + + *tpg = NULL; + + ptr = cfg->config_tpg_list; + while (ptr) { + if (strcmp(tpg_name, ptr->tpg_name) == 0) { + break; + } + ptr = ptr->tpg_next; + } + + if (ptr) { + return (EEXIST); + } + + ptr = calloc(1, sizeof (it_tpg_t)); + if (!ptr) { + return (ENOMEM); + } + + ptr->tpg_generation = 1; + (void) strlcpy(ptr->tpg_name, tpg_name, sizeof (ptr->tpg_name)); + + /* create the portal */ + ret = it_portal_create(cfg, ptr, &portal, portal_ip_port); + if (ret != 0) { + free(ptr); + return (ret); + } + + ptr->tpg_next = cfg->config_tpg_list; + cfg->config_tpg_list = ptr; + cfg->config_tpg_count++; + + *tpg = ptr; + + return (0); +} + +/* + * Function: it_tpg_delete() + * + * Delete target portal group represented by 'tpg', where 'tpg' is an + * existing it_tpg_t structure within the global configuration 'cfg'. + * The target portal group removal will not take effect until the + * modified configuration is committed by calling it_config_commit(). + * + * Parameters: + * cfg The current iSCSI configuration obtained from + * it_config_load() + * tpg Pointer to the it_tpg_t structure representing + * the target portal group + * force Remove this target portal group even if it's + * associated with one or more targets. + * + * Return Values: + * 0 Success + * EINVAL Invalid parameter + * EBUSY Portal group associated with one or more targets. + */ +int +it_tpg_delete(it_config_t *cfg, it_tpg_t *tpg, boolean_t force) +{ + it_tpg_t *ptr; + it_tpg_t *prev = NULL; + it_tgt_t *tgt; + it_tpgt_t *tpgt; + it_tpgt_t *ntpgt; + + if (!cfg || !tpg) { + return (EINVAL); + } + + ptr = cfg->config_tpg_list; + while (ptr) { + if (strcmp(ptr->tpg_name, tpg->tpg_name) == 0) { + break; + } + prev = ptr; + ptr = ptr->tpg_next; + } + + if (!ptr) { + return (0); + } + + /* + * See if any targets are using this portal group. + * If there are, and the force flag is not set, fail. + */ + tgt = cfg->config_tgt_list; + while (tgt) { + tpgt = tgt->tgt_tpgt_list; + while (tpgt) { + ntpgt = tpgt->tpgt_next; + + if (strcmp(tpgt->tpgt_tpg_name, tpg->tpg_name) + == 0) { + if (!force) { + return (EBUSY); + } + it_tpgt_delete(cfg, tgt, tpgt); + } + + tpgt = ntpgt; + } + tgt = tgt->tgt_next; + } + + /* Now that it's not in use anywhere, remove the TPG */ + if (prev) { + prev->tpg_next = ptr->tpg_next; + } else { + cfg->config_tpg_list = ptr->tpg_next; + } + ptr->tpg_next = NULL; + + cfg->config_tpg_count--; + + it_tpg_free(ptr); + + return (0); +} + +/* + * Function: it_tpg_free() + * + * Deallocates resources associated with an it_tpg_t structure. + * If tpg->next is not NULL, frees all members of the list. + */ +void +it_tpg_free(it_tpg_t *tpg) +{ + it_tpg_free_cmn(tpg); +} + +/* + * Function: it_portal_create() + * + * Add an it_portal_t structure presenting a new portal to the specified + * target portal group. The change to the target portal group will not take + * effect until the modified configuration is committed by calling + * it_config_commit(). + * + * Parameters: + * cfg The current iSCSI configration obtained from + * it_config_load() + * tpg Pointer to the it_tpg_t structure representing the + * target portal group + * portal Pointer to the it_portal_t structure representing + * the portal + * portal_ip_port A string containing an appropriately formatted + * IP address or IP address:port in either IPv4 or + * IPv6 format. + * Return Values: + * 0 Success + * ENOMEM Could not allocate resources + * EINVAL Invalid parameter + * EEXIST Portal already configured for another portal group + */ +int +it_portal_create(it_config_t *cfg, it_tpg_t *tpg, it_portal_t **portal, + char *portal_ip_port) +{ + struct sockaddr_storage sa; + it_portal_t *ptr; + it_tpg_t *ctpg = NULL; + + if (!cfg || !tpg || !portal || !portal_ip_port) { + return (EINVAL); + } + + if ((it_common_convert_sa(portal_ip_port, &sa, ISCSI_LISTEN_PORT)) + == NULL) { + return (EINVAL); + } + + /* Check that this portal doesn't appear in any other tag */ + ctpg = cfg->config_tpg_list; + while (ctpg) { + ptr = ctpg->tpg_portal_list; + for (; ptr != NULL; ptr = ptr->portal_next) { + if (it_sa_compare(&(ptr->portal_addr), &sa) != 0) { + continue; + } + + /* + * Existing in the same group is not an error, + * but don't add it again. + */ + if (strcmp(ctpg->tpg_name, tpg->tpg_name) == 0) { + return (0); + } else { + /* Not allowed */ + return (EEXIST); + } + } + ctpg = ctpg->tpg_next; + } + + ptr = calloc(1, sizeof (it_portal_t)); + if (!ptr) { + return (ENOMEM); + } + + (void) memcpy(&(ptr->portal_addr), &sa, + sizeof (struct sockaddr_storage)); + ptr->portal_next = tpg->tpg_portal_list; + tpg->tpg_portal_list = ptr; + tpg->tpg_portal_count++; + tpg->tpg_generation++; + + return (0); +} + +/* + * Function: it_portal_delete() + * + * Remove the specified portal from the specified target portal group. + * The portal removal will not take effect until the modified configuration + * is committed by calling it_config_commit(). + * + * Parameters: + * cfg The current iSCSI configration obtained from + * it_config_load() + * tpg Pointer to the it_tpg_t structure representing the + * target portal group + * portal Pointer to the it_portal_t structure representing + * the portal + */ +void +it_portal_delete(it_config_t *cfg, it_tpg_t *tpg, it_portal_t *portal) +{ + it_portal_t *ptr; + it_portal_t *prev = NULL; + + if (!cfg || !tpg || !portal) { + return; + } + + ptr = tpg->tpg_portal_list; + while (ptr) { + if (memcmp(&(ptr->portal_addr), &(portal->portal_addr), + sizeof (ptr->portal_addr)) == 0) { + break; + } + prev = ptr; + ptr = ptr->portal_next; + } + + if (!ptr) { + return; + } + + if (prev) { + prev->portal_next = ptr->portal_next; + } else { + tpg->tpg_portal_list = ptr->portal_next; + } + tpg->tpg_portal_count--; + tpg->tpg_generation++; + + free(ptr); +} + +/* + * Function: it_ini_create() + * + * Add an initiator context to the global configuration. The new + * initiator context will not be instantiated until the modified + * configuration is committed by calling it_config_commit(). + * + * Parameters: + * cfg The current iSCSI configration obtained from + * it_config_load() + * ini Pointer to the it_ini_t structure representing + * the initiator context. + * ini_node_name The iSCSI node name of the remote initiator. + * + * Return Values: + * 0 Success + * ENOMEM Could not allocate resources + * EINVAL Invalid parameter. + * EFAULT Invalid initiator name + */ +int +it_ini_create(it_config_t *cfg, it_ini_t **ini, char *ini_node_name) +{ + it_ini_t *ptr; + + if (!cfg || !ini || !ini_node_name) { + return (EINVAL); + } + + /* + * Ensure this is a valid ini name + */ + if (!validate_iscsi_name(ini_node_name)) { + return (EFAULT); + } + + ptr = cfg->config_ini_list; + while (ptr) { + if (strcasecmp(ptr->ini_name, ini_node_name) == 0) { + break; + } + ptr = ptr->ini_next; + } + + if (ptr) { + return (EEXIST); + } + + ptr = calloc(1, sizeof (it_ini_t)); + if (!ptr) { + return (ENOMEM); + } + + (void) strlcpy(ptr->ini_name, ini_node_name, sizeof (ptr->ini_name)); + ptr->ini_generation = 1; + /* nvlist for props? */ + + ptr->ini_next = cfg->config_ini_list; + cfg->config_ini_list = ptr; + cfg->config_ini_count++; + + *ini = ptr; + + return (0); +} + +/* + * Function: it_ini_setprop() + * + * Validate the provided property list and set the initiator properties. + * If errlist is not NULL, returns detailed errors for each property + * that failed. The format for errorlist is key = property, + * value = error string. + * + * Parameters: + * + * ini The initiator being updated. + * proplist nvlist_t containing properties for this target. + * errlist (optional) nvlist_t of errors encountered when + * validating the properties. + * + * Return Values: + * 0 Success + * EINVAL Invalid property + * + */ +int +it_ini_setprop(it_ini_t *ini, nvlist_t *proplist, nvlist_t **errlist) +{ + int ret; + nvlist_t *errs = NULL; + nvlist_t *iprops = NULL; + char *val = NULL; + + if (!ini || !proplist) { + return (EINVAL); + } + + if (errlist) { + (void) nvlist_alloc(&errs, 0, 0); + *errlist = errs; + } + + /* + * copy the existing properties, merge, then validate + * the merged properties before committing them. + */ + if (ini->ini_properties) { + ret = nvlist_dup(ini->ini_properties, &iprops, 0); + } else { + ret = nvlist_alloc(&iprops, NV_UNIQUE_NAME, 0); + } + + if (ret != 0) { + return (ret); + } + + ret = nvlist_merge(iprops, proplist, 0); + if (ret != 0) { + nvlist_free(iprops); + return (ret); + } + + /* unset chap username if requested */ + if ((nvlist_lookup_string(proplist, PROP_CHAP_USER, &val)) == 0) { + if (strcasecmp(val, "none") == 0) { + (void) nvlist_remove_all(iprops, PROP_CHAP_USER); + } + } + + /* base64 encode the CHAP secret, if it's changed */ + if ((nvlist_lookup_string(proplist, PROP_CHAP_SECRET, &val)) == 0) { + char bsecret[MAX_BASE64_LEN]; + + ret = it_val_pass(PROP_CHAP_SECRET, val, errs); + if (ret == 0) { + (void) memset(bsecret, 0, MAX_BASE64_LEN); + + ret = iscsi_binary_to_base64_str((uint8_t *)val, + strlen(val), bsecret, MAX_BASE64_LEN); + + if (ret == 0) { + /* replace the value in the nvlist */ + ret = nvlist_add_string(iprops, + PROP_CHAP_SECRET, bsecret); + } + } + } + + if (ret == 0) { + ret = it_validate_iniprops(iprops, errs); + } + + if (ret != 0) { + if (iprops) { + nvlist_free(iprops); + } + return (ret); + } + + if (ini->ini_properties) { + nvlist_free(ini->ini_properties); + } + ini->ini_properties = iprops; + + return (0); +} + +/* + * Function: it_ini_delete() + * + * Remove the specified initiator context from the global configuration. + * The removal will not take effect until the modified configuration is + * committed by calling it_config_commit(). + * + * Parameters: + * cfg The current iSCSI configration obtained from + * it_config_load() + * ini Pointer to the it_ini_t structure representing + * the initiator context. + */ +void +it_ini_delete(it_config_t *cfg, it_ini_t *ini) +{ + it_ini_t *ptr; + it_ini_t *prev = NULL; + + if (!cfg || !ini) { + return; + } + + ptr = cfg->config_ini_list; + while (ptr) { + if (strcasecmp(ptr->ini_name, ini->ini_name) == 0) { + break; + } + prev = ptr; + ptr = ptr->ini_next; + } + + if (!ptr) { + return; + } + + if (prev) { + prev->ini_next = ptr->ini_next; + } else { + cfg->config_ini_list = ptr->ini_next; + } + + ptr->ini_next = NULL; /* Only free this initiator */ + + cfg->config_ini_count--; + + it_ini_free(ptr); +} + +/* + * Function: it_ini_free() + * + * Deallocates resources of an it_ini_t structure. If ini->next is + * not NULL, frees all members of the list. + */ +void +it_ini_free(it_ini_t *ini) +{ + it_ini_free_cmn(ini); +} + +/* + * Goes through the target property list and validates + * each entry. If errs is non-NULL, will return explicit errors + * for each property that fails validation. + */ +static int +it_validate_tgtprops(nvlist_t *nvl, nvlist_t *errs) +{ + int errcnt = 0; + nvpair_t *nvp = NULL; + data_type_t nvtype; + char *name; + char *val; + char *auth = NULL; + + if (!nvl) { + return (0); + } + + while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { + name = nvpair_name(nvp); + nvtype = nvpair_type(nvp); + + if (!name) { + continue; + } + + val = NULL; + if (strcmp(name, PROP_TARGET_CHAP_USER) == 0) { + if (nvtype != DATA_TYPE_STRING) { + PROPERR(errs, name, + gettext("must be a string value")); + errcnt++; + continue; + } + } else if (strcmp(name, PROP_TARGET_CHAP_SECRET) == 0) { + /* + * must be between 12 and 255 chars in cleartext. + * will be base64 encoded when it's set. + */ + if (nvtype == DATA_TYPE_STRING) { + (void) nvpair_value_string(nvp, &val); + } + + if (!val) { + PROPERR(errs, name, + gettext("must be a string value")); + errcnt++; + continue; + } + } else if (strcmp(name, PROP_ALIAS) == 0) { + if (nvtype != DATA_TYPE_STRING) { + PROPERR(errs, name, + gettext("must be a string value")); + errcnt++; + continue; + } + } else if (strcmp(name, PROP_AUTH) == 0) { + if (nvtype == DATA_TYPE_STRING) { + val = NULL; + (void) nvpair_value_string(nvp, &val); + } + + if (!val) { + PROPERR(errs, name, + gettext("must be a string value")); + errcnt++; + continue; + } + if ((strcmp(val, PA_AUTH_NONE) != 0) && + (strcmp(val, PA_AUTH_CHAP) != 0) && + (strcmp(val, PA_AUTH_RADIUS) != 0) && + (strcmp(val, "default") != 0)) { + PROPERR(errs, val, gettext( + "must be none, chap, radius or default")); + errcnt++; + } + auth = val; + continue; + } else if (strcmp(name, PROP_OLD_TARGET_NAME) == 0) { + continue; + } else { + /* unrecognized property */ + PROPERR(errs, name, gettext("unrecognized property")); + errcnt++; + } + } + + if (errcnt) { + return (EINVAL); + } + + /* if auth is being set to default, remove from this nvlist */ + if (auth && (strcmp(auth, "default") == 0)) { + (void) nvlist_remove_all(nvl, PROP_AUTH); + } + + return (0); +} + +/* + * Goes through the config property list and validates + * each entry. If errs is non-NULL, will return explicit errors + * for each property that fails validation. + */ +static int +it_validate_configprops(nvlist_t *nvl, nvlist_t *errs) +{ + int errcnt = 0; + nvpair_t *nvp = NULL; + data_type_t nvtype; + char *name; + char *val; + struct sockaddr_storage sa; + boolean_t update_rad_server = B_FALSE; + char *rad_server; + char *auth = NULL; + + if (!nvl) { + return (0); + } + + while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { + name = nvpair_name(nvp); + nvtype = nvpair_type(nvp); + + if (!name) { + continue; + } + + val = NULL; + + /* prefetch string value as we mostly need it */ + if (nvtype == DATA_TYPE_STRING) { + (void) nvpair_value_string(nvp, &val); + } + + if (strcmp(name, PROP_ALIAS) == 0) { + if (!val) { + PROPERR(errs, name, + gettext("must be a string value")); + errcnt++; + } + } else if (strcmp(name, PROP_AUTH) == 0) { + if (!val) { + PROPERR(errs, name, + gettext("must be a string value")); + errcnt++; + continue; + } + + if ((strcmp(val, PA_AUTH_NONE) != 0) && + (strcmp(val, PA_AUTH_CHAP) != 0) && + (strcmp(val, PA_AUTH_RADIUS) != 0)) { + PROPERR(errs, PROP_AUTH, + gettext("must be none, chap or radius")); + errcnt++; + } + + auth = val; + + } else if (strcmp(name, PROP_ISNS_ENABLED) == 0) { + if (nvtype != DATA_TYPE_BOOLEAN_VALUE) { + PROPERR(errs, name, + gettext("must be a boolean value")); + errcnt++; + } + } else if (strcmp(name, PROP_ISNS_SERVER) == 0) { + char **arr = NULL; + uint32_t acount = 0; + + (void) nvlist_lookup_string_array(nvl, name, + &arr, &acount); + + while (acount > 0) { + if (strcasecmp(arr[acount - 1], "none") == 0) { + break; + } + if ((it_common_convert_sa(arr[acount - 1], + &sa, 0)) == NULL) { + PROPERR(errs, arr[acount - 1], + gettext("invalid address")); + errcnt++; + } + acount--; + } + + } else if (strcmp(name, PROP_RADIUS_SECRET) == 0) { + if (!val) { + PROPERR(errs, name, + gettext("must be a string value")); + errcnt++; + continue; + } + } else if (strcmp(name, PROP_RADIUS_SERVER) == 0) { + struct sockaddr_storage sa; + if (!val) { + PROPERR(errs, name, + gettext("must be a string value")); + errcnt++; + continue; + } + + if ((it_common_convert_sa(val, &sa, + DEFAULT_RADIUS_PORT)) == NULL) { + PROPERR(errs, name, + gettext("invalid address")); + errcnt++; + } else { + /* + * rewrite this property to ensure port + * number is added. + */ + + if (sockaddr_to_str(&sa, &rad_server) == 0) { + update_rad_server = B_TRUE; + } + } + } else { + /* unrecognized property */ + PROPERR(errs, name, gettext("unrecognized property")); + errcnt++; + } + } + + /* + * If we successfully reformatted the radius server to add the port + * number then update the nvlist + */ + if (update_rad_server) { + (void) nvlist_add_string(nvl, PROP_RADIUS_SERVER, rad_server); + free(rad_server); + } + + /* + * if auth = radius, ensure radius server & secret are set. + */ + if (auth) { + if (strcmp(auth, PA_AUTH_RADIUS) == 0) { + /* need server & secret for radius */ + if (!nvlist_exists(nvl, PROP_RADIUS_SERVER)) { + PROPERR(errs, PROP_RADIUS_SERVER, + gettext("missing required property")); + errcnt++; + } + if (!nvlist_exists(nvl, PROP_RADIUS_SECRET)) { + PROPERR(errs, PROP_RADIUS_SECRET, + gettext("missing required property")); + errcnt++; + } + } + } + + if (errcnt) { + return (EINVAL); + } + + return (0); +} + +/* + * Goes through the ini property list and validates + * each entry. If errs is non-NULL, will return explicit errors + * for each property that fails validation. + */ +static int +it_validate_iniprops(nvlist_t *nvl, nvlist_t *errs) +{ + int errcnt = 0; + nvpair_t *nvp = NULL; + data_type_t nvtype; + char *name; + char *val; + + if (!nvl) { + return (0); + } + + while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { + name = nvpair_name(nvp); + nvtype = nvpair_type(nvp); + + if (!name) { + continue; + } + + if (strcmp(name, PROP_CHAP_USER) == 0) { + if (nvtype != DATA_TYPE_STRING) { + PROPERR(errs, name, + gettext("must be a string value")); + errcnt++; + continue; + } + } else if (strcmp(name, PROP_CHAP_SECRET) == 0) { + /* + * must be between 12 and 255 chars in cleartext. + * will be base64 encoded when it's set. + */ + if (nvtype == DATA_TYPE_STRING) { + val = NULL; + (void) nvpair_value_string(nvp, &val); + } + + if (!val) { + PROPERR(errs, name, + gettext("must be a string value")); + errcnt++; + continue; + } + } else { + /* unrecognized property */ + PROPERR(errs, name, gettext("unrecognized property")); + errcnt++; + } + } + + if (errcnt) { + return (EINVAL); + } + + return (0); +} + +static int +it_iqn_generate(char *iqn_buf, int iqn_buf_len, char *opt_iqn_suffix) +{ + int ret; + uuid_t id; + char id_str[UUID_PRINTABLE_STRING_LENGTH]; + + uuid_generate_random(id); + uuid_unparse(id, id_str); + + if (opt_iqn_suffix) { + ret = snprintf(iqn_buf, iqn_buf_len, DEFAULT_IQN + "%02d:%s.%s", TARGET_NAME_VERS, id_str, opt_iqn_suffix); + } else { + ret = snprintf(iqn_buf, iqn_buf_len, DEFAULT_IQN + "%02d:%s", TARGET_NAME_VERS, id_str); + } + + if (ret > iqn_buf_len) { + return (1); + } + + return (0); +} + +static int +it_val_pass(char *name, char *val, nvlist_t *e) +{ + size_t sz; + + if (!name || !val) { + return (EINVAL); + } + + /* + * must be at least 12 chars and less than 256 chars cleartext. + */ + sz = strlen(val); + + /* + * Since we will be automatically encoding secrets we don't really + * need the prefix anymore. + */ + if (sz < 12) { + PROPERR(e, name, gettext("secret too short")); + } else if (sz > 255) { + PROPERR(e, name, gettext("secret too long")); + } else { + /* all is well */ + return (0); + } + + return (1); +} + +/* + * Function: validate_iscsi_name() + * + * Ensures the passed-in string is a valid IQN or EUI iSCSI name + * + */ +boolean_t +validate_iscsi_name(char *in_name) +{ + size_t in_len; + int i; + char month[3]; + + if (in_name == NULL) { + return (B_FALSE); + } + + in_len = strlen(in_name); + if (in_len < 12) { + return (B_FALSE); + } + + if (IS_IQN_NAME(in_name)) { + /* + * IQN names are iqn.yyyy-mm.<xxx> + */ + if ((!isdigit(in_name[4])) || + (!isdigit(in_name[5])) || + (!isdigit(in_name[6])) || + (!isdigit(in_name[7])) || + (in_name[8] != '-') || + (!isdigit(in_name[9])) || + (!isdigit(in_name[10])) || + (in_name[11] != '.')) { + return (B_FALSE); + } + + (void) strncpy(month, &(in_name[9]), 2); + month[2] = '\0'; + + i = atoi(month); + if ((i < 0) || (i > 12)) { + return (B_FALSE); + } + + /* + * RFC 3722: if using only ASCII chars, only the following + * chars are allowed: dash, dot, colon, lower case a-z, 0-9. + * We allow upper case names, which should be folded + * to lower case names later. + */ + for (i = 12; i < in_len; i++) { + char c = in_name[i]; + + if ((c != '-') && (c != '.') && (c != ':') && + !isalpha(c) && !isdigit(c)) { + return (B_FALSE); + } + } + + /* Finally, validate the overall length, in wide chars */ + in_len = mbstowcs(NULL, in_name, 0); + if (in_len > ISCSI_NAME_LEN_MAX) { + return (B_FALSE); + } + } else if (IS_EUI_NAME(in_name)) { + /* + * EUI names are "eui." + 16 hex chars + */ + if (in_len != 20) { + return (B_FALSE); + } + + for (i = 4; i < in_len; i++) { + if (!isxdigit(in_name[i])) { + return (B_FALSE); + } + } + } else { + return (B_FALSE); + } + + return (B_TRUE); +} + +static boolean_t +is_iscsit_enabled(void) +{ + char *state; + + state = smf_get_state(ISCSIT_FMRI); + if (state != NULL) { + if (strcmp(state, SCF_STATE_STRING_ONLINE) == 0) { + free(state); + return (B_TRUE); + } + free(state); + } + + return (B_FALSE); +} + +/* + * Function: canonical_iscsi_name() + * + * Fold the iqn iscsi name to lower-case and the EUI-64 identifier of + * the eui iscsi name to upper-case. + * Ensures the passed-in string is a valid IQN or EUI iSCSI name + */ +void +canonical_iscsi_name(char *tgt) +{ + if (IS_IQN_NAME(tgt)) { + /* lowercase iqn names */ + iqnstr(tgt); + } else { + /* uppercase EUI-64 identifier */ + euistr(tgt); + } +} + +/* + * Fold an iqn name to lower-case. + */ +static void +iqnstr(char *s) +{ + if (s != NULL) { + while (*s) { + *s = tolower(*s); + s++; + } + } +} + +/* + * Fold the EUI-64 identifier of a eui name to upper-case. + */ +static void +euistr(char *s) +{ + if (s != NULL) { + char *l = s + 4; + while (*l) { + *l = toupper(*l); + l++; + } + } +} diff --git a/usr/src/lib/libiscsit/common/libiscsit.h b/usr/src/lib/libiscsit/common/libiscsit.h new file mode 100644 index 0000000..704c790 --- /dev/null +++ b/usr/src/lib/libiscsit/common/libiscsit.h @@ -0,0 +1,545 @@ +/* + * 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. + */ +/* + * Copyright 2011 Nexenta Systems, Inc. All rights reserved. + */ + +#ifndef _LIBISCSIT_H +#define _LIBISCSIT_H + +#ifndef _KERNEL +#include <libnvpair.h> +#include <sys/socket.h> +#endif + +#include <sys/iscsit/iscsit_common.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define MAX_TARGETS 255 /* maximum targets that may be created */ +#define MAX_TPGT 256 +#define CFG_TPGTLIST "tpgt-list" + +#define IS_IQN_NAME(s) (strncmp((s), "iqn.", 4) == 0) +#define IS_EUI_NAME(s) (strncmp((s), "eui.", 4) == 0) + +/* + * We change the default IQN here to org.illumos. + * Other distros using it need to change accordingly. + */ + +#define DEFAULT_IQN "iqn.2010-08.org.illumos:" + +/* + * Object Hierarchy + * + * _______________________ + * | | + * | iSCSI Target Config | + * | it_config_t | + * |_______________________| + * | | + * | | + * | | ________ ________ ________ + * | | | | | | | | + * | | | Target |-->| Target |-- - - -->| Target | + * | | |________| |________| |________| + * | | | + * | | | + * | | | + * | | | ______ ______ + * | | | | | | | + * | | +----->| TPGT |-- - - -->| TPGT | + * | | |______| |______| + * | | | | + * | +--+ | | + * | | _______ _______ | ______ | + * | | | | | |<--+ | |<--+ + * | +->| TPG |-->| TPG |-- - - -->| TPG | + * | |_______| |_______| |______| + * | + * | ___________ ___________ ___________ + * | | | | | | | + * +---->| Initiator |-->| Initiator |-- - - -->| Initiator | + * | Context | | Context | | Context | + * |___________| |___________| |___________| + * + * + * it_config_t includes a list of global properties + * + * Targets include a list of properties which override the global properties + * if set + * + * Initiators also include a list of properties but never inherit properties + * from the global config. + */ + +/* + * Function: it_config_load() + * + * Allocate and create an it_config_t structure representing the + * current iSCSI configuration. This structure is compiled using + * the 'provider' data returned by stmfGetProviderData(). If there + * is no provider data associated with iscsit, the it_config_t + * structure will be set to a default configuration. + * + * Parameters: + * cfg A C representation of the current iSCSI configuration + * + * Return Values: + * 0 Success + * ENOMEM Could not allocate resources + * EINVAL Invalid parameter + */ +int +it_config_load(it_config_t **cfg); + +/* + * Function: it_config_commit() + * + * Informs the iscsit service that the configuration has changed and + * commits the new configuration to persistent store by calling + * stmfSetProviderData. This function can be called multiple times + * during a configuration sequence if necessary. + * + * Parameters: + * cfg A C representation of the current iSCSI configuration + * + * Return Values: + * 0 Success + * ENOMEM Could not allocate resources + * EINVAL Invalid it_config_t structure + * STMF_ERROR_SERVICE_DATA_VERSION Configuration was updated by another + * client. See stmfSetProviderDataProt(). + */ +int +it_config_commit(it_config_t *cfg); + +/* + * Function: it_config_setprop() + * + * Validate the provided property list and set the global properties + * for iSCSI Target. If errlist is not NULL, returns detailed + * errors for each property that failed. The format for errorlist + * is key = property, value = error string. + * + * Parameters: + * + * cfg The current iSCSI configuration obtained from + * it_config_load() + * proplist nvlist_t containing properties for this target. + * errlist (optional) nvlist_t of errors encountered when + * validating the properties. + * + * Return Values: + * 0 Success + * ENOMEM Could not allocate resources + * EINVAL Invalid property + * + */ +int +it_config_setprop(it_config_t *cfg, nvlist_t *proplist, nvlist_t **errlist); + +/* + * Function: it_config_free() + * + * Free any resources associated with the it_config_t structure. + * + * Parameters: + * cfg A C representation of the current iSCSI configuration + */ +void +it_config_free(it_config_t *cfg); + +/* + * Function: it_tgt_create() + * + * Allocate and create an it_tgt_t structure representing a new iSCSI + * target node. If tgt_name is NULL, then a unique target node name will + * be generated automatically. Otherwise, the value of tgt_name will be + * used as the target node name. The new it_tgt_t structure is added to + * the target list (cfg_tgt_list) in the configuration structure, and the + * new target will not be instantiated until the modified configuration + * is committed by calling it_config_commit(). + * + * Parameters: + * cfg The current iSCSI configuration obtained from + * it_config_load() + * tgt Pointer to an iSCSI target structure + * tgt_name The target node name for the target to be created. + * The name must be in either IQN or EUI format. If + * this value is NULL, a node name will be generated + * automatically in IQN format. + * + * Return Values: + * 0 Success + * ENOMEM Could not allocate resources + * EINVAL Invalid parameter or creating would create too many + * targets. + * EEXIST The requested target node name is already configured + * EFAULT Invalid iSCSI target name + */ +int +it_tgt_create(it_config_t *cfg, it_tgt_t **tgt, char *tgt_name); + +/* + * Function: it_tgt_setprop() + * + * Validate the provided property list and set the properties for + * the specified target. If errlist is not NULL, returns detailed + * errors for each property that failed. The format for errorlist + * is key = property, value = error string. + * + * Parameters: + * + * cfg The current iSCSI configuration obtained from + * it_config_load() + * tgt Pointer to an iSCSI target structure + * proplist nvlist_t containing properties for this target. + * errlist (optional) nvlist_t of errors encountered when + * validating the properties. + * + * Return Values: + * 0 Success + * ENOMEM Could not allocate resources + * EINVAL Invalid property + * + */ +int +it_tgt_setprop(it_config_t *cfg, it_tgt_t *tgt, nvlist_t *proplist, + nvlist_t **errlist); + + +/* + * Function: it_tgt_delete() + * + * Delete target represented by 'tgt', where 'tgt' is an existing + * it_tgt_t structure within the configuration 'cfg'. The target removal + * will not take effect until the modified configuration is committed + * by calling it_config_commit(). + * + * Parameters: + * cfg The current iSCSI configuration obtained from + * it_config_load() + * tgt Pointer to an iSCSI target structure + * force Set the target to offline before removing it from + * the config. If not specified, the operation will + * fail if the target is determined to be online. + * + * Return Values: + * 0 Success + * EBUSY Target is online + */ +int +it_tgt_delete(it_config_t *cfg, it_tgt_t *tgt, boolean_t force); + +/* + * Function: it_tpgt_create() + * + * Allocate and create an it_tpgt_t structure representing a new iSCSI + * target portal group tag. The new it_tpgt_t structure is added to the + * target tpgt list (tgt_tpgt_list) in the it_tgt_t structure. The new + * target portal group tag will not be instantiated until the modified + * configuration is committed by calling it_config_commit(). + * + * Parameters: + * cfg The current iSCSI configuration obtained from + * it_config_load() + * tgt Pointer to the iSCSI target structure associated + * with the target portal group tag + * tpgt Pointer to a target portal group tag structure + * tpg_name The name of the TPG to be associated with this TPGT + * tpgt_tag 16-bit numerical identifier for this TPGT. Valid + * values are 2 through 65535. If tpgt_tag is '0', + * this function will assign an appropriate tag number. + * If tpgt_tag is != 0, and the requested number is + * unavailable, another value will be chosen. + * + * Return Values: + * 0 Success + * ENOMEM Could not allocate resources + * EINVAL Invalid parameter + * EEXIST Specified TPG is already associated with the target + * E2BIG All tag numbers already in use + */ +int +it_tpgt_create(it_config_t *cfg, it_tgt_t *tgt, it_tpgt_t **tpgt, + char *tpg_name, uint16_t tpgt_tag); + +/* + * Function: it_tpgt_delete() + * + * Delete the target portal group tag represented by 'tpgt', where + * 'tpgt' is an existing is_tpgt_t structure within the target 'tgt'. + * The target portal group tag removal will not take effect until the + * modified configuation is committed by calling it_config_commit(). + * + * Parameters: + * cfg The current iSCSI configuration obtained from + * it_config_load() + * tgt Pointer to the iSCSI target structure associated + * with the target portal group tag + * tpgt Pointer to a target portal group tag structure + */ +void +it_tpgt_delete(it_config_t *cfg, it_tgt_t *tgt, it_tpgt_t *tpgt); + +/* + * Function: it_tpg_create() + * + * Allocate and create an it_tpg_t structure representing a new iSCSI + * target portal group. The new it_tpg_t structure is added to the global + * tpg list (cfg_tgt_list) in the it_config_t structure. The new target + * portal group will not be instantiated until the modified configuration + * is committed by calling it_config_commit(). + * + * Parameters: + * cfg The current iSCSI configuration obtained from + * it_config_load() + * tpg Pointer to the it_tpg_t structure representing + * the target portal group + * tpg_name Identifier for the target portal group + * portal_ip_port A string containing an appropriatedly formatted + * IP address:port. Both IPv4 and IPv6 addresses are + * permitted. This value becomes the first portal in + * the TPG -- applications can add additional values + * using it_portal_create() before committing the TPG. + * Return Values: + * 0 Success + * ENOMEM Cannot allocate resources + * EINVAL Invalid parameter + * EEXIST Portal already configured for another portal group + * associated with this target. + */ +int +it_tpg_create(it_config_t *cfg, it_tpg_t **tpg, char *tpg_name, + char *portal_ip_port); + +/* + * Function: it_tpg_delete() + * + * Delete target portal group represented by 'tpg', where 'tpg' is an + * existing it_tpg_t structure within the global configuration 'cfg'. + * The target portal group removal will not take effect until the + * modified configuration is committed by calling it_config_commit(). + * + * Parameters: + * cfg The current iSCSI configuration obtained from + * it_config_load() + * tpg Pointer to the it_tpg_t structure representing + * the target portal group + * force Remove this target portal group even if it's + * associated with one or more targets. + * + * Return Values: + * 0 Success + * EINVAL Invalid parameter + * EBUSY Portal group associated with one or more targets. + */ +int +it_tpg_delete(it_config_t *cfg, it_tpg_t *tpg, boolean_t force); + +/* + * Function: it_portal_create() + * + * Add an it_portal_t structure representing a new portal to the specified + * target portal group. The change to the target portal group will not take + * effect until the modified configuration is committed by calling + * it_config_commit(). + * + * Parameters: + * cfg The current iSCSI configration obtained from + * it_config_load() + * tpg Pointer to the it_tpg_t structure representing the + * target portal group or "none" to remove + * portal Pointer to the it_portal_t structure representing + * the portal + * portal_ip_port A string containing an appropriately formatted + * IP address or IP address:port in either IPv4 or + * IPv6 format. + * Return Values: + * 0 Success + * ENOMEM Could not allocate resources + * EINVAL Invalid parameter + * EEXIST Portal already configured for another portal group + */ +int +it_portal_create(it_config_t *cfg, it_tpg_t *tpg, it_portal_t **portal, + char *portal_ip_port); + +/* + * Function: it_portal_delete() + * + * Remove the specified portal from the specified target portal group. + * The portal removal will not take effect until the modified configuration + * is committed by calling it_config_commit(). + * + * Parameters: + * cfg The current iSCSI configration obtained from + * it_config_load() + * tpg Pointer to the it_tpg_t structure representing the + * target portal group + * portal Pointer to the it_portal_t structure representing + * the portal + */ +void +it_portal_delete(it_config_t *cfg, it_tpg_t *tpg, it_portal_t *portal); + +/* + * Function: it_ini_create() + * + * Add an initiator context to the global configuration. The new + * initiator context will not be instantiated until the modified + * configuration is committed by calling it_config_commit(). + * + * Parameters: + * cfg The current iSCSI configration obtained from + * it_config_load() + * ini Pointer to the it_ini_t structure representing + * the initiator context. + * ini_node_name The iSCSI node name of the remote initiator. + * + * Return Values: + * 0 Success + * ENOMEM Could not allocate resources + * EINVAL Invalid parameter. + * EEXIST Initiator already configured + * EFAULT Invalid initiator name + */ +int +it_ini_create(it_config_t *cfg, it_ini_t **ini, char *ini_node_name); + +/* + * Function: it_ini_setprop() + * + * Validate the provided property list and set the initiator properties. + * If errlist is not NULL, returns detailed errors for each property + * that failed. The format for errorlist is + * key = property, value = error string. + * + * Parameters: + * + * ini The initiator being updated. + * proplist nvlist_t containing properties for this target. + * errlist (optional) nvlist_t of errors encountered when + * validating the properties. + * + * Return Values: + * 0 Success + * ENOMEM Could not allocate resources + * EINVAL Invalid property + * + */ +int +it_ini_setprop(it_ini_t *ini, nvlist_t *proplist, nvlist_t **errlist); + +/* + * Function: it_ini_delete() + * + * Remove the specified initiator context from the global configuration. + * The removal will not take effect until the modified configuration is + * committed by calling it_config_commit(). + * + * Parameters: + * cfg The current iSCSI configration obtained from + * it_config_load() + * ini Pointer to the it_ini_t structure representing + * the initiator context. + */ +void +it_ini_delete(it_config_t *cfg, it_ini_t *ini); + +/* + * Function: it_config_free() + * + * Free any resources associated with the it_config_t structure. + * + * Parameters: + * cfg A C representation of the current iSCSI configuration + */ +void +it_config_free(it_config_t *cfg); + +/* + * Function: it_tgt_free() + * + * Frees an it_tgt_t structure. If tgt_next is not NULL, frees + * all structures in the list. + */ +void +it_tgt_free(it_tgt_t *tgt); + +/* + * Function: it_tpgt_free() + * + * Deallocates resources of an it_tpgt_t structure. If tpgt->next + * is not NULL, frees all members of the list. + */ +void +it_tpgt_free(it_tpgt_t *tpgt); + +/* + * Function: it_tpg_free() + * + * Deallocates resources associated with an it_tpg_t structure. + * If tpg->next is not NULL, frees all members of the list. + */ +void +it_tpg_free(it_tpg_t *tpg); + +/* + * Function: it_ini_free() + * + * Deallocates resources of an it_ini_t structure. If ini->next is + * not NULL, frees all members of the list. + */ +void +it_ini_free(it_ini_t *ini); + +/* + * Function: validate_iscsi_name() + * + * Ensures the passed-in string is a valid IQN or EUI iSCSI name + */ +boolean_t +validate_iscsi_name(char *in_name); + +/* + * Function: canonical_iscsi_name() + * + * Fold the iqn iscsi name to lower-case and the EUI-64 identifier of + * the eui iscsi name to upper-case. + * Ensures the passed-in string is a valid IQN or EUI iSCSI name + */ +void +canonical_iscsi_name(char *tgt); + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBISCSIT_H */ diff --git a/usr/src/man/man1m/itadm.1m b/usr/src/man/man1m/itadm.1m new file mode 100644 index 0000000..12a3e7e --- /dev/null +++ b/usr/src/man/man1m/itadm.1m @@ -0,0 +1,943 @@ +'\" te +.\" Copyright (c) 2008, Sun Microsystems, Inc. All Rights Reserved +.\" 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] +.TH ITADM 1M "Aug 4, 2009" +.SH NAME +itadm \- administer iSCSI targets +.SH SYNOPSIS +.LP +.nf +\fBitadm\fR create-target [\fB-a\fR,\fB--auth-method\fR radius | chap | none | default] + [\fB-s\fR,\fB--chap-secret\fR] [\fB-S\fR,\fB--chap-secret-file\fR \fIpath\fR] + [\fB-u\fR,\fB--chap-user\fR \fIchap-user-name\fR] [\fB-n\fR,\fB--node-name\fR \fItarget_node_name\fR] + [\fB-l\fR,\fB--alias\fR \fIalias\fR] [\fB-t\fR,\fB--tpg\fR \fItpg-name\fR[,\fItpg-name\fR]] +.fi + +.LP +.nf +\fBitadm\fR modify-target [\fB-a\fR,\fB--auth-method\fR radius | chap | none | default] + [\fB-s\fR,\fB--chap-secret\fR] [\fB-S\fR,\fB--chap-secret-file\fR \fIpath\fR] + [\fB-u\fR,\fB--chap-user\fR \fIchap-user-name\fR] [\fB-n\fR,\fB--node-name\fR \fInew_target_node_name\fR] + [\fB-l\fR,\fB--alias\fR \fIalias\fR] [\fB-t\fR,\fB--tpg\fR \fItpg-name\fR[,\fItpg-name\fR]] \fItarget_node_name\fR +.fi + +.LP +.nf +\fBitadm\fR delete-target [\fB-f\fR,\fB--force\fR] \fItarget_node_name\fR +.fi + +.LP +.nf +\fBitadm\fR list-target [\fB-v\fR,\fB--verbose\fR] [\fItarget_node_name\fR] +.fi + +.LP +.nf +\fBitadm\fR create-tpg \fItpg_name\fR \fIIP-address\fR[:\fIport\fR] [\fIIP-address\fR[:\fIport\fR]]... +.fi + +.LP +.nf +\fBitadm\fR list-tpg [\fB-v\fR,\fB--verbose\fR] [\fItpg_name\fR] +.fi + +.LP +.nf +\fBitadm\fR delete-tpg [\fB-f\fR,\fB--force\fR] \fItpg_name\fR +.fi + +.LP +.nf +\fBitadm\fR create-initiator [\fB-s\fR,\fB--chap-secret\fR] [\fB-S\fR,\fB--chap-secret-file\fR \fIpath\fR] + [\fB-u\fR,\fB--chap-user\fR \fIchap-user-name\fR] \fIinitiator_node_name\fR +.fi + +.LP +.nf +\fBitadm\fR modify-initiator [\fB-s\fR,\fB--chap-secret\fR] [\fB-S\fR,\fB--chap-secret-file\fR \fIpath\fR] + [\fB-u\fR,\fB--chap-user\fR \fIchap-user-name\fR] \fIinitiator_node_name\fR +.fi + +.LP +.nf +\fBitadm\fR list-initiator [\fB-v\fR,\fB--verbose\fR] \fIinitiator_node_name\fR +.fi + +.LP +.nf +\fBitadm\fR delete-initiator \fIinitiator_node_name\fR +.fi + +.LP +.nf +\fBitadm\fR modify-defaults [\fB-a\fR,\fB--auth-method\fR radius | chap | none] + [\fB-r\fR,\fB--radius-server\fR \fIIP-address\fR[:\fIport\fR]] [\fB-d\fR,\fB--radius-secret\fR] + [\fB-D\fR,\fB--radius-secret-file\fR \fIpath\fR][\fB-i\fR,\fB--isns\fR enable | disable] + [\fB-I\fR,\fB--isns-server\fR \fIIP-address\fR[:\fIport\fR][,\fIIP-address\fR[:\fIport\fR]]] +.fi + +.LP +.nf +\fBitadm\fR list-defaults +.fi + +.SH DESCRIPTION +.sp +.LP +The \fBitadm\fR command manages Internet SCSI (iSCSI) target nodes within the +SCSI Target Mode Framework described in \fBstmfadm\fR(1M) and +\fBlibstmf\fR(3LIB). This allows the iSCSI initiators to access STMF logical +units using the iSCSI protocol. In addition to iSCSI target nodes, \fBitadm\fR +manages two other classes of managed objects: iSCSI Target Portal Groups, and +iSCSI Initiator Node Contexts. +.sp +.LP +\fBitadm\fR is implemented as a set of subcommands with options and operands +for each subcommand. These subcommands are described in their own section, +below. In addition to its subcommands, \fBitadm\fR has a help command, which +displays the utility's usage information. The help command is invoked with the +\fB-?\fR option. +.SS "iSCSI Target Portal Groups" +.sp +.LP +An iSCSI Target Network Portal is an IP address and TCP port that can be used +by an initiator node to connect to an iSCSI target. A collection of these +portals is called a Target Portal Group (TPG). You can use a TPG to limit +access to an iSCSI target. Use the \fBitadm modify\fR \fB-t\fR command to bind +a specific iSCSI target to the TPG. An iSCSI listener is created on each IP +address that belongs to the TPG, and listens for connections to the iSCSI +target. +.sp +.LP +A TPG is identified by a unique name provided when the TPG is created. A +numerical "Target Portal Group Tag" from the range 2-65535 is automatically +generated when the TPG is created. The Target Portal Group Tag 1 is reserved +for the "default" target portal group that is used when no explicit Target +Portal Groups are set on the target. The portal for the default TPG matches +requests from all network interfaces on port 3260. +.SS "iSCSI Initiator Node Contexts" +.sp +.LP +Certain operations such as authentication by means of Challenge Handshake +Authentication Protocol (CHAP) require parameters associated with a remote +iSCSI Initiator Node. These parameters are associated with an iSCSI Initiator +Node Context. An iSCSI Initiator Node Context is identified by its Initiator +Node Name, formatted in either IQN or EUI format (see RFC 3720). For example: +.sp +.in +2 +.nf +iqn.1986-03.com.sun:01:e00000000000.47d55444 +eui.02004567A425678D +.fi +.in -2 +.sp + +.SS "Specifying IP Addresses" +.sp +.LP +A number of \fBitadm\fR subcommands require that you specify one or more IP +addresses with optional port numbers. For IPv4, use standard dotted decimal +notation. For IPv6, enclose addresses in square brackets. The following are +example specifications. +.sp +.in +2 +.nf +\fBIPv4\fR: 10.2.4.1 + 10.2.4.1:3260 +\fBIPv6\fR: [1080:0:0:0:8:800:200C:417A] + [1080:0:0:0:8:800:200C:417A]:3260 +.fi +.in -2 +.sp + +.SH SUB-COMMANDS +.sp +.LP +The following are the \fBitadm\fR subcommands with their options. +.SS "\fBitadm create-target\fR" +.sp +.in +2 +.nf +itadm create-target [-a,--auth-method radius | chap | none | default] + [-s,--chap-secret] + [-S,--chap-secret-file \fIpath\fR] [-u,--chap-user \fIchap-user-name\fR] + [-n,--node-name \fItarget_node_name\fR] [-l,--alias \fIalias\fR] + [-t,--tpg \fItpg-name\fR[,\fItpg-name\fR,...]] +.fi +.in -2 +.sp + +.sp +.LP +Create a iSCSI target with the specified options. Options are as follows. +.sp +.ne 2 +.na +\fB\fB-a\fR,\fB--auth-method\fR \fBradius | chap | none | default\fR\fR +.ad +.sp .6 +.RS 4n +Specifies the authentication method to use for the target. Valid values are +\fBradius\fR, \fBchap\fR, and \fBnone\fR. \fBchap\fR indicates that initiators +connecting to this target must be authenticated using the Challenge Handshake +Authentication Protocol (CHAP). \fBradius\fR indicates initiators should also +be authenticated by means of CHAP but the required authentication parameters +should be obtained from a central RADIUS server (see the \fBradius-server\fR +and \fBradius-secret\fR options). \fBnone\fR means that no authentication is +required to connect to the target. \fBdefault\fR means the target will use the +global setting of this property. (See the \fBmodify-defaults\fR subcommand.) +.RE + +.sp +.ne 2 +.na +\fB\fB-s\fR,\fB--chap-secret\fR\fR +.ad +.sp .6 +.RS 4n +The CHAP secret to send during mutual CHAP authentication. There is no default +for this property. Maximum length is 255 characters; minimum required length is +12 characters. +.RE + +.sp +.ne 2 +.na +\fB\fB-S\fR,\fB--chap-secret-file\fR \fIpath\fR\fR +.ad +.sp .6 +.RS 4n +Path to a temporary file containing the CHAP secret as described in the +\fB-s\fR option. +.RE + +.sp +.ne 2 +.na +\fB\fB-u\fR,\fB--chap-user\fR \fIchap-user-name\fR\fR +.ad +.sp .6 +.RS 4n +Specifies the CHAP username for a target for use in mutual CHAP authentication. +This value is allowed only for targets, cannot be set globally, and is used +only when the initiator node is configured to use mutual CHAP authentication. +If no value is specified then the target node name is used as the username. See +\fBiscsiadm\fR(1M). +.RE + +.sp +.ne 2 +.na +\fB\fB-n\fR,\fB--node-name\fR \fItarget_node_name\fR\fR +.ad +.sp .6 +.RS 4n +An iSCSI Target Node is identified by its Target Node Name, formatted in either +IQN or EUI format (see RFC 3720). This option establishes that name. +.RE + +.sp +.ne 2 +.na +\fB\fB-l\fR,\fB--alias\fR \fIalias\fR\fR +.ad +.sp .6 +.RS 4n +An alternate identifier associated with a target node. The identifier does not +need to be unique. +.RE + +.sp +.ne 2 +.na +\fB\fB-t\fR,\fB--tpg\fR \fItpg-name\fR[,\fItpg-name\fR,...]\fR +.ad +.sp .6 +.RS 4n +A list of Target Portal Group (TPG) identifiers that specifies the TPGs that an +initiator can use to access a specific target or the keyword \fBdefault\fR. If +\fBdefault\fR is specified, the target will use the default portal, +\fBINADDR_ANY:3260\fR. +.RE + +.SS "\fBitadm modify-target\fR" +.sp +.in +2 +.nf +itadm modify-target [\fB-a\fR,\fB--auth-method\fR radius | chap | none | default] + [\fB-s\fR,\fB--chap-secret\fR] [\fB-S\fR,\fB--chap-secret-file\fR \fIpath\fR] + [\fB-u\fR,\fB--chap-user\fR \fIchap-user-name\fR] [\fB-n\fR,\fB--node-name\fR \fInew_tgt_node_name\fR] + [\fB-l\fR,\fB--alias\fR \fIalias\fR] [\fB-t\fR,\fB--tpg\fR \fItpg-name\fR[,\fItpg-name\fR]] \fItarget_node_name\fR +.fi +.in -2 +.sp + +.sp +.LP +Modify an iSCSI target according to the specified options. Options are as +follows. +.sp +.ne 2 +.na +\fB\fB-a\fR,\fB--auth-method\fR \fBradius | chap | none | default\fR\fR +.ad +.sp .6 +.RS 4n +As described under the \fBcreate-target\fR subcommand, above. +.RE + +.sp +.ne 2 +.na +\fB\fB-s\fR,\fB--chap-secret\fR\fR +.ad +.sp .6 +.RS 4n +As described under the \fBcreate-target\fR subcommand, above. +.RE + +.sp +.ne 2 +.na +\fB\fB-S\fR,\fB--chap-secret-file\fR \fIpath\fR\fR +.ad +.sp .6 +.RS 4n +As described under the \fBcreate-target\fR subcommand, above. +.RE + +.sp +.ne 2 +.na +\fB\fB-u\fR,\fB--chap-user\fR \fIchap-user-name\fR\fR +.ad +.sp .6 +.RS 4n +As described under the \fBcreate-target\fR subcommand, above. To remove an +explicitly set CHAP username use \fB-u\fR \fBnone\fR. +.RE + +.sp +.ne 2 +.na +\fB\fB-n\fR,\fB--node-name\fR \fItarget_node_name\fR\fR +.ad +.sp .6 +.RS 4n +Renames the target. See also the description of \fB-n\fR under the +\fBcreate-target\fR subcommand, above. +.RE + +.sp +.ne 2 +.na +\fB\fB-l\fR,\fB--alias\fR \fIalias\fR\fR +.ad +.sp .6 +.RS 4n +As described under the \fBcreate-target\fR subcommand, above. To remove an +explicitly set alias use \fB-l\fR \fBnone\fR. +.RE + +.sp +.ne 2 +.na +\fB\fB-t\fR,\fB--tpg\fR \fItpg-name\fR[,\fItpg-name\fR,...]\fR +.ad +.sp .6 +.RS 4n +As described under the \fBcreate-target\fR subcommand, above. +.RE + +.SS "\fBitadm list-target\fR" +.sp +.in +2 +.nf +itadm list-target [\fB-v\fR,\fB--verbose\fR] [\fItarget_node_name\fR] +.fi +.in -2 +.sp + +.sp +.LP +List information about the configured targets. If \fItarget_node_name\fR is +specified, list only the information for that target. Option is as follows. +.sp +.ne 2 +.na +\fB\fB-v\fR,\fB--verbose\fR\fR +.ad +.sp .6 +.RS 4n +Verbose mode. +.RE + +.SS "\fBitadm delete-target\fR" +.sp +.in +2 +.nf +itadm delete-target [\fB-f\fR,\fB--force\fR] \fItarget_node_name\fR +.fi +.in -2 +.sp + +.sp +.LP +Delete the target specified by \fItarget_node_name\fR. The target must be +\fBoffline\fR before it can be deleted. Option is as follows. +.sp +.ne 2 +.na +\fB\fB-f\fR,\fB--force\fR\fR +.ad +.sp .6 +.RS 4n +If the target persists in an online state, this option attempts to offline the +target before deleting it. +.RE + +.SS "\fBitadm create-tpg\fR" +.sp +.in +2 +.nf +itadm create-tpg \fItpg_name\fR \fIIP-address\fR[:\fIport\fR]... +.fi +.in -2 +.sp + +.sp +.LP +Create an iSCSI target portal group made up of the specified portals and assign +it the identifier \fItpg_name\fR. Each portal is an IP address and port pair. +IPv4 portals are specified in dotted address notation, for example, +\fB172.31.255.255\fR. IPv6 portal addresses must be enclosed in square +brackets. +.sp +.LP +This subcommand has no options. +.SS "\fBitadm list-tpg\fR" +.sp +.in +2 +.nf +itadm list-tpg [\fB-v\fR,\fB--verbose\fR] [\fItpg_name\fR] +.fi +.in -2 +.sp + +.sp +.LP +List information about the configured target portal group. If \fItpg_name\fR is +specified then list only the information about the target portal group +associated with that \fItpg_name\fR. Option is as follows. +.sp +.ne 2 +.na +\fB\fB-v\fR,\fB--verbose\fR\fR +.ad +.sp .6 +.RS 4n +Verbose mode. +.RE + +.SS "\fBitadm delete-tpg\fR" +.sp +.in +2 +.nf +itadm delete-tpg [\fB-f\fR,\fB--force\fR] \fItpg_name\fR +.fi +.in -2 +.sp + +.sp +.LP +Delete the target portal group associated with \fItpg_name\fR. Option is as +follows. +.sp +.ne 2 +.na +\fB\fB-f\fR,\fB--force\fR\fR +.ad +.sp .6 +.RS 4n +If the TPG is associated with any targets, the request to delete will be denied +unless this option is specified. +.RE + +.SS "\fBitadm create-initiator\fR" +.sp +.in +2 +.nf +itadm create-initiator [\fB-s\fR,\fB--chap-secret\fR] [\fB-S\fR,\fB--chap-secret-file\fR \fIpath\fR] + [\fB-u\fR,\fB--chap-user\fR \fIchap-user-name\fR] \fIinitiator_node_name\fR +.fi +.in -2 +.sp + +.sp +.LP +Configure parameters associated with the remote initiator named +\fIinitiator_node_name\fR. Options are as follows. +.sp +.ne 2 +.na +\fB\fB-s\fR,\fB--chap-secret\fR\fR +.ad +.sp .6 +.RS 4n +As described under the \fBcreate-target\fR subcommand, above. +.RE + +.sp +.ne 2 +.na +\fB\fB-S\fR,\fB--chap-secret-file\fR \fIpath\fR\fR +.ad +.sp .6 +.RS 4n +As described under the \fBcreate-target\fR subcommand, above. +.RE + +.sp +.ne 2 +.na +\fB\fB-u\fR,\fB--chap-user\fR \fIchap-user-name\fR\fR +.ad +.sp .6 +.RS 4n +Specifies the CHAP username for an initiator, for use in CHAP authentication. +If no value is specified then the initiator node name is used as the username. +.RE + +.SS "\fBitadm modify-initiator\fR" +.sp +.in +2 +.nf +itadm modify-initiator [\fB-s\fR,\fB--chap-secret\fR] [\fB-S\fR,\fB--chap-secret-file\fR \fIpath\fR] + [\fB-u\fR,\fB--chap-user\fR \fIchap-user-name\fR] \fIinitiator_node_name\fR +.fi +.in -2 +.sp + +.sp +.LP +Modify parameters associated with the remote initiator named +\fIinitiator_node_name\fR. Options are as follows. +.sp +.ne 2 +.na +\fB\fB-s\fR,\fB--chap-secret\fR\fR +.ad +.sp .6 +.RS 4n +As described under the \fBcreate-target\fR subcommand, above. +.RE + +.sp +.ne 2 +.na +\fB\fB-S\fR,\fB--chap-secret-file\fR \fIpath\fR\fR +.ad +.sp .6 +.RS 4n +As described under the \fBcreate-target\fR subcommand, above. +.RE + +.sp +.ne 2 +.na +\fB\fB-u\fR,\fB--chap-user\fR \fIchap-user-name\fR\fR +.ad +.sp .6 +.RS 4n +Specifies the CHAP username for an initiator, for use in CHAP authentication. +If no value is specified then the initiator node name is used as the username. +.RE + +.SS "\fBitadm delete-initiator\fR" +.sp +.in +2 +.nf +itadm delete-initiator \fIinitiator_node_name\fR +.fi +.in -2 +.sp + +.sp +.LP +Delete parameters associated with the remote initiator named +\fIinitiator_node_name\fR. This subcommand has no options. +.SS "\fBitadm list-initiator\fR" +.sp +.in +2 +.nf +\fBitadm\fR list-initiator [\fB-v\fR,\fB--verbose\fR] \fIinitiator_node_name\fR +.fi +.in -2 +.sp + +.sp +.LP +List parameters associated with the initiator named \fIinitiator_node_name\fR. +Option is as follows. +.sp +.ne 2 +.na +\fB\fB-v\fR,\fB--verbose\fR\fR +.ad +.sp .6 +.RS 4n +Verbose mode. +.RE + +.SS "\fBitadm modify-defaults\fR" +.sp +.in +2 +.nf +itadm modify-defaults [\fB-a\fR,\fB--auth-method\fR radius | chap | none] + [\fB-r\fR,\fB--radius-server\fR \fIIP-address\fR[:\fIport\fR]] [\fB-d\fR,\fB--radius-secret\fR] + [\fB-D\fR,\fB--radius-secret-file\fR \fIpath\fR][\fB-i\fR,\fB--isns\fR enable | disable] + [\fB-I\fR,\fB--isns-server\fR \fIIP-address\fR[:\fIport\fR][,\fIIP-address\fR[:\fIport\fR]]] +.fi +.in -2 +.sp + +.sp +.LP +Modify default parameters. Options are as follows. +.sp +.ne 2 +.na +\fB\fB-a\fR,\fB--auth-method\fR \fBradius | chap | none\fR\fR +.ad +.sp .6 +.RS 4n +Specifies the default authentication method to use for all targets. Valid +values are \fBradius\fR, \fBchap\fR, and \fBnone\fR. \fBchap\fR indicates that +initiators connecting to this target must be authenticated using Challenge +Handshake Authentication Protocol (CHAP). \fBradius\fR indicates initiators +should also be authenticated by means of CHAP, but the required authentication +parameters should be obtained from a central RADIUS server. (See +\fB--radius-server\fR and \fB--radius-secret\fR options.) \fBnone\fR means that +no authentication is required to connect to the target. Individual targets can +override this global setting using the \fB-a\fR option of the +\fBcreate-target\fR and \fBmodify-target\fR subcommands. +.RE + +.sp +.ne 2 +.na +\fB\fB-d\fR,\fB--radius-secret\fR\fR +.ad +.sp .6 +.RS 4n +RADIUS Shared Secret for centralized CHAP authentication. +.RE + +.sp +.ne 2 +.na +\fB\fB-D\fR,\fB--radius-secret-file\fR \fIpath\fR\fR +.ad +.sp .6 +.RS 4n +Path to a temporary file containing the CHAP secret as described in the +\fB-d\fR option. +.RE + +.sp +.ne 2 +.na +\fB\fB-i\fR,\fB--sns\fR \fBenable | disable\fR\fR +.ad +.sp .6 +.RS 4n +Specifies whether targets should be registered with the set of defined iSCSI +Name Service (iSNS) servers. +.RE + +.sp +.ne 2 +.na +\fB\fB-I\fR,\fB--isns-server\fR +\fIIP-address\fR[:\fIport\fR][,\fIIP-address\fR[:\fIport\fR],...]\fR +.ad +.sp .6 +.RS 4n +Defines a list of iSNS servers with which iSCSI target nodes will be registered +when the \fBisns\fR option associated with the respective target is set. Up to +eight iSNS servers can be specified. To remove all iSNS servers, use \fB-I\fR +\fBnone\fR. +.RE + +.sp +.ne 2 +.na +\fB\fB-r\fR,\fB--radius-server\fR \fIIP-address\fR[:\fIport\fR]\fR +.ad +.sp .6 +.RS 4n +Specify the IP address of the RADIUS server used for centralized CHAP +authentication. +.RE + +.SS "\fBitadm list-defaults\fR" +.sp +.in +2 +.nf +itadm list-defaults +.fi +.in -2 +.sp + +.sp +.LP +List information about the default properties. This subcommand has no options. +.SH EXAMPLES +.LP +\fBExample 1 \fRCreating a Target +.sp +.LP +The following command creates a target. + +.sp +.in +2 +.nf +# \fBitadm create-target\fR +Target iqn.1986-03.com.sun:02:72e1b181-7bce-c0e6-851e-ec0d8cf14b7a +successfully created +.fi +.in -2 +.sp + +.LP +\fBExample 2 \fRCreating a Target with a Specific Name +.sp +.LP +The following command creates a target with a specific IQN. + +.sp +.in +2 +.nf +# \fBitadm create-target -n eui.20387ab8943ef7548\fR +or: +# \fBitadm create-target \e +-n iqn.1986-03.com.sun:02:a9a366f8-cc2b-f291-840948c7f29e\fR +.fi +.in -2 +.sp + +.LP +\fBExample 3 \fRChanging a Name +.sp +.LP +The following command changes an IQN for an existing target. + +.sp +.in +2 +.nf +# \fBitadm modify-target -n eui.20387ab8943ef7548 \e +iqn.1986-03.com.sun:02:a9a366f8-909b-cc2b-f291-840948c7f29e\fR +.fi +.in -2 +.sp + +.LP +\fBExample 4 \fRSetting up CHAP Authentication +.sp +.LP +The following command sets up CHAP authentication for a target using the +default CHAP username. + +.sp +.in +2 +.nf +# \fBitadm modify-initiator -s iqn.1986-03.com.sun:01:e00000000000.47d55444\fR +\fBEnter CHAP secret:\fR ********* +\fBRe-enter secret:\fR ********* + +# \fBitadm modify-target -a chap eui.20387ab8943ef7548\fR +.fi +.in -2 +.sp + +.LP +\fBExample 5 \fRCreating Target Portal Groups +.sp +.LP +The following command creates two target portal groups, \fBA\fR and \fBB\fR, +using port 8000 for the addresses in TPG 2. + +.sp +.in +2 +.nf +# \fBitadm create-tpg A 192.168.0.1 192.168.0.2\fR +# \fBitadm create-tpg B 192.168.0.2:8000 192.168.0.2:8000\fR +.fi +.in -2 +.sp + +.LP +\fBExample 6 \fRConfiguring a Target to Use TPGs +.sp +.LP +The following command configures a target to use TPGs A and B. + +.sp +.in +2 +.nf +# \fBitadm modify-target -t A,B eui.20387ab8943ef7548\fR +.fi +.in -2 +.sp + +.LP +\fBExample 7 \fRSetting up RADIUS Authentication for Specific Target +.sp +.LP +The following command sets up RADIUS authentication for a specific target. + +.sp +.in +2 +.nf +# \fBitadm modify-defaults -r 192.168.10.1 -d\fR +\fBEnter RADIUS secret:\fR ********* +\fBRe-enter secret:\fR ********* + +# \fBitadm modify-target -a radius eui.20387ab8943ef7548\fR +.fi +.in -2 +.sp + +.LP +\fBExample 8 \fRSetting up RADIUS Authentication for All Targets +.sp +.LP +The following command sets up RADIUS authentication for all targets. + +.sp +.in +2 +.nf +# \fBitadm modify-defaults -d -r 192.168.10.1 -a radius\fR +\fBEnter RADIUS secret:\fR ********* +\fBRe-enter secret:\fR ********* +.fi +.in -2 +.sp + +.sp +.LP +The preceding command assumes all targets were created with \fB-a\fR +\fBdefault\fR. + +.LP +\fBExample 9 \fRListing Default Properties +.sp +.LP +The following command lists default properties. + +.sp +.in +2 +.nf +# \fBitadm list-defaults\fR +iSCSI Target Default Properties: + +alias: \fInone\fR +auth: \fInone\fR +radiusserver: \fInone\fR +radiussecret: unset +isns: disabled +isnsserver: 2.3.4.5,4.5.6.7 +.fi +.in -2 +.sp + +.LP +\fBExample 10 \fRListing Targets +.sp +.LP +The following command lists targets. + +.sp +.in +2 +.nf +# \fBitadm list-target\fR +TARGET NAME STATE SESSIONS +iqn.1986-03.com.sun:02:72e1b181-7bce-c0e6-851e-ec0d8cf14b7a online 0 +iqn.1986-03.com.sun:02:2cb0c526-c05a-e279-e396-a367006f4227 online 0 +iqn.1986-03.com.sun:02:d14125bb-1c9d-c28d-97b0-f89259b642f3 online 0 +iqn.1986-03.com.sun:02:03ff9fc5-794a-e9b4-a081-bb82917c292a online 0 +.fi +.in -2 +.sp + +.LP +\fBExample 11 \fRListing Targets (Verbose) +.sp +.LP +The following command lists targets with the verbose option. + +.sp +.in +2 +.nf +# \fBitadm list-target -v\fR +TARGET NAME STATE SESSIONS +iqn.1986-03.com.sun:02:d23e68d8-2d79-c988-98e7-a6361689d33c online 0 + alias: - + auth: none (defaults) + targetchapuser: - + targetchapsecret: unset + tpg-tags: default +iqn.1986-03.com.sun:02:94ec46d4-c8e1-6993-ef03-ffc1dcd66606 online 1 + alias: - + auth: chap + targetchapuser: - + targetchapsecret: unset + tpg-tags: nge1_ipv4 = 3 +.fi +.in -2 +.sp + +.LP +\fBExample 12 \fRListing a Specific Target +.sp +.LP +The following command lists targets with the verbose option. + +.sp +.in +2 +.nf +# \fBitadm list-target -v \e +iqn.1986-03.com.sun:02:2cb0c526-c05a-e279-e396-a367006f4227\fR +TARGET NAME STATE SESSIONS +iqn.1986-03.com.sun:02:2cb0c526-c05a-e279-e396-a367006f4227 online 1 + alias: - + auth: chap + targetchapuser: - + targetchapsecret: unset + tpg-tags: nge1_ipv4 = 3 +.fi +.in -2 +.sp + +.SH ATTRIBUTES +.sp +.LP +See \fBattributes\fR(5) for descriptions of the following attributes: +.sp + +.sp +.TS +box; +c | c +l | l . +ATTRIBUTE TYPE ATTRIBUTE VALUE +_ +Interface Stability Committed +.TE + +.SH SEE ALSO +.sp +.LP +\fBiscsiadm\fR(1M), \fBstmfadm\fR(1M), \fBlibstmf\fR(3LIB), \fBattributes\fR(5) diff --git a/usr/src/man/man3iscsit/it_config_load.3iscsit b/usr/src/man/man3iscsit/it_config_load.3iscsit new file mode 100644 index 0000000..046fed8 --- /dev/null +++ b/usr/src/man/man3iscsit/it_config_load.3iscsit @@ -0,0 +1,182 @@ +'\" te +.\" Copyright (c) 2008, Sun Microsystems Inc. All Rights Reserved. +.\" 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] +.TH IT_CONFIG_LOAD 3ISCSIT "Oct 1, 2008" +.SH NAME +it_config_load, it_config_commit, it_config_setprop, it_config_free \- set and +retrieve configuration data for the iSCSI Target Port Provider +.SH SYNOPSIS +.LP +.nf +cc [ \fIflag\fR\&.\|.\|. ] \fIfile\fR\&.\|.\|. \fB-liscsit\fR [ \fIlibrary\fR\&.\|.\|. ] +#include <libiscsit.h> + +\fBint\fR \fBit_config_load\fR(\fBit_config_t **\fR\fIcfg\fR); +.fi + +.LP +.nf +\fBint\fR \fBit_config_commit\fR(\fBit_config_t *\fR\fIcfg\fR); +.fi + +.LP +.nf +\fBint\fR \fBit_config_setprop\fR(\fBit_config_t *\fR\fIcfg\fR, \fBnvlist_t *\fR\fIproplist\fR, + \fBnvlist_t **\fR\fIerrlist\fR); +.fi + +.LP +.nf +\fBvoid\fR \fBit_config_free\fR(\fBit_config_t *\fR\fIcfg\fR); +.fi + +.SH PARAMETERS +.sp +.ne 2 +.na +\fB\fIcfg\fR\fR +.ad +.RS 12n +a pointer to the iSCSI configuration structure +.RE + +.sp +.ne 2 +.na +\fB\fIproplist\fR\fR +.ad +.RS 12n +a pointer to an \fBnvlist_t\fR containing the global properties to be set +.RE + +.sp +.ne 2 +.na +\fB\fIerrlist\fR\fR +.ad +.RS 12n +an optional pointer to an \fBnvlist_t\fR that will be used to store specific +errors (if any) when validating global properties +.RE + +.SH DESCRIPTION +.sp +.LP +The \fBit_config_load()\fR function allocates and creates an \fBit_config_t\fR +structure representing the current iSCSI configuration. This structure is +compiled using the "provider" data returned by +\fBstmfGetProviderData\fR(3STMF). If there is no provider data associated with +iscsit, the \fBit_config_t\fR structure is set to a default configuration. +.sp +.LP +The \fBit_config_commit()\fR function informs the iscsit service that the +configuration has changed and commits the new configuration to the persistent +store by calling \fBstmfSetProviderData\fR(3STMF). This function can be called +multiple times during a configuration sequence, if necessary. +.sp +.LP +The \fBit_config_setprop()\fR function validates the provided property list and +sets the global properties for iSCSI Target. If \fIerrlist\fR is not +\fINULL\fR, this function returns detailed errors for each property that +failed. The format for \fIerrorlist\fR is \fIkey\fR = \fIproperty\fR, +\fIvalue\fR = \fIerror\fR string. +.sp +.LP +The \fBit_config_free()\fR function frees resources associated with the +\fBit_config_t\fR structure. +.sp +.LP +Global nvlist properties are as follows: +.sp + +.sp +.TS +c c c +l l l . +\fBnvlist\fR Key Type Valid Values +_ +\fBalias\fR \fBstring\fR any string +\fBauth\fR \fBstring\fR \fBradius\fR, \fBchap\fR, or \fBnone\fR +\fBisns\fR \fBboolean\fR \fBB_TRUE\fR, \fBB_FALSE\fR +\fBisnsserver\fR \fBstring\fR array T{ +Array of portal specifications of the form IPaddress:port. Port is optional; if not specified, the default iSNS port number of 3205 will be used. IPv6 addresses should be enclosed in square brackets '[' ']'. If "none" is specified, all defined iSNS servers will be removed from the configuration. +T} +\fBradiusserver\fR \fBstring\fR T{ +IPaddress:port specification as described for 'isnsserver' +T} +\fBradiussecret\fR \fBstring\fR T{ +string of at least 12 characters but not more than 255 characters. secret will be base64 encoded when stored. +T} +.TE + +.SH RETURN VALUES +.sp +.LP +The \fBit_config_load()\fR, \fBit_config_commit()\fR, and +\fBit_config_setprop()\fR functions return 0 on success and an error value on +failure. +.SH ERRORS +.sp +.LP +The \fBit_config_load()\fR, \fBit_config_commit()\fR, and +\fBit_config_setprop()\fR functions will fail if: +.sp +.ne 2 +.na +\fB\fBEINVAL\fR\fR +.ad +.RS 10n +A parameter or property is invalid. +.RE + +.sp +.ne 2 +.na +\fB\fBENOMEM\fR\fR +.ad +.RS 10n +Resources could not be allocated. +.RE + +.sp +.LP +The \fBit_config_commit()\fR function will also fail if: +.sp +.ne 2 +.na +\fB\fBSTMF_ERROR_SERVICE_DATA_VERSION\fR\fR +.ad +.sp .6 +.RS 4n +The configuration was updated by another client. See +\fBstmfSetProviderData\fR(3STMF). +.RE + +.SH ATTRIBUTES +.sp +.LP +See \fBattributes\fR(5) for descriptions of the following attributes: +.sp + +.sp +.TS +box; +c | c +l | l . +ATTRIBUTE TYPE ATTRIBUTE VALUE +_ +Interface Stability Committed +_ +MT-Level MT-Safe +.TE + +.SH SEE ALSO +.sp +.LP +\fBit_ini_create\fR(3ISCSIT), \fBit_portal_create\fR(3ISCSIT), +\fBit_tgt_create\fR(3ISCSIT), \fBit_tpg_create\fR(3ISCSIT), +\fBlibiscsit\fR(3LIB), \fBlibnvpair\fR(3LIB), \fBlibstmf\fR(3LIB), +\fBstmfGetProviderData\fR(3STMF), \fBstmfSetProviderData\fR(3STMF), +\fBattributes\fR(5) diff --git a/usr/src/man/man3iscsit/it_ini_create.3iscsit b/usr/src/man/man3iscsit/it_ini_create.3iscsit new file mode 100644 index 0000000..56944f9 --- /dev/null +++ b/usr/src/man/man3iscsit/it_ini_create.3iscsit @@ -0,0 +1,188 @@ +'\" te +.\" Copyright (c) 2008, Sun Microsystems Inc. All Rights Reserved. +.\" 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] +.TH IT_INI_CREATE 3ISCSIT "Oct 1, 2008" +.SH NAME +it_ini_create, it_ini_setprop, it_ini_delete, it_ini_free \- create, modify and +delete iSCSI Initiator Contexts +.SH SYNOPSIS +.LP +.nf +cc [ \fIflag\fR\&.\|.\|. ] \fIfile\fR\&.\|.\|. \fB-liscsit\fR [ \fIlibrary\fR\&.\|.\|. ] +#include <libiscsit.h> + +\fBint\fR \fBit_ini_create\fR(\fBit_config_t *\fR\fIcfg\fR, \fBit_ini_t **\fR\fIini\fR, + \fBchar *\fR\fIini_node_name\fR); +.fi + +.LP +.nf +\fBint\fR \fBit_ini_setprop\fR(\fBit_ini_t *\fR\fIini\fR, \fBnvlist_t *\fR\fIproplist\fR, + \fBnvlist_t **\fR\fIerrlist\fR);); +.fi + +.LP +.nf +\fBvoid\fR \fBit_ini_delete\fR(\fBit_config_t *\fR\fIcfg\fR, \fBit_ini_t *\fR\fIini\fR); +.fi + +.LP +.nf +\fBvoid\fR \fBit_ini_free\fR(\fBit_ini_t *\fR\fIini\fR); +.fi + +.SH PARAMETERS +.sp +.ne 2 +.na +\fB\fIcfg\fR\fR +.ad +.RS 17n +a pointer t the iSCSI configuration structure +.RE + +.sp +.ne 2 +.na +\fB\fIini\fR\fR +.ad +.RS 17n +a pointer to the \fBit_ini_t\fR structure representing the initiator context +.RE + +.sp +.ne 2 +.na +\fB\fIini_node_name\fR\fR +.ad +.RS 17n +the iSCSI node name of the remote initiator +.RE + +.sp +.ne 2 +.na +\fB\fIproplist\fR\fR +.ad +.RS 17n +a pointer to an \fBnvlist_t\fR containing the initiator properties to be set +.RE + +.sp +.ne 2 +.na +\fB\fIerrlist\fR\fR +.ad +.RS 17n +an optional pointer to an \fBnvlist_t\fR that will be used to store specific +errors (if any) when validating initiator properties +.RE + +.SH DESCRIPTION +.sp +.LP +The \fBit_ini_create()\fR function adds an initiator context to the global +configuration. +.sp +.LP +The \fBit_ini_setprop()\fR function validates the provided property list and +sets the properties for the specified initiator. If \fIerrlist\fR is not +\fINULL\fR, this function returns detailed errors for each property that +failed. The format for \fIerrlist\fR is \fIkey\fR = \fIproperty\fR, +\fIvalue\fR = \fIerror\fR string. +.sp +.LP +The \fBit_ini_delete()\fR function removes the specified initiator context from +the global configuration. +.sp +.LP +The \fBit_ini_free()\fR function deallocates resources of an \fBit_ini_t\fR +structure. If \fIini\fR\(->\fInext\fR is not \fINULL\fR, this function frees +all members of the list. +.sp +.LP +Configuration changes as a result of these functions are not instantiated until +the modified configuration is committed by calling +\fBit_config_commit\fR(3ISCSIT). +.sp +.LP +Initiator nvlist properties are as follows: +.sp + +.sp +.TS +c c c +l l l . +\fBnvlist\fR Key Type Valid Values +_ +\fBchapuser\fR \fBstring\fR any string, or \fBnone\fR to remove +\fBchapsecret\fR \fBstring\fR T{ +string of at least 12 characters but not more than 255 characters. secret will be base64 encoded when stored. +T} +.TE + +.SH RETURN VALUES +.sp +.LP +The \fBit_ini_create()\fR, \fBit_ini_setprop()\fR, and \fBit_ini_delete()\fR +functions return 0 on success and an error value on failure. +.SH ERRORS +.sp +.LP +The \fBit_ini_create()\fR, \fBit_ini_setprop()\fR, and \fBit_ini_delete()\fR +functions will fail if: +.sp +.ne 2 +.na +\fB\fBEEXIST\fR\fR +.ad +.RS 10n +The requested initiator context is already configured. +.RE + +.sp +.ne 2 +.na +\fB\fBEINVAL\fR\fR +.ad +.RS 10n +A parameter or property is invalid. +.RE + +.sp +.ne 2 +.na +\fB\fBENOMEM\fR\fR +.ad +.RS 10n +Resources could not be allocated. +.RE + +.SH ATTRIBUTES +.sp +.LP +See \fBattributes\fR(5) for descriptions of the following attributes: +.sp + +.sp +.TS +box; +c | c +l | l . +ATTRIBUTE TYPE ATTRIBUTE VALUE +_ +Interface Stability Committed +_ +MT-Level MT-Safe +.TE + +.SH SEE ALSO +.sp +.LP +\fBit_config_commit\fR(3ISCSIT), \fBit_portal_create\fR(3ISCSIT), +\fBit_tgt_create\fR(3ISCSIT), \fBit_tpg_create\fR(3ISCSIT), +\fBlibiscsit\fR(3LIB), \fBlibnvpair\fR(3LIB), \fBlibstmf\fR(3LIB), +\fBstmfGetProviderData\fR(3STMF), \fBstmfSetProviderData\fR(3STMF), +\fBattributes\fR(5) diff --git a/usr/src/man/man3iscsit/it_portal_create.3iscsit b/usr/src/man/man3iscsit/it_portal_create.3iscsit new file mode 100644 index 0000000..ef12705 --- /dev/null +++ b/usr/src/man/man3iscsit/it_portal_create.3iscsit @@ -0,0 +1,139 @@ +'\" te +.\" Copyright (c) 2008, Sun Microsystems Inc. All Rights Reserved. +.\" 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] +.TH IT_PORTAL_CREATE 3ISCSIT "Oct 1, 2008" +.SH NAME +it_portal_create, it_portal_delete \- create and delete iSCSI portals +.SH SYNOPSIS +.LP +.nf +cc [ \fIflag\fR\&.\|.\|. ] \fIfile\fR\&.\|.\|. \fB-liscsit\fR [ \fIlibrary\fR\&.\|.\|. ] +#include <libiscsit.h> + +\fBint\fR \fBit_portal_create\fR(\fBit_config_t *\fR\fIcfg\fR, \fBit_tpg_t *\fR\fItpg\fR, + \fBit_portal_t **\fR\fIportal\fR, \fBchar *\fR\fIportal_ip_port\fR); +.fi + +.LP +.nf +\fBvoid\fR \fBit_portal_delete\fR(\fBit_config_t *\fR\fIcfg\fR, \fBit_tpg_t *\fR\fItpg\fR, + \fBit_portal_t *\fR\fIportal\fR); +.fi + +.SH PARAMETERS +.sp +.ne 2 +.na +\fB\fIcfg\fR\fR +.ad +.RS 18n +a pointer to the iSCSI configuration structure +.RE + +.sp +.ne 2 +.na +\fB\fItpg\fR\fR +.ad +.RS 18n +a pointer to the \fBit_tpg_t\fR structure representing the target portal group +.RE + +.sp +.ne 2 +.na +\fB\fIportal\fR\fR +.ad +.RS 18n +a pointer to the \fBit_portal_t\fR structure representing the portal +.RE + +.sp +.ne 2 +.na +\fB\fIportal_ip_port\fR\fR +.ad +.RS 18n +a string containing an appropriatedly formatted IP address:port. Both IPv4 and +IPv6 addresses are permitted. IPv6 addresses should be enclosed in square +brackets ('[', ']'). +.RE + +.SH DESCRIPTION +.sp +.LP +The \fBit_portal_create()\fR function adds an \fBit_portal_t\fR structure +representing a new portal to the specified target portal group. A portal may +belong to one and only one target portal group. +.sp +.LP +The \fBit_portal_delete()\fR function removes the specified portal from the +specified target portal group. +.sp +.LP +Configuration changes as a result of these functions are not instantiated until +the modified configuration is committed by calling +\fBit_config_commit\fR(3ISCSIT). +.SH RETURN VALUES +.sp +.LP +The \fBit_portal_create()\fR function returns 0 on success and an error value +on failure. +.SH ERRORS +.sp +.LP +The \fBit_portal_create()\fR function will fail if: +.sp +.ne 2 +.na +\fB\fBEEXIST\fR\fR +.ad +.RS 10n +The portal was already configured for another portal group. +.RE + +.sp +.ne 2 +.na +\fB\fBEINVAL\fR\fR +.ad +.RS 10n +A parameter is invalid. +.RE + +.sp +.ne 2 +.na +\fB\fBENOMEM\fR\fR +.ad +.RS 10n +Resources could not be allocated. +.RE + +.SH ATTRIBUTES +.sp +.LP +See \fBattributes\fR(5) for descriptions of the following attributes: +.sp + +.sp +.TS +box; +c | c +l | l . +ATTRIBUTE TYPE ATTRIBUTE VALUE +_ +Interface Stability Committed +_ +MT-Level MT-Safe +.TE + +.SH SEE ALSO +.sp +.LP +\fBit_ini_create\fR(3ISCSIT), \fBit_tgt_create\fR(3ISCSIT), +\fBit_tpg_create\fR(3ISCSIT), \fBlibiscsit\fR(3LIB), +\fBit_config_commit\fR(3ISCSIT), \fBlibiscsit\fR(3LIB), \fBlibnvpair\fR(3LIB), +\fBlibstmf\fR(3LIB), \fBattributes\fR(5) diff --git a/usr/src/man/man3iscsit/it_tgt_create.3iscsit b/usr/src/man/man3iscsit/it_tgt_create.3iscsit new file mode 100644 index 0000000..30cc41f --- /dev/null +++ b/usr/src/man/man3iscsit/it_tgt_create.3iscsit @@ -0,0 +1,291 @@ +'\" te +.\" Copyright (c) 2008, Sun Microsystems Inc. All Rights Reserved. +.\" 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] +.TH IT_TGT_CREATE 3ISCSIT "Oct 1, 2008" +.SH NAME +it_tgt_create, it_tgt_setprop, it_tgt_delete, it_tpgt_create, it_tpgt_delete, +it_tgt_free, it_tpgt_free \- create, modify and delete iSCSI Targets +.SH SYNOPSIS +.LP +.nf +cc [ \fIflag\fR\&.\|.\|. ] \fIfile\fR\&.\|.\|. \fB-liscsit\fR [ \fIlibrary\fR\&.\|.\|. ] +#include <libiscsit.h> + +\fBint\fR \fBit_tgt_create\fR(\fBit_config_t **\fR\fIcfg\fR, \fBit_tgt_t **\fR\fItgt\fR, + \fBchar *\fR\fItgt_name\fR); +.fi + +.LP +.nf +\fBint\fR \fBit_tgt_setprop\fR(\fBit_config_t *\fR\fIcfg\fR \fBit_tgt_t *\fR\fItgt\fR, + \fBnvlist_t *\fR\fIproplist\fR, \fBnvlist_t **\fR\fIerrlist\fR); +.fi + +.LP +.nf +\fBint\fR \fBit_tgt_delete\fR(\fBit_config_t *\fR\fIcfg\fR, \fBit_tgt_t *\fR\fItgt\fR, + \fBboolean_t\fR \fIforce\fR); +.fi + +.LP +.nf +\fBint\fR \fBit_tpgt_create\fR(\fBit_config_t *\fR\fIcfg\fR, \fBit_tgt_t *\fR\fItgt\fR, + \fBit_tpgt_t **\fR\fItpgt\fR, \fBchar *\fR\fItpg_name\fR, \fBuint16_t\fR \fItpgt_tag\fR); +.fi + +.LP +.nf +\fBvoid\fR \fBit_tpgt_delete\fR(\fBit_config_t *\fR\fIcfg\fR, \fBit_tgt_t *\fR\fItgt\fR, + \fBit_tpgt_t *\fR\fItpgt\fR); +.fi + +.LP +.nf +\fBvoid\fR \fBit_tgt_free\fR(\fBit_tgt_t *\fR\fItgt\fR); +.fi + +.LP +.nf +\fBvoid\fR \fBit_tpgt_free\fR(\fBit_tpgt_t *\fR\fItpgt\fR); +.fi + +.SH PARAMETERS +.sp +.ne 2 +.na +\fB\fIcfg\fR\fR +.ad +.RS 13n +a pointer to the iSCSI configuration structure +.RE + +.sp +.ne 2 +.na +\fB\fItgt\fR\fR +.ad +.RS 13n +a pointer to an iSCSI target structure +.RE + +.sp +.ne 2 +.na +\fB\fItgt_name\fR\fR +.ad +.RS 13n +the target node name for the target to be created. The name must be in either +IQN or EUI format. If this value is \fINULL\fR, a node name will be generated +automatically in IQN format. +.RE + +.sp +.ne 2 +.na +\fB\fIproplist\fR\fR +.ad +.RS 13n +a pointer to an \fBnvlist_t\fR containing the target properties to be set +.RE + +.sp +.ne 2 +.na +\fB\fIerrlist\fR\fR +.ad +.RS 13n +an optional pointer to an \fBnvlist_t\fR that will be used to store specific +errors (if any) when validating target properties +.RE + +.sp +.ne 2 +.na +\fB\fIforce\fR\fR +.ad +.RS 13n +a boolean value indicating if the target should be set to offline before +removing it from the configuration. If not specified, the operation will fail +if the target is determined to be online +.RE + +.sp +.ne 2 +.na +\fB\fItpgt\fR\fR +.ad +.RS 13n +a pointer to a target portal group tag structure +.RE + +.sp +.ne 2 +.na +\fB\fItpgt_name\fR\fR +.ad +.RS 13n +the name of the target portal group to be associated with this target portal +group tag +.RE + +.sp +.ne 2 +.na +\fB\fItpgt_tag\fR\fR +.ad +.RS 13n +a 16-bit numerical identifier for this target portal group tag. Valid values +are 2 through 65535. If tpgt_tag is '0', \fBit_tpgt_create()\fR will assign an +appropriate tag number. If \fItpgt_tag\fR is != 0, and the requested tag number +is unavailable, another value will be chosen. +.RE + +.SH DESCRIPTION +.sp +.LP +The \fBit_tgt_create()\fR function allocates and creates an \fBit_tgt_t\fR +structure representing a new iSCSI target node. If \fItgt_name\fR is +\fINULL\fR, then a unique target node name will be generated automatically. +Otherwise, the value of \fItgt_name\fR will be used as the target node name. +The new \fBit_tgt_t\fR structure is added to the target list +(\fIcfg_tgt_list\fR) in the configuration structure. +.sp +.LP +The \fBit_tgt_setprop()\fR function validates the provided property list and +sets the properties for the specified target. If \fIerrlist\fR is not +\fINULL\fR, this function returns detailed errors for each property that +failed. The format for \fIerrlist\fR is \fIkey\fR = \fIproperty\fR, +\fIvalue\fR = \fIerror\fR string. +.sp +.LP +The \fBit_tgt_delete()\fR function removes the target represented by \fItgt\fR +from the configuration. The \fItgt\fR argument is an existing \fBit_tgt_t\fR +structure within the configuration \fIcfg\fR. +.sp +.LP +The \fBit_tpgt_create()\fR function allocates and creates an \fBit_tpgt_t\fR +structure representing a new iSCSI target portal group tag. The new +\fBit_tpgt_t\fR structure is added to the target \fItpgt\fR list +(\fItgt_tpgt_list\fR) in the \fBit_tgt_t\fR structure. +.sp +.LP +The \fBit_tpgt_delete()\fR function removes the target portal group tag +represented by \fItpgt\fR, from the configuration. The \fItpgt\fR argument is +an existing \fBis_tpgt_t\fR structure within the target \fItgt\fR. +.sp +.LP +The \fBit_tgt_free()\fR function frees an \fBit_tgt_t\fR structure. If +\fItgt\fR\(->\fInext\fR is not \fINULL\fR, this function frees all structures +in the list. +.sp +.LP +The \fBit_tpgt_free()\fR function deallocates resources of an \fBit_tpgt_t\fR +structure. If \fItpgt\fR->\fInext\fR is not \fINULL\fR, this function frees +all members of the list. +.sp +.LP +Configuration changes as a result of these functions are not instantiated until +the modified configuration is committed by calling +\fBit_config_commit\fR(3ISCSIT). +.sp +.LP +Target nvlist properties are as follows: +.sp + +.sp +.TS +c c c +l l l . +\fBnvlist\fR Key Type Valid Values +_ +\fBtargetchapuser\fR \fBstring\fR any string, or \fBnone\fR to remove +\fBtargetchapsecret\fR \fBstring\fR T{ +string of at least 12 characters but not more than 255 characters. secret will be base64 encoded when stored. +T} +\fBalias\fR \fBstring\fR any string or \fBnone\fR to remove +\fBauth\fR \fBstring\fR \fBradius\fR, \fBchap\fR, or \fBnone\fR +.TE + +.SH RETURN VALUES +.sp +.LP +The \fBit_tgt_create()\fR, \fBit_tgt_setprop()\fR, \fBit_tgt_delete()\fR, +\fBit_tpgt_create()\fR, and \fBit_tpgt_delete()\fR functions return 0 on +success and an error value on failure. +.SH ERRORS +.sp +.LP +The \fBit_tgt_create()\fR, \fBit_tgt_setprop()\fR, \fBit_tgt_delete()\fR, +\fBit_tpgt_create()\fR, and \fBit_tpgt_delete()\fR functions will fail if: +.sp +.ne 2 +.na +\fB\fBE2BIG\fR\fR +.ad +.RS 10n +All tag numbers are already in use. +.RE + +.sp +.ne 2 +.na +\fB\fBEBUSY\fR\fR +.ad +.RS 10n +The target is online. +.RE + +.sp +.ne 2 +.na +\fB\fBEEXIST\fR\fR +.ad +.RS 10n +The requested target node name is already configured. +.RE + +.sp +.ne 2 +.na +\fB\fBEINVAL\fR\fR +.ad +.RS 10n +A parameter or property is invalid. +.RE + +.sp +.ne 2 +.na +\fB\fBENOMEM\fR\fR +.ad +.RS 10n +Resources could not be allocated. +.RE + +.SH ATTRIBUTES +.sp +.LP +See \fBattributes\fR(5) for descriptions of the following attributes: +.sp + +.sp +.TS +box; +c | c +l | l . +ATTRIBUTE TYPE ATTRIBUTE VALUE +_ +Interface Stability Committed +_ +MT-Level MT-Safe +.TE + +.SH SEE ALSO +.sp +.LP +\fBit_config_commit\fR(3ISCSIT), \fBit_ini_create\fR(3ISCSIT), +\fBit_portal_create\fR(3ISCSIT), \fBit_tpg_create\fR(3ISCSIT), +\fBlibiscsit\fR(3LIB), \fBlibnvpair\fR(3LIB), \fBlibstmf\fR(3LIB), +\fBattributes\fR(5) diff --git a/usr/src/man/man3iscsit/it_tpg_create.3iscsit b/usr/src/man/man3iscsit/it_tpg_create.3iscsit new file mode 100644 index 0000000..2ba3628 --- /dev/null +++ b/usr/src/man/man3iscsit/it_tpg_create.3iscsit @@ -0,0 +1,175 @@ +'\" te +.\" Copyright (c) 2008, Sun Microsystems Inc. All Rights Reserved. +.\" 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] +.TH IT_TPG_CREATE 3ISCSIT "Oct 1, 2008" +.SH NAME +it_tpg_create, it_tpg_delete, it_tpg_free \- create and delete iSCSI +target portal groups +.SH SYNOPSIS +.LP +.nf +cc [ \fIflag\fR\&.\|.\|. ] \fIfile\fR\&.\|.\|. \fB-liscsit\fR [ \fIlibrary\fR\&.\|.\|. ] +#include <libiscsit.h> + +\fBint\fR \fBit_tpg_create\fR(\fBit_config_t *\fR\fIcfg\fR, \fBit_tpg_t **\fR\fItpg\fR, + \fBchar *\fR\fItpg_name\fR, \fBchar *\fR\fIportal_ip_port\fR); +.fi + +.LP +.nf +\fBint\fR \fBit_tpg_delete\fR(\fBit_config_t *\fR\fIcfg\fR, \fBit_tpg_t *\fR\fItpg\fR, + \fBboolean_t\fR \fIforce\fR);); +.fi + +.LP +.nf +\fBvoid\fR \fBit_tpg_free\fR(\fBit_tpg_t *\fR\fItpg\fR); +.fi + +.SH PARAMETERS +.sp +.ne 2 +.na +\fB\fIcfg\fR\fR +.ad +.RS 18n +a pointer t the iSCSI configuration structure +.RE + +.sp +.ne 2 +.na +\fB\fItpg\fR\fR +.ad +.RS 18n +a pointer to the \fBit_tpg_t\fR structure representing the target portal group +.RE + +.sp +.ne 2 +.na +\fB\fItpg_name\fR\fR +.ad +.RS 18n +an identifier for the target portal group +.RE + +.sp +.ne 2 +.na +\fB\fIportal_ip_port\fR\fR +.ad +.RS 18n +a string containing an appropriately formatted IP \fBaddress\fR:\fBport\fR. +Both IPv4 and IPv6 addresses are permitted. This value becomes the first portal +in the target portal group. Applications can add additional values using +\fBit_portal_create\fR(3ISCSIT) before committing the target portal group. IPv6 +addresses should be enclosed in square brackets ('[', ']'). +.RE + +.sp +.ne 2 +.na +\fB\fIforce\fR\fR +.ad +.RS 18n +boolean value indicating if the target portal group should be removed even if +it is associated with one or more targets. If not \fBB_TRUE\fR, the operation +will fail if the target product group is associated with a target. +.RE + +.SH DESCRIPTION +.sp +.LP +The \fBit_tpg_create()\fR function allocates and creates an \fBit_tpg_t\fR +structure representing a new iSCSI target portal group. The new \fBit_tpg_t\fR +structure is added to the global \fItpg\fR list (\fIcfg_tgt_list\fR) in the +\fBit_config_t\fR structure. +.sp +.LP +The \fBit_tpg_delete()\fR function deletes the target portal group represented +by \fItpg\fR, where \fItpg\fR is an existing \fBit_tpg_t\fR structure within +the global configuration \fIcfg\fR. +.sp +.LP +The \fBit_tpg_free()\fR function deallocates resources associated with an +\fBit_tpg_t\fR structure. If \fItpg\fR->\fInext\fR is not \fINULL\fR, this +function frees all members of the list. +.sp +.LP +Configuration changes as a result of these functions are not instantiated until +the modified configuration is committed by calling +\fBit_config_commit\fR(3ISCSIT). +.SH RETURN VALUES +.sp +.LP +The \fBit_tpg_create()\fR and \fBit_tpg_delete()\fR functions return 0 on +success and an error value on failure. +.SH ERRORS +.sp +.LP +The \fBit_tpg_create()\fR and \fBit_tpg_delete()\fR functions will fail if: +.sp +.ne 2 +.na +\fB\fBEBUSY\fR\fR +.ad +.RS 10n +The portal group is associated with one or more targets. +.RE + +.sp +.ne 2 +.na +\fB\fBEEXIST\fR\fR +.ad +.RS 10n +The portal was already configured for another portal group associated with this +target. +.RE + +.sp +.ne 2 +.na +\fB\fBEINVAL\fR\fR +.ad +.RS 10n +A parameter is invalid. +.RE + +.sp +.ne 2 +.na +\fB\fBENOMEM\fR\fR +.ad +.RS 10n +Resources could not be allocated. +.RE + +.SH ATTRIBUTES +.sp +.LP +See \fBattributes\fR(5) for descriptions of the following attributes: +.sp + +.sp +.TS +box; +c | c +l | l . +ATTRIBUTE TYPE ATTRIBUTE VALUE +_ +Interface Stability Committed +_ +MT-Level MT-Safe +.TE + +.SH SEE ALSO +.sp +.LP +\fBit_config_commit\fR(3ISCSIT), \fBit_ini_create\fR(3ISCSIT), +\fBit_portal_create\fR(3ISCSIT), \fBit_tgt_create\fR(3ISCSIT), +\fBlibiscsit\fR(3LIB), \fBlibnvpair\fR(3LIB), \fBlibstmf\fR(3LIB), +\fBattributes\fR(5) diff --git a/usr/src/man/man3lib/libiscsit.3lib b/usr/src/man/man3lib/libiscsit.3lib new file mode 100644 index 0000000..0f162fc --- /dev/null +++ b/usr/src/man/man3lib/libiscsit.3lib @@ -0,0 +1,87 @@ +'\" te +.\" Copyright (c) 2008, Sun Microsystems, Inc. All Rights Reserved. +.\" 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] +.TH LIBISCSIT 3LIB "Oct 1, 2008" +.SH NAME +libiscsit \- iSCSI Management library +.SH SYNOPSIS +.LP +.nf +cc [ \fIflag\fR... ] \fIfile\fR... \fB-liscsit\fR [ \fIlibrary\fR... ] +#include <libiscsit.h> +.fi + +.SH DESCRIPTION +.sp +.LP +Functions in this library provide management services for COMSTAR iSCSI target +ports. +.SH INTERFACES +.sp +.LP +The shared object \fBlibiscsit.so.1\fR provides the public interfaces defined +below. See \fBIntro\fR(3) for additional information on shared object +interfaces. +.sp + +.sp +.TS +l l +l l . +\fBit_config_commit\fR \fBit_config_free\fR +\fBit_config_load\fR \fBit_config_setprop\fR +\fBit_ini_create\fR \fBit_ini_delete\fR +\fBit_ini_free\fR \fBit_ini_setprop\fR +\fBit_portal_create\fR \fBit_portal_delete\fR +\fBit_tgt_create\fR \fBit_tgt_delete\fR +\fBit_tgt_free\fR \fBit_tgt_setprop\fR +\fBit_tpg_create\fR \fBit_tpg_delete\fR +\fBit_tpg_free\fR \fBit_tpgt_create\fR +\fBit_tpgt_delete\fR \fBit_tpgt_free\fR +.TE + +.SH FILES +.sp +.ne 2 +.na +\fB\fB/lib/libiscsit.so.1\fR\fR +.ad +.RS 26n +shared object +.RE + +.sp +.ne 2 +.na +\fB\fB/lib/64/libiscsit.so.1\fR\fR +.ad +.RS 26n +64-bit shared object +.RE + +.SH ATTRIBUTES +.sp +.LP +See \fBattributes\fR(5) for descriptions of the following attributes: +.sp + +.sp +.TS +box; +c | c +l | l . +ATTRIBUTE TYPE ATTRIBUTE VALUE +_ +Interface Stability Committed +_ +MT-Level MT-Safe +.TE + +.SH SEE ALSO +.sp +.LP +\fBIntro\fR(3), \fBit_config_load\fR(3ISCSIT), \fBit_ini_create\fR(3ISCSIT), +\fBit_portal_create\fR(3ISCSIT), \fBit_tgt_create\fR(3ISCSIT), +\fBit_tpg_create\fR(3ISCSIT), \fBattributes\fR(5) diff --git a/usr/src/uts/common/sys/iscsit/iscsit_common.h b/usr/src/uts/common/sys/iscsit/iscsit_common.h new file mode 100644 index 0000000..4d5d58e --- /dev/null +++ b/usr/src/uts/common/sys/iscsit/iscsit_common.h @@ -0,0 +1,516 @@ +/* + * 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. + */ + +#ifndef _ISCSIT_COMMON_H_ +#define _ISCSIT_COMMON_H_ + +#ifdef _KERNEL +#include <sys/nvpair.h> +#else +#include <libnvpair.h> +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define ISCSIT_API_VERS0 0 + +#define ISCSIT_MODNAME "iscsit" +#define ISCSIT_NODE "/devices/pseudo/iscsit@0:iscsit" + +typedef enum { + ITCFG_SUCCESS = 0, + ITCFG_INVALID, + ITCFG_TGT_CREATE_ERR, + ITCFG_MISC_ERR +} it_cfg_status_t; + +/* + * This structure is passed back to the driver during ISCSIT_IOC_ENABLE_SVC + * in order to provide the fully qualified hostname for use as the EID + * by iSNS. + */ + +#define ISCSIT_MAX_HOSTNAME_LEN 256 + +typedef struct iscsit_hostinfo_s { + uint32_t length; + char fqhn[ISCSIT_MAX_HOSTNAME_LEN]; +} iscsit_hostinfo_t; + +#define ISCSIT_IOC_SET_CONFIG 1 +#define ISCSIT_IOC_GET_STATE 2 +#define ISCSIT_IOC_ENABLE_SVC 101 +#define ISCSIT_IOC_DISABLE_SVC 102 + +/* XXX Rationalize these with other error values (used in it_smf.c */ +#define ITADM_SUCCESS 0 +#define ITADM_FATAL_ERROR 0x1 +#define ITADM_NO_MEM 0x2 +#define ITADM_INVALID 0x4 +#define ITADM_NODATA 0x8 +#define ITADM_PERM 0x10 + + +#define PROP_AUTH "auth" +#define PROP_ALIAS "alias" +#define PROP_CHAP_USER "chapuser" +#define PROP_CHAP_SECRET "chapsecret" +#define PROP_TARGET_CHAP_USER "targetchapuser" +#define PROP_TARGET_CHAP_SECRET "targetchapsecret" +#define PROP_RADIUS_SERVER "radiusserver" +#define PROP_RADIUS_SECRET "radiussecret" +#define PROP_ISNS_ENABLED "isns" +#define PROP_ISNS_SERVER "isnsserver" +#define PROP_OLD_TARGET_NAME "oldtargetname" + +#define PA_AUTH_RADIUS "radius" +#define PA_AUTH_CHAP "chap" +#define PA_AUTH_NONE "none" + +typedef struct { + int set_cfg_vers; + int set_cfg_pnvlist_len; + caddr_t set_cfg_pnvlist; +} iscsit_ioc_set_config_t; + +typedef struct { + int getst_vers; + int getst_pnvlist_len; + char *getst_pnvlist; +} iscsit_ioc_getstate_t; + +#ifdef _SYSCALL32 +typedef struct { + int set_cfg_vers; + int set_cfg_pnvlist_len; + caddr32_t set_cfg_pnvlist; +} iscsit_ioc_set_config32_t; + +typedef struct { + int getst_vers; + int getst_pnvlist_len; + caddr32_t getst_pnvlist; +} iscsit_ioc_getstate32_t; +#endif /* _SYSCALL32 */ + +/* Shared user/kernel structures */ +/* Maximum size of a Target Portal Group name */ +#define MAX_TPG_NAMELEN 256 /* XXX */ + +/* Maximum size of an iSCSI Target Node name */ +#define MAX_ISCSI_NODENAMELEN 256 /* XXX */ + +/* + * A target portal group tag is a binding between a target and a target + * portal group along with a numerical value associated with that binding. + * The numerical identifier is used as the 'target portal group tag' defined + * in RFC3720. + * + * tpgt_tpg_name The name of the target portal group associated with + * this target portal group tag. + * tpgt_generation Generation number which is incremented each time the + * structure changes. + * tpgt_next Next target portal group tag in th list of target portal + * group tags. If tpgt_next is NUL, then this is the last + * target portal group in the list. + * tpgt_tag A numerical identifier that uniquely identifies a + * target portal group within the associated target node. + */ +typedef struct it_tpgt_s { + char tpgt_tpg_name[MAX_TPG_NAMELEN]; + uint64_t tpgt_generation; + struct it_tpgt_s *tpgt_next; + uint16_t tpgt_tag; +} it_tpgt_t; + +/* + * An iSCSI target node is represented by an it_tgt_structure. Each + * target node includes a list of associated target portal group tags + * and a list of properties. + * + * tgt_name The iSCSI target node name in either IQN or EUI + * format (see RFC3720). + * tgt_generation Generation number which is incremented each time + * the structure changes. + * tgt_next Next target in the list of targets. If tgt_next + * is NULL, then this is the last target in the list. + * tgt_tpgt_list A linked list representing the current target + * portal group tags associated with this target. + * tgt_tpgt_count The number of currently defined target portal + * group tags. + * tgt_properties An nvlist representation of the properties + * associated with this target. This list can be + * manipulated using libnvpair(3lib), and should be + * validated and stored using it_tgt_setprop(). + * + * Target nvlist Properties: + * + * nvlist Key Type Valid Values + * -------------------------------------------------------------------- + * targetchapuser string any string or "none" to remove + * targetchapsecret string string of at least 12 characters + * but not more than 255 characters. + * secret will be base64 encoded when + * stored. + * alias string any string or "none" to remove + * auth string "radius", "chap", or "none" + * + */ +typedef struct it_tgt_s { + char tgt_name[MAX_ISCSI_NODENAMELEN]; + uint64_t tgt_generation; + struct it_tgt_s *tgt_next; + it_tpgt_t *tgt_tpgt_list; + uint32_t tgt_tpgt_count; + nvlist_t *tgt_properties; +} it_tgt_t; + +/* + * A target portal is represented by an IP address and a listening + * TCP port. + * + * portal_addr sockaddr_storage structure representing the + * IPv4 or IPv6 address and TCP port associated + * with the portal. + * portal_next Next portal in the list of portals. If + * portal_next is NULL, this is the last portal + * in the list. + */ +typedef struct it_portal_s { + struct sockaddr_storage portal_addr; + struct it_portal_s *portal_next; +} it_portal_t; + +/* + * A portal is an IP address and TCP port and a portal group is a set + * of portals. Each defined portal belongs to exactly one portal group. + * Applications can associate a target portal group with a particular + * target using a target portal group name. Initiators can only connect + * to targets through the portals associated with the target's target + * portal group tags. + * + * tpg_name Identifier for the target portal group. + * tpg_generation Generation number which is incremented each + * time this structure changes. + * tpg_next Next target portal group in the list of target + * portal groups. If tpg_next is NULL, this is the + * last target portal group in the list. + * tpg_portal_count Number of it_portal_t structures in the list. + * tpg_portal_list Linked list of it_portal_t structures. + */ +typedef struct it_tpg_s { + char tpg_name[MAX_TPG_NAMELEN]; + uint64_t tpg_generation; + struct it_tpg_s *tpg_next; + uint32_t tpg_portal_count; + it_portal_t *tpg_portal_list; +} it_tpg_t; + +/* + * A context representing a remote iSCSI initiator node. The purpose + * of this structure is to maintain information specific to a remote + * initiator such as the CHAP username and CHAP secret. + * + * ini_name the iSCSI node name of the remote initiator. + * ini_generation Generation number which is incremented each + * time this structure changes. + * ini_next Next initiator in the list of initiators. + * If ini_next is NULL, this is the last initiator + * in the list. + * ini_properties Name/Value list containing the properties + * associated with the initiator context. This list + * can be manipulated using libnvpair(3lib), and should + * be validated and stored using it_ini_setprop(). + * + * Initiator nvlist Properties: + * + * nvlist Key Type Valid Values + * -------------------------------------------------------------------- + * chapuser string any string + * chapsecret string string of at least 12 characters + * but not more than 255 characters. + * secret will be base64 encoded when + * stored. + */ +typedef struct it_ini_s { + char ini_name[MAX_ISCSI_NODENAMELEN]; + uint64_t ini_generation; + struct it_ini_s *ini_next; + nvlist_t *ini_properties; +} it_ini_t; + + +/* + * This structure represents a complete configuration for the iscsit + * port provider. In addition to the global configuration, it_config_t + * includes lists of child objects including targets, target portal + * groups and initiator contexts. Each object includes a "generation" + * value which is used by the iscsit kernel driver to identify changes + * from one configuration update to the next. + * + * stmf_token A uint64_t that contains the value returned from a + * successful call to stmfGetProviderDataProt(3STMF). + * This token is used to verify that the configuration + * data persistently stored in COMSTAR has not been + * modified since this version was loaded. + * config_version Version number for this configuration structure + * config_tgt_list Linked list of target contexts representing the + * currently defined targets. Applications can add + * targets to or remove targets from this list using + * the it_tgt_create and it_tgt_delete functions. + * config_tgt_count The number of currently defined targets. + * config_tpg_list Linked list of target portal group contexts. + * Applications can add or remove target portal groups + * to/from this list using the it_tpg_create and + * it_tpg_delete functions. + * config_tpg_count The number of currently defined target portal groups + * config_ini_list Linked list of initiator contexts. Applications + * can add initiator contexts or remove initiator + * contexts from this list using the it_ini_create + * and it_ini_delete functions. + * config_ini_count The number of currently defined initiator contexts. + * config_global_properties + * Name/Value list representing the current global + * property settings. This list can be manipulated + * using libnvpair(3lib), and should be validated + * and stored using it_config_setprop(). + * config_isns_svr_list + * Linked list of currently defined iSNS servers. + * Applications can add or remove iSNS servers by + * using the it_config_setprop() function and changing + * the array of iSNS servers stored in the "isnsserver" + * property. + * config_isns_svr_count + * The number of currently defined iSNS servers. + * + * Global nvlist Properties: + * + * nvlist Key Type Valid Values + * -------------------------------------------------------------------- + * alias string any string + * auth string "radius", "chap", or "none" + * isns boolean B_TRUE, B_FALSE + * isnsserver string array Array of portal specifications of + * the form IPaddress:port. Port + * is optional; if not specified, the + * default iSNS port number of 3205 will + * be used. IPv6 addresses should + * be enclosed in square brackets '[' ']'. + * If "none" is specified, all defined + * iSNS servers will be removed from the + * configuration. + * radiusserver string IPaddress:port specification as + * described for 'isnsserver'. + * radiussecret string string of at least 12 characters + * but not more than 255 characters. + * secret will be base64 encoded when + * stored. + */ +typedef struct it_config_s { + uint64_t stmf_token; + uint32_t config_version; + it_tgt_t *config_tgt_list; + uint32_t config_tgt_count; + it_tpg_t *config_tpg_list; + uint32_t config_tpg_count; + it_ini_t *config_ini_list; + uint32_t config_ini_count; + it_portal_t *config_isns_svr_list; + uint32_t config_isns_svr_count; + nvlist_t *config_global_properties; +} it_config_t; + + +/* Functions to convert iSCSI target structures to/from nvlists. */ +int +it_config_to_nv(it_config_t *cfg, nvlist_t **nvl); + +/* + * nvlist version of config is 3 list-of-list, + 1 proplist. arrays + * are interesting, but lists-of-lists are more useful when doing + * individual lookups when we later add support for it. Also, no + * need to store name in individual struct representation. + */ +int +it_nv_to_config(nvlist_t *nvl, it_config_t **cfg); + +int +it_nv_to_tgtlist(nvlist_t *nvl, uint32_t *count, it_tgt_t **tgtlist); + +int +it_tgtlist_to_nv(it_tgt_t *tgtlist, nvlist_t **nvl); + +int +it_tgt_to_nv(it_tgt_t *tgt, nvlist_t **nvl); + +int +it_nv_to_tgt(nvlist_t *nvl, char *name, it_tgt_t **tgt); + +int +it_tpgt_to_nv(it_tpgt_t *tpgt, nvlist_t **nvl); + +int +it_nv_to_tpgt(nvlist_t *nvl, char *name, it_tpgt_t **tpgt); + +int +it_tpgtlist_to_nv(it_tpgt_t *tpgtlist, nvlist_t **nvl); + +int +it_nv_to_tpgtlist(nvlist_t *nvl, uint32_t *count, it_tpgt_t **tpgtlist); + +int +it_tpg_to_nv(it_tpg_t *tpg, nvlist_t **nvl); + +int +it_nv_to_tpg(nvlist_t *nvl, char *name, it_tpg_t **tpg); + +int +it_tpglist_to_nv(it_tpg_t *tpglist, nvlist_t **nvl); + +int +it_nv_to_tpglist(nvlist_t *nvl, uint32_t *count, it_tpg_t **tpglist); + +int +it_ini_to_nv(it_ini_t *ini, nvlist_t **nvl); + +int +it_nv_to_ini(nvlist_t *nvl, char *name, it_ini_t **ini); + +int +it_inilist_to_nv(it_ini_t *inilist, nvlist_t **nvl); + +int +it_nv_to_inilist(nvlist_t *nvl, uint32_t *count, it_ini_t **inilist); + +it_tgt_t * +it_tgt_lookup(it_config_t *cfg, char *tgt_name); + +it_tpg_t * +it_tpg_lookup(it_config_t *cfg, char *tpg_name); + +it_portal_t * +it_sns_svr_lookup(it_config_t *cfg, struct sockaddr_storage *sa); + +it_portal_t * +it_portal_lookup(it_tpg_t *cfg_tpg, struct sockaddr_storage *sa); + +int +it_sa_compare(struct sockaddr_storage *sa1, struct sockaddr_storage *sa2); + +/* + * Convert a sockaddr to the string representation, suitable for + * storing in an nvlist or printing out in a list. + */ +int +sockaddr_to_str(struct sockaddr_storage *sa, char **addr); + +/* + * Convert a char string to a sockaddr structure + * + * default_port should be the port to be used, if not specified + * as part of the supplied string 'arg'. + */ +struct sockaddr_storage * +it_common_convert_sa(char *arg, struct sockaddr_storage *buf, + uint32_t default_port); + +/* + * Convert an string array of IP-addr:port to a portal list + */ +int +it_array_to_portallist(char **arr, uint32_t count, uint32_t default_port, + it_portal_t **portallist, uint32_t *list_count); + +/* + * Function: it_config_free_cmn() + * + * Free any resources associated with the it_config_t structure. + * + * Parameters: + * cfg A C representation of the current iSCSI configuration + */ +void +it_config_free_cmn(it_config_t *cfg); + +/* + * Function: it_tgt_free_cmn() + * + * Frees an it_tgt_t structure. If tgt_next is not NULL, frees + * all structures in the list. + */ +void +it_tgt_free_cmn(it_tgt_t *tgt); + +/* + * Function: it_tpgt_free_cmn() + * + * Deallocates resources of an it_tpgt_t structure. If tpgt->next + * is not NULL, frees all members of the list. + */ +void +it_tpgt_free_cmn(it_tpgt_t *tpgt); + +/* + * Function: it_tpg_free_cmn() + * + * Deallocates resources associated with an it_tpg_t structure. + * If tpg->next is not NULL, frees all members of the list. + */ +void +it_tpg_free_cmn(it_tpg_t *tpg); + +/* + * Function: it_ini_free_cmn() + * + * Deallocates resources of an it_ini_t structure. If ini->next is + * not NULL, frees all members of the list. + */ +void +it_ini_free_cmn(it_ini_t *ini); + +/* + * Function: iscsi_binary_to_base64_str() + * + * Encodes a byte array into a base64 string. + */ +int +iscsi_binary_to_base64_str(uint8_t *in_buf, int in_buf_len, + char *base64_str_buf, int base64_buf_len); + +/* + * Function: iscsi_base64_str_to_binary() + * + * Decodes a base64 string into a byte array + */ +int +iscsi_base64_str_to_binary(char *hstr, int hstr_len, + uint8_t *binary, int binary_buf_len, int *out_len); + +#ifdef __cplusplus +} +#endif + +#endif /* _ISCSIT_COMMON_H_ */ |