blob: 443c0895b1318d3469093fdad49a00b88eeb5b3e [file] [log] [blame]
/*
* 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