diff options
Diffstat (limited to 'usr/src/cmd/mpathadm/cmdparse.c')
-rw-r--r-- | usr/src/cmd/mpathadm/cmdparse.c | 932 |
1 files changed, 932 insertions, 0 deletions
diff --git a/usr/src/cmd/mpathadm/cmdparse.c b/usr/src/cmd/mpathadm/cmdparse.c new file mode 100644 index 0000000000..9080e1cfdd --- /dev/null +++ b/usr/src/cmd/mpathadm/cmdparse.c @@ -0,0 +1,932 @@ +/* + * 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 <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 HELP_USAGE 2 +#define DETAIL_USAGE 3 + +/* 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 getSubcommand(char *, subcommand_t **); +static char *getExecBasename(char *); +static void usage(uint_t); +static void subUsage(uint_t, subcommand_t *); +static void subUsageObject(uint_t, subcommand_t *, object_t *); +static int getObject(char *, object_t **); +static int getObjectRules(uint_t, objectRules_t **); +static char *getLongOption(int); +static optionProp_t *getOptions(uint_t, uint_t); +static char *getOptionArgDesc(int); + +/* global data */ +static struct option *_longOptions; +static subcommand_t *_subcommands; +static object_t *_objects; +static objectRules_t *_objectRules; +static optionRules_t *_optionRules; +static optionTbl_t *_clientOptionTbl; +static char *commandName; + + +/* + * input: + * object - object value + * output: + * opCmd - pointer to opCmd_t structure allocated by caller + * + * On successful return, opCmd contains the rules for the value in + * object. On failure, the contents of opCmd is unspecified. + * + * Returns: + * zero on success + * non-zero on failure + * + */ +static int +getObjectRules(uint_t object, objectRules_t **objectRules) +{ + objectRules_t *sp; + + for (sp = _objectRules; sp->value; sp++) { + if (sp->value == object) { + *objectRules = sp; + return (0); + } + } + return (1); +} + +/* + * input: + * arg - pointer to array of char containing object string + * + * output: + * object - pointer to object_t structure pointer + * on success, contains the matching object structure based on + * input object name + * + * Returns: + * zero on success + * non-zero otherwise + * + */ +static int +getObject(char *arg, object_t **object) +{ + + object_t *op; + int len; + + for (op = _objects; op->name; op++) { + len = strlen(arg); + if (len == strlen(op->name) && + strncasecmp(arg, op->name, len) == 0) { + *object = op; + return (0); + } + } + return (1); +} + +/* + * input: + * arg - pointer to array of char containing subcommand string + * output: + * subcommand - pointer to subcommand_t pointer + * on success, contains the matching subcommand structure based on + * input subcommand name + * + * Returns: + * zero on success + * non-zero on failure + */ +static int +getSubcommand(char *arg, subcommand_t **subcommand) +{ + subcommand_t *sp; + int len; + + for (sp = _subcommands; sp->name; sp++) { + len = strlen(arg); + if (len == strlen(sp->name) && + strncasecmp(arg, sp->name, len) == 0) { + *subcommand = sp; + return (0); + } + } + return (1); +} + +/* + * input: + * object - object for which to get options + * subcommand - subcommand for which to get options + * + * Returns: + * on success, optionsProp_t pointer to structure matching input object + * value + * on failure, NULL is returned + */ +static optionProp_t * +getOptions(uint_t object, uint_t subcommand) +{ + uint_t currObject; + optionRules_t *op = _optionRules; + while (op && ((currObject = op->objectValue) != 0)) { + if ((currObject == object) && + (op->subcommandValue == subcommand)) { + return (&(op->optionProp)); + } + op++; + } + return (NULL); +} + +/* + * 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, HELP_USAGE, DETAIL_USAGE + * subcommand - pointer to subcommand_t structure + * + * Returns: + * none + * + */ +static void +subUsage(uint_t usageType, subcommand_t *subcommand) +{ + int i; + object_t *objp; + + + (void) fprintf(stdout, "%s:\t%s %s [", + gettext("Usage"), commandName, subcommand->name); + + for (i = 0; standardSubCmdOptions[i].name; i++) { + (void) fprintf(stdout, "-%c", + standardSubCmdOptions[i].val); + if (standardSubCmdOptions[i+1].name) + (void) fprintf(stdout, ","); + } + + (void) fprintf(stdout, "] %s [", "<OBJECT>"); + + for (i = 0; standardSubCmdOptions[i].name; i++) { + (void) fprintf(stdout, "-%c", + standardSubCmdOptions[i].val); + if (standardSubCmdOptions[i+1].name) + (void) fprintf(stdout, ","); + } + + (void) fprintf(stdout, "] %s", "[<OPERAND>]"); + (void) fprintf(stdout, "\n"); + + if (usageType == GENERAL_USAGE) { + return; + } + + (void) fprintf(stdout, "%s:\n", gettext("Usage by OBJECT")); + + /* + * iterate through object table + * For each object, print appropriate usage + * based on rules tables + */ + for (objp = _objects; objp->value; objp++) { + subUsageObject(usageType, subcommand, objp); + } +} + +/* + * Print usage for a subcommand and object. + * + * input: + * usage type - GENERAL_USAGE, HELP_USAGE, DETAIL_USAGE + * subcommand - pointer to subcommand_t structure + * objp - pointer to a object_t structure + * + * Returns: + * none + * + */ +static void +subUsageObject(uint_t usageType, subcommand_t *subcommand, object_t *objp) +{ + int i; + objectRules_t *objRules = NULL; + opCmd_t *opCmd = NULL; + optionProp_t *options; + char *optionArgDesc; + char *longOpt; + + + if (getObjectRules(objp->value, &objRules) != 0) { + /* + * internal subcommand rules table error + * no object entry in object + */ + assert(0); + } + + opCmd = &(objRules->opCmd); + + if (opCmd->invOpCmd & subcommand->value) { + return; + } + + options = getOptions(objp->value, subcommand->value); + + /* print generic subcommand usage */ + (void) fprintf(stdout, "\t%s %s ", commandName, subcommand->name); + + /* print object */ + (void) fprintf(stdout, "%s ", objp->name); + + /* print options if applicable */ + if (options != NULL) { + if (options->required) { + (void) fprintf(stdout, "%s", gettext("<")); + } else { + (void) fprintf(stdout, "%s", gettext("[")); + } + (void) fprintf(stdout, "%s", gettext("OPTIONS")); + if (options->required) { + (void) fprintf(stdout, "%s ", gettext(">")); + } else { + (void) fprintf(stdout, "%s ", gettext("]")); + } + } + + /* print operand requirements */ + if (opCmd->optOpCmd & subcommand->value) { + (void) fprintf(stdout, gettext("[")); + } + if (!(opCmd->noOpCmd & subcommand->value)) { + (void) fprintf(stdout, gettext("<")); + if (objRules->operandDefinition) { + (void) fprintf(stdout, "%s", + objRules->operandDefinition); + } else { + /* + * Missing operand description + * from table + */ + assert(0); + } + } + if (opCmd->multOpCmd & subcommand->value) { + (void) fprintf(stdout, gettext(" ...")); + } + if (!(opCmd->noOpCmd & subcommand->value)) { + (void) fprintf(stdout, gettext(">")); + } + if (opCmd->optOpCmd & subcommand->value) { + (void) fprintf(stdout, gettext("]")); + } + + if (usageType == HELP_USAGE) { + (void) fprintf(stdout, "\n"); + return; + } + + /* print options for subcommand, object */ + if (options != NULL && options->optionString != NULL) { + (void) fprintf(stdout, "\n\t%s:", gettext("OPTIONS")); + for (i = 0; i < strlen(options->optionString); i++) { + if ((longOpt = getLongOption( + options->optionString[i])) + == NULL) { + /* no long option exists for short option */ + assert(0); + } + (void) fprintf(stdout, "\n\t\t-%c, --%s ", + options->optionString[i], longOpt); + optionArgDesc = + getOptionArgDesc(options->optionString[i]); + if (optionArgDesc != NULL) { + (void) fprintf(stdout, "<%s>", optionArgDesc); + } + if (options->exclusive && + strchr(options->exclusive, + options->optionString[i])) { + (void) fprintf(stdout, " (%s)", + gettext("exclusive")); + } + } + } + (void) fprintf(stdout, "\n"); +} + +/* + * input: + * type of usage statement to print + * + * Returns: + * return value of subUsage + */ +static void +usage(uint_t usageType) +{ + int i; + subcommand_t subcommand; + subcommand_t *sp; + + /* print general command usage */ + (void) fprintf(stdout, "%s:\t%s ", + gettext("Usage"), commandName); + + for (i = 0; standardCmdOptions[i].name; i++) { + (void) fprintf(stdout, "-%c", + standardCmdOptions[i].val); + if (standardCmdOptions[i+1].name) + (void) fprintf(stdout, ","); + } + + if (usageType == HELP_USAGE || usageType == GENERAL_USAGE) { + for (i = 0; standardSubCmdOptions[i].name; i++) { + (void) fprintf(stdout, ",--%s", + standardSubCmdOptions[i].name); + if (standardSubCmdOptions[i+1].name) + (void) fprintf(stdout, ","); + } + } + + (void) fprintf(stdout, "\n"); + + + /* print all subcommand usage */ + for (sp = _subcommands; sp->name; sp++) { + subcommand.name = sp->name; + subcommand.value = sp->value; + if (usageType == HELP_USAGE) { + (void) fprintf(stdout, "\n"); + } + subUsage(usageType, &subcommand); + } +} + +/* + * 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, and subUsageObject handling command, + * subcommand and object usage respectively. + * + * When syntax is successfully validated, the associated function is called + * using the subcommands table functions. + * + * Syntax is as follows: + * command subcommand object [<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; + char *versionString; + char optionStringAll[MAXOPTIONSTRING + 1]; + optionProp_t *availOptions; + objectRules_t *objRules = NULL; + opCmd_t *opCmd = NULL; + subcommand_t *subcommand; + object_t *object; + cmdOptions_t cmdOptions[MAXOPTIONS + 1]; + struct option *lp; + optionTbl_t *optionTbl; + struct option intLongOpt[MAXOPTIONS + 1]; + + /* + * Check for NULLs on mandatory input arguments + * + * Note: longOptionTbl and optionRulesTbl can be NULL in the case + * where there is no caller defined options + * + */ + if (synTable.versionString == NULL || + synTable.subcommandTbl == NULL || + synTable.objectRulesTbl == NULL || + synTable.objectTbl == NULL || + funcRet == NULL) { + assert(0); + } + + + versionString = synTable.versionString; + + /* set global command name */ + commandName = getExecBasename(argv[0]); + + /* Set unbuffered output */ + setbuf(stdout, NULL); + + /* load globals */ + _subcommands = synTable.subcommandTbl; + _objectRules = synTable.objectRulesTbl; + _optionRules = synTable.optionRulesTbl; + _objects = synTable.objectTbl; + _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(HELP_USAGE); + return (1); + } else { + usage(GENERAL_USAGE); + return (1); + } + case 'V': + (void) fprintf(stdout, "%s: %s %s\n", + commandName, gettext("Version"), + versionString); + return (1); + 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 (getSubcommand(argv[1], &subcommand) != 0) { + (void) fprintf(stderr, "%s: %s\n", + commandName, gettext("invalid subcommand")); + usage(GENERAL_USAGE); + return (1); + } + + if (argc == 2) { + (void) fprintf(stderr, "%s: %s\n", + commandName, gettext("missing object")); + subUsage(GENERAL_USAGE, subcommand); + return (1); + } + + getoptargv = argv; + getoptargv++; + getoptargc = argc; + getoptargc -= 1; + + while ((opt = getopt_long(getoptargc, getoptargv, "+?", + standardSubCmdOptions, NULL)) != EOF) { + switch (opt) { + case '?': + /* + * getopt can return a '?' when no + * option letters match string. Check for + * the 'real' '?' in optopt. + */ + if (optopt == '?') { + subUsage(HELP_USAGE, subcommand); + return (1); + } else { + subUsage(GENERAL_USAGE, subcommand); + return (1); + } + default: + break; + } + } + + + /* + * object is always in the third argument. If there is no + * recognized object in the third argument, print error, + * help usage for the subcommand and then return. + */ + if (getObject(argv[2], &object) != 0) { + (void) fprintf(stderr, "%s: %s\n", + commandName, gettext("invalid object")); + subUsage(HELP_USAGE, subcommand); + return (1); + } + + if (getObjectRules(object->value, &objRules) != 0) { + /* + * internal subcommand rules table error + * no object entry in object table + */ + assert(0); + } + + opCmd = &(objRules->opCmd); + + /* + * Is command valid for this object? + */ + if (opCmd->invOpCmd & subcommand->value) { + (void) fprintf(stderr, "%s: %s %s\n", commandName, + gettext("invalid subcommand for"), object->name); + subUsage(HELP_USAGE, subcommand); + return (1); + } + + /* + * offset getopt arg begin since + * getopt(3C) assumes options + * follow first argument + */ + getoptargv = argv; + getoptargv++; + getoptargv++; + getoptargc = argc; + getoptargc -= 2; + + (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, check against object + * and subcommand (option rules table) for validity. + * This is done later. + */ + while ((opt = getopt_long(getoptargc, getoptargv, optionStringAll, + _longOptions, NULL)) != EOF) { + switch (opt) { + case '?': + if (optopt == '?') { + subUsageObject(DETAIL_USAGE, + subcommand, object); + return (1); + } else { + subUsage(GENERAL_USAGE, subcommand); + return (1); + } + default: + cmdOptions[i].optval = opt; + if (optarg) { + len = strlen(optarg); + if (len > sizeof (cmdOptions[i].optarg) + - 1) { + (void) fprintf(stderr, + "%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 + 2; + + /* + * Check validity of given options, if any were given + */ + + /* get option string for this object and subcommand */ + availOptions = getOptions(object->value, subcommand->value); + + if (cmdOptions[0].optval != 0) { /* options were input */ + if (availOptions == NULL) { /* no options permitted */ + (void) fprintf(stderr, "%s: %s\n", + commandName, gettext("no options permitted")); + subUsageObject(HELP_USAGE, subcommand, object); + return (1); + } + for (i = 0; cmdOptions[i].optval; i++) { + /* Check for invalid options */ + if (availOptions->optionString == NULL) { + /* + * internal option table error + * There must be an option string if + * there is an entry in the table + */ + assert(0); + } + /* is the option in the available option string? */ + + if (!(strchr(availOptions->optionString, + cmdOptions[i].optval))) { + (void) fprintf(stderr, + "%s: '-%c': %s\n", + commandName, cmdOptions[i].optval, + gettext("invalid option")); + subUsageObject(DETAIL_USAGE, subcommand, + object); + return (1); + + /* Check for exclusive options */ + } else if (cmdOptions[1].optval != 0 && + availOptions->exclusive && + strchr(availOptions->exclusive, + cmdOptions[i].optval)) { + (void) fprintf(stderr, + "%s: '-%c': %s\n", + commandName, cmdOptions[i].optval, + gettext("is an exclusive option")); + subUsageObject(DETAIL_USAGE, subcommand, + object); + return (1); + } + } + } else { /* no options were input */ + if (availOptions != NULL && + (availOptions->required)) { + (void) fprintf(stderr, "%s: %s\n", + commandName, + gettext("at least one option required")); + subUsageObject(DETAIL_USAGE, subcommand, + object); + return (1); + } + } + + /* + * If there are no more arguments (operands), + * check to see if this is okay + */ + if ((operInd == argc) && + (opCmd->reqOpCmd & subcommand->value)) { + (void) fprintf(stderr, "%s: %s %s %s\n", + commandName, subcommand->name, + object->name, gettext("requires an operand")); + subUsageObject(HELP_USAGE, subcommand, object); + return (1); + } + + /* + * If there are more operands, + * check to see if this is okay + */ + if ((argc > operInd) && + (opCmd->noOpCmd & subcommand->value)) { + (void) fprintf(stderr, "%s: %s %s %s\n", + commandName, subcommand->name, + object->name, gettext("takes no operands")); + subUsageObject(HELP_USAGE, subcommand, object); + return (1); + } + + /* + * If there is more than one more operand, + * check to see if this is okay + */ + if ((argc > operInd) && ((argc - operInd) != 1) && + !(opCmd->multOpCmd & subcommand->value)) { + (void) fprintf(stderr, "%s: %s %s %s\n", + commandName, subcommand->name, object->name, + gettext("accepts only a single operand")); + subUsageObject(HELP_USAGE, subcommand, object); + return (1); + } + + /* Finished syntax checks */ + + + /* Call appropriate function */ + *funcRet = subcommand->handler(argc - operInd, &argv[operInd], + object->value, &cmdOptions[0], callArgs); + + return (0); +} |