OpenSolaris_b135/cmd/pools/poolcfg/poolcfg.y

%{
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (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 2005 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

/*
 * Overview of poolcfg(1)
 *
 * poolcfg(1) implements a small grammar for manipulating pools configurations.
 * yacc(1) is used to generate the parser and poolcfg.l contains a simple lexer
 * (generted by lex(1)) to perform lexical processsing of the input.
 *
 * Refer to the poolcfg(1) manpage for more details of the grammar.
 *
 * The parser is designed so that all operations implement the same interface.
 * This allows the parser to simply build up the command (using the cmd
 * variable) by storing arguments and a pointer to the desired function in the
 * cmd. The command is executed when the commands production is matched.
 *
 * Properties and associations are stored in simple linked lists and processed
 * in the order submitted by the user.
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <locale.h>
#include <libintl.h>
#include <sys/utsname.h>

#include <pool.h>
#include "utils.h"
#include "poolcfg.h"



#define	USAGE1	\
"Usage:\n" \
"%s -h\n" \
"%s -c command [ -d | [ file ] ]\n" \
"%s -f command-file [-d | [ file ] ]\n\n"

#define	USAGE2	\
"command:\n" \
"  info [entity name]\n" \
"         display configuration (or specified portion) in readable form\n" \
"  create entity name [property-list]\n" \
"         make an entity of the specified type and name\n" \
"  destroy entity name\n" \
"         remove the specified entity\n" \
"  modify entity name [property-list]\n" \
"         change the listed properties on the named entity\n" \
"  associate pool name [resource-list]\n" \
"         connect one or more resources to a pool, or replace one or more\n" \
"         existing connections\n" \
"  transfer to resource name [component-list]\n" \
"         transfer one or more discreet components to a resource\n" \
"  transfer [quantity] from resource src to tgt\n" \
"         transfer a resource quantity from src to tgt\n" \
"  transfer [quantity] to resource tgt from src\n" \
"         transfer a resource quantity to tgt from src\n" \
"  discover\n" \
"         create a system entity, with one pool entity and resources to\n" \
"         match current system configuration\n" \
"  rename entity old_name to new_name\n" \
"         change the name of the entity on the system to its new name\n\n" \
"property-list:\n" \
"  ( proptype name = value [ ; proptype name = value ]* )\n" \
"         where multiple definitions in the sentence for a given\n" \
"         proptype, name pair are ignored; the last one provided is used.\n" \
"         For property deletion, use \"~ proptype name\"\n\n" \
"resource-list:\n" \
"  ( resource name [; resource name ] )\n" \
"         where multiple uses of a resource are ignored; the last provided\n" \
"         is the one used.\n" \
"         There is no deletion syntax for resource lists.\n" \
"component-list:\n" \
"  ( cpu id [; cpu id ] )\n" \
"         where multiple uses of the same component cause the last provided\n" \
"         to be the one used.\n" \
"         There is no deletion syntax for component lists.\n" \
"entity:\n" \
"  system | pool | pset | cpu\n" \
"         where cpu is only valid for transfer, info and modify commands.\n" \
"resource:\n" \
"  pset\n\n" \
"proptype:\n" \
"  boolean | int | uint | string | float\n\n"

int dofile = PO_FALSE;			/* poolcfg.l uses this for errors */
int conf_edit_error = POE_OK;		/* cached error for error reporting */
int conf_edit_errno = 0;		/* cached errno for error reporting */
int conf_list_error = POE_OK;		/* cached error for error reporting */
int conf_list_errno = 0;		/* cached errno for error reporting */
static const char cmdname[] = "poolcfg";
static const char cmd_options[] = "c:df:h";
static void usage(int);
static const char *max_suffix = ".max";
static const char *min_suffix = ".min";

static const char *conf_file = NULL;	/* Location of target config */
static cmd_t *cmd = NULL;		/* Command being processed */
static pool_conf_t *conf = NULL;	/* Config to be processed */
static int edited = PO_FALSE;		/* Has the configuration been changed */

/* yacc externals */
extern FILE *yyin;
extern int yydebug;
extern void yyerror(char *s);

/* Utility functions */
static void arg_parse(const char *);
static void file_parse(const char *);
static cmd_t *alloc_cmd(void);
static prop_t *alloc_prop(prop_op_t);
static assoc_t *alloc_assoc(int, const char *);
static void free_cmd(cmd_t *);
static void check_conf_name(cmd_t *);
static void prop_list_walk(cmd_t *, pool_elem_t *);
static void assoc_list_walk(cmd_t *, pool_t *);
static void transfer_list_walk(cmd_t *, pool_resource_t *);
static void terminate(void);
static pool_component_t *get_cpu(const char *);
static void process_min_max(pool_resource_t *);

/* Info Commands */
static void parser_conf_info(cmd_t *);
static void parser_pool_info(cmd_t *);
static void parser_resource_info(cmd_t *, const char *);
static void parser_pset_info(cmd_t *);
static void parser_cpu_info(cmd_t *);

/* Create Commands */
static void parser_conf_create(cmd_t *);
static void parser_pool_create(cmd_t *);
static void parser_resource_create(cmd_t *, const char *);
static void parser_pset_create(cmd_t *);

/* Destroy Commands */
static void parser_conf_destroy(cmd_t *);
static void parser_pool_destroy(cmd_t *);
static void parser_resource_destroy(cmd_t *, const char *);
static void parser_pset_destroy(cmd_t *);

/* Modify Commands */
static void parser_conf_modify(cmd_t *);
static void parser_pool_modify(cmd_t *);
static void parser_resource_modify(cmd_t *, const char *);
static void parser_pset_modify(cmd_t *);
static void parser_cpu_modify(cmd_t *);

/* Associate Commands */
static void parser_pool_associate(cmd_t *);

/* Assign Commands */
static void parser_resource_xtransfer(cmd_t *);
static void parser_resource_transfer(cmd_t *);

/* Discover Commands */
static void parser_conf_discover(cmd_t *);

/* Rename Commands */
static void parser_rename(cmd_t *, pool_elem_t *, const char *);
static void parser_conf_rename(cmd_t *);
static void parser_pool_rename(cmd_t *);
static void parser_pset_rename(cmd_t *);


%}

%union {
	double dval;
	uint64_t uval;
	int64_t ival;
	char *sval;
	uchar_t bval;
	cmd_t *cmd;
	prop_t *prop;
	pv_u val;
	assoc_t *assoc;
}

%start commands

%token PCC_INFO PCC_CREATE PCC_DESTROY PCC_MODIFY PCC_ASSOC PCC_DISC PCC_RENAME
%token PCC_TRANSFER
%token PCK_FROM PCK_TO PCK_OPENLST PCK_CLOSELST PCK_SEPLST PCK_ASSIGN PCK_UNDEF
PCK_COMMAND
%token PCV_FILENAME PCV_SYMBOL PCV_VAL_INT PCV_VAL_UINT PCV_VAL_FLOAT
PCV_VAL_STRING PCV_VAL_BOOLEAN
%token PCT_INT PCT_UINT PCT_BOOLEAN PCT_FLOAT PCT_STRING
%token PCE_SYSTEM PCE_POOL PCE_PSET PCE_CPU

%type <ival> PCV_VAL_INT
%type <uval> PCV_VAL_UINT
%type <bval> PCV_VAL_BOOLEAN
%type <dval> PCV_VAL_FLOAT
%type <sval> PCV_VAL_STRING
%type <sval> PCV_SYMBOL
%type <sval> PCV_FILENAME

%type <ival> PCC_INFO
%type <ival> PCE_SYSTEM PCE_POOL PCE_PSET PCE_CPU
%type <ival> entity proptype info_entity modify_entity
%type <sval> name src tgt
%type <cmd> command
%type <cmd> list_command info_command edit_command create_command
destroy_command modify_command associate_command discover_command
rename_command transfer_command transfer_qty transfer_components
%type <prop> prop_remove prop_assign prop_op prop_ops property_list
%type <assoc> resource_assign resource_assigns resource_list
%type <assoc> component_assign component_assigns component_list
%type <val> value
%type <ival> resource component

%%

commands: command
	{
		if ($1->cmd != NULL)
			$1->cmd($1);
		free_cmd($1);
	}
	| commands command
	{
		if ($2->cmd != NULL)
			$2->cmd($2);
		free_cmd($2);
	}
	| command error { YYERROR;};

command: list_command
	| edit_command
	{
		if (conf_edit_error != POE_OK) {
			if ($1->cmd != parser_conf_create &&
			    $1->cmd != parser_conf_discover) {
				die(gettext(ERR_CONF_LOAD), conf_file,
				    get_errstr_err(conf_edit_error,
				        conf_edit_errno));
			}
		}
		edited = PO_TRUE;
	};

list_command: info_command
	{
		if (conf_list_error != POE_OK) {
			if ($1->cmd != parser_conf_create &&
			    $1->cmd != parser_conf_discover) {
				die(gettext(ERR_CONF_LOAD), conf_file,
				    get_errstr_err(conf_list_error,
				        conf_list_errno));
			}
		}
	}
	| discover_command {conf_list_error = conf_edit_error = POE_OK;};

edit_command: create_command
	| destroy_command
	| modify_command
	| associate_command
	| transfer_command
	| rename_command;

info_command: PCC_INFO
	{
		if (($$ = alloc_cmd()) == NULL)
			YYERROR;
		cmd = $$;
		$$->cmd = &parser_conf_info;
	}
	| PCC_INFO info_entity name
	{
		if (($$ = alloc_cmd()) == NULL)
			YYERROR;
		cmd = $$;
		switch ($2) {
		case PCE_SYSTEM:
			$$->cmd = &parser_conf_info;
			break;
		case PCE_POOL:
			$$->cmd = &parser_pool_info;
			break;
		case PCE_PSET:
			$$->cmd = &parser_pset_info;
			break;
		case PCE_CPU:
			$$->cmd = &parser_cpu_info;
			break;
		default:
			warn(gettext(ERR_UNKNOWN_ENTITY), $2);
			YYERROR;
		}
		$$->cmd_tgt1 = $3;
	};

create_command: PCC_CREATE entity name
	{
		if (($$ = alloc_cmd()) == NULL)
			YYERROR;
		cmd = $$;
		switch ($2) {
		case PCE_SYSTEM:
			$$->cmd = &parser_conf_create;
			/*
			 * When creating a new system element, ensure
			 * pre-existing errors are ignored.
			 */
			conf_list_error = conf_edit_error = POE_OK;
			break;
		case PCE_POOL:
			$$->cmd = &parser_pool_create;
			break;
		case PCE_PSET:
			$$->cmd = &parser_pset_create;
			break;
		default:
			warn(gettext(ERR_UNKNOWN_ENTITY), $2);
			YYERROR;
		}
		$$->cmd_tgt1 = $3;
	}
	| create_command property_list;

destroy_command: PCC_DESTROY entity name
	{
		if (($$ = alloc_cmd()) == NULL)
			YYERROR;
		cmd = $$;
		switch ($2) {
		case PCE_SYSTEM:
			$$->cmd = &parser_conf_destroy;
			break;
		case PCE_POOL:
			$$->cmd = &parser_pool_destroy;
			break;
		case PCE_PSET:
			$$->cmd = &parser_pset_destroy;
			break;
		default:
			warn(gettext(ERR_UNKNOWN_ENTITY), $2);
			YYERROR;
		}
		$$->cmd_tgt1 = $3;
	};

modify_command: PCC_MODIFY modify_entity name
	{
		if (($$ = alloc_cmd()) == NULL)
			YYERROR;
		cmd = $$;
		switch ($2) {
		case PCE_SYSTEM:
			$$->cmd = &parser_conf_modify;
			break;
		case PCE_POOL:
			$$->cmd = &parser_pool_modify;
			break;
		case PCE_PSET:
			$$->cmd = &parser_pset_modify;
			break;
		case PCE_CPU:
			$$->cmd = &parser_cpu_modify;
			break;
		default:
			warn(gettext(ERR_UNKNOWN_ENTITY), $2);
			YYERROR;
		}
		$$->cmd_tgt1 = $3;
	}
	| modify_command property_list;

associate_command: PCC_ASSOC PCE_POOL name
	{
		if (($$ = alloc_cmd()) == NULL)
			YYERROR;
		cmd = $$;
		$$->cmd = &parser_pool_associate;
		cmd->cmd_tgt1 = $3;
	}
	| associate_command resource_list;

transfer_command: transfer_qty
	| transfer_components;

transfer_components: PCC_TRANSFER PCK_TO PCE_PSET name
	{
		if (($$ = alloc_cmd()) == NULL)
			YYERROR;
		cmd = $$;
		$$->cmd = &parser_resource_xtransfer;
		cmd->cmd_tgt1 = $4;
	}
	| transfer_components component_list;

transfer_qty: PCC_TRANSFER PCV_VAL_UINT PCK_FROM PCE_PSET src PCK_TO tgt
	{
		if (($$ = alloc_cmd()) == NULL)
			YYERROR;
		cmd = $$;
		$$->cmd = &parser_resource_transfer;
		cmd->cmd_tgt1 = $5;
		cmd->cmd_tgt2 = $7;
		cmd->cmd_qty = $2;
	}
	| PCC_TRANSFER  PCV_VAL_UINT PCK_TO PCE_PSET tgt PCK_FROM src
	{
		if (($$ = alloc_cmd()) == NULL)
			YYERROR;
		cmd = $$;
		$$->cmd = &parser_resource_transfer;
		cmd->cmd_tgt1 = $7;
		cmd->cmd_tgt2 = $5;
		cmd->cmd_qty = $2;
	};
			
discover_command: PCC_DISC
	{
		if (($$ = alloc_cmd()) == NULL)
			YYERROR;
		cmd = $$;
		$$->cmd = &parser_conf_discover;
	};

rename_command: PCC_RENAME entity name PCK_TO name
	{
		if (($$ = alloc_cmd()) == NULL)
			YYERROR;
		cmd = $$;
		switch ($2) {
		case PCE_SYSTEM:
			$$->cmd = &parser_conf_rename;
			break;
		case PCE_POOL:
			$$->cmd = &parser_pool_rename;
			break;
		case PCE_PSET:
			$$->cmd = &parser_pset_rename;
			break;
		default:
			warn(gettext(ERR_UNKNOWN_ENTITY), $2);
			YYERROR;
		}
		$$->cmd_tgt1 = $3;
		$$->cmd_tgt2 = $5;
	};

modify_entity: entity
	| PCE_CPU  {$$ = PCE_CPU;};

info_entity: entity
	| PCE_CPU  {$$ = PCE_CPU;};

entity: PCE_SYSTEM {$$ = PCE_SYSTEM;}
	| PCE_POOL {$$ = PCE_POOL;}
	| PCE_PSET {$$ = PCE_PSET;};

name: PCV_SYMBOL;

src: PCV_SYMBOL;

tgt: PCV_SYMBOL;

value: PCV_VAL_INT { $$.i = $1;}
	| PCV_VAL_UINT { $$.u = $1;}
	| PCV_VAL_FLOAT { $$.d = $1;}
	| PCV_VAL_BOOLEAN { $$.b = $1;}
	| PCV_VAL_STRING { $$.s = $1;};

prop_remove: PCK_UNDEF proptype name
	{
		if (($$ = alloc_prop(po_remove)) == NULL)
			YYERROR;
		$$->prop_name = $3;
	};

prop_op: prop_assign
	| prop_remove;

prop_ops: prop_op
	{
		prop_t *prop = NULL;
		prop_t *prev = NULL;

		for (prop = cmd->cmd_prop_list; prop != NULL;
		    prop = prop->prop_next)
			prev = prop; /* Find end of list */
		if (prev != NULL)
			prev->prop_next = $1;
		else
			cmd->cmd_prop_list = $1;
		$$ = cmd->cmd_prop_list;
	}
	| prop_ops PCK_SEPLST prop_op
	{
		prop_t *prop = NULL;
		prop_t *prev = NULL;

		for (prop = cmd->cmd_prop_list; prop != NULL;
		    prop = prop->prop_next)
			prev = prop; /* Find end of list */
		if (prev != NULL)
			prev->prop_next = $3;
		else
			cmd->cmd_prop_list = $3;
		$$ = cmd->cmd_prop_list;

	};

prop_assign: proptype name PCK_ASSIGN value
	{
		if (($$ = alloc_prop(po_create)) == NULL)
			YYERROR;
		$$->prop_name = $2;
		switch ($1) {
		case PCT_INT:
			pool_value_set_int64($$->prop_value, $4.i);
			break;
		case PCT_UINT:
			pool_value_set_uint64($$->prop_value, $4.u);
			break;
		case PCT_BOOLEAN:
			pool_value_set_bool($$->prop_value, $4.b);
			break;
		case PCT_FLOAT:
			pool_value_set_double($$->prop_value, $4.d);
			break;
		case PCT_STRING:
			pool_value_set_string($$->prop_value, $4.s);
			break;
		}
	};

property_list: PCK_OPENLST prop_ops PCK_CLOSELST
	{
		$$ = $2;
	};

resource_assigns: resource_assign
	{
		assoc_t *assoc = NULL;
		assoc_t *prev = NULL;

		for (assoc = cmd->cmd_assoc_list; assoc != NULL;
		    assoc = assoc->assoc_next)
			prev = assoc; /* Find end of list */
		if (prev != NULL)
			prev->assoc_next = $1;
		else
			cmd->cmd_assoc_list = $1;
		$$ = cmd->cmd_assoc_list;
	}

	| resource_assigns PCK_SEPLST resource_assign
	{
		assoc_t *assoc = NULL;
		assoc_t *prev = NULL;

		for (assoc = cmd->cmd_assoc_list; assoc != NULL;
		    assoc = assoc->assoc_next)
			prev = assoc; /* Find end of list */
		if (prev != NULL)
			prev->assoc_next = $3;
		$$ = $3;
	};

resource_assign: resource name
	{
		if (($$ = alloc_assoc($1, $2)) == NULL)
			YYERROR;
	};

resource: PCE_PSET {$$ = PCE_PSET;};

resource_list: PCK_OPENLST resource_assigns PCK_CLOSELST
	{
		$$ = $2;
	};

component_assigns: component_assign
	{
		assoc_t *assoc = NULL;
		assoc_t *prev = NULL;

		for (assoc = cmd->cmd_assoc_list; assoc != NULL;
		    assoc = assoc->assoc_next)
			prev = assoc; /* Find end of list */
		if (prev != NULL)
			prev->assoc_next = $1;
		else
			cmd->cmd_assoc_list = $1;
		$$ = cmd->cmd_assoc_list;
	}

	| component_assigns PCK_SEPLST component_assign
	{
		assoc_t *assoc = NULL;
		assoc_t *prev = NULL;

		for (assoc = cmd->cmd_assoc_list; assoc != NULL;
		    assoc = assoc->assoc_next)
			prev = assoc; /* Find end of list */
		if (prev != NULL)
			prev->assoc_next = $3;
		$$ = $3;
	};

component_list: PCK_OPENLST component_assigns PCK_CLOSELST
	{
		$$ = $2;
	};

component_assign: component name
	{
		if (($$ = alloc_assoc($1, $2)) == NULL)
			YYERROR;
	};

component: PCE_CPU {$$ = PCE_CPU;};

proptype: PCT_INT {$$ = PCT_INT;}
	| PCT_UINT {$$ = PCT_UINT;}
	| PCT_BOOLEAN {$$ = PCT_BOOLEAN;}
	| PCT_FLOAT {$$ = PCT_FLOAT;}
	| PCT_STRING {$$ = PCT_STRING;};

%%

#ifndef	TEXT_DOMAIN
#define	TEXT_DOMAIN "SYS_TEST"
#endif

int
main(int argc, char *argv[])
{
	int opt;
	int docmd = PO_FALSE;

	(void) getpname(argv[0]);
	(void) setlocale(LC_ALL, "");
	(void) textdomain(TEXT_DOMAIN);
	if (atexit(terminate) != 0) {
		die(gettext(ERR_SET_TERM), get_errstr());
	}

	conf_file = pool_static_location();

	yydebug = 0;
	while ((opt = getopt(argc, argv, cmd_options)) != (int)EOF) {

		switch (opt) {
		case 'c': /* Process command line */
			if (dofile == PO_TRUE)
				usage(1);
			arg_parse(optarg);
			docmd = PO_TRUE;
			break;
		case 'd': /* Manipulate dynamic configuration */
			conf_file = pool_dynamic_location();
			break;
		case 'f': /* Process command file */
			if (docmd == PO_TRUE)
				usage(1);
			file_parse(optarg);
			dofile = PO_TRUE;
			break;
		case 'h':
			usage(2);
			break;
		case '?':
		default:
			usage(1);
			break;
		}
	}
	if (docmd == PO_FALSE && dofile == PO_FALSE)
		usage(1);

	if (optind == argc - 1) {
		if (strcmp(conf_file, pool_dynamic_location()) == 0)
			usage(1);
		conf_file = argv[optind];
	} else if (optind <  argc - 1)
		usage(1);

	if ((conf = pool_conf_alloc()) == NULL) {
		die(gettext(ERR_ALLOC_ELEMENT), gettext(CONFIGURATION),
		    get_errstr());
	}
	/*
	 * Opening a conf is complex, since we may be opening one of the
	 * following:
	 *	- An existing configuration that we can modify
	 *	- An existing configuration that we can't modify
	 *	- A new configuration that we can modify
	 *	- A new configuration that we can't modify
	 * The parser_conf_discover() function closes the file and reopens
	 * in PO_CREAT mode, so we only need be concerned here with the
	 * first two cases.
	 * Always try to open RDWR, if fail try RDONLY. Don't check
	 * if that fails, since we may be trying to discover a configuration
	 * in which case it's valid for both open attempts to fail. Later, when
	 * processing commands, if we don't have a valid configuration and
	 * we are trying to process a command which isn't a create or a discover
	 * we will fail the command as there is no valid configuration to
	 * work with.
	 */
	if (pool_conf_open(conf, conf_file, PO_RDWR) != 0) {
		conf_edit_error = pool_error();
		conf_edit_errno = errno;
		if (pool_conf_open(conf, conf_file, PO_RDONLY) != 0) {
			conf_list_error = pool_error();
			conf_list_errno = errno;
		}
	}

	if (yyparse() == 0) {
		if (pool_conf_status(conf) >= POF_VALID) {
			if (pool_conf_validate(conf, POV_STRICT) == PO_FAIL) {
				die(gettext(ERR_VALIDATION_FAILED),
				    get_errstr());
			}
			/*
			 * If the user attempted to change the configuration,
			 * then we should try to save the changes.
			 */
			if (edited == PO_TRUE) {
				if (pool_conf_commit(conf, 0) == PO_FAIL) {
					die(gettext(ERR_CONFIG_SAVE_FAILED),
					    get_errstr());
				}
			}
			pool_conf_close(conf);
		}
	} else {
		die(gettext(ERR_CMDPARSE_FAILED));
	}

	/*
	 * Cleanup is performed in terminate(), using atexit
	 */
	return (0);
}

/*
 * Info Commands
 * Invoke the appropriate libpool info function and display the returned
 * information.
 */
static void
parser_conf_info(cmd_t *cmd)
{
	char *info_buf;
	const char *tgt = cmd->cmd_tgt1;
	pool_value_t *pv = NULL;
	pool_elem_t *pe;

	if ((pe = pool_conf_to_elem(conf)) == NULL)
		die(gettext(ERR_GET_ELEMENT_DETAILS),
		    gettext(CONFIGURATION), "unknown", get_errstr());

	if (tgt != NULL)
		check_conf_name(cmd);
	else {
		if ((pv = pool_value_alloc()) == NULL)
			die(gettext(ERR_GET_ELEMENT_DETAILS),
			    gettext(CONFIGURATION), "unknown", get_errstr());
		if (pool_get_property(conf, pe, "system.name", pv) ==
		    POC_INVAL ||
		    pool_value_get_string(pv, &tgt) != PO_SUCCESS)
			die(gettext(ERR_GET_ELEMENT_DETAILS),
			    gettext(CONFIGURATION), "unknown", get_errstr());
	}
	if ((info_buf = pool_conf_info(conf, PO_TRUE)) == NULL) {
		die(gettext(ERR_GET_ELEMENT_DETAILS), gettext(CONFIGURATION),
		    tgt, get_errstr());
	}
	if (pv != NULL) {
		pool_value_free(pv);
	}
	(void) printf("%s\n", info_buf);
	free(info_buf);
}

static void
parser_pool_info(cmd_t *cmd)
{
	pool_t *pool;
	char *info_buf;

	if ((pool = pool_get_pool(conf, cmd->cmd_tgt1)) == NULL)
		die(gettext(ERR_LOCATE_ELEMENT), gettext(POOL), cmd->cmd_tgt1,
		    get_errstr());

	if ((info_buf = pool_info(conf, pool, PO_TRUE)) == NULL)
		die(gettext(ERR_GET_ELEMENT_DETAILS), gettext(POOL),
		    cmd->cmd_tgt1, get_errstr());
	(void) printf("%s\n", info_buf);
	free(info_buf);
}

static void
parser_resource_info(cmd_t *cmd, const char *type)
{
	pool_resource_t *resource;
	char *info_buf;

	if ((resource = pool_get_resource(conf, type, cmd->cmd_tgt1)) == NULL)
		die(gettext(ERR_LOCATE_ELEMENT), gettext(RESOURCE),
		    cmd->cmd_tgt1, get_errstr());

	if ((info_buf = pool_resource_info(conf, resource, PO_TRUE)) == NULL)
		die(gettext(ERR_GET_ELEMENT_DETAILS), gettext(RESOURCE),
		    cmd->cmd_tgt1, get_errstr());
	(void) printf("%s\n", info_buf);
	free(info_buf);
}

static void
parser_pset_info(cmd_t *cmd)
{
	parser_resource_info(cmd, PSET);
}

static void
parser_cpu_info(cmd_t *cmd)
{
	pool_component_t *comp;
	char *info_buf;

	if ((comp = get_cpu(cmd->cmd_tgt1)) == NULL)
		die(gettext(ERR_LOCATE_ELEMENT), gettext(CPU),
		    cmd->cmd_tgt1, get_errstr());
	if ((info_buf = pool_component_info(conf, comp, PO_TRUE)) == NULL) {
		die(gettext(ERR_GET_ELEMENT_DETAILS), gettext(CPU),
		    cmd->cmd_tgt1, get_errstr());
	}
	(void) printf("%s\n", info_buf);
	free(info_buf);
}

/*
 * Create Commands
 * Invoke the appropriate libpool create function and perform any requested
 * property operations.
 */
static void
parser_conf_create(cmd_t *cmd)
{
	const char *tmp_name;
	pool_elem_t *pe;

	if (conf != NULL && pool_conf_status(conf) >= POF_VALID)
		pool_conf_close(conf);
	if (pool_conf_open(conf, conf_file, PO_CREAT) != 0) {
		die(gettext(ERR_CREATE_ELEMENT), gettext(CONFIGURATION),
		    cmd->cmd_tgt1, get_errstr());
	}
	tmp_name = cmd->cmd_tgt1;
	cmd->cmd_tgt1 = cmd->cmd_tgt2;
	cmd->cmd_tgt2 = tmp_name;
	parser_conf_rename(cmd);
	if ((pe = pool_conf_to_elem(conf)) == NULL)
		die(gettext(ERR_GET_ELEMENT_DETAILS),
		    gettext(CONFIGURATION), "unknown", get_errstr());
	prop_list_walk(cmd, pe);
}

static void
parser_pool_create(cmd_t *cmd)
{
	pool_t *pool;

	if ((pool = pool_create(conf, cmd->cmd_tgt1)) == NULL)
		die(gettext(ERR_CREATE_ELEMENT), gettext(POOL), cmd->cmd_tgt1,
		    get_errstr());
	prop_list_walk(cmd, pool_to_elem(conf, pool));
}

static void
parser_resource_create(cmd_t *cmd, const char *type)
{
	pool_resource_t *resource;

	if ((resource = pool_resource_create(conf, type, cmd->cmd_tgt1))
	    == NULL)
		die(gettext(ERR_CREATE_ELEMENT), type, cmd->cmd_tgt1,
		    get_errstr());
	
	process_min_max(resource);

	prop_list_walk(cmd, pool_resource_to_elem(conf, resource));
}

static void
parser_pset_create(cmd_t *cmd)
{
	parser_resource_create(cmd, PSET);
}

/*
 * Rename Commands
 * Rename the target by calling pool_put_property for the name property.
 */
static void
parser_rename(cmd_t *cmd, pool_elem_t *pe, const char *name)
{
	pool_value_t *pv;

	if ((pv = pool_value_alloc()) == NULL) {
		die(gettext(ERR_ALLOC_ELEMENT), gettext(RESOURCE),
		    get_errstr());
	}
	pool_value_set_string(pv, cmd->cmd_tgt2);
	if (pool_put_property(conf, pe, name, pv) != 0)
		die(gettext(ERR_PUT_PROPERTY), name, get_errstr());
	pool_value_free(pv);
}

static void
parser_conf_rename(cmd_t *cmd)
{
	pool_elem_t *pe;

	if ((pe = pool_conf_to_elem(conf)) == NULL)
		die(gettext(ERR_GET_ELEMENT_DETAILS),
		    gettext(CONFIGURATION), "unknown", get_errstr());

	if (cmd->cmd_tgt1 != NULL)
		check_conf_name(cmd);

	parser_rename(cmd, pe, SYSTEM_NAME);
}

static void
parser_pool_rename(cmd_t *cmd)
{
	pool_t *pool;

	if ((pool = pool_get_pool(conf, cmd->cmd_tgt1)) == NULL)
		die(gettext(ERR_LOCATE_ELEMENT), gettext(POOL), cmd->cmd_tgt1,
		    get_errstr());

	parser_rename(cmd, pool_to_elem(conf, pool), POOL_NAME);
}

static void
parser_pset_rename(cmd_t *cmd)
{
	pool_resource_t *resource;

	if ((resource = pool_get_resource(conf, PSET, cmd->cmd_tgt1)) == NULL)
		die(gettext(ERR_LOCATE_ELEMENT), gettext(PSET), cmd->cmd_tgt1,
		    get_errstr());

	parser_rename(cmd, pool_resource_to_elem(conf, resource), PSET_NAME);
}

/*
 * Destroy Commands
 * Invoke the appropriate libpool destroy function to remove the target of the
 * command from the configuration.
 */
static void
parser_conf_destroy(cmd_t *cmd)
{
	if (cmd->cmd_tgt1 != NULL)
		check_conf_name(cmd);

	if (pool_conf_remove(conf) != 0)
		die(gettext(ERR_DESTROY_ELEMENT), gettext(CONFIGURATION),
		    cmd->cmd_tgt1, get_errstr());
}

static void
parser_pool_destroy(cmd_t *cmd)
{
	pool_t *pool;

	if ((pool = pool_get_pool(conf, cmd->cmd_tgt1)) == NULL)
		die(gettext(ERR_LOCATE_ELEMENT), gettext(POOL), cmd->cmd_tgt1,
		    get_errstr());

	if (pool_destroy(conf, pool) != 0)
		die(gettext(ERR_DESTROY_ELEMENT), gettext(POOL), cmd->cmd_tgt1,
		    get_errstr());
}

static void
parser_resource_destroy(cmd_t *cmd, const char *type)
{
	pool_resource_t *resource;

	if ((resource = pool_get_resource(conf, type, cmd->cmd_tgt1)) == NULL)
		die(gettext(ERR_LOCATE_ELEMENT), type, cmd->cmd_tgt1,
		    get_errstr());

	if (pool_resource_destroy(conf, resource) != 0)
		die(gettext(ERR_DESTROY_ELEMENT), type, cmd->cmd_tgt1,
		    get_errstr());
}

static void
parser_pset_destroy(cmd_t *cmd)
{
	parser_resource_destroy(cmd, PSET);
}

/*
 * Modify Commands
 * Perform any requested property operations.
 */
static void
parser_conf_modify(cmd_t *cmd)
{
	pool_elem_t *pe;

	if ((pe = pool_conf_to_elem(conf)) == NULL)
		die(gettext(ERR_GET_ELEMENT_DETAILS),
		    gettext(CONFIGURATION), "unknown", get_errstr());

	if (cmd->cmd_tgt1 != NULL)
		check_conf_name(cmd);

	prop_list_walk(cmd, pe);
}

static void
parser_pool_modify(cmd_t *cmd)
{
	pool_t *pool;

	if ((pool = pool_get_pool(conf, cmd->cmd_tgt1)) == NULL)
		die(gettext(ERR_LOCATE_ELEMENT), gettext(POOL), cmd->cmd_tgt1,
		    get_errstr());
	prop_list_walk(cmd, pool_to_elem(conf, pool));
}

static void
parser_resource_modify(cmd_t *cmd, const char *type)
{
	pool_resource_t *resource;

	if ((resource = pool_get_resource(conf, type, cmd->cmd_tgt1)) == NULL)
		die(gettext(ERR_LOCATE_ELEMENT), gettext(RESOURCE),
		    cmd->cmd_tgt1, get_errstr());

	process_min_max(resource);

	prop_list_walk(cmd, pool_resource_to_elem(conf, resource));
}

static void
parser_pset_modify(cmd_t *cmd)
{
	parser_resource_modify(cmd, PSET);
}

static void
parser_cpu_modify(cmd_t *cmd)
{
	pool_component_t *comp;

	if ((comp = get_cpu(cmd->cmd_tgt1)) == NULL)
		die(gettext(ERR_LOCATE_ELEMENT), gettext(CPU),
		    cmd->cmd_tgt1, get_errstr());
	prop_list_walk(cmd, pool_component_to_elem(conf, comp));
}

/*
 * Discover Commands
 * Invoke the libpool pool_conf_open function so that discovery will be
 * performed.
 */

/*ARGSUSED*/
static void
parser_conf_discover(cmd_t *cmd)
{
	struct utsname utsname;

	if (strcmp(conf_file, pool_dynamic_location()) == 0)
		return;

	if (uname(&utsname) < 0)
		die(gettext(ERR_CREATE_ELEMENT), gettext(CONFIGURATION),
		    "unknown", get_errstr());

	if (conf != NULL && pool_conf_status(conf) >= POF_VALID)
		pool_conf_close(conf);
	if (pool_conf_open(conf, pool_dynamic_location(), PO_RDONLY) != 0) {
		die(gettext(ERR_CREATE_ELEMENT), gettext(CONFIGURATION),
		    utsname.nodename, get_errstr());
	}
	if (pool_conf_export(conf, conf_file, POX_NATIVE) != 0) {
		die(gettext(ERR_CREATE_ELEMENT), gettext(CONFIGURATION),
		    utsname.nodename, get_errstr());
	}
	(void) pool_conf_close(conf);
	if (pool_conf_open(conf, conf_file, PO_RDWR) != 0) {
		die(gettext(ERR_CREATE_ELEMENT), gettext(CONFIGURATION),
		    utsname.nodename, get_errstr());
	}
}

/*
 * Associate Commands
 * Walk the list of specified associations so that the target pool will be
 * associated with the required resources.
 */

static void
parser_pool_associate(cmd_t *cmd)
{
	pool_t *pool;

	if ((pool = pool_get_pool(conf, cmd->cmd_tgt1)) == NULL)
		die(gettext(ERR_LOCATE_ELEMENT), gettext(POOL), cmd->cmd_tgt1,
		    get_errstr());
	assoc_list_walk(cmd, pool);
}

/*
 * Assign Commands
 * Walk the list of specified assignations so that the required
 * components will be assigned to the target resource.
 */

static void
parser_resource_xtransfer(cmd_t *cmd)
{
	pool_resource_t *resource;

	if ((resource = pool_get_resource(conf, PSET, cmd->cmd_tgt1)) == NULL)
		die(gettext(ERR_LOCATE_ELEMENT), gettext(RESOURCE),
		    cmd->cmd_tgt1, get_errstr());
	transfer_list_walk(cmd, resource);
}

/*
 * Transfer Commands
 * Transfer the specified quantity of resource between the src and the tgt.
 */

static void
parser_resource_transfer(cmd_t *cmd)
{
	pool_resource_t *src;
	pool_resource_t *tgt;

	if ((src = pool_get_resource(conf, PSET, cmd->cmd_tgt1)) == NULL)
		die(gettext(ERR_LOCATE_ELEMENT), gettext(RESOURCE),
		    cmd->cmd_tgt1, get_errstr());
	if ((tgt = pool_get_resource(conf, PSET, cmd->cmd_tgt2)) == NULL)
		die(gettext(ERR_LOCATE_ELEMENT), gettext(RESOURCE),
		    cmd->cmd_tgt2, get_errstr());
	if (pool_resource_transfer(conf, src, tgt, cmd->cmd_qty) != PO_SUCCESS)
		die(gettext(ERR_XFER_QUANTITY), cmd->cmd_qty,
		    cmd->cmd_tgt1, cmd->cmd_tgt2, get_errstr());
}

/*
 * arg_parse() puts the parser into command parsing mode. Create a tmpfile
 * and instruct the parser to read instructions from this location by setting
 * yyin to the value returned by tmpfile. Write the command into the file.
 * Then seek back to to the start of the file so that the parser can read
 * the instructions.
 */
static void
arg_parse(const char *command)
{
	if ((yyin = tmpfile()) == NULL)
		die(gettext(ERR_CMD_FILE_INIT), strerror(errno));
	if (fwrite(command, strlen(command), 1, yyin) != 1)
		die(gettext(ERR_CMD_FILE_INIT), strerror(errno));
	if (fseek(yyin, 0, SEEK_SET) != 0)
		die(gettext(ERR_CMD_FILE_INIT), strerror(errno));
}

/*
 * file_parse() puts the parser into command file parsing mode. Firstly check
 * to see if the user wishes to parse from standard input, if so do nothing.
 * Attempt to open the specified file and instruct the parser to read
 * instructions from this location by setting yyin to the value returned by
 * fopen.
 */
static void
file_parse(const char *file)
{
	if (strcmp(file, "-") == 0)
		return;

	if ((yyin = fopen(file, "r")) == NULL) {
		die(gettext(ERR_CMD_FILE_INIT), strerror(errno));
	}
}

/*
 * free_cmd() releases the resources associated with the supplied cmd parameter.
 */
static void
free_cmd(cmd_t *cmd)
{
	prop_t *prop = cmd->cmd_prop_list;
	assoc_t *assoc = cmd->cmd_assoc_list;

	free((void *)cmd->cmd_tgt1);
	free((void *)cmd->cmd_tgt2);
	while (prop != NULL) {
		prop_t *tmp = prop;
		prop = prop->prop_next;
		pool_value_free(tmp->prop_value);
		free((void *)tmp->prop_name);
		free(tmp);
	}
	while (assoc != NULL) {
		assoc_t *tmp = assoc;
		assoc = assoc->assoc_next;
		free((void *)tmp->assoc_name);
		free(tmp);
	}
	free(cmd);
}

/*
 * alloc_cmd() allocates the required resources for a cmd_t. On failure, a
 * warning is issued and NULL is returned.
 */
static cmd_t *
alloc_cmd(void)
{
	cmd_t *cmd;

	if ((cmd = malloc(sizeof (cmd_t))) == NULL) {
		warn(gettext(ERR_CMD_LINE_ALLOC));
		return (NULL);
	}

	(void) memset(cmd, 0, sizeof (cmd_t));

	return (cmd);
}

/*
 * alloc_prop() allocates the required resources for a prop_t. On failure, a
 * warning is issued and NULL is returned. The prop_t is initialised with
 * the prop_op_t parameter.
 */
static prop_t *
alloc_prop(prop_op_t op)
{
	prop_t *prop;

	if ((prop = malloc(sizeof (prop_t))) == NULL) {
		warn(gettext(ERR_PROP_ALLOC));
		return (NULL);
	}

	(void) memset(prop, 0, sizeof (prop_t));
	if ((prop->prop_value = pool_value_alloc()) == NULL) {
		warn(gettext(ERR_PROP_ALLOC));
		free(prop);
		return (NULL);
	}
	prop->prop_op = op;
	return (prop);
}

/*
 * alloc_assoc() allocates the required resources for an assoc_t. On failure, a
 * warning is issued and NULL is returned. The assoc_t is initialised with
 * the type and name of the association.
 */
static assoc_t *
alloc_assoc(int type, const char *name)
{
	assoc_t *assoc;

	if ((assoc = malloc(sizeof (assoc_t))) == NULL) {
		warn(gettext(ERR_ASSOC_ALLOC));
		return (NULL);
	}
	(void) memset(assoc, 0, sizeof (assoc_t));
	assoc->assoc_type = type;
	assoc->assoc_name = name;
	return (assoc);
}

/*
 * check_conf_name() ensures the the name of the system in the configuration
 * which is being manipulated matches the name of the system in the command.
 * If not, the command is terminated with an appropriate error message.
 */
static void
check_conf_name(cmd_t *cmd)
{
	pool_value_t *pv;
	const char *name;
	pool_elem_t *pe;

	if ((pe = pool_conf_to_elem(conf)) == NULL)
		die(gettext(ERR_GET_ELEMENT_DETAILS),
		    gettext(CONFIGURATION), "unknown", get_errstr());


	if ((pv = pool_value_alloc()) == NULL) {
		die(gettext(ERR_ALLOC_ELEMENT), gettext(RESOURCE),
		    get_errstr());
	}

	if (pool_get_property(conf, pe, SYSTEM_NAME, pv)
	    == POC_INVAL)
		die(gettext(ERR_GET_PROPERTY), gettext(SYSTEM_NAME),
		    get_errstr());

	if (pool_value_get_string(pv, &name) == PO_FAIL)
		die(gettext(ERR_GET_PROPERTY), gettext(SYSTEM_NAME),
		    get_errstr());

	if (strcmp(cmd->cmd_tgt1, name) != 0) {
		die(gettext(ERR_WRONG_SYSTEM_NAME), cmd->cmd_tgt1);
	}
	pool_value_free(pv);
}

/*
 * usage() display brief or verbose help for the poolcfg(1) command.
 */
static void
usage(int help)
{
	if (help >= 1)
		(void) fprintf(stderr, gettext(USAGE1), cmdname, cmdname,
		    cmdname);
	if (help >= 2)
		(void) fprintf(stderr, gettext(USAGE2));
	exit(E_USAGE);
}

/*
 * prop_list_walk() walks the property manipulation requests and either puts
 * or removes the property as appropriate.
 */
static void
prop_list_walk(cmd_t *cmd, pool_elem_t *pe)
{
	prop_t *prop;

	for (prop = cmd->cmd_prop_list; prop != NULL; prop = prop->prop_next) {
		switch (prop->prop_op) {
		case po_create:
			if (pool_put_property(conf, pe, prop->prop_name,
			    prop->prop_value) != 0)
				die(gettext(ERR_PUT_PROPERTY),
				    prop->prop_name, get_errstr());
			break;
		case po_remove:
			if (pool_rm_property(conf, pe, prop->prop_name) != 0)
				die(gettext(ERR_REMOVE_PROPERTY),
				    prop->prop_name, get_errstr());
			break;
		}
	}
}

/*
 * assoc_list_walk() walks the resource association requests and attempts
 * to associate the pool with the specified resource.
 */
static void
assoc_list_walk(cmd_t *cmd, pool_t *pool)
{
	assoc_t *assoc;

	for (assoc = cmd->cmd_assoc_list; assoc != NULL;
	    assoc = assoc->assoc_next) {
		pool_resource_t *resource;

		switch (assoc->assoc_type) {
		case PCE_PSET:
			if ((resource = pool_get_resource(conf,
			    PSET, assoc->assoc_name)) == NULL)
				die(gettext(ERR_LOCATE_ELEMENT), gettext(PSET),
				    assoc->assoc_name, get_errstr());
			break;
		default:
			die(gettext(ERR_UNKNOWN_RESOURCE),
			    assoc->assoc_type);
			break;
		}
		if (pool_associate(conf, pool, resource) != 0)
			die(gettext(ERR_ASSOC_RESOURCE), assoc->assoc_name,
			    get_errstr());
	}
}

/*
 * transfer_list_walk() walks the component assign requests and attempts
 * to assign the component with the specified resource.
 */
static void
transfer_list_walk(cmd_t *cmd, pool_resource_t *tgt)
{
	assoc_t *assoc;

	for (assoc = cmd->cmd_assoc_list; assoc != NULL;
	    assoc = assoc->assoc_next) {
		pool_component_t *comp;
		pool_resource_t *src;
		pool_component_t *xfer[2] = {NULL};	

		if ((comp = get_cpu(assoc->assoc_name)) == NULL)
			die(gettext(ERR_LOCATE_ELEMENT), gettext(CPU),
			    assoc->assoc_name, get_errstr());
		if ((src = pool_get_owning_resource(conf, comp)) == NULL)
			die(gettext(ERR_XFER_COMPONENT), gettext(COMPONENT),
			    assoc->assoc_name, cmd->cmd_tgt1, get_errstr());
		xfer[0] = comp;
		if (pool_resource_xtransfer(conf, src, tgt, xfer) !=
		    PO_SUCCESS)
			die(gettext(ERR_XFER_COMPONENT), gettext(COMPONENT),
			    assoc->assoc_name, cmd->cmd_tgt1, get_errstr());
	}
}

/*
 * terminate() is invoked when poolcfg exits. It cleans up
 * configurations and closes the parser input stream.
 */
static void
terminate(void)
{
	if (conf != NULL) {
		(void) pool_conf_close(conf);
		pool_conf_free(conf);
	}
	if (yyin != stdin)
		(void) fclose(yyin);
}

/*
 * get_cpu() takes the name of a CPU components and attempts to locate
 * the element with that name. If the name is not formatted correctly
 * (i.e. contains non-numeric characters) then the function terminates
 * execution. If the components cannot be uniquely identified by the
 * name, then NULL is returned.
 */
static pool_component_t *
get_cpu(const char *name)
{
	pool_component_t **components;
	uint_t nelem;
	int64_t sysid;
	pool_value_t *vals[3] = {NULL};
	pool_component_t *ret;
	const char *c;

	if ((vals[0] = pool_value_alloc()) == NULL)
		return (NULL);
	if ((vals[1] = pool_value_alloc()) == NULL) {
		pool_value_free(vals[0]);
		return (NULL);
	}
	if (pool_value_set_string(vals[0], "cpu") != PO_SUCCESS ||
	    pool_value_set_name(vals[0], "type") != PO_SUCCESS) {
		pool_value_free(vals[0]);
		pool_value_free(vals[1]);
		return (NULL);
	}

	for (c = name; *c != NULL; c++) {
		if (!isdigit(*c)){
			pool_value_free(vals[0]);
			pool_value_free(vals[1]);
			die(gettext(ERR_LOCATE_ELEMENT), gettext(CPU),
		    	    cmd->cmd_tgt1, gettext("CPU id should only contain "
			    "digits"));
		}
	}
	sysid = strtoll(name, NULL, 0);
	if (errno == ERANGE || errno == EINVAL) {
		pool_value_free(vals[0]);
		pool_value_free(vals[1]);
		return (NULL);
	}
	pool_value_set_int64(vals[1], sysid);
	if (pool_value_set_name(vals[1], CPU_SYSID) != PO_SUCCESS) {
		pool_value_free(vals[0]);
		pool_value_free(vals[1]);
		return (NULL);
	}
	if ((components = pool_query_components(conf, &nelem, vals)) ==
	    NULL) {
		pool_value_free(vals[0]);
		pool_value_free(vals[1]);
		return (NULL);
	}
	if (nelem != 1) {
		free(components);
		pool_value_free(vals[0]);
		pool_value_free(vals[1]);
		return (NULL);
	}
	pool_value_free(vals[0]);
	pool_value_free(vals[1]);
	ret = components[0];
	free(components);
	return (ret);
}

/*
 * process_min_max() ensures that "min" and "max" properties are
 * processed correctly by poolcfg. libpool enforces validity
 * constraints on these properties and so it's important that changes
 * to them are supplied to the library in the correct order.
 */
void
process_min_max(pool_resource_t *resource)
{
	prop_t *minprop = NULL;
	prop_t *maxprop = NULL;
	prop_t *prop;

	/*
	 * Before walking the list of properties, it has to be checked
	 * to ensure there are no clashes between min and max. If
	 * there are, then process these properties immediately.
	 */
	for (prop = cmd->cmd_prop_list; prop != NULL; prop = prop->prop_next) {
		const char *pos;

		if ((pos = strstr(prop->prop_name, min_suffix)) != NULL)
			if (pos == prop->prop_name + strlen(prop->prop_name)
			    - 4)
				minprop = prop;
		if ((pos = strstr(prop->prop_name, max_suffix)) != NULL)
			if (pos == prop->prop_name + strlen(prop->prop_name)
			    - 4)
				maxprop = prop;
	}
	if (minprop && maxprop) {
		pool_value_t *pv;
		uint64_t smin, smax, dmax;
		const char *type;
		char *prop_name;
		pool_elem_t *pe = pool_resource_to_elem(conf, resource);

		if ((pv = pool_value_alloc()) == NULL)
			die(gettext(ERR_NOMEM));

		(void) pool_get_property(conf, pe, "type", pv);
		(void) pool_value_get_string(pv, &type);

		if ((prop_name = malloc(strlen(type) + strlen(max_suffix)
		    + 1)) == NULL)
			die(gettext(ERR_NOMEM));

		(void) sprintf(prop_name, "%s%s", type, max_suffix);
		(void) pool_get_property(conf, pe, prop_name, pv);
		(void) pool_value_get_uint64(pv, &dmax);

		(void) pool_value_get_uint64(minprop->prop_value, &smin);

		(void) pool_value_get_uint64(maxprop->prop_value, &smax);
		if (smin < dmax) {
			(void) pool_put_property(conf, pe,
			minprop->prop_name, minprop->prop_value);
		} else {
			(void) pool_put_property(conf, pe,
			maxprop->prop_name, maxprop->prop_value);
		}
		free((void *)prop_name);
		pool_value_free(pv);
	}
}