%{ /* * 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. */ /* * 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 #include #include #include #include #include #include #include #include #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 PCV_VAL_INT %type PCV_VAL_UINT %type PCV_VAL_BOOLEAN %type PCV_VAL_FLOAT %type PCV_VAL_STRING %type PCV_SYMBOL %type PCV_FILENAME %type PCC_INFO %type PCE_SYSTEM PCE_POOL PCE_PSET PCE_CPU %type entity proptype info_entity modify_entity %type name src tgt %type command %type 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_remove prop_assign prop_op prop_ops property_list %type resource_assign resource_assigns resource_list %type component_assign component_assigns component_list %type value %type 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 != '\0'; 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); } }