diff options
Diffstat (limited to 'lib/ipmi_cfgp.c')
| -rw-r--r-- | lib/ipmi_cfgp.c | 542 | 
1 files changed, 542 insertions, 0 deletions
| diff --git a/lib/ipmi_cfgp.c b/lib/ipmi_cfgp.c new file mode 100644 index 0000000..b8af80d --- /dev/null +++ b/lib/ipmi_cfgp.c @@ -0,0 +1,542 @@ +/* + * Copyright (c) 2016 Pentair Technical Products. All right reserved + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Pentair Technical Products or the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * PENTAIR TECHNICAL SOLUTIONS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <malloc.h> +#include <string.h> + +#include <ipmitool/helper.h> +#include <ipmitool/ipmi_cfgp.h> +#include <ipmitool/log.h> + +/* ipmi_cfgp_init  initialize configuration parameter context + * @param ctx      context to initialize + * @param set      array of parameter descriptors + * @param count    amount of descriptors supplied + * @param handler  function to do real job on parameters from the set + * @param priv     private data for the handler + */ +int +ipmi_cfgp_init(struct ipmi_cfgp_ctx *ctx, const struct ipmi_cfgp *set, +		unsigned int count, const char *cmdname, +		ipmi_cfgp_handler_t handler, void *priv) +{ +	if (ctx == NULL || set == NULL || handler == NULL || !cmdname) { +		return -1; +	} + +	memset(ctx, 0, sizeof(struct ipmi_cfgp_ctx)); + +	ctx->set = set; +	ctx->count = count; +	ctx->cmdname = cmdname; +	ctx->handler = handler; +	ctx->priv = priv; + +	return 0; +} + +/* ipmi_cfgp_uninit  destroy data list attached to context + * @param ctx          parameter context to clear + * @returns  0 -- list destroyed + *          -1 -- ctx is NULL + */ +int +ipmi_cfgp_uninit(struct ipmi_cfgp_ctx *ctx) +{ +	struct ipmi_cfgp_data *d; + +	if (ctx == NULL) { +		return -1; +	} + +	while (ctx->v) { +		d = ctx->v; +		ctx->v = d->next; +		free(d); +		d = NULL; +	} + +	return 0; +} + +/* lookup_cfgp -- find a parameter in a set*/ +static const struct ipmi_cfgp * +lookup_cfgp(const struct ipmi_cfgp_ctx *ctx, const char *name) +{ +	const struct ipmi_cfgp *p; +	int i; + +	for (i = 0; i < ctx->count; i++) { +		p = &ctx->set[i]; + +		if (p->name && !strcasecmp(p->name, name)) { +			return p; +		} +	} + +	return NULL; +} + +/* ipmi_cfgp_parse_sel parse parameter selector + * (parameter ID, set selector, block selector) from cmdline. + * + * @param ctx     configuration parameter context to use + * @param argc    elements left in argv + * @param argv    array of arguments + * @param sel     where to store parsed selector + * + * @returns       >=0 number of argv elements used + *                <0 error + */ +int +ipmi_cfgp_parse_sel(struct ipmi_cfgp_ctx *ctx, +		int argc, const char **argv, struct ipmi_cfgp_sel *sel) +{ +	const struct ipmi_cfgp *p; + +	if (ctx == NULL || argv == NULL || sel == NULL) { +		return -1; +	} + +	sel->param = -1; +	sel->set = -1; +	sel->block = -1; + +	if (argc == 0) { +		/* no parameter specified, good for print, save */ +		return 0; +	} + +	p = lookup_cfgp(ctx, argv[0]); +	if (p == NULL) { +		lprintf(LOG_ERR, "invalid parameter"); +		return -1; +	} + +	sel->param = p - ctx->set; +	sel->set = p->is_set ? -1 : 0; +	sel->block = p->has_blocks ? -1 : 0; + +	if (argc == 1 || !p->is_set) { +		/* No set and block selector applicable or specified */ +		return 1; +	} + +	if (str2int(argv[1], &sel->set) +		|| sel->set < 0 +		|| (sel->set == 0 && p->first_set)) { +		lprintf(LOG_ERR, "invalid set selector"); +		return -1; +	} + +	if (argc == 2 || !p->has_blocks) { +		/* No block selector applicable or specified */ +		return 2; +	} + +	if (str2int(argv[2], &sel->block) +		|| sel->block < 0 +		|| (sel->block == 0 && p->first_block)) { +		lprintf(LOG_ERR, "invalid block selector"); +		return -1; +	} + +	return 3; +} + +/* cfgp_add_data  adds block of data to list in the configuration + * parameter context + * + * @param ctx    context to add data to + * @param data   parameter data + */ +static void +cfgp_add_data(struct ipmi_cfgp_ctx *ctx, struct ipmi_cfgp_data *data) +{ +	struct ipmi_cfgp_data **pprev = &ctx->v; + +	data->next = NULL; + +	while (*pprev) { +		pprev = &(*pprev)->next; +	} + +	*pprev = data; +} + +/* cfgp_usage     prints format for configuration parameter + * + * @param p       configuration parameter descriptor + * @param write   0 if no value is expected, !=0 otherwise + */ +static void +cfgp_usage(const struct ipmi_cfgp *p, int write) +{ +	if (p->name == NULL) { +		return; +	} + +	if (write && p->format == NULL) { +		return; +	} + +	printf("    %s%s%s %s\n", +		p->name, p->is_set ? " <set_sel>" : "", +		p->has_blocks ? " <block_sel>" : "", +		write ? p->format : ""); +} + +/* ipmi_cfgp_usage     prints format for configuration parameter set + * + * @param set       configuration parameter descriptor array + * @param count     number of elements in set + * @param write     0 if no value is expected, !=0 otherwise + */ +void +ipmi_cfgp_usage(const struct ipmi_cfgp *set, int count, int write) +{ +	const struct ipmi_cfgp *p; +	int i; + +	if (set == NULL) { +		return; +	} + +	for (i = 0; i < count; i++) { +		p = &set[i]; + +		if (write && p->access == CFGP_RDONLY) { +			continue; +		} + +		if (!write && p->access == CFGP_WRONLY) { +			continue; +		} + +		cfgp_usage(p, write); +	} +} + +/* ipmi_cfgp_parse_data   parse parameter data from command line into context + * @param ctx        context to add data + * @param sel        parameter selector + * @param argc       number of elements in argv + * @param argv       array of unparsed arguments + * + * @returns          0 on success + *                  <0 on error + */ +int +ipmi_cfgp_parse_data(struct ipmi_cfgp_ctx *ctx, +		const struct ipmi_cfgp_sel *sel, int argc, const char **argv) +{ +	const struct ipmi_cfgp *p; +	struct ipmi_cfgp_data *data; +	struct ipmi_cfgp_action action; + +	if (ctx == NULL || sel == NULL || argv == NULL) { +		return -1; +	} + +	if (sel->param == -1 || sel->param >= ctx->count) { +		lprintf(LOG_ERR, "invalid parameter, must be one of:"); +		ipmi_cfgp_usage(ctx->set, ctx->count, 1); +		return -1; +	} + +	if (sel->set == -1) { +		lprintf(LOG_ERR, "set selector is not specified"); +		return -1; +	} + +	if (sel->block == -1) { +		lprintf(LOG_ERR, "block selector is not specified"); +		return -1; +	} + +	p = &ctx->set[sel->param]; + +	if (p->size == 0) { +		return -1; +	} + +	data = malloc(sizeof(struct ipmi_cfgp_data) + p->size); +	if (data == NULL) { +		return -1; +	} + +	memset(data, 0, sizeof(struct ipmi_cfgp_data) + p->size); + +	action.type = CFGP_PARSE; +	action.set = sel->set; +	action.block = sel->block; +	action.argc = argc; +	action.argv = argv; +	action.file = NULL; + +	if (ctx->handler(ctx->priv, p, &action, data->data) != 0) { +		ipmi_cfgp_usage(p, 1, 1); +		free(data); +		data = NULL; +		return -1; +	} + +	data->sel = *sel; + +	cfgp_add_data(ctx, data); +	return 0; +} + +/* cfgp_get_param -- get parameter data from MC into data list within context + * + * @param ctx      context + * @param p        parameter descriptor + * @param set      parameter set selector, can be -1 to scan all set selectors + * @param block    parameter block selector, can be -1 to get all blocks + * @param quiet    set to non-zero to continue on errors + *                 (required for -1 to work) + * @returns        0 on success, non-zero otherwise + */ +static int +cfgp_get_param(struct ipmi_cfgp_ctx *ctx, const struct ipmi_cfgp *p, +		int set, int block, int quiet) +{ +	struct ipmi_cfgp_data *data; +	struct ipmi_cfgp_action action; +	int cset; +	int cblock; +	int ret; + +	if (p->size == 0) { +		return -1; +	} + +	action.type = CFGP_GET; +	action.argc = 0; +	action.argv = NULL; +	action.file = NULL; + +	if (set == -1 && !p->is_set) { +		set = 0; +	} + +	if (block == -1 && !p->has_blocks) { +		block = 0; +	} + +	if (set == -1) { +		cset = p->first_set; +	} else { +		cset = set; +	} + +	action.quiet = quiet; + +	do { +		if (block == -1) { +			cblock = p->first_block; +		} else { +			cblock = block; +		} + +		do { +			data = malloc(sizeof(struct ipmi_cfgp_data) + p->size); +			if (data == NULL) { +				return -1; +			} + +			memset(data, 0, sizeof(struct ipmi_cfgp_data) + p->size); + +			action.set = cset; +			action.block = cblock; + +			ret = ctx->handler(ctx->priv, p, &action, data->data); +			if (ret != 0) { +				free(data); +				data = NULL; + +				if (!action.quiet) { +					return ret; +				} +				break; +			} + +			data->sel.param = p - ctx->set; +			data->sel.set = cset; +			data->sel.block = cblock; + +			cfgp_add_data(ctx, data); + +			cblock++; +			action.quiet = 1; +		} while (block == -1); + +		if (ret != 0 && cblock == p->first_block) { +			break; +		} + +		cset++; +	} while (set == -1); + +	return 0; +} + +/* ipmi_cfgp_get -- get parameters data from MC into data list within context + * + * @param ctx      context + * @param sel      parameter selector + * @returns        0 on success, non-zero otherwise + */ +int +ipmi_cfgp_get(struct ipmi_cfgp_ctx *ctx, const struct ipmi_cfgp_sel *sel) +{ +	int i; +	int ret; + +	if (ctx == NULL || sel == NULL) { +		return -1; +	} + +	if (sel->param != -1) { +		if (sel->param >= ctx->count) { +			return -1; +		} + +		ret = cfgp_get_param(ctx, &ctx->set[sel->param], +			sel->set, sel->block, 0); +		if (ret) { +			return -1; +		} +		return 0; +	} + +	for (i = 0; i < ctx->count; i++) { +		if (ctx->set[i].access == CFGP_WRONLY) { +			continue; +		} + +		if (cfgp_get_param(ctx, &ctx->set[i], sel->set, sel->block, 1)) { +			return -1; +		} +	} + +	return 0; +} + +static int +cfgp_do_action(struct ipmi_cfgp_ctx *ctx, int action_type, +		const struct ipmi_cfgp_sel *sel, FILE *file, int filter) +{ +	const struct ipmi_cfgp *p; +	struct ipmi_cfgp_data *data; +	struct ipmi_cfgp_action action; +	int ret; + +	if (ctx == NULL || sel == NULL) { +		return -1; +	} + +	action.type = action_type; +	action.argc = 0; +	action.argv = NULL; +	action.file = file; + +	for (data = ctx->v; data != NULL; data = data->next) { +		if (sel->param != -1 && sel->param != data->sel.param) { +			continue; +		} +		if (sel->set != -1 && sel->set != data->sel.set) { +			continue; +		} +		if (sel->block != -1 && sel->block != data->sel.block) { +			continue; +		} +		if (ctx->set[data->sel.param].access == filter) { +			continue; +		} + +		p = &ctx->set[data->sel.param]; + +		action.set = data->sel.set; +		action.block = data->sel.block; + +		if (action_type == CFGP_SAVE) { +			fprintf(file, "%s %s ", ctx->cmdname, p->name); +			if (p->is_set) { +				fprintf(file, "%d ", data->sel.set); +			} +			if (p->has_blocks) { +				fprintf(file, "%d ", data->sel.block); +			} +		} + +		ret = ctx->handler(ctx->priv, p, &action, data->data); + +		if (action_type == CFGP_SAVE) { +			fputc('\n', file); +		} + +		if (ret != 0) { +			return -1; +		} +	} + +	return 0; +} + +int +ipmi_cfgp_set(struct ipmi_cfgp_ctx *ctx, const struct ipmi_cfgp_sel *sel) +{ +	return cfgp_do_action(ctx, CFGP_SET, sel, NULL, CFGP_RDONLY); +} + +int +ipmi_cfgp_save(struct ipmi_cfgp_ctx *ctx, +		const struct ipmi_cfgp_sel *sel, FILE *file) +{ +	if (file == NULL) { +		return -1; +	} + +	return cfgp_do_action(ctx, CFGP_SAVE, sel, file, CFGP_RDONLY); +} + +int +ipmi_cfgp_print(struct ipmi_cfgp_ctx *ctx, +		const struct ipmi_cfgp_sel *sel, FILE *file) +{ +	if (file == NULL) { +		return -1; +	} + +	return cfgp_do_action(ctx, CFGP_PRINT, sel, file, CFGP_RESERVED); +} | 
