| /* |
| * 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 2007 Sun Microsystems, Inc. All rights reserved. |
| * Use is subject to license terms. |
| */ |
| |
| #pragma ident "%Z%%M% %I% %E% SMI" |
| |
| /* |
| * zonecfg is a lex/yacc based command interpreter used to manage zone |
| * configurations. The lexer (see zonecfg_lex.l) builds up tokens, which |
| * the grammar (see zonecfg_grammar.y) builds up into commands, some of |
| * which takes resources and/or properties as arguments. See the block |
| * comments near the end of zonecfg_grammar.y for how the data structures |
| * which keep track of these resources and properties are built up. |
| * |
| * The resource/property data structures are inserted into a command |
| * structure (see zonecfg.h), which also keeps track of command names, |
| * miscellaneous arguments, and function handlers. The grammar selects |
| * the appropriate function handler, each of which takes a pointer to a |
| * command structure as its sole argument, and invokes it. The grammar |
| * itself is "entered" (a la the Matrix) by yyparse(), which is called |
| * from read_input(), our main driving function. That in turn is called |
| * by one of do_interactive(), cmd_file() or one_command_at_a_time(), each |
| * of which is called from main() depending on how the program was invoked. |
| * |
| * The rest of this module consists of the various function handlers and |
| * their helper functions. Some of these functions, particularly the |
| * X_to_str() functions, which maps command, resource and property numbers |
| * to strings, are used quite liberally, as doing so results in a better |
| * program w/rt I18N, reducing the need for translation notes. |
| */ |
| |
| #include <sys/mntent.h> |
| #include <sys/varargs.h> |
| #include <sys/sysmacros.h> |
| |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <strings.h> |
| #include <unistd.h> |
| #include <ctype.h> |
| #include <stdlib.h> |
| #include <assert.h> |
| #include <sys/stat.h> |
| #include <zone.h> |
| #include <arpa/inet.h> |
| #include <netdb.h> |
| #include <locale.h> |
| #include <libintl.h> |
| #include <alloca.h> |
| #include <signal.h> |
| #include <wait.h> |
| #include <libtecla.h> |
| #include <libzfs.h> |
| #include <sys/brand.h> |
| #include <libbrand.h> |
| |
| #include <libzonecfg.h> |
| #include "zonecfg.h" |
| |
| #if !defined(TEXT_DOMAIN) /* should be defined by cc -D */ |
| #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it wasn't */ |
| #endif |
| |
| #define PAGER "/usr/bin/more" |
| #define EXEC_PREFIX "exec " |
| #define EXEC_LEN (strlen(EXEC_PREFIX)) |
| |
| struct help { |
| uint_t cmd_num; |
| char *cmd_name; |
| uint_t flags; |
| char *short_usage; |
| }; |
| |
| extern int yyparse(void); |
| extern int lex_lineno; |
| |
| #define MAX_LINE_LEN 1024 |
| #define MAX_CMD_HIST 1024 |
| #define MAX_CMD_LEN 1024 |
| |
| #define ONE_MB 1048576 |
| |
| /* |
| * Each SHELP_ should be a simple string. |
| */ |
| |
| #define SHELP_ADD "add <resource-type>\n\t(global scope)\n" \ |
| "add <property-name> <property-value>\n\t(resource scope)" |
| #define SHELP_CANCEL "cancel" |
| #define SHELP_CLEAR "clear <property-name>" |
| #define SHELP_COMMIT "commit" |
| #define SHELP_CREATE "create [-F] [ -a <path> | -b | -t <template> ]" |
| #define SHELP_DELETE "delete [-F]" |
| #define SHELP_END "end" |
| #define SHELP_EXIT "exit [-F]" |
| #define SHELP_EXPORT "export [-f output-file]" |
| #define SHELP_HELP "help [commands] [syntax] [usage] [<command-name>]" |
| #define SHELP_INFO "info [<resource-type> [property-name=property-value]*]" |
| #define SHELP_REMOVE "remove [-F] <resource-type> " \ |
| "[ <property-name>=<property-value> ]*\n" \ |
| "\t(global scope)\n" \ |
| "remove <property-name> <property-value>\n" \ |
| "\t(resource scope)" |
| #define SHELP_REVERT "revert [-F]" |
| #define SHELP_SELECT "select <resource-type> { <property-name>=" \ |
| "<property-value> }" |
| #define SHELP_SET "set <property-name>=<property-value>" |
| #define SHELP_VERIFY "verify" |
| |
| static struct help helptab[] = { |
| { CMD_ADD, "add", HELP_RES_PROPS, SHELP_ADD, }, |
| { CMD_CANCEL, "cancel", 0, SHELP_CANCEL, }, |
| { CMD_CLEAR, "clear", HELP_PROPS, SHELP_CLEAR, }, |
| { CMD_COMMIT, "commit", 0, SHELP_COMMIT, }, |
| { CMD_CREATE, "create", 0, SHELP_CREATE, }, |
| { CMD_DELETE, "delete", 0, SHELP_DELETE, }, |
| { CMD_END, "end", 0, SHELP_END, }, |
| { CMD_EXIT, "exit", 0, SHELP_EXIT, }, |
| { CMD_EXPORT, "export", 0, SHELP_EXPORT, }, |
| { CMD_HELP, "help", 0, SHELP_HELP }, |
| { CMD_INFO, "info", HELP_RES_PROPS, SHELP_INFO, }, |
| { CMD_REMOVE, "remove", HELP_RES_PROPS, SHELP_REMOVE, }, |
| { CMD_REVERT, "revert", 0, SHELP_REVERT, }, |
| { CMD_SELECT, "select", HELP_RES_PROPS, SHELP_SELECT, }, |
| { CMD_SET, "set", HELP_PROPS, SHELP_SET, }, |
| { CMD_VERIFY, "verify", 0, SHELP_VERIFY, }, |
| { 0 }, |
| }; |
| |
| #define MAX_RT_STRLEN 16 |
| |
| /* These *must* match the order of the RT_ define's from zonecfg.h */ |
| static char *res_types[] = { |
| "unknown", |
| "zonename", |
| "zonepath", |
| "autoboot", |
| "pool", |
| "fs", |
| "inherit-pkg-dir", |
| "net", |
| "device", |
| "rctl", |
| "attr", |
| "dataset", |
| "limitpriv", |
| "bootargs", |
| "brand", |
| "dedicated-cpu", |
| "capped-memory", |
| ALIAS_MAXLWPS, |
| ALIAS_MAXSHMMEM, |
| ALIAS_MAXSHMIDS, |
| ALIAS_MAXMSGIDS, |
| ALIAS_MAXSEMIDS, |
| ALIAS_SHARES, |
| "scheduling-class", |
| "ip-type", |
| NULL |
| }; |
| |
| /* These *must* match the order of the PT_ define's from zonecfg.h */ |
| static char *prop_types[] = { |
| "unknown", |
| "zonename", |
| "zonepath", |
| "autoboot", |
| "pool", |
| "dir", |
| "special", |
| "type", |
| "options", |
| "address", |
| "physical", |
| "name", |
| "value", |
| "match", |
| "priv", |
| "limit", |
| "action", |
| "raw", |
| "limitpriv", |
| "bootargs", |
| "brand", |
| "ncpus", |
| "importance", |
| "swap", |
| "locked", |
| ALIAS_SHARES, |
| ALIAS_MAXLWPS, |
| ALIAS_MAXSHMMEM, |
| ALIAS_MAXSHMIDS, |
| ALIAS_MAXMSGIDS, |
| ALIAS_MAXSEMIDS, |
| ALIAS_MAXLOCKEDMEM, |
| ALIAS_MAXSWAP, |
| "scheduling-class", |
| "ip-type", |
| NULL |
| }; |
| |
| /* These *must* match the order of the PROP_VAL_ define's from zonecfg.h */ |
| static char *prop_val_types[] = { |
| "simple", |
| "complex", |
| "list", |
| }; |
| |
| /* |
| * The various _cmds[] lists below are for command tab-completion. |
| */ |
| |
| /* |
| * remove has a space afterwards because it has qualifiers; the other commands |
| * that have qualifiers (add, select, etc.) don't need a space here because |
| * they have their own _cmds[] lists below. |
| */ |
| static const char *global_scope_cmds[] = { |
| "add", |
| "clear", |
| "commit", |
| "create", |
| "delete", |
| "exit", |
| "export", |
| "help", |
| "info", |
| "remove ", |
| "revert", |
| "select", |
| "set", |
| "verify", |
| NULL |
| }; |
| |
| static const char *add_cmds[] = { |
| "add fs", |
| "add inherit-pkg-dir", |
| "add net", |
| "add device", |
| "add rctl", |
| "add attr", |
| "add dataset", |
| "add dedicated-cpu", |
| "add capped-memory", |
| NULL |
| }; |
| |
| static const char *clear_cmds[] = { |
| "clear autoboot", |
| "clear pool", |
| "clear limitpriv", |
| "clear bootargs", |
| "clear scheduling-class", |
| "clear ip-type", |
| "clear " ALIAS_MAXLWPS, |
| "clear " ALIAS_MAXSHMMEM, |
| "clear " ALIAS_MAXSHMIDS, |
| "clear " ALIAS_MAXMSGIDS, |
| "clear " ALIAS_MAXSEMIDS, |
| "clear " ALIAS_SHARES, |
| NULL |
| }; |
| |
| static const char *remove_cmds[] = { |
| "remove fs ", |
| "remove inherit-pkg-dir ", |
| "remove net ", |
| "remove device ", |
| "remove rctl ", |
| "remove attr ", |
| "remove dataset ", |
| "remove dedicated-cpu ", |
| "remove capped-memory ", |
| NULL |
| }; |
| |
| static const char *select_cmds[] = { |
| "select fs ", |
| "select inherit-pkg-dir ", |
| "select net ", |
| "select device ", |
| "select rctl ", |
| "select attr ", |
| "select dataset ", |
| "select dedicated-cpu", |
| "select capped-memory", |
| NULL |
| }; |
| |
| static const char *set_cmds[] = { |
| "set zonename=", |
| "set zonepath=", |
| "set brand=", |
| "set autoboot=", |
| "set pool=", |
| "set limitpriv=", |
| "set bootargs=", |
| "set scheduling-class=", |
| "set ip-type=", |
| "set " ALIAS_MAXLWPS "=", |
| "set " ALIAS_MAXSHMMEM "=", |
| "set " ALIAS_MAXSHMIDS "=", |
| "set " ALIAS_MAXMSGIDS "=", |
| "set " ALIAS_MAXSEMIDS "=", |
| "set " ALIAS_SHARES "=", |
| NULL |
| }; |
| |
| static const char *info_cmds[] = { |
| "info fs ", |
| "info inherit-pkg-dir ", |
| "info net ", |
| "info device ", |
| "info rctl ", |
| "info attr ", |
| "info dataset ", |
| "info capped-memory", |
| "info dedicated-cpu", |
| "info zonename", |
| "info zonepath", |
| "info autoboot", |
| "info pool", |
| "info limitpriv", |
| "info bootargs", |
| "info brand", |
| "info scheduling-class", |
| "info ip-type", |
| "info max-lwps", |
| "info max-shm-memory", |
| "info max-shm-ids", |
| "info max-msg-ids", |
| "info max-sem-ids", |
| "info cpu-shares", |
| NULL |
| }; |
| |
| static const char *fs_res_scope_cmds[] = { |
| "add options ", |
| "cancel", |
| "end", |
| "exit", |
| "help", |
| "info", |
| "remove options ", |
| "set dir=", |
| "set raw=", |
| "set special=", |
| "set type=", |
| "clear raw", |
| NULL |
| }; |
| |
| static const char *net_res_scope_cmds[] = { |
| "cancel", |
| "end", |
| "exit", |
| "help", |
| "info", |
| "set address=", |
| "set physical=", |
| NULL |
| }; |
| |
| static const char *ipd_res_scope_cmds[] = { |
| "cancel", |
| "end", |
| "exit", |
| "help", |
| "info", |
| "set dir=", |
| NULL |
| }; |
| |
| static const char *device_res_scope_cmds[] = { |
| "cancel", |
| "end", |
| "exit", |
| "help", |
| "info", |
| "set match=", |
| NULL |
| }; |
| |
| static const char *attr_res_scope_cmds[] = { |
| "cancel", |
| "end", |
| "exit", |
| "help", |
| "info", |
| "set name=", |
| "set type=", |
| "set value=", |
| NULL |
| }; |
| |
| static const char *rctl_res_scope_cmds[] = { |
| "add value ", |
| "cancel", |
| "end", |
| "exit", |
| "help", |
| "info", |
| "remove value ", |
| "set name=", |
| NULL |
| }; |
| |
| static const char *dataset_res_scope_cmds[] = { |
| "cancel", |
| "end", |
| "exit", |
| "help", |
| "info", |
| "set name=", |
| NULL |
| }; |
| |
| static const char *pset_res_scope_cmds[] = { |
| "cancel", |
| "end", |
| "exit", |
| "help", |
| "info", |
| "set ncpus=", |
| "set importance=", |
| "clear importance", |
| NULL |
| }; |
| |
| static const char *mcap_res_scope_cmds[] = { |
| "cancel", |
| "end", |
| "exit", |
| "help", |
| "info", |
| "set physical=", |
| "set swap=", |
| "set locked=", |
| "clear physical", |
| "clear swap", |
| "clear locked", |
| NULL |
| }; |
| |
| /* Global variables */ |
| |
| /* set early in main(), never modified thereafter, used all over the place */ |
| static char *execname; |
| |
| /* set in main(), used all over the place */ |
| static zone_dochandle_t handle; |
| |
| /* used all over the place */ |
| static char zone[ZONENAME_MAX]; |
| static char revert_zone[ZONENAME_MAX]; |
| |
| /* global brand operations */ |
| static brand_handle_t brand; |
| |
| /* set in modifying functions, checked in read_input() */ |
| static bool need_to_commit = FALSE; |
| bool saw_error; |
| |
| /* set in yacc parser, checked in read_input() */ |
| bool newline_terminated; |
| |
| /* set in main(), checked in lex error handler */ |
| bool cmd_file_mode; |
| |
| /* set in exit_func(), checked in read_input() */ |
| static bool time_to_exit = FALSE, force_exit = FALSE; |
| |
| /* used in short_usage() and zerr() */ |
| static char *cmd_file_name = NULL; |
| |
| /* checked in read_input() and other places */ |
| static bool ok_to_prompt = FALSE; |
| |
| /* set and checked in initialize() */ |
| static bool got_handle = FALSE; |
| |
| /* initialized in do_interactive(), checked in initialize() */ |
| static bool interactive_mode; |
| |
| /* set if configuring the global zone */ |
| static bool global_zone = FALSE; |
| |
| /* set in main(), checked in multiple places */ |
| static bool read_only_mode; |
| |
| static bool global_scope = TRUE; /* scope is outer/global or inner/resource */ |
| static int resource_scope; /* should be in the RT_ list from zonecfg.h */ |
| static int end_op = -1; /* operation on end is either add or modify */ |
| |
| int num_prop_vals; /* for grammar */ |
| |
| /* |
| * These are for keeping track of resources as they are specified as part of |
| * the multi-step process. They should be initialized by add_resource() or |
| * select_func() and filled in by add_property() or set_func(). |
| */ |
| static struct zone_fstab old_fstab, in_progress_fstab; |
| static struct zone_fstab old_ipdtab, in_progress_ipdtab; |
| static struct zone_nwiftab old_nwiftab, in_progress_nwiftab; |
| static struct zone_devtab old_devtab, in_progress_devtab; |
| static struct zone_rctltab old_rctltab, in_progress_rctltab; |
| static struct zone_attrtab old_attrtab, in_progress_attrtab; |
| static struct zone_dstab old_dstab, in_progress_dstab; |
| static struct zone_psettab old_psettab, in_progress_psettab; |
| static struct zone_mcaptab old_mcaptab, in_progress_mcaptab; |
| |
| static GetLine *gl; /* The gl_get_line() resource object */ |
| |
| static void bytes_to_units(char *str, char *buf, int bufsize); |
| |
| /* Functions begin here */ |
| |
| static bool |
| initial_match(const char *line1, const char *line2, int word_end) |
| { |
| if (word_end <= 0) |
| return (TRUE); |
| return (strncmp(line1, line2, word_end) == 0); |
| } |
| |
| static int |
| add_stuff(WordCompletion *cpl, const char *line1, const char **list, |
| int word_end) |
| { |
| int i, err; |
| |
| for (i = 0; list[i] != NULL; i++) { |
| if (initial_match(line1, list[i], word_end)) { |
| err = cpl_add_completion(cpl, line1, 0, word_end, |
| list[i] + word_end, "", ""); |
| if (err != 0) |
| return (err); |
| } |
| } |
| return (0); |
| } |
| |
| static |
| /* ARGSUSED */ |
| CPL_MATCH_FN(cmd_cpl_fn) |
| { |
| if (global_scope) { |
| /* |
| * The MAX/MIN tests below are to make sure we have at least |
| * enough characters to distinguish from other prefixes (MAX) |
| * but only check MIN(what we have, what we're checking). |
| */ |
| if (strncmp(line, "add ", MAX(MIN(word_end, 4), 1)) == 0) |
| return (add_stuff(cpl, line, add_cmds, word_end)); |
| if (strncmp(line, "clear ", MAX(MIN(word_end, 6), 2)) == 0) |
| return (add_stuff(cpl, line, clear_cmds, word_end)); |
| if (strncmp(line, "select ", MAX(MIN(word_end, 7), 3)) == 0) |
| return (add_stuff(cpl, line, select_cmds, word_end)); |
| if (strncmp(line, "set ", MAX(MIN(word_end, 4), 3)) == 0) |
| return (add_stuff(cpl, line, set_cmds, word_end)); |
| if (strncmp(line, "remove ", MAX(MIN(word_end, 7), 1)) == 0) |
| return (add_stuff(cpl, line, remove_cmds, word_end)); |
| if (strncmp(line, "info ", MAX(MIN(word_end, 5), 1)) == 0) |
| return (add_stuff(cpl, line, info_cmds, word_end)); |
| return (add_stuff(cpl, line, global_scope_cmds, word_end)); |
| } |
| switch (resource_scope) { |
| case RT_FS: |
| return (add_stuff(cpl, line, fs_res_scope_cmds, word_end)); |
| case RT_IPD: |
| return (add_stuff(cpl, line, ipd_res_scope_cmds, word_end)); |
| case RT_NET: |
| return (add_stuff(cpl, line, net_res_scope_cmds, word_end)); |
| case RT_DEVICE: |
| return (add_stuff(cpl, line, device_res_scope_cmds, word_end)); |
| case RT_RCTL: |
| return (add_stuff(cpl, line, rctl_res_scope_cmds, word_end)); |
| case RT_ATTR: |
| return (add_stuff(cpl, line, attr_res_scope_cmds, word_end)); |
| case RT_DATASET: |
| return (add_stuff(cpl, line, dataset_res_scope_cmds, word_end)); |
| case RT_DCPU: |
| return (add_stuff(cpl, line, pset_res_scope_cmds, word_end)); |
| case RT_MCAP: |
| return (add_stuff(cpl, line, mcap_res_scope_cmds, word_end)); |
| } |
| return (0); |
| } |
| |
| /* |
| * For the main CMD_func() functions below, several of them call getopt() |
| * then check optind against argc to make sure an extra parameter was not |
| * passed in. The reason this is not caught in the grammar is that the |
| * grammar just checks for a miscellaneous TOKEN, which is *expected* to |
| * be "-F" (for example), but could be anything. So (for example) this |
| * check will prevent "create bogus". |
| */ |
| |
| cmd_t * |
| alloc_cmd(void) |
| { |
| return (calloc(1, sizeof (cmd_t))); |
| } |
| |
| void |
| free_cmd(cmd_t *cmd) |
| { |
| int i; |
| |
| for (i = 0; i < MAX_EQ_PROP_PAIRS; i++) |
| if (cmd->cmd_property_ptr[i] != NULL) { |
| property_value_ptr_t pp = cmd->cmd_property_ptr[i]; |
| |
| switch (pp->pv_type) { |
| case PROP_VAL_SIMPLE: |
| free(pp->pv_simple); |
| break; |
| case PROP_VAL_COMPLEX: |
| free_complex(pp->pv_complex); |
| break; |
| case PROP_VAL_LIST: |
| free_list(pp->pv_list); |
| break; |
| } |
| } |
| for (i = 0; i < cmd->cmd_argc; i++) |
| free(cmd->cmd_argv[i]); |
| free(cmd); |
| } |
| |
| complex_property_ptr_t |
| alloc_complex(void) |
| { |
| return (calloc(1, sizeof (complex_property_t))); |
| } |
| |
| void |
| free_complex(complex_property_ptr_t complex) |
| { |
| if (complex == NULL) |
| return; |
| free_complex(complex->cp_next); |
| if (complex->cp_value != NULL) |
| free(complex->cp_value); |
| free(complex); |
| } |
| |
| list_property_ptr_t |
| alloc_list(void) |
| { |
| return (calloc(1, sizeof (list_property_t))); |
| } |
| |
| void |
| free_list(list_property_ptr_t list) |
| { |
| if (list == NULL) |
| return; |
| if (list->lp_simple != NULL) |
| free(list->lp_simple); |
| free_complex(list->lp_complex); |
| free_list(list->lp_next); |
| free(list); |
| } |
| |
| void |
| free_outer_list(list_property_ptr_t list) |
| { |
| if (list == NULL) |
| return; |
| free_outer_list(list->lp_next); |
| free(list); |
| } |
| |
| static struct zone_rctlvaltab * |
| alloc_rctlvaltab(void) |
| { |
| return (calloc(1, sizeof (struct zone_rctlvaltab))); |
| } |
| |
| static char * |
| rt_to_str(int res_type) |
| { |
| assert(res_type >= RT_MIN && res_type <= RT_MAX); |
| return (res_types[res_type]); |
| } |
| |
| static char * |
| pt_to_str(int prop_type) |
| { |
| assert(prop_type >= PT_MIN && prop_type <= PT_MAX); |
| return (prop_types[prop_type]); |
| } |
| |
| static char * |
| pvt_to_str(int pv_type) |
| { |
| assert(pv_type >= PROP_VAL_MIN && pv_type <= PROP_VAL_MAX); |
| return (prop_val_types[pv_type]); |
| } |
| |
| static char * |
| cmd_to_str(int cmd_num) |
| { |
| assert(cmd_num >= CMD_MIN && cmd_num <= CMD_MAX); |
| return (helptab[cmd_num].cmd_name); |
| } |
| |
| /* |
| * This is a separate function rather than a set of define's because of the |
| * gettext() wrapping. |
| */ |
| |
| /* |
| * TRANSLATION_NOTE |
| * Each string below should have \t follow \n whenever needed; the |
| * initial \t and the terminal \n will be provided by the calling function. |
| */ |
| |
| static char * |
| long_help(int cmd_num) |
| { |
| static char line[1024]; /* arbitrary large amount */ |
| |
| assert(cmd_num >= CMD_MIN && cmd_num <= CMD_MAX); |
| switch (cmd_num) { |
| case CMD_HELP: |
| return (gettext("Prints help message.")); |
| case CMD_CREATE: |
| (void) snprintf(line, sizeof (line), |
| gettext("Creates a configuration for the " |
| "specified zone. %s should be\n\tused to " |
| "begin configuring a new zone. If overwriting an " |
| "existing\n\tconfiguration, the -F flag can be " |
| "used to force the action. If\n\t-t template is " |
| "given, creates a configuration identical to the\n" |
| "\tspecified template, except that the zone name " |
| "is changed from\n\ttemplate to zonename. '%s -a' " |
| "creates a configuration from a\n\tdetached " |
| "zonepath. '%s -b' results in a blank " |
| "configuration.\n\t'%s' with no arguments applies " |
| "the Sun default settings."), |
| cmd_to_str(CMD_CREATE), cmd_to_str(CMD_CREATE), |
| cmd_to_str(CMD_CREATE), cmd_to_str(CMD_CREATE)); |
| return (line); |
| case CMD_EXIT: |
| return (gettext("Exits the program. The -F flag can " |
| "be used to force the action.")); |
| case CMD_EXPORT: |
| return (gettext("Prints configuration to standard " |
| "output, or to output-file if\n\tspecified, in " |
| "a form suitable for use in a command-file.")); |
| case CMD_ADD: |
| return (gettext("Add specified resource to " |
| "configuration.")); |
| case CMD_DELETE: |
| return (gettext("Deletes the specified zone. The -F " |
| "flag can be used to force the\n\taction.")); |
| case CMD_REMOVE: |
| return (gettext("Remove specified resource from " |
| "configuration. The -F flag can be used\n\tto " |
| "force the action.")); |
| case CMD_SELECT: |
| (void) snprintf(line, sizeof (line), |
| gettext("Selects a resource to modify. " |
| "Resource modification is completed\n\twith the " |
| "command \"%s\". The property name/value pairs " |
| "must uniquely\n\tidentify a resource. Note that " |
| "the curly braces ('{', '}') mean one\n\tor more " |
| "of whatever is between them."), |
| cmd_to_str(CMD_END)); |
| return (line); |
| case CMD_SET: |
| return (gettext("Sets property values.")); |
| case CMD_CLEAR: |
| return (gettext("Clears property values.")); |
| case CMD_INFO: |
| return (gettext("Displays information about the " |
| "current configuration. If resource\n\ttype is " |
| "specified, displays only information about " |
| "resources of\n\tthe relevant type. If resource " |
| "id is specified, displays only\n\tinformation " |
| "about that resource.")); |
| case CMD_VERIFY: |
| return (gettext("Verifies current configuration " |
| "for correctness (some resource types\n\thave " |
| "required properties).")); |
| case CMD_COMMIT: |
| (void) snprintf(line, sizeof (line), |
| gettext("Commits current configuration. " |
| "Configuration must be committed to\n\tbe used by " |
| "%s. Until the configuration is committed, " |
| "changes \n\tcan be removed with the %s " |
| "command. This operation is\n\tattempted " |
| "automatically upon completion of a %s " |
| "session."), "zoneadm", cmd_to_str(CMD_REVERT), |
| "zonecfg"); |
| return (line); |
| case CMD_REVERT: |
| return (gettext("Reverts configuration back to the " |
| "last committed state. The -F flag\n\tcan be " |
| "used to force the action.")); |
| case CMD_CANCEL: |
| return (gettext("Cancels resource/property " |
| "specification.")); |
| case CMD_END: |
| return (gettext("Ends resource/property " |
| "specification.")); |
| } |
| /* NOTREACHED */ |
| return (NULL); |
| } |
| |
| /* |
| * Called with verbose TRUE when help is explicitly requested, FALSE for |
| * unexpected errors. |
| */ |
| |
| void |
| usage(bool verbose, uint_t flags) |
| { |
| FILE *fp = verbose ? stdout : stderr, *newfp; |
| bool need_to_close = FALSE; |
| char *pager; |
| int i; |
| |
| /* don't page error output */ |
| if (verbose && interactive_mode) { |
| if ((pager = getenv("PAGER")) == NULL) |
| pager = PAGER; |
| if ((newfp = popen(pager, "w")) != NULL) { |
| need_to_close = TRUE; |
| fp = newfp; |
| } |
| } |
| if (flags & HELP_META) { |
| (void) fprintf(fp, gettext("More help is available for the " |
| "following:\n")); |
| (void) fprintf(fp, "\n\tcommands ('%s commands')\n", |
| cmd_to_str(CMD_HELP)); |
| (void) fprintf(fp, "\tsyntax ('%s syntax')\n", |
| cmd_to_str(CMD_HELP)); |
| (void) fprintf(fp, "\tusage ('%s usage')\n\n", |
| cmd_to_str(CMD_HELP)); |
| (void) fprintf(fp, gettext("You may also obtain help on any " |
| "command by typing '%s <command-name>.'\n"), |
| cmd_to_str(CMD_HELP)); |
| } |
| if (flags & HELP_RES_SCOPE) { |
| switch (resource_scope) { |
| case RT_FS: |
| (void) fprintf(fp, gettext("The '%s' resource scope is " |
| "used to configure a file-system.\n"), |
| rt_to_str(resource_scope)); |
| (void) fprintf(fp, gettext("Valid commands:\n")); |
| (void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET), |
| pt_to_str(PT_DIR), gettext("<path>")); |
| (void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET), |
| pt_to_str(PT_SPECIAL), gettext("<path>")); |
| (void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET), |
| pt_to_str(PT_RAW), gettext("<raw-device>")); |
| (void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET), |
| pt_to_str(PT_TYPE), gettext("<file-system type>")); |
| (void) fprintf(fp, "\t%s %s %s\n", cmd_to_str(CMD_ADD), |
| pt_to_str(PT_OPTIONS), |
| gettext("<file-system options>")); |
| (void) fprintf(fp, "\t%s %s %s\n", |
| cmd_to_str(CMD_REMOVE), pt_to_str(PT_OPTIONS), |
| gettext("<file-system options>")); |
| (void) fprintf(fp, gettext("Consult the file-system " |
| "specific manual page, such as mount_ufs(1M), " |
| "for\ndetails about file-system options. Note " |
| "that any file-system options with an\nembedded " |
| "'=' character must be enclosed in double quotes, " |
| /*CSTYLED*/ |
| "such as \"%s=5\".\n"), MNTOPT_RETRY); |
| break; |
| case RT_IPD: |
| (void) fprintf(fp, gettext("The '%s' resource scope is " |
| "used to configure a directory\ninherited from the " |
| "global zone into a non-global zone in read-only " |
| "mode.\n"), rt_to_str(resource_scope)); |
| (void) fprintf(fp, gettext("Valid commands:\n")); |
| (void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET), |
| pt_to_str(PT_DIR), gettext("<path>")); |
| break; |
| case RT_NET: |
| (void) fprintf(fp, gettext("The '%s' resource scope is " |
| "used to configure a network interface.\n"), |
| rt_to_str(resource_scope)); |
| (void) fprintf(fp, gettext("Valid commands:\n")); |
| (void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET), |
| pt_to_str(PT_ADDRESS), gettext("<IP-address>")); |
| (void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET), |
| pt_to_str(PT_PHYSICAL), gettext("<interface>")); |
| (void) fprintf(fp, gettext("See ifconfig(1M) for " |
| "details of the <interface> string.\n")); |
| (void) fprintf(fp, gettext("%s %s is valid if the %s " |
| "property is set to %s, otherwise it must not be " |
| "set.\n"), |
| cmd_to_str(CMD_SET), pt_to_str(PT_ADDRESS), |
| pt_to_str(PT_IPTYPE), "shared"); |
| break; |
| case RT_DEVICE: |
| (void) fprintf(fp, gettext("The '%s' resource scope is " |
| "used to configure a device node.\n"), |
| rt_to_str(resource_scope)); |
| (void) fprintf(fp, gettext("Valid commands:\n")); |
| (void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET), |
| pt_to_str(PT_MATCH), gettext("<device-path>")); |
| break; |
| case RT_RCTL: |
| (void) fprintf(fp, gettext("The '%s' resource scope is " |
| "used to configure a resource control.\n"), |
| rt_to_str(resource_scope)); |
| (void) fprintf(fp, gettext("Valid commands:\n")); |
| (void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET), |
| pt_to_str(PT_NAME), gettext("<string>")); |
| (void) fprintf(fp, "\t%s %s (%s=%s,%s=%s,%s=%s)\n", |
| cmd_to_str(CMD_ADD), pt_to_str(PT_VALUE), |
| pt_to_str(PT_PRIV), gettext("<priv-value>"), |
| pt_to_str(PT_LIMIT), gettext("<number>"), |
| pt_to_str(PT_ACTION), gettext("<action-value>")); |
| (void) fprintf(fp, "\t%s %s (%s=%s,%s=%s,%s=%s)\n", |
| cmd_to_str(CMD_REMOVE), pt_to_str(PT_VALUE), |
| pt_to_str(PT_PRIV), gettext("<priv-value>"), |
| pt_to_str(PT_LIMIT), gettext("<number>"), |
| pt_to_str(PT_ACTION), gettext("<action-value>")); |
| (void) fprintf(fp, "%s\n\t%s := privileged\n" |
| "\t%s := none | deny\n", gettext("Where"), |
| gettext("<priv-value>"), gettext("<action-value>")); |
| break; |
| case RT_ATTR: |
| (void) fprintf(fp, gettext("The '%s' resource scope is " |
| "used to configure a generic attribute.\n"), |
| rt_to_str(resource_scope)); |
| (void) fprintf(fp, gettext("Valid commands:\n")); |
| (void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET), |
| pt_to_str(PT_NAME), gettext("<name>")); |
| (void) fprintf(fp, "\t%s %s=boolean\n", |
| cmd_to_str(CMD_SET), pt_to_str(PT_TYPE)); |
| (void) fprintf(fp, "\t%s %s=true | false\n", |
| cmd_to_str(CMD_SET), pt_to_str(PT_VALUE)); |
| (void) fprintf(fp, gettext("or\n")); |
| (void) fprintf(fp, "\t%s %s=int\n", cmd_to_str(CMD_SET), |
| pt_to_str(PT_TYPE)); |
| (void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET), |
| pt_to_str(PT_VALUE), gettext("<integer>")); |
| (void) fprintf(fp, gettext("or\n")); |
| (void) fprintf(fp, "\t%s %s=string\n", |
| cmd_to_str(CMD_SET), pt_to_str(PT_TYPE)); |
| (void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET), |
| pt_to_str(PT_VALUE), gettext("<string>")); |
| (void) fprintf(fp, gettext("or\n")); |
| (void) fprintf(fp, "\t%s %s=uint\n", |
| cmd_to_str(CMD_SET), pt_to_str(PT_TYPE)); |
| (void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET), |
| pt_to_str(PT_VALUE), gettext("<unsigned integer>")); |
| break; |
| case RT_DATASET: |
| (void) fprintf(fp, gettext("The '%s' resource scope is " |
| "used to export ZFS datasets.\n"), |
| rt_to_str(resource_scope)); |
| (void) fprintf(fp, gettext("Valid commands:\n")); |
| (void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET), |
| pt_to_str(PT_NAME), gettext("<name>")); |
| break; |
| case RT_DCPU: |
| (void) fprintf(fp, gettext("The '%s' resource scope " |
| "configures the 'pools' facility to dedicate\na " |
| "subset of the system's processors to this zone " |
| "while it is running.\n"), |
| rt_to_str(resource_scope)); |
| (void) fprintf(fp, gettext("Valid commands:\n")); |
| (void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET), |
| pt_to_str(PT_NCPUS), |
| gettext("<unsigned integer | range>")); |
| (void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET), |
| pt_to_str(PT_IMPORTANCE), |
| gettext("<unsigned integer>")); |
| break; |
| case RT_MCAP: |
| (void) fprintf(fp, gettext("The '%s' resource scope is " |
| "used to set an upper limit (a cap) on the\n" |
| "amount of physical memory, swap space and locked " |
| "memory that can be used by\nthis zone.\n"), |
| rt_to_str(resource_scope)); |
| (void) fprintf(fp, gettext("Valid commands:\n")); |
| (void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET), |
| pt_to_str(PT_PHYSICAL), |
| gettext("<qualified unsigned decimal>")); |
| (void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET), |
| pt_to_str(PT_SWAP), |
| gettext("<qualified unsigned decimal>")); |
| (void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET), |
| pt_to_str(PT_LOCKED), |
| gettext("<qualified unsigned decimal>")); |
| break; |
| } |
| (void) fprintf(fp, gettext("And from any resource scope, you " |
| "can:\n")); |
| (void) fprintf(fp, "\t%s\t%s\n", cmd_to_str(CMD_END), |
| gettext("(to conclude this operation)")); |
| (void) fprintf(fp, "\t%s\t%s\n", cmd_to_str(CMD_CANCEL), |
| gettext("(to cancel this operation)")); |
| (void) fprintf(fp, "\t%s\t%s\n", cmd_to_str(CMD_EXIT), |
| gettext("(to exit the zonecfg utility)")); |
| } |
| if (flags & HELP_USAGE) { |
| (void) fprintf(fp, "%s:\t%s %s\n", gettext("usage"), |
| execname, cmd_to_str(CMD_HELP)); |
| (void) fprintf(fp, "\t%s -z <zone>\t\t\t(%s)\n", |
| execname, gettext("interactive")); |
| (void) fprintf(fp, "\t%s -z <zone> <command>\n", execname); |
| (void) fprintf(fp, "\t%s -z <zone> -f <command-file>\n", |
| execname); |
| } |
| if (flags & HELP_SUBCMDS) { |
| (void) fprintf(fp, "%s:\n\n", gettext("Commands")); |
| for (i = 0; i <= CMD_MAX; i++) { |
| (void) fprintf(fp, "%s\n", helptab[i].short_usage); |
| if (verbose) |
| (void) fprintf(fp, "\t%s\n\n", long_help(i)); |
| } |
| } |
| if (flags & HELP_SYNTAX) { |
| if (!verbose) |
| (void) fprintf(fp, "\n"); |
| (void) fprintf(fp, "<zone> := [A-Za-z0-9][A-Za-z0-9_.-]*\n"); |
| (void) fprintf(fp, gettext("\t(except the reserved words " |
| "'%s' and anything starting with '%s')\n"), "global", |
| "SUNW"); |
| (void) fprintf(fp, |
| gettext("\tName must be less than %d characters.\n"), |
| ZONENAME_MAX); |
| if (verbose) |
| (void) fprintf(fp, "\n"); |
| } |
| if (flags & HELP_NETADDR) { |
| (void) fprintf(fp, gettext("\n<net-addr> :=")); |
| (void) fprintf(fp, |
| gettext("\t<IPv4-address>[/<IPv4-prefix-length>] |\n")); |
| (void) fprintf(fp, |
| gettext("\t\t<IPv6-address>/<IPv6-prefix-length> |\n")); |
| (void) fprintf(fp, |
| gettext("\t\t<hostname>[/<IPv4-prefix-length>]\n")); |
| (void) fprintf(fp, gettext("See inet(3SOCKET) for IPv4 and " |
| "IPv6 address syntax.\n")); |
| (void) fprintf(fp, gettext("<IPv4-prefix-length> := [0-32]\n")); |
| (void) fprintf(fp, |
| gettext("<IPv6-prefix-length> := [0-128]\n")); |
| (void) fprintf(fp, |
| gettext("<hostname> := [A-Za-z0-9][A-Za-z0-9-.]*\n")); |
| } |
| if (flags & HELP_RESOURCES) { |
| (void) fprintf(fp, "<%s> := %s | %s | %s | %s | %s | %s |\n\t" |
| "%s | %s | %s\n\n", |
| gettext("resource type"), rt_to_str(RT_FS), |
| rt_to_str(RT_IPD), rt_to_str(RT_NET), rt_to_str(RT_DEVICE), |
| rt_to_str(RT_RCTL), rt_to_str(RT_ATTR), |
| rt_to_str(RT_DATASET), rt_to_str(RT_DCPU), |
| rt_to_str(RT_MCAP)); |
| } |
| if (flags & HELP_PROPS) { |
| (void) fprintf(fp, gettext("For resource type ... there are " |
| "property types ...:\n")); |
| (void) fprintf(fp, "\t%s\t%s\n", gettext("(global)"), |
| pt_to_str(PT_ZONENAME)); |
| (void) fprintf(fp, "\t%s\t%s\n", gettext("(global)"), |
| pt_to_str(PT_ZONEPATH)); |
| (void) fprintf(fp, "\t%s\t%s\n", gettext("(global)"), |
| pt_to_str(PT_BRAND)); |
| (void) fprintf(fp, "\t%s\t%s\n", gettext("(global)"), |
| pt_to_str(PT_AUTOBOOT)); |
| (void) fprintf(fp, "\t%s\t%s\n", gettext("(global)"), |
| pt_to_str(PT_BOOTARGS)); |
| (void) fprintf(fp, "\t%s\t%s\n", gettext("(global)"), |
| pt_to_str(PT_POOL)); |
| (void) fprintf(fp, "\t%s\t%s\n", gettext("(global)"), |
| pt_to_str(PT_LIMITPRIV)); |
| (void) fprintf(fp, "\t%s\t%s\n", gettext("(global)"), |
| pt_to_str(PT_SCHED)); |
| (void) fprintf(fp, "\t%s\t%s\n", gettext("(global)"), |
| pt_to_str(PT_IPTYPE)); |
| (void) fprintf(fp, "\t%s\t%s\n", gettext("(global)"), |
| pt_to_str(PT_MAXLWPS)); |
| (void) fprintf(fp, "\t%s\t%s\n", gettext("(global)"), |
| pt_to_str(PT_MAXSHMMEM)); |
| (void) fprintf(fp, "\t%s\t%s\n", gettext("(global)"), |
| pt_to_str(PT_MAXSHMIDS)); |
| (void) fprintf(fp, "\t%s\t%s\n", gettext("(global)"), |
| pt_to_str(PT_MAXMSGIDS)); |
| (void) fprintf(fp, "\t%s\t%s\n", gettext("(global)"), |
| pt_to_str(PT_MAXSEMIDS)); |
| (void) fprintf(fp, "\t%s\t%s\n", gettext("(global)"), |
| pt_to_str(PT_SHARES)); |
| (void) fprintf(fp, "\t%s\t\t%s, %s, %s, %s\n", rt_to_str(RT_FS), |
| pt_to_str(PT_DIR), pt_to_str(PT_SPECIAL), |
| pt_to_str(PT_RAW), pt_to_str(PT_TYPE), |
| pt_to_str(PT_OPTIONS)); |
| (void) fprintf(fp, "\t%s\t%s\n", rt_to_str(RT_IPD), |
| pt_to_str(PT_DIR)); |
| (void) fprintf(fp, "\t%s\t\t%s, %s\n", rt_to_str(RT_NET), |
| pt_to_str(PT_ADDRESS), pt_to_str(PT_PHYSICAL)); |
| (void) fprintf(fp, "\t%s\t\t%s\n", rt_to_str(RT_DEVICE), |
| pt_to_str(PT_MATCH)); |
| (void) fprintf(fp, "\t%s\t\t%s, %s\n", rt_to_str(RT_RCTL), |
| pt_to_str(PT_NAME), pt_to_str(PT_VALUE)); |
| (void) fprintf(fp, "\t%s\t\t%s, %s, %s\n", rt_to_str(RT_ATTR), |
| pt_to_str(PT_NAME), pt_to_str(PT_TYPE), |
| pt_to_str(PT_VALUE)); |
| (void) fprintf(fp, "\t%s\t\t%s\n", rt_to_str(RT_DATASET), |
| pt_to_str(PT_NAME)); |
| (void) fprintf(fp, "\t%s\t%s, %s\n", rt_to_str(RT_DCPU), |
| pt_to_str(PT_NCPUS), pt_to_str(PT_IMPORTANCE)); |
| (void) fprintf(fp, "\t%s\t%s, %s, %s\n", rt_to_str(RT_MCAP), |
| pt_to_str(PT_PHYSICAL), pt_to_str(PT_SWAP), |
| pt_to_str(PT_LOCKED)); |
| } |
| if (need_to_close) |
| (void) pclose(fp); |
| } |
| |
| /* PRINTFLIKE1 */ |
| static void |
| zerr(const char *fmt, ...) |
| { |
| va_list alist; |
| static int last_lineno; |
| |
| /* lex_lineno has already been incremented in the lexer; compensate */ |
| if (cmd_file_mode && lex_lineno > last_lineno) { |
| if (strcmp(cmd_file_name, "-") == 0) |
| (void) fprintf(stderr, gettext("On line %d:\n"), |
| lex_lineno - 1); |
| else |
| (void) fprintf(stderr, gettext("On line %d of %s:\n"), |
| lex_lineno - 1, cmd_file_name); |
| last_lineno = lex_lineno; |
| } |
| va_start(alist, fmt); |
| (void) vfprintf(stderr, fmt, alist); |
| (void) fprintf(stderr, "\n"); |
| va_end(alist); |
| } |
| |
| static void |
| zone_perror(char *prefix, int err, bool set_saw) |
| { |
| zerr("%s: %s", prefix, zonecfg_strerror(err)); |
| if (set_saw) |
| saw_error = TRUE; |
| } |
| |
| /* |
| * zone_perror() expects a single string, but for remove and select |
| * we have both the command and the resource type, so this wrapper |
| * function serves the same purpose in a slightly different way. |
| */ |
| |
| static void |
| z_cmd_rt_perror(int cmd_num, int res_num, int err, bool set_saw) |
| { |
| zerr("%s %s: %s", cmd_to_str(cmd_num), rt_to_str(res_num), |
| zonecfg_strerror(err)); |
| if (set_saw) |
| saw_error = TRUE; |
| } |
| |
| /* returns Z_OK if successful, Z_foo from <libzonecfg.h> otherwise */ |
| static int |
| initialize(bool handle_expected) |
| { |
| int err; |
| char brandname[MAXNAMELEN]; |
| |
| if (zonecfg_check_handle(handle) != Z_OK) { |
| if ((err = zonecfg_get_handle(zone, handle)) == Z_OK) { |
| got_handle = TRUE; |
| if (zonecfg_get_brand(handle, brandname, |
| sizeof (brandname)) != Z_OK) { |
| zerr("Zone %s is inconsistent: missing " |
| "brand attribute", zone); |
| exit(Z_ERR); |
| } |
| if ((brand = brand_open(brandname)) == NULL) { |
| zerr("Zone %s uses non-existent brand \"%s\"." |
| " Unable to continue", zone, brandname); |
| exit(Z_ERR); |
| } |
| } else if (global_zone && err == Z_NO_ZONE && !got_handle && |
| !read_only_mode) { |
| /* |
| * We implicitly create the global zone config if it |
| * doesn't exist. |
| */ |
| zone_dochandle_t tmphandle; |
| |
| if ((tmphandle = zonecfg_init_handle()) == NULL) { |
| zone_perror(execname, Z_NOMEM, TRUE); |
| exit(Z_ERR); |
| } |
| |
| err = zonecfg_get_template_handle("SUNWblank", zone, |
| tmphandle); |
| |
| if (err != Z_OK) { |
| zonecfg_fini_handle(tmphandle); |
| zone_perror("SUNWblank", err, TRUE); |
| return (err); |
| } |
| |
| need_to_commit = TRUE; |
| zonecfg_fini_handle(handle); |
| handle = tmphandle; |
| got_handle = TRUE; |
| |
| } else { |
| zone_perror(zone, err, handle_expected || got_handle); |
| if (err == Z_NO_ZONE && !got_handle && |
| interactive_mode && !read_only_mode) |
| (void) printf(gettext("Use '%s' to begin " |
| "configuring a new zone.\n"), |
| cmd_to_str(CMD_CREATE)); |
| return (err); |
| } |
| } |
| return (Z_OK); |
| } |
| |
| static bool |
| state_atleast(zone_state_t state) |
| { |
| zone_state_t state_num; |
| int err; |
| |
| if ((err = zone_get_state(zone, &state_num)) != Z_OK) { |
| /* all states are greater than "non-existent" */ |
| if (err == Z_NO_ZONE) |
| return (B_FALSE); |
| zerr(gettext("Unexpectedly failed to determine state " |
| "of zone %s: %s"), zone, zonecfg_strerror(err)); |
| exit(Z_ERR); |
| } |
| return (state_num >= state); |
| } |
| |
| /* |
| * short_usage() is for bad syntax: getopt() issues, too many arguments, etc. |
| */ |
| |
| void |
| short_usage(int command) |
| { |
| /* lex_lineno has already been incremented in the lexer; compensate */ |
| if (cmd_file_mode) { |
| if (strcmp(cmd_file_name, "-") == 0) |
| (void) fprintf(stderr, |
| gettext("syntax error on line %d\n"), |
| lex_lineno - 1); |
| else |
| (void) fprintf(stderr, |
| gettext("syntax error on line %d of %s\n"), |
| lex_lineno - 1, cmd_file_name); |
| } |
| (void) fprintf(stderr, "%s:\n%s\n", gettext("usage"), |
| helptab[command].short_usage); |
| saw_error = TRUE; |
| } |
| |
| /* |
| * long_usage() is for bad semantics: e.g., wrong property type for a given |
| * resource type. It is also used by longer_usage() below. |
| */ |
| |
| void |
| long_usage(uint_t cmd_num, bool set_saw) |
| { |
| (void) fprintf(set_saw ? stderr : stdout, "%s:\n%s\n", gettext("usage"), |
| helptab[cmd_num].short_usage); |
| (void) fprintf(set_saw ? stderr : stdout, "\t%s\n", long_help(cmd_num)); |
| if (set_saw) |
| saw_error = TRUE; |
| } |
| |
| /* |
| * longer_usage() is for 'help foo' and 'foo -?': call long_usage() and also |
| * any extra usage() flags as appropriate for whatever command. |
| */ |
| |
| void |
| longer_usage(uint_t cmd_num) |
| { |
| long_usage(cmd_num, FALSE); |
| if (helptab[cmd_num].flags != 0) { |
| (void) printf("\n"); |
| usage(TRUE, helptab[cmd_num].flags); |
| } |
| } |
| |
| /* |
| * scope_usage() is simply used when a command is called from the wrong scope. |
| */ |
| |
| static void |
| scope_usage(uint_t cmd_num) |
| { |
| zerr(gettext("The %s command only makes sense in the %s scope."), |
| cmd_to_str(cmd_num), |
| global_scope ? gettext("resource") : gettext("global")); |
| saw_error = TRUE; |
| } |
| |
| /* |
| * On input, TRUE => yes, FALSE => no. |
| * On return, TRUE => 1, FALSE => no, could not ask => -1. |
| */ |
| |
| static int |
| ask_yesno(bool default_answer, const char *question) |
| { |
| char line[64]; /* should be enough to answer yes or no */ |
| |
| if (!ok_to_prompt) { |
| saw_error = TRUE; |
| return (-1); |
| } |
| for (;;) { |
| if (printf("%s (%s)? ", question, |
| default_answer ? "[y]/n" : "y/[n]") < 0) |
| return (-1); |
| if (fgets(line, sizeof (line), stdin) == NULL) |
| return (-1); |
| |
| if (line[0] == '\n') |
| return (default_answer ? 1 : 0); |
| if (tolower(line[0]) == 'y') |
| return (1); |
| if (tolower(line[0]) == 'n') |
| return (0); |
| } |
| } |
| |
| /* |
| * Prints warning if zone already exists. |
| * In interactive mode, prompts if we should continue anyway and returns Z_OK |
| * if so, Z_ERR if not. In non-interactive mode, exits with Z_ERR. |
| * |
| * Note that if a zone exists and its state is >= INSTALLED, an error message |
| * will be printed and this function will return Z_ERR regardless of mode. |
| */ |
| |
| static int |
| check_if_zone_already_exists(bool force) |
| { |
| char line[ZONENAME_MAX + 128]; /* enough to ask a question */ |
| zone_dochandle_t tmphandle; |
| int res, answer; |
| |
| if ((tmphandle = zonecfg_init_handle()) == NULL) { |
| zone_perror(execname, Z_NOMEM, TRUE); |
| exit(Z_ERR); |
| } |
| res = zonecfg_get_handle(zone, tmphandle); |
| zonecfg_fini_handle(tmphandle); |
| if (res != Z_OK) |
| return (Z_OK); |
| |
| if (state_atleast(ZONE_STATE_INSTALLED)) { |
| zerr(gettext("Zone %s already installed; %s not allowed."), |
| zone, cmd_to_str(CMD_CREATE)); |
| return (Z_ERR); |
| } |
| |
| if (force) { |
| (void) printf(gettext("Zone %s already exists; overwriting.\n"), |
| zone); |
| return (Z_OK); |
| } |
| (void) snprintf(line, sizeof (line), |
| gettext("Zone %s already exists; %s anyway"), zone, |
| cmd_to_str(CMD_CREATE)); |
| if ((answer = ask_yesno(FALSE, line)) == -1) { |
| zerr(gettext("Zone exists, input not from terminal and -F not " |
| "specified:\n%s command ignored, exiting."), |
| cmd_to_str(CMD_CREATE)); |
| exit(Z_ERR); |
| } |
| return (answer == 1 ? Z_OK : Z_ERR); |
| } |
| |
| static bool |
| zone_is_read_only(int cmd_num) |
| { |
| if (strncmp(zone, "SUNW", 4) == 0) { |
| zerr(gettext("%s: zones beginning with SUNW are read-only."), |
| zone); |
| saw_error = TRUE; |
| return (TRUE); |
| } |
| if (read_only_mode) { |
| zerr(gettext("%s: cannot %s in read-only mode."), zone, |
| cmd_to_str(cmd_num)); |
| saw_error = TRUE; |
| return (TRUE); |
| } |
| return (FALSE); |
| } |
| |
| /* |
| * Create a new configuration. |
| */ |
| void |
| create_func(cmd_t *cmd) |
| { |
| int err, arg; |
| char zone_template[ZONENAME_MAX]; |
| char attach_path[MAXPATHLEN]; |
| zone_dochandle_t tmphandle; |
| bool force = FALSE; |
| bool attach = FALSE; |
| |
| assert(cmd != NULL); |
| |
| /* This is the default if no arguments are given. */ |
| (void) strlcpy(zone_template, "SUNWdefault", sizeof (zone_template)); |
| |
| optind = 0; |
| while ((arg = getopt(cmd->cmd_argc, cmd->cmd_argv, "?a:bFt:")) |
| != EOF) { |
| switch (arg) { |
| case '?': |
| if (optopt == '?') |
| longer_usage(CMD_CREATE); |
| else |
| short_usage(CMD_CREATE); |
| return; |
| case 'a': |
| (void) strlcpy(attach_path, optarg, |
| sizeof (attach_path)); |
| attach = TRUE; |
| break; |
| case 'b': |
| (void) strlcpy(zone_template, "SUNWblank", |
| sizeof (zone_template)); |
| break; |
| case 'F': |
| force = TRUE; |
| break; |
| case 't': |
| (void) strlcpy(zone_template, optarg, |
| sizeof (zone_template)); |
| break; |
| default: |
| short_usage(CMD_CREATE); |
| return; |
| } |
| } |
| if (optind != cmd->cmd_argc) { |
| short_usage(CMD_CREATE); |
| return; |
| } |
| |
| if (zone_is_read_only(CMD_CREATE)) |
| return; |
| |
| if (check_if_zone_already_exists(force) != Z_OK) |
| return; |
| |
| /* |
| * Get a temporary handle first. If that fails, the old handle |
| * will not be lost. Then finish whichever one we don't need, |
| * to avoid leaks. Then get the handle for zone_template, and |
| * set the name to zone: this "copy, rename" method is how |
| * create -[b|t] works. |
| */ |
| if ((tmphandle = zonecfg_init_handle()) == NULL) { |
| zone_perror(execname, Z_NOMEM, TRUE); |
| exit(Z_ERR); |
| } |
| |
| if (attach) |
| err = zonecfg_get_attach_handle(attach_path, zone, B_FALSE, |
| tmphandle); |
| else |
| err = zonecfg_get_template_handle(zone_template, zone, |
| tmphandle); |
| |
| if (err != Z_OK) { |
| zonecfg_fini_handle(tmphandle); |
| if (attach && err == Z_NO_ZONE) |
| (void) fprintf(stderr, gettext("invalid path to " |
| "detached zone\n")); |
| else if (attach && err == Z_INVALID_DOCUMENT) |
| (void) fprintf(stderr, gettext("Cannot attach to an " |
| "earlier release of the operating system\n")); |
| else |
| zone_perror(zone_template, err, TRUE); |
| return; |
| } |
| |
| need_to_commit = TRUE; |
| zonecfg_fini_handle(handle); |
| handle = tmphandle; |
| got_handle = TRUE; |
| } |
| |
| /* |
| * This malloc()'s memory, which must be freed by the caller. |
| */ |
| static char * |
| quoteit(char *instr) |
| { |
| char *outstr; |
| size_t outstrsize = strlen(instr) + 3; /* 2 quotes + '\0' */ |
| |
| if ((outstr = malloc(outstrsize)) == NULL) { |
| zone_perror(zone, Z_NOMEM, FALSE); |
| exit(Z_ERR); |
| } |
| if (strchr(instr, ' ') == NULL) { |
| (void) strlcpy(outstr, instr, outstrsize); |
| return (outstr); |
| } |
| (void) snprintf(outstr, outstrsize, "\"%s\"", instr); |
| return (outstr); |
| } |
| |
| static void |
| export_prop(FILE *of, int prop_num, char *prop_id) |
| { |
| char *quote_str; |
| |
| if (strlen(prop_id) == 0) |
| return; |
| quote_str = quoteit(prop_id); |
| (void) fprintf(of, "%s %s=%s\n", cmd_to_str(CMD_SET), |
| pt_to_str(prop_num), quote_str); |
| free(quote_str); |
| } |
| |
| void |
| export_func(cmd_t *cmd) |
| { |
| struct zone_nwiftab nwiftab; |
| struct zone_fstab fstab; |
| struct zone_devtab devtab; |
| struct zone_attrtab attrtab; |
| struct zone_rctltab rctltab; |
| struct zone_dstab dstab; |
| struct zone_psettab psettab; |
| struct zone_mcaptab mcaptab; |
| struct zone_rctlvaltab *valptr; |
| int err, arg; |
| char zonepath[MAXPATHLEN], outfile[MAXPATHLEN], pool[MAXNAMELEN]; |
| char bootargs[BOOTARGS_MAX]; |
| char sched[MAXNAMELEN]; |
| char brand[MAXNAMELEN]; |
| char *limitpriv; |
| FILE *of; |
| boolean_t autoboot; |
| zone_iptype_t iptype; |
| bool need_to_close = FALSE; |
| |
| assert(cmd != NULL); |
| |
| outfile[0] = '\0'; |
| optind = 0; |
| while ((arg = getopt(cmd->cmd_argc, cmd->cmd_argv, "?f:")) != EOF) { |
| switch (arg) { |
| case '?': |
| if (optopt == '?') |
| longer_usage(CMD_EXPORT); |
| else |
| short_usage(CMD_EXPORT); |
| return; |
| case 'f': |
| (void) strlcpy(outfile, optarg, sizeof (outfile)); |
| break; |
| default: |
| short_usage(CMD_EXPORT); |
| return; |
| } |
| } |
| if (optind != cmd->cmd_argc) { |
| short_usage(CMD_EXPORT); |
| return; |
| } |
| if (strlen(outfile) == 0) { |
| of = stdout; |
| } else { |
| if ((of = fopen(outfile, "w")) == NULL) { |
| zerr(gettext("opening file %s: %s"), |
| outfile, strerror(errno)); |
| goto done; |
| } |
| setbuf(of, NULL); |
| need_to_close = TRUE; |
| } |
| |
| if ((err = initialize(TRUE)) != Z_OK) |
| goto done; |
| |
| (void) fprintf(of, "%s -b\n", cmd_to_str(CMD_CREATE)); |
| |
| if (zonecfg_get_zonepath(handle, zonepath, sizeof (zonepath)) == Z_OK && |
| strlen(zonepath) > 0) |
| (void) fprintf(of, "%s %s=%s\n", cmd_to_str(CMD_SET), |
| pt_to_str(PT_ZONEPATH), zonepath); |
| |
| if ((zone_get_brand(zone, brand, sizeof (brand)) == Z_OK) && |
| (strcmp(brand, NATIVE_BRAND_NAME) != 0)) |
| (void) fprintf(of, "%s %s=%s\n", cmd_to_str(CMD_SET), |
| pt_to_str(PT_BRAND), brand); |
| |
| if (zonecfg_get_autoboot(handle, &autoboot) == Z_OK) |
| (void) fprintf(of, "%s %s=%s\n", cmd_to_str(CMD_SET), |
| pt_to_str(PT_AUTOBOOT), autoboot ? "true" : "false"); |
| |
| if (zonecfg_get_bootargs(handle, bootargs, sizeof (bootargs)) == Z_OK && |
| strlen(bootargs) > 0) { |
| (void) fprintf(of, "%s %s=%s\n", cmd_to_str(CMD_SET), |
| pt_to_str(PT_BOOTARGS), bootargs); |
| } |
| |
| if (zonecfg_get_pool(handle, pool, sizeof (pool)) == Z_OK && |
| strlen(pool) > 0) |
| (void) fprintf(of, "%s %s=%s\n", cmd_to_str(CMD_SET), |
| pt_to_str(PT_POOL), pool); |
| |
| if (zonecfg_get_limitpriv(handle, &limitpriv) == Z_OK && |
| strlen(limitpriv) > 0) { |
| (void) fprintf(of, "%s %s=%s\n", cmd_to_str(CMD_SET), |
| pt_to_str(PT_LIMITPRIV), limitpriv); |
| free(limitpriv); |
| } |
| |
| if (zonecfg_get_sched_class(handle, sched, sizeof (sched)) == Z_OK && |
| strlen(sched) > 0) |
| (void) fprintf(of, "%s %s=%s\n", cmd_to_str(CMD_SET), |
| pt_to_str(PT_SCHED), sched); |
| |
| if (zonecfg_get_iptype(handle, &iptype) == Z_OK) { |
| switch (iptype) { |
| case ZS_SHARED: |
| (void) fprintf(of, "%s %s=%s\n", cmd_to_str(CMD_SET), |
| pt_to_str(PT_IPTYPE), "shared"); |
| break; |
| case ZS_EXCLUSIVE: |
| (void) fprintf(of, "%s %s=%s\n", cmd_to_str(CMD_SET), |
| pt_to_str(PT_IPTYPE), "exclusive"); |
| break; |
| } |
| } |
| |
| if ((err = zonecfg_setipdent(handle)) != Z_OK) { |
| zone_perror(zone, err, FALSE); |
| goto done; |
| } |
| while (zonecfg_getipdent(handle, &fstab) == Z_OK) { |
| (void) fprintf(of, "%s %s\n", cmd_to_str(CMD_ADD), |
| rt_to_str(RT_IPD)); |
| export_prop(of, PT_DIR, fstab.zone_fs_dir); |
| (void) fprintf(of, "%s\n", cmd_to_str(CMD_END)); |
| } |
| (void) zonecfg_endipdent(handle); |
| |
| if ((err = zonecfg_setfsent(handle)) != Z_OK) { |
| zone_perror(zone, err, FALSE); |
| goto done; |
| } |
| while (zonecfg_getfsent(handle, &fstab) == Z_OK) { |
| zone_fsopt_t *optptr; |
| |
| (void) fprintf(of, "%s %s\n", cmd_to_str(CMD_ADD), |
| rt_to_str(RT_FS)); |
| export_prop(of, PT_DIR, fstab.zone_fs_dir); |
| export_prop(of, PT_SPECIAL, fstab.zone_fs_special); |
| export_prop(of, PT_RAW, fstab.zone_fs_raw); |
| export_prop(of, PT_TYPE, fstab.zone_fs_type); |
| for (optptr = fstab.zone_fs_options; optptr != NULL; |
| optptr = optptr->zone_fsopt_next) { |
| /* |
| * Simple property values with embedded equal signs |
| * need to be quoted to prevent the lexer from |
| * mis-parsing them as complex name=value pairs. |
| */ |
| if (strchr(optptr->zone_fsopt_opt, '=')) |
| (void) fprintf(of, "%s %s \"%s\"\n", |
| cmd_to_str(CMD_ADD), |
| pt_to_str(PT_OPTIONS), |
| optptr->zone_fsopt_opt); |
| else |
| (void) fprintf(of, "%s %s %s\n", |
| cmd_to_str(CMD_ADD), |
| pt_to_str(PT_OPTIONS), |
| optptr->zone_fsopt_opt); |
| } |
| (void) fprintf(of, "%s\n", cmd_to_str(CMD_END)); |
| zonecfg_free_fs_option_list(fstab.zone_fs_options); |
| } |
| (void) zonecfg_endfsent(handle); |
| |
| if ((err = zonecfg_setnwifent(handle)) != Z_OK) { |
| zone_perror(zone, err, FALSE); |
| goto done; |
| } |
| while (zonecfg_getnwifent(handle, &nwiftab) == Z_OK) { |
| (void) fprintf(of, "%s %s\n", cmd_to_str(CMD_ADD), |
| rt_to_str(RT_NET)); |
| export_prop(of, PT_ADDRESS, nwiftab.zone_nwif_address); |
| export_prop(of, PT_PHYSICAL, nwiftab.zone_nwif_physical); |
| (void) fprintf(of, "%s\n", cmd_to_str(CMD_END)); |
| } |
| (void) zonecfg_endnwifent(handle); |
| |
| if ((err = zonecfg_setdevent(handle)) != Z_OK) { |
| zone_perror(zone, err, FALSE); |
| goto done; |
| } |
| while (zonecfg_getdevent(handle, &devtab) == Z_OK) { |
| (void) fprintf(of, "%s %s\n", cmd_to_str(CMD_ADD), |
| rt_to_str(RT_DEVICE)); |
| export_prop(of, PT_MATCH, devtab.zone_dev_match); |
| (void) fprintf(of, "%s\n", cmd_to_str(CMD_END)); |
| } |
| (void) zonecfg_enddevent(handle); |
| |
| if ((err = zonecfg_setrctlent(handle)) != Z_OK) { |
| zone_perror(zone, err, FALSE); |
| goto done; |
| } |
| while (zonecfg_getrctlent(handle, &rctltab) == Z_OK) { |
| (void) fprintf(of, "%s rctl\n", cmd_to_str(CMD_ADD)); |
| export_prop(of, PT_NAME, rctltab.zone_rctl_name); |
| for (valptr = rctltab.zone_rctl_valptr; valptr != NULL; |
| valptr = valptr->zone_rctlval_next) { |
| fprintf(of, "%s %s (%s=%s,%s=%s,%s=%s)\n", |
| cmd_to_str(CMD_ADD), pt_to_str(PT_VALUE), |
| pt_to_str(PT_PRIV), valptr->zone_rctlval_priv, |
| pt_to_str(PT_LIMIT), valptr->zone_rctlval_limit, |
| pt_to_str(PT_ACTION), valptr->zone_rctlval_action); |
| } |
| (void) fprintf(of, "%s\n", cmd_to_str(CMD_END)); |
| zonecfg_free_rctl_value_list(rctltab.zone_rctl_valptr); |
| } |
| (void) zonecfg_endrctlent(handle); |
| |
| if ((err = zonecfg_setattrent(handle)) != Z_OK) { |
| zone_perror(zone, err, FALSE); |
| goto done; |
| } |
| while (zonecfg_getattrent(handle, &attrtab) == Z_OK) { |
| (void) fprintf(of, "%s %s\n", cmd_to_str(CMD_ADD), |
| rt_to_str(RT_ATTR)); |
| export_prop(of, PT_NAME, attrtab.zone_attr_name); |
| export_prop(of, PT_TYPE, attrtab.zone_attr_type); |
| export_prop(of, PT_VALUE, attrtab.zone_attr_value); |
| (void) fprintf(of, "%s\n", cmd_to_str(CMD_END)); |
| } |
| (void) zonecfg_endattrent(handle); |
| |
| if ((err = zonecfg_setdsent(handle)) != Z_OK) { |
| zone_perror(zone, err, FALSE); |
| goto done; |
| } |
| while (zonecfg_getdsent(handle, &dstab) == Z_OK) { |
| (void) fprintf(of, "%s %s\n", cmd_to_str(CMD_ADD), |
| rt_to_str(RT_DATASET)); |
| export_prop(of, PT_NAME, dstab.zone_dataset_name); |
| (void) fprintf(of, "%s\n", cmd_to_str(CMD_END)); |
| } |
| (void) zonecfg_enddsent(handle); |
| |
| if (zonecfg_getpsetent(handle, &psettab) == Z_OK) { |
| (void) fprintf(of, "%s %s\n", cmd_to_str(CMD_ADD), |
| rt_to_str(RT_DCPU)); |
| if (strcmp(psettab.zone_ncpu_min, psettab.zone_ncpu_max) == 0) |
| (void) fprintf(of, "%s %s=%s\n", cmd_to_str(CMD_SET), |
| pt_to_str(PT_NCPUS), psettab.zone_ncpu_max); |
| else |
| (void) fprintf(of, "%s %s=%s-%s\n", cmd_to_str(CMD_SET), |
| pt_to_str(PT_NCPUS), psettab.zone_ncpu_min, |
| psettab.zone_ncpu_max); |
| if (psettab.zone_importance[0] != '\0') |
| (void) fprintf(of, "%s %s=%s\n", cmd_to_str(CMD_SET), |
| pt_to_str(PT_IMPORTANCE), psettab.zone_importance); |
| (void) fprintf(of, "%s\n", cmd_to_str(CMD_END)); |
| } |
| |
| if (zonecfg_getmcapent(handle, &mcaptab) == Z_OK) { |
| char buf[128]; |
| |
| (void) fprintf(of, "%s %s\n", cmd_to_str(CMD_ADD), |
| rt_to_str(RT_MCAP)); |
| bytes_to_units(mcaptab.zone_physmem_cap, buf, sizeof (buf)); |
| (void) fprintf(of, "%s %s=%s\n", cmd_to_str(CMD_SET), |
| pt_to_str(PT_PHYSICAL), buf); |
| (void) fprintf(of, "%s\n", cmd_to_str(CMD_END)); |
| } |
| |
| done: |
| if (need_to_close) |
| (void) fclose(of); |
| } |
| |
| void |
| exit_func(cmd_t *cmd) |
| { |
| int arg, answer; |
| |
| optind = 0; |
| while ((arg = getopt(cmd->cmd_argc, cmd->cmd_argv, "?F")) != EOF) { |
| switch (arg) { |
| case '?': |
| longer_usage(CMD_EXIT); |
| return; |
| case 'F': |
| force_exit = TRUE; |
| break; |
| default: |
| short_usage(CMD_EXIT); |
| return; |
| } |
| } |
| if (optind < cmd->cmd_argc) { |
| short_usage(CMD_EXIT); |
| return; |
| } |
| |
| if (global_scope || force_exit) { |
| time_to_exit = TRUE; |
| return; |
| } |
| |
| answer = ask_yesno(FALSE, "Resource incomplete; really quit"); |
| if (answer == -1) { |
| zerr(gettext("Resource incomplete, input " |
| "not from terminal and -F not specified:\n%s command " |
| "ignored, but exiting anyway."), cmd_to_str(CMD_EXIT)); |
| exit(Z_ERR); |
| } else if (answer == 1) { |
| time_to_exit = TRUE; |
| } |
| /* (answer == 0) => just return */ |
| } |
| |
| static int |
| validate_zonepath_syntax(char *path) |
| { |
| if (path[0] != '/') { |
| zerr(gettext("%s is not an absolute path."), path); |
| return (Z_ERR); |
| } |
| if (strcmp(path, "/") == 0) { |
| zerr(gettext("/ is not allowed as a %s."), |
| pt_to_str(PT_ZONEPATH)); |
| return (Z_ERR); |
| } |
| return (Z_OK); |
| } |
| |
| static void |
| add_resource(cmd_t *cmd) |
| { |
| int type; |
| struct zone_psettab tmp_psettab; |
| struct zone_mcaptab tmp_mcaptab; |
| uint64_t tmp_mcap; |
| char pool[MAXNAMELEN]; |
| |
| if ((type = cmd->cmd_res_type) == RT_UNKNOWN) { |
| long_usage(CMD_ADD, TRUE); |
| goto bad; |
| } |
| |
| switch (type) { |
| case RT_FS: |
| bzero(&in_progress_fstab, sizeof (in_progress_fstab)); |
| return; |
| case RT_IPD: |
| if (state_atleast(ZONE_STATE_INSTALLED)) { |
| zerr(gettext("Zone %s already installed; %s %s not " |
| "allowed."), zone, cmd_to_str(CMD_ADD), |
| rt_to_str(RT_IPD)); |
| goto bad; |
| } |
| bzero(&in_progress_ipdtab, sizeof (in_progress_ipdtab)); |
| return; |
| case RT_NET: |
| bzero(&in_progress_nwiftab, sizeof (in_progress_nwiftab)); |
| return; |
| case RT_DEVICE: |
| bzero(&in_progress_devtab, sizeof (in_progress_devtab)); |
| return; |
| case RT_RCTL: |
| if (global_zone) |
| zerr(gettext("WARNING: Setting a global zone resource " |
| "control too low could deny\nservice " |
| "to even the root user; " |
| "this could render the system impossible\n" |
| "to administer. Please use caution.")); |
| bzero(&in_progress_rctltab, sizeof (in_progress_rctltab)); |
| return; |
| case RT_ATTR: |
| bzero(&in_progress_attrtab, sizeof (in_progress_attrtab)); |
| return; |
| case RT_DATASET: |
| bzero(&in_progress_dstab, sizeof (in_progress_dstab)); |
| return; |
| case RT_DCPU: |
| /* Make sure there isn't already a cpu-set entry. */ |
| if (zonecfg_lookup_pset(handle, &tmp_psettab) == Z_OK) { |
| zerr(gettext("The %s resource already exists."), |
| rt_to_str(RT_DCPU)); |
| goto bad; |
| } |
| |
| /* Make sure the pool property isn't set. */ |
| if (zonecfg_get_pool(handle, pool, sizeof (pool)) == Z_OK && |
| strlen(pool) > 0) { |
| zerr(gettext("The %s property is already set. " |
| "A persistent pool is incompatible with\nthe %s " |
| "resource."), |
| pt_to_str(PT_POOL), rt_to_str(RT_DCPU)); |
| goto bad; |
| } |
| |
| bzero(&in_progress_psettab, sizeof (in_progress_psettab)); |
| return; |
| case RT_MCAP: |
| /* |
| * Make sure there isn't already a mem-cap entry or max-swap |
| * or max-locked rctl. |
| */ |
| if (zonecfg_lookup_mcap(handle, &tmp_mcaptab) == Z_OK || |
| zonecfg_get_aliased_rctl(handle, ALIAS_MAXSWAP, &tmp_mcap) |
| == Z_OK || |
| zonecfg_get_aliased_rctl(handle, ALIAS_MAXLOCKEDMEM, |
| &tmp_mcap) == Z_OK) { |
| zerr(gettext("The %s resource or a related resource " |
| "control already exists."), rt_to_str(RT_MCAP)); |
| goto bad; |
| } |
| if (global_zone) |
| zerr(gettext("WARNING: Setting a global zone memory " |
| "cap too low could deny\nservice " |
| "to even the root user; " |
| "this could render the system impossible\n" |
| "to administer. Please use caution.")); |
| bzero(&in_progress_mcaptab, sizeof (in_progress_mcaptab)); |
| return; |
| default: |
| zone_perror(rt_to_str(type), Z_NO_RESOURCE_TYPE, TRUE); |
| long_usage(CMD_ADD, TRUE); |
| usage(FALSE, HELP_RESOURCES); |
| } |
| bad: |
| global_scope = TRUE; |
| end_op = -1; |
| } |
| |
| static void |
| do_complex_rctl_val(complex_property_ptr_t cp) |
| { |
| struct zone_rctlvaltab *rctlvaltab; |
| complex_property_ptr_t cx; |
| bool seen_priv = FALSE, seen_limit = FALSE, seen_action = FALSE; |
| rctlblk_t *rctlblk; |
| int err; |
| |
| if ((rctlvaltab = alloc_rctlvaltab()) == NULL) { |
| zone_perror(zone, Z_NOMEM, TRUE); |
| exit(Z_ERR); |
| } |
| for (cx = cp; cx != NULL; cx = cx->cp_next) { |
| switch (cx->cp_type) { |
| case PT_PRIV: |
| if (seen_priv) { |
| zerr(gettext("%s already specified"), |
| pt_to_str(PT_PRIV)); |
| goto bad; |
| } |
| (void) strlcpy(rctlvaltab->zone_rctlval_priv, |
| cx->cp_value, |
| sizeof (rctlvaltab->zone_rctlval_priv)); |
| seen_priv = TRUE; |
| break; |
| case PT_LIMIT: |
| if (seen_limit) { |
| zerr(gettext("%s already specified"), |
| pt_to_str(PT_LIMIT)); |
| goto bad; |
| } |
| (void) strlcpy(rctlvaltab->zone_rctlval_limit, |
| cx->cp_value, |
| sizeof (rctlvaltab->zone_rctlval_limit)); |
| seen_limit = TRUE; |
| break; |
| case PT_ACTION: |
| if (seen_action) { |
| zerr(gettext("%s already specified"), |
| pt_to_str(PT_ACTION)); |
| goto bad; |
| } |
| (void) strlcpy(rctlvaltab->zone_rctlval_action, |
| cx->cp_value, |
| sizeof (rctlvaltab->zone_rctlval_action)); |
| seen_action = TRUE; |
| break; |
| default: |
| zone_perror(pt_to_str(PT_VALUE), |
| Z_NO_PROPERTY_TYPE, TRUE); |
| long_usage(CMD_ADD, TRUE); |
| usage(FALSE, HELP_PROPS); |
| zonecfg_free_rctl_value_list(rctlvaltab); |
| return; |
| } |
| } |
| if (!seen_priv) |
| zerr(gettext("%s not specified"), pt_to_str(PT_PRIV)); |
| if (!seen_limit) |
| zerr(gettext("%s not specified"), pt_to_str(PT_LIMIT)); |
| if (!seen_action) |
| zerr(gettext("%s not specified"), pt_to_str(PT_ACTION)); |
| if (!seen_priv || !seen_limit || !seen_action) |
| goto bad; |
| rctlvaltab->zone_rctlval_next = NULL; |
| rctlblk = alloca(rctlblk_size()); |
| /* |
| * Make sure the rctl value looks roughly correct; we won't know if |
| * it's truly OK until we verify the configuration on the target |
| * system. |
| */ |
| if (zonecfg_construct_rctlblk(rctlvaltab, rctlblk) != Z_OK || |
| !zonecfg_valid_rctlblk(rctlblk)) { |
| zerr(gettext("Invalid %s %s specification"), rt_to_str(RT_RCTL), |
| pt_to_str(PT_VALUE)); |
| goto bad; |
| } |
| err = zonecfg_add_rctl_value(&in_progress_rctltab, rctlvaltab); |
| if (err != Z_OK) |
| zone_perror(pt_to_str(PT_VALUE), err, TRUE); |
| return; |
| |
| bad: |
| zonecfg_free_rctl_value_list(rctlvaltab); |
| } |
| |
| static void |
| add_property(cmd_t *cmd) |
| { |
| char *prop_id; |
| int err, res_type, prop_type; |
| property_value_ptr_t pp; |
| list_property_ptr_t l; |
| |
| res_type = resource_scope; |
| prop_type = cmd->cmd_prop_name[0]; |
| if (res_type == RT_UNKNOWN || prop_type == PT_UNKNOWN) { |
| long_usage(CMD_ADD, TRUE); |
| return; |
| } |
| |
| if (cmd->cmd_prop_nv_pairs != 1) { |
| long_usage(CMD_ADD, TRUE); |
| return; |
| } |
| |
| if (initialize(TRUE) != Z_OK) |
| return; |
| |
| switch (res_type) { |
| case RT_FS: |
| if (prop_type != PT_OPTIONS) { |
| zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE, |
| TRUE); |
| long_usage(CMD_ADD, TRUE); |
| usage(FALSE, HELP_PROPS); |
| return; |
| } |
| pp = cmd->cmd_property_ptr[0]; |
| if (pp->pv_type != PROP_VAL_SIMPLE && |
| pp->pv_type != PROP_VAL_LIST) { |
| zerr(gettext("A %s or %s value was expected here."), |
| pvt_to_str(PROP_VAL_SIMPLE), |
| pvt_to_str(PROP_VAL_LIST)); |
| saw_error = TRUE; |
| return; |
| } |
| if (pp->pv_type == PROP_VAL_SIMPLE) { |
| if (pp->pv_simple == NULL) { |
| long_usage(CMD_ADD, TRUE); |
| return; |
| } |
| prop_id = pp->pv_simple; |
| err = zonecfg_add_fs_option(&in_progress_fstab, |
| prop_id); |
| if (err != Z_OK) |
| zone_perror(pt_to_str(prop_type), err, TRUE); |
| } else { |
| list_property_ptr_t list; |
| |
| for (list = pp->pv_list; list != NULL; |
| list = list->lp_next) { |
| prop_id = list->lp_simple; |
| if (prop_id == NULL) |
| break; |
| err = zonecfg_add_fs_option( |
| &in_progress_fstab, prop_id); |
| if (err != Z_OK) |
| zone_perror(pt_to_str(prop_type), err, |
| TRUE); |
| } |
| } |
| return; |
| case RT_RCTL: |
| if (prop_type != PT_VALUE) { |
| zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE, |
| TRUE); |
| long_usage(CMD_ADD, TRUE); |
| usage(FALSE, HELP_PROPS); |
| return; |
| } |
| pp = cmd->cmd_property_ptr[0]; |
| if (pp->pv_type != PROP_VAL_COMPLEX && |
| pp->pv_type != PROP_VAL_LIST) { |
| zerr(gettext("A %s or %s value was expected here."), |
| pvt_to_str(PROP_VAL_COMPLEX), |
| pvt_to_str(PROP_VAL_LIST)); |
| saw_error = TRUE; |
| return; |
| } |
| if (pp->pv_type == PROP_VAL_COMPLEX) { |
| do_complex_rctl_val(pp->pv_complex); |
| return; |
| } |
| for (l = pp->pv_list; l != NULL; l = l->lp_next) |
| do_complex_rctl_val(l->lp_complex); |
| return; |
| default: |
| zone_perror(rt_to_str(res_type), Z_NO_RESOURCE_TYPE, TRUE); |
| long_usage(CMD_ADD, TRUE); |
| usage(FALSE, HELP_RESOURCES); |
| return; |
| } |
| } |
| |
| static boolean_t |
| gz_invalid_resource(int type) |
| { |
| return (global_zone && (type == RT_FS || type == RT_IPD || |
| type == RT_NET || type == RT_DEVICE || type == RT_ATTR || |
| type == RT_DATASET)); |
| } |
| |
| static boolean_t |
| gz_invalid_rt_property(int type) |
| { |
| return (global_zone && (type == RT_ZONENAME || type == RT_ZONEPATH || |
| type == RT_AUTOBOOT || type == RT_LIMITPRIV || |
| type == RT_BOOTARGS || type == RT_BRAND || type == RT_SCHED || |
| type == RT_IPTYPE)); |
| } |
| |
| static boolean_t |
| gz_invalid_property(int type) |
| { |
| return (global_zone && (type == PT_ZONENAME || type == PT_ZONEPATH || |
| type == PT_AUTOBOOT || type == PT_LIMITPRIV || |
| type == PT_BOOTARGS || type == PT_BRAND || type == PT_SCHED || |
| type == PT_IPTYPE)); |
| } |
| |
| void |
| add_func(cmd_t *cmd) |
| { |
| int arg; |
| |
| assert(cmd != NULL); |
| |
| optind = 0; |
| if ((arg = getopt(cmd->cmd_argc, cmd->cmd_argv, "?")) != EOF) { |
| switch (arg) { |
| case '?': |
| longer_usage(CMD_ADD); |
| return; |
| default: |
| short_usage(CMD_ADD); |
| return; |
| } |
| } |
| if (optind != cmd->cmd_argc) { |
| short_usage(CMD_ADD); |
| return; |
| } |
| |
| if (zone_is_read_only(CMD_ADD)) |
| return; |
| |
| if (initialize(TRUE) != Z_OK) |
| return; |
| if (global_scope) { |
| if (gz_invalid_resource(cmd->cmd_res_type)) { |
| zerr(gettext("Cannot add a %s resource to the " |
| "global zone."), rt_to_str(cmd->cmd_res_type)); |
| saw_error = TRUE; |
| return; |
| } |
| |
| global_scope = FALSE; |
| resource_scope = cmd->cmd_res_type; |
| end_op = CMD_ADD; |
| add_resource(cmd); |
| } else |
| add_property(cmd); |
| } |
| |
| /* |
| * This routine has an unusual implementation, because it tries very |
| * hard to succeed in the face of a variety of failure modes. |
| * The most common and most vexing occurs when the index file and |
| * the /etc/zones/<zonename.xml> file are not both present. In |
| * this case, delete must eradicate as much of the zone state as is left |
| * so that the user can later create a new zone with the same name. |
| */ |
| void |
| delete_func(cmd_t *cmd) |
| { |
| int err, arg, answer; |
| char line[ZONENAME_MAX + 128]; /* enough to ask a question */ |
| bool force = FALSE; |
| |
| optind = 0; |
| while ((arg = getopt(cmd->cmd_argc, cmd->cmd_argv, "?F")) != EOF) { |
| switch (arg) { |
| case '?': |
| longer_usage(CMD_DELETE); |
| return; |
| case 'F': |
| force = TRUE; |
| break; |
| default: |
| short_usage(CMD_DELETE); |
| return; |
| } |
| } |
| if (optind != cmd->cmd_argc) { |
| short_usage(CMD_DELETE); |
| return; |
| } |
| |
| if (zone_is_read_only(CMD_DELETE)) |
| return; |
| |
| if (!force) { |
| /* |
| * Initialize sets up the global called "handle" and warns the |
| * user if the zone is not configured. In force mode, we don't |
| * trust that evaluation, and hence skip it. (We don't need the |
| * handle to be loaded anyway, since zonecfg_destroy is done by |
| * zonename). However, we also have to take care to emulate the |
| * messages spit out by initialize; see below. |
| */ |
| if (initialize(TRUE) != Z_OK) |
| return; |
| |
| (void) snprintf(line, sizeof (line), |
| gettext("Are you sure you want to delete zone %s"), zone); |
| if ((answer = ask_yesno(FALSE, line)) == -1) { |
| zerr(gettext("Input not from terminal and -F not " |
| "specified:\n%s command ignored, exiting."), |
| cmd_to_str(CMD_DELETE)); |
| exit(Z_ERR); |
| } |
| if (answer != 1) |
| return; |
| } |
| |
| if ((err = zonecfg_destroy(zone, force)) != Z_OK) { |
| if ((err == Z_BAD_ZONE_STATE) && !force) { |
| zerr(gettext("Zone %s not in %s state; %s not " |
| "allowed. Use -F to force %s."), |
| zone, zone_state_str(ZONE_STATE_CONFIGURED), |
| cmd_to_str(CMD_DELETE), cmd_to_str(CMD_DELETE)); |
| } else { |
| zone_perror(zone, err, TRUE); |
| } |
| } |
| need_to_commit = FALSE; |
| |
| /* |
| * Emulate initialize's messaging; if there wasn't a valid handle to |
| * begin with, then user had typed delete (or delete -F) multiple |
| * times. So we emit a message. |
| * |
| * We only do this in the 'force' case because normally, initialize() |
| * takes care of this for us. |
| */ |
| if (force && zonecfg_check_handle(handle) != Z_OK && interactive_mode) |
| (void) printf(gettext("Use '%s' to begin " |
| "configuring a new zone.\n"), cmd_to_str(CMD_CREATE)); |
| |
| /* |
| * Time for a new handle: finish the old one off first |
| * then get a new one properly to avoid leaks. |
| */ |
| if (got_handle) { |
| zonecfg_fini_handle(handle); |
| if ((handle = zonecfg_init_handle()) == NULL) { |
| zone_perror(execname, Z_NOMEM, TRUE); |
| exit(Z_ERR); |
| } |
| if ((err = zonecfg_get_handle(zone, handle)) != Z_OK) { |
| /* If there was no zone before, that's OK */ |
| if (err != Z_NO_ZONE) |
| zone_perror(zone, err, TRUE); |
| got_handle = FALSE; |
| } |
| } |
| } |
| |
| static int |
| fill_in_fstab(cmd_t *cmd, struct zone_fstab *fstab, bool fill_in_only) |
| { |
| int err, i; |
| property_value_ptr_t pp; |
| |
| if ((err = initialize(TRUE)) != Z_OK) |
| return (err); |
| |
| bzero(fstab, sizeof (*fstab)); |
| for (i = 0; i < cmd->cmd_prop_nv_pairs; i++) { |
| pp = cmd->cmd_property_ptr[i]; |
| if (pp->pv_type != PROP_VAL_SIMPLE || pp->pv_simple == NULL) { |
| zerr(gettext("A simple value was expected here.")); |
| saw_error = TRUE; |
| return (Z_INSUFFICIENT_SPEC); |
| } |
| switch (cmd->cmd_prop_name[i]) { |
| case PT_DIR: |
| (void) strlcpy(fstab->zone_fs_dir, pp->pv_simple, |
| sizeof (fstab->zone_fs_dir)); |
| break; |
| case PT_SPECIAL: |
| (void) strlcpy(fstab->zone_fs_special, pp->pv_simple, |
| sizeof (fstab->zone_fs_special)); |
| break; |
| case PT_RAW: |
| (void) strlcpy(fstab->zone_fs_raw, pp->pv_simple, |
| sizeof (fstab->zone_fs_raw)); |
| break; |
| case PT_TYPE: |
| (void) strlcpy(fstab->zone_fs_type, pp->pv_simple, |
| sizeof (fstab->zone_fs_type)); |
| break; |
| default: |
| zone_perror(pt_to_str(cmd->cmd_prop_name[i]), |
| Z_NO_PROPERTY_TYPE, TRUE); |
| return (Z_INSUFFICIENT_SPEC); |
| } |
| } |
| if (fill_in_only) |
| return (Z_OK); |
| return (zonecfg_lookup_filesystem(handle, fstab)); |
| } |
| |
| static int |
| fill_in_ipdtab(cmd_t *cmd, struct zone_fstab *ipdtab, bool fill_in_only) |
| { |
| int err, i; |
| property_value_ptr_t pp; |
| |
| if ((err = initialize(TRUE)) != Z_OK) |
| return (err); |
| |
| bzero(ipdtab, sizeof (*ipdtab)); |
| for (i = 0; i < cmd->cmd_prop_nv_pairs; i++) { |
| pp = cmd->cmd_property_ptr[i]; |
| if (pp->pv_type != PROP_VAL_SIMPLE || pp->pv_simple == NULL) { |
| zerr(gettext("A simple value was expected here.")); |
| saw_error = TRUE; |
| return (Z_INSUFFICIENT_SPEC); |
| } |
| switch (cmd->cmd_prop_name[i]) { |
| case PT_DIR: |
| (void) strlcpy(ipdtab->zone_fs_dir, pp->pv_simple, |
| sizeof (ipdtab->zone_fs_dir)); |
| break; |
| default: |
| zone_perror(pt_to_str(cmd->cmd_prop_name[i]), |
| Z_NO_PROPERTY_TYPE, TRUE); |
| return (Z_INSUFFICIENT_SPEC); |
| } |
| } |
| if (fill_in_only) |
| return (Z_OK); |
| return (zonecfg_lookup_ipd(handle, ipdtab)); |
| } |
| |
| static int |
| fill_in_nwiftab(cmd_t *cmd, struct zone_nwiftab *nwiftab, bool fill_in_only) |
| { |
| int err, i; |
| property_value_ptr_t pp; |
| |
| if ((err = initialize(TRUE)) != Z_OK) |
| return (err); |
| |
| bzero(nwiftab, sizeof (*nwiftab)); |
| for (i = 0; i < cmd->cmd_prop_nv_pairs; i++) { |
| pp = cmd->cmd_property_ptr[i]; |
| if (pp->pv_type != PROP_VAL_SIMPLE || pp->pv_simple == NULL) { |
| zerr(gettext("A simple value was expected here.")); |
| saw_error = TRUE; |
| return (Z_INSUFFICIENT_SPEC); |
| } |
| switch (cmd->cmd_prop_name[i]) { |
| case PT_ADDRESS: |
| (void) strlcpy(nwiftab->zone_nwif_address, |
| pp->pv_simple, sizeof (nwiftab->zone_nwif_address)); |
| break; |
| case PT_PHYSICAL: |
| (void) strlcpy(nwiftab->zone_nwif_physical, |
| pp->pv_simple, |
| sizeof (nwiftab->zone_nwif_physical)); |
| break; |
| default: |
| zone_perror(pt_to_str(cmd->cmd_prop_name[i]), |
| Z_NO_PROPERTY_TYPE, TRUE); |
| return (Z_INSUFFICIENT_SPEC); |
| } |
| } |
| if (fill_in_only) |
| return (Z_OK); |
| err = zonecfg_lookup_nwif(handle, nwiftab); |
| return (err); |
| } |
| |
| static int |
| fill_in_devtab(cmd_t *cmd, struct zone_devtab *devtab, bool fill_in_only) |
| { |
| int err, i; |
| property_value_ptr_t pp; |
| |
| if ((err = initialize(TRUE)) != Z_OK) |
| return (err); |
| |
| bzero(devtab, sizeof (*devtab)); |
| for (i = 0; i < cmd->cmd_prop_nv_pairs; i++) { |
| pp = cmd->cmd_property_ptr[i]; |
| if (pp->pv_type != PROP_VAL_SIMPLE || pp->pv_simple == NULL) { |
| zerr(gettext("A simple value was expected here.")); |
| saw_error = TRUE; |
| return (Z_INSUFFICIENT_SPEC); |
| } |
| switch (cmd->cmd_prop_name[i]) { |
| case PT_MATCH: |
| (void) strlcpy(devtab->zone_dev_match, pp->pv_simple, |
| sizeof (devtab->zone_dev_match)); |
| break; |
| default: |
| zone_perror(pt_to_str(cmd->cmd_prop_name[i]), |
| Z_NO_PROPERTY_TYPE, TRUE); |
| return (Z_INSUFFICIENT_SPEC); |
| } |
| } |
| if (fill_in_only) |
| return (Z_OK); |
| err = zonecfg_lookup_dev(handle, devtab); |
| return (err); |
| } |
| |
| static int |
| fill_in_rctltab(cmd_t *cmd, struct zone_rctltab *rctltab, bool fill_in_only) |
| { |
| int err, i; |
| property_value_ptr_t pp; |
| |
| if ((err = initialize(TRUE)) != Z_OK) |
| return (err); |
| |
| bzero(rctltab, sizeof (*rctltab)); |
| for (i = 0; i < cmd->cmd_prop_nv_pairs; i++) { |
| pp = cmd->cmd_property_ptr[i]; |
| if (pp->pv_type != PROP_VAL_SIMPLE || pp->pv_simple == NULL) { |
| zerr(gettext("A simple value was expected here.")); |
| saw_error = TRUE; |
| return (Z_INSUFFICIENT_SPEC); |
| } |
| switch (cmd->cmd_prop_name[i]) { |
| case PT_NAME: |
| (void) strlcpy(rctltab->zone_rctl_name, pp->pv_simple, |
| sizeof (rctltab->zone_rctl_name)); |
| break; |
| default: |
| zone_perror(pt_to_str(cmd->cmd_prop_name[i]), |
| Z_NO_PROPERTY_TYPE, TRUE); |
| return (Z_INSUFFICIENT_SPEC); |
| } |
| } |
| if (fill_in_only) |
| return (Z_OK); |
| err = zonecfg_lookup_rctl(handle, rctltab); |
| return (err); |
| } |
| |
| static int |
| fill_in_attrtab(cmd_t *cmd, struct zone_attrtab *attrtab, bool fill_in_only) |
| { |
| int err, i; |
| property_value_ptr_t pp; |
| |
| if ((err = initialize(TRUE)) != Z_OK) |
| return (err); |
| |
| bzero(attrtab, sizeof (*attrtab)); |
| for (i = 0; i < cmd->cmd_prop_nv_pairs; i++) { |
| pp = cmd->cmd_property_ptr[i]; |
| if (pp->pv_type != PROP_VAL_SIMPLE || pp->pv_simple == NULL) { |
| zerr(gettext("A simple value was expected here.")); |
| saw_error = TRUE; |
| return (Z_INSUFFICIENT_SPEC); |
| } |
| switch (cmd->cmd_prop_name[i]) { |
| case PT_NAME: |
| (void) strlcpy(attrtab->zone_attr_name, pp->pv_simple, |
| sizeof (attrtab->zone_attr_name)); |
| break; |
| case PT_TYPE: |
| (void) strlcpy(attrtab->zone_attr_type, pp->pv_simple, |
| sizeof (attrtab->zone_attr_type)); |
| break; |
| case PT_VALUE: |
| (void) strlcpy(attrtab->zone_attr_value, pp->pv_simple, |
| sizeof (attrtab->zone_attr_value)); |
| break; |
| default: |
| zone_perror(pt_to_str(cmd->cmd_prop_name[i]), |
| Z_NO_PROPERTY_TYPE, TRUE); |
| return (Z_INSUFFICIENT_SPEC); |
| } |
| } |
| if (fill_in_only) |
| return (Z_OK); |
| err = zonecfg_lookup_attr(handle, attrtab); |
| return (err); |
| } |
| |
| static int |
| fill_in_dstab(cmd_t *cmd, struct zone_dstab *dstab, bool fill_in_only) |
| { |
| int err, i; |
| property_value_ptr_t pp; |
| |
| if ((err = initialize(TRUE)) != Z_OK) |
| return (err); |
| |
| dstab->zone_dataset_name[0] = '\0'; |
| for (i = 0; i < cmd->cmd_prop_nv_pairs; i++) { |
| pp = cmd->cmd_property_ptr[i]; |
| if (pp->pv_type != PROP_VAL_SIMPLE || pp->pv_simple == NULL) { |
| zerr(gettext("A simple value was expected here.")); |
| saw_error = TRUE; |
| return (Z_INSUFFICIENT_SPEC); |
| } |
| switch (cmd->cmd_prop_name[i]) { |
| case PT_NAME: |
| (void) strlcpy(dstab->zone_dataset_name, pp->pv_simple, |
| sizeof (dstab->zone_dataset_name)); |
| break; |
| default: |
| zone_perror(pt_to_str(cmd->cmd_prop_name[i]), |
| Z_NO_PROPERTY_TYPE, TRUE); |
| return (Z_INSUFFICIENT_SPEC); |
| } |
| } |
| if (fill_in_only) |
| return (Z_OK); |
| return (zonecfg_lookup_ds(handle, dstab)); |
| } |
| |
| static void |
| remove_aliased_rctl(int type, char *name) |
| { |
| int err; |
| uint64_t tmp; |
| |
| if ((err = zonecfg_get_aliased_rctl(handle, name, &tmp)) != Z_OK) { |
| zerr("%s %s: %s", cmd_to_str(CMD_CLEAR), pt_to_str(type), |
| zonecfg_strerror(err)); |
| saw_error = TRUE; |
| return; |
| } |
| if ((err = zonecfg_rm_aliased_rctl(handle, name)) != Z_OK) { |
| zerr("%s %s: %s", cmd_to_str(CMD_CLEAR), pt_to_str(type), |
| zonecfg_strerror(err)); |
| saw_error = TRUE; |
| } else { |
| need_to_commit = TRUE; |
| } |
| } |
| |
| static boolean_t |
| prompt_remove_resource(cmd_t *cmd, char *rsrc) |
| { |
| int num; |
| int answer; |
| int arg; |
| boolean_t force = B_FALSE; |
| char prompt[128]; |
| |
| optind = 0; |
| while ((arg = getopt(cmd->cmd_argc, cmd->cmd_argv, "F")) != EOF) { |
| switch (arg) { |
| case 'F': |
| force = B_TRUE; |
| break; |
| default: |
| return (B_FALSE); |
| } |
| } |
| |
| num = zonecfg_num_resources(handle, rsrc); |
| |
| if (num == 0) { |
| z_cmd_rt_perror(CMD_REMOVE, cmd->cmd_res_type, Z_NO_ENTRY, |
| TRUE); |
| return (B_FALSE); |
| } |
| if (num > 1 && !force) { |
| if (!interactive_mode) { |
| zerr(gettext("There are multiple instances of this " |
| "resource. Either qualify the resource to\n" |
| "remove a single instance or use the -F option to " |
| "remove all instances.")); |
| saw_error = TRUE; |
| return (B_FALSE); |
| } |
| (void) snprintf(prompt, sizeof (prompt), gettext( |
| "Are you sure you want to remove ALL '%s' resources"), |
| rsrc); |
| answer = ask_yesno(FALSE, prompt); |
| if (answer == -1) { |
| zerr(gettext("Resource incomplete.")); |
| return (B_FALSE); |
| } |
| if (answer != 1) |
| return (B_FALSE); |
| } |
| return |