diff options
Diffstat (limited to 'usr/src/common')
-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 |
4 files changed, 2746 insertions, 0 deletions
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; + } +} |