zonecfg.c revision 9e5186559a0d5066276f956c3177577225dcc9b0
/*
* 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
* 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 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* configurations. The lexer (see zonecfg_lex.l) builds up tokens, which
* the grammar (see zonecfg_grammar.y) builds up into commands, some of
* comments near the end of zonecfg_grammar.y for how the data structures
* which keep track of these resources and properties are built up.
*
* 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
*/
#include <sys/sysmacros.h>
#include <errno.h>
#include <strings.h>
#include <unistd.h>
#include <ctype.h>
#include <stdlib.h>
#include <assert.h>
#include <zone.h>
#include <netdb.h>
#include <locale.h>
#include <libintl.h>
#include <alloca.h>
#include <regex.h>
#include <signal.h>
#include <libtecla.h>
#include <libzfs.h>
#include <libzonecfg.h>
#include "zonecfg.h"
#if !defined(TEXT_DOMAIN) /* should be defined by cc -D */
#endif
struct help {
char *cmd_name;
char *short_usage;
};
extern int yyparse(void);
extern int lex_lineno;
#define MAX_LINE_LEN 1024
#define MAX_CMD_HIST 1024
/*
* 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_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 <resource-type> { <property-name>=<property-" \
"value> }\n\t(global scope)\nremove <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"
{ 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",
};
/* 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",
};
/* These *must* match the order of the PT_ 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 and set) don't need a space here because
* they have their own _cmds[] lists below.
*/
static const char *global_scope_cmds[] = {
"add",
"commit",
"create",
"delete",
"exit",
"export",
"help",
"info",
"remove ",
"revert",
"select",
"set",
"verify",
};
static const char *add_cmds[] = {
"add fs",
"add inherit-pkg-dir",
"add net",
"add device",
"add rctl",
"add attr",
"add dataset",
};
static const char *select_cmds[] = {
"select fs ",
"select inherit-pkg-dir ",
"select net ",
"select device ",
"select rctl ",
"select attr ",
"select dataset ",
};
static const char *set_cmds[] = {
"set zonename=",
"set zonepath=",
"set autoboot=",
"set pool=",
};
static const char *fs_res_scope_cmds[] = {
"add options ",
"cancel",
"end",
"exit",
"help",
"info",
"set dir=",
"set raw=",
"set special=",
"set type=",
};
static const char *net_res_scope_cmds[] = {
"cancel",
"end",
"exit",
"help",
"info",
"set address=",
"set physical=",
};
static const char *ipd_res_scope_cmds[] = {
"cancel",
"end",
"exit",
"help",
"info",
"set dir=",
};
static const char *device_res_scope_cmds[] = {
"cancel",
"end",
"exit",
"help",
"info",
"set match=",
};
static const char *attr_res_scope_cmds[] = {
"cancel",
"end",
"exit",
"help",
"info",
"set name=",
"set type=",
"set value=",
};
static const char *rctl_res_scope_cmds[] = {
"add value ",
"cancel",
"end",
"exit",
"help",
"info",
"set name=",
};
static const char *dataset_res_scope_cmds[] = {
"cancel",
"end",
"exit",
"help",
"info",
"set name=",
};
/* 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];
/* 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() */
/* 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 in main(), checked in multiple places */
static bool read_only_mode;
static int resource_scope; /* should be in the RT_ list from zonecfg.h */
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().
*/
/* Functions begin here */
static bool
{
if (word_end <= 0)
return (TRUE);
}
static int
int word_end)
{
int i, err;
if (err != 0)
return (err);
}
}
return (0);
}
static
/* ARGSUSED */
{
if (global_scope) {
/*
* enough characters to distinguish from other prefixes (MAX)
* but only check MIN(what we have, what we're checking).
*/
}
switch (resource_scope) {
case RT_FS:
case RT_IPD:
case RT_NET:
case RT_DEVICE:
case RT_RCTL:
case RT_ATTR:
case RT_DATASET:
}
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)
{
}
void
{
int i;
for (i = 0; i < MAX_EQ_PROP_PAIRS; i++)
case PROP_VAL_SIMPLE:
break;
case PROP_VAL_COMPLEX:
break;
case PROP_VAL_LIST:
break;
}
}
}
alloc_complex(void)
{
}
void
{
return;
}
alloc_list(void)
{
}
void
{
return;
}
void
{
return;
}
static struct zone_rctlvaltab *
alloc_rctlvaltab(void)
{
}
static char *
{
}
static char *
{
return (prop_types[prop_type]);
}
static char *
pvt_to_str(int pv_type)
{
return (prop_val_types[pv_type]);
}
static char *
cmd_to_str(int cmd_num)
{
}
/*
* 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 *
{
switch (cmd_num) {
case CMD_HELP:
return (gettext("Prints help message."));
case CMD_CREATE:
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."),
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. Note that the curly\n\tbraces "
"('{', '}') mean one or more of whatever "
"is between them."));
case CMD_SELECT:
gettext("Selects a resource to modify. "
"Resource modification is completed\n\twith the "
"must uniquely\n\tidentify a resource. Note that "
"the curly braces ('{', '}') mean one\n\tor more "
"of whatever is between them."),
return (line);
case CMD_SET:
return (gettext("Sets 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:
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 "
"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:
"specification."));
case CMD_END:
"specification."));
}
/* NOTREACHED */
return (NULL);
}
/*
* Called with verbose TRUE when help is explicitly requested, FALSE for
* unexpected errors.
*/
void
{
bool need_to_close = FALSE;
char *pager;
int i;
/* don't page error output */
if (verbose && interactive_mode) {
}
}
"following:\n"));
"command by typing '%s <command-name>.'\n"),
}
if (flags & HELP_RES_SCOPE) {
switch (resource_scope) {
case RT_FS:
"used to configure a file-system.\n"),
gettext("<file-system options>"));
"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:
"used to configure a directory\ninherited from the "
"global zone into a non-global zone in read-only "
break;
case RT_NET:
"used to configure a network interface.\n"),
"details of the <interface> string.\n"));
break;
case RT_DEVICE:
"used to configure a device node.\n"),
break;
case RT_RCTL:
"used to configure a resource control.\n"),
break;
case RT_ATTR:
"used to configure a generic attribute.\n"),
break;
case RT_DATASET:
"used to export ZFS datasets.\n"),
break;
}
"can:\n"));
gettext("(to conclude this operation)"));
gettext("(to cancel this operation)"));
gettext("(to exit the zonecfg utility)"));
}
if (flags & HELP_USAGE) {
execname);
}
if (flags & HELP_SUBCMDS) {
for (i = 0; i <= CMD_MAX; i++) {
if (verbose)
}
}
if (flags & HELP_SYNTAX) {
if (!verbose)
"'%s' and anything starting with '%s')\n"), "global",
"SUNW");
gettext("\tName must be less than %d characters.\n"),
if (verbose)
}
if (flags & HELP_NETADDR) {
gettext("\t<IPv4-address>[/<IPv4-prefix-length>] |\n"));
gettext("\t\t<IPv6-address>/<IPv6-prefix-length> |\n"));
gettext("\t\t<hostname>[/<IPv4-prefix-length>]\n"));
"IPv6 address syntax.\n"));
gettext("<IPv6-prefix-length> := [0-128]\n"));
gettext("<hostname> := [A-Za-z0-9][A-Za-z0-9-.]*\n"));
}
if (flags & HELP_RESOURCES) {
}
if (flags & HELP_PROPS) {
"property types ...:\n"));
}
if (need_to_close)
}
/* PRINTFLIKE1 */
static void
{
static int last_lineno;
/* lex_lineno has already been incremented in the lexer; compensate */
lex_lineno - 1);
else
}
}
static void
{
if (set_saw)
}
/*
* 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
{
if (set_saw)
}
/* returns Z_OK if successful, Z_foo from <libzonecfg.h> otherwise */
static int
initialize(bool handle_expected)
{
int err;
got_handle = TRUE;
} else {
"configuring a new zone.\n"),
return (err);
}
}
return (Z_OK);
}
static bool
{
int err;
/* all states are greater than "non-existent" */
return (B_FALSE);
}
}
/*
* 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) {
gettext("syntax error on line %d\n"),
lex_lineno - 1);
else
gettext("syntax error on line %d of %s\n"),
}
}
/*
* 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
{
if (set_saw)
}
/*
* longer_usage() is for 'help foo' and 'foo -?': call long_usage() and also
* any extra usage() flags as appropriate for whatever command.
*/
void
{
(void) printf("\n");
}
}
/*
* scope_usage() is simply used when a command is called from the wrong scope.
*/
static void
{
}
/*
* On input, TRUE => yes, FALSE => no.
* On return, TRUE => 1, FALSE => no, could not ask => -1.
*/
static int
{
if (!ok_to_prompt) {
return (-1);
}
for (;;) {
return (-1);
return (-1);
if (line[0] == '\n')
return (default_answer ? 1 : 0);
return (1);
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
{
}
return (Z_OK);
if (state_atleast(ZONE_STATE_INSTALLED)) {
return (Z_ERR);
}
if (force) {
zone);
return (Z_OK);
}
"specified:\n%s command ignored, exiting."),
}
}
static bool
zone_is_read_only(int cmd_num)
{
zone);
return (TRUE);
}
if (read_only_mode) {
return (TRUE);
}
return (FALSE);
}
/*
* Create a new configuration.
*/
void
{
char zone_template[ZONENAME_MAX];
char attach_path[MAXPATHLEN];
/* This is the default if no arguments are given. */
optind = 0;
switch (arg) {
case '?':
if (optopt == '?')
else
return;
case 'a':
sizeof (attach_path));
break;
case 'b':
sizeof (zone_template));
break;
case 'F':
break;
case 't':
sizeof (zone_template));
break;
default:
return;
}
}
return;
}
if (zone_is_read_only(CMD_CREATE))
return;
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 (attach)
else
"detached zone\n"));
"earlier release of the operating system\n"));
else
return;
}
got_handle = TRUE;
}
/*
* This malloc()'s memory, which must be freed by the caller.
*/
static char *
{
char *outstr;
}
return (outstr);
}
return (outstr);
}
static void
{
char *quote_str;
return;
}
void
{
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_rctlvaltab *valptr;
bool need_to_close = FALSE;
outfile[0] = '\0';
optind = 0;
switch (arg) {
case '?':
if (optopt == '?')
else
return;
case 'f':
break;
default:
return;
}
}
return;
}
} else {
goto done;
}
}
goto done;
goto done;
}
}
(void) zonecfg_endipdent(handle);
goto done;
}
/*
* Simple property values with embedded equal signs
* need to be quoted to prevent the lexer from
* mis-parsing them as complex name=value pairs.
*/
else
}
}
(void) zonecfg_endfsent(handle);
goto done;
}
}
(void) zonecfg_endnwifent(handle);
goto done;
}
}
(void) zonecfg_enddevent(handle);
goto done;
}
}
}
(void) zonecfg_endrctlent(handle);
goto done;
}
}
(void) zonecfg_endattrent(handle);
goto done;
}
}
(void) zonecfg_enddsent(handle);
done:
if (need_to_close)
}
void
{
optind = 0;
switch (arg) {
case '?':
return;
case 'F':
force_exit = TRUE;
break;
default:
return;
}
}
return;
}
if (global_scope || force_exit) {
time_to_exit = TRUE;
return;
}
if (answer == -1) {
"not from terminal and -F not specified:\n%s command "
} else if (answer == 1) {
time_to_exit = TRUE;
}
/* (answer == 0) => just return */
}
static int
validate_zonepath_syntax(char *path)
{
if (path[0] != '/') {
return (Z_ERR);
}
return (Z_ERR);
}
return (Z_OK);
}
static void
{
int type;
goto bad;
}
switch (type) {
case RT_FS:
return;
case RT_IPD:
if (state_atleast(ZONE_STATE_INSTALLED)) {
goto bad;
}
return;
case RT_NET:
return;
case RT_DEVICE:
return;
case RT_RCTL:
return;
case RT_ATTR:
return;
case RT_DATASET:
return;
default:
}
bad:
global_scope = TRUE;
end_op = -1;
}
static void
{
struct zone_rctlvaltab *rctlvaltab;
int err;
}
case PT_PRIV:
if (seen_priv) {
goto bad;
}
sizeof (rctlvaltab->zone_rctlval_priv));
break;
case PT_LIMIT:
if (seen_limit) {
goto bad;
}
sizeof (rctlvaltab->zone_rctlval_limit));
seen_limit = TRUE;
break;
case PT_ACTION:
if (seen_action) {
goto bad;
}
sizeof (rctlvaltab->zone_rctlval_action));
seen_action = TRUE;
break;
default:
return;
}
}
if (!seen_priv)
if (!seen_limit)
if (!seen_action)
goto bad;
/*
* 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.
*/
goto bad;
}
return;
bad:
}
static void
{
char *prop_id;
return;
}
return;
}
return;
switch (res_type) {
case RT_FS:
if (prop_type != PT_OPTIONS) {
TRUE);
return;
}
return;
}
return;
}
prop_id);
} else {
break;
TRUE);
}
}
return;
case RT_RCTL:
TRUE);
return;
}
return;
}
return;
}
return;
default:
return;
}
}
void
{
int arg;
optind = 0;
switch (arg) {
case '?':
return;
default:
return;
}
}
return;
}
if (zone_is_read_only(CMD_ADD))
return;
return;
if (global_scope) {
} else
}
/*
* 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
{
optind = 0;
switch (arg) {
case '?':
return;
case 'F':
break;
default:
return;
}
}
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.
*/
return;
"specified:\n%s command ignored, exiting."),
}
if (answer != 1)
return;
}
"allowed. Use -F to force %s."),
} else {
}
}
/*
* 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.
*/
/*
* Time for a new handle: finish the old one off first
* then get a new one properly to avoid leaks.
*/
if (got_handle) {
}
/* If there was no zone before, that's OK */
got_handle = FALSE;
}
}
}
static int
{
int err, i;
return (err);
for (i = 0; i < cmd->cmd_prop_nv_pairs; i++) {
return (Z_INSUFFICIENT_SPEC);
}
switch (cmd->cmd_prop_name[i]) {
case PT_DIR:
sizeof (fstab->zone_fs_dir));
break;
case PT_SPECIAL:
sizeof (fstab->zone_fs_special));
break;
case PT_RAW:
sizeof (fstab->zone_fs_raw));
break;
case PT_TYPE:
sizeof (fstab->zone_fs_type));
break;
default:
return (Z_INSUFFICIENT_SPEC);
}
}
if (fill_in_only)
return (Z_OK);
}
static int
{
int err, i;
return (err);
for (i = 0; i < cmd->cmd_prop_nv_pairs; i++) {
return (Z_INSUFFICIENT_SPEC);
}
switch (cmd->cmd_prop_name[i]) {
case PT_DIR:
sizeof (ipdtab->zone_fs_dir));
break;
default:
return (Z_INSUFFICIENT_SPEC);
}
}
if (fill_in_only)
return (Z_OK);
}
static int
{
int err, i;
return (err);
for (i = 0; i < cmd->cmd_prop_nv_pairs; i++) {
return (Z_INSUFFICIENT_SPEC);
}
switch (cmd->cmd_prop_name[i]) {
case PT_ADDRESS:
break;
case PT_PHYSICAL:
sizeof (nwiftab->zone_nwif_physical));
break;
default:
return (Z_INSUFFICIENT_SPEC);
}
}
if (fill_in_only)
return (Z_OK);
return (err);
}
static int
{
int err, i;
return (err);
for (i = 0; i < cmd->cmd_prop_nv_pairs; i++) {
return (Z_INSUFFICIENT_SPEC);
}
switch (cmd->cmd_prop_name[i]) {
case PT_MATCH:
sizeof (devtab->zone_dev_match));
break;
default:
return (Z_INSUFFICIENT_SPEC);
}
}
if (fill_in_only)
return (Z_OK);
return (err);
}
static int
{
int err, i;
return (err);
for (i = 0; i < cmd->cmd_prop_nv_pairs; i++) {
return (Z_INSUFFICIENT_SPEC);
}
switch (cmd->cmd_prop_name[i]) {
case PT_NAME:
sizeof (rctltab->zone_rctl_name));
break;
default:
return (Z_INSUFFICIENT_SPEC);
}
}
if (fill_in_only)
return (Z_OK);
return (err);
}
static int
{
int err, i;
return (err);
for (i = 0; i < cmd->cmd_prop_nv_pairs; i++) {
return (Z_INSUFFICIENT_SPEC);
}
switch (cmd->cmd_prop_name[i]) {
case PT_NAME:
sizeof (attrtab->zone_attr_name));
break;
case PT_TYPE:
sizeof (attrtab->zone_attr_type));
break;
case PT_VALUE:
sizeof (attrtab->zone_attr_value));
break;
default:
return (Z_INSUFFICIENT_SPEC);
}
}
if (fill_in_only)
return (Z_OK);
return (err);
}
static int
{
int err, i;
return (err);
for (i = 0; i < cmd->cmd_prop_nv_pairs; i++) {
return (Z_INSUFFICIENT_SPEC);
}
switch (cmd->cmd_prop_name[i]) {
case PT_NAME:
sizeof (dstab->zone_dataset_name));
break;
default:
return (Z_INSUFFICIENT_SPEC);
}
}
if (fill_in_only)
return (Z_OK);
}
static void
{
struct zone_fstab fstab;
struct zone_nwiftab nwiftab;
struct zone_devtab devtab;
struct zone_attrtab attrtab;
struct zone_rctltab rctltab;
struct zone_dstab dstab;
return;
}
return;
switch (type) {
case RT_FS:
return;
}
else
return;
case RT_IPD:
if (state_atleast(ZONE_STATE_INSTALLED)) {
return;
}
return;
}
else
return;
case RT_NET:
return;
}
else
return;
case RT_DEVICE:
return;
}
else
return;
case RT_RCTL:
return;
}
else
return;
case RT_ATTR:
return;
}
else
return;
case RT_DATASET:
return;
}
else
return;
default:
return;
}
}
static void
{
char *prop_id;
struct zone_rctlvaltab *rctlvaltab;
return;
}
return;
}
return;
switch (res_type) {
case RT_FS:
if (prop_type != PT_OPTIONS) {
TRUE);
return;
}
return;
}
return;
}
prop_id);
} else {
break;
TRUE);
}
}
return;
case RT_RCTL:
TRUE);
return;
}
return;
}
}
case PT_PRIV:
sizeof (rctlvaltab->zone_rctlval_priv));
break;
case PT_LIMIT:
sizeof (rctlvaltab->zone_rctlval_limit));
break;
case PT_ACTION:
sizeof (rctlvaltab->zone_rctlval_action));
break;
default:
return;
}
}
return;
default:
return;
}
}
void
{
if (zone_is_read_only(CMD_REMOVE))
return;
if (global_scope)
else
}
void
{
if (zone_is_read_only(CMD_SELECT))
return;
if (global_scope) {
end_op = CMD_SELECT;
} else {
return;
}
return;
}
return;
switch (type) {
case RT_FS:
global_scope = TRUE;
}
sizeof (struct zone_fstab));
return;
case RT_IPD:
if (state_atleast(ZONE_STATE_INCOMPLETE)) {
"allowed."), zone,
global_scope = TRUE;
end_op = -1;
return;
}
global_scope = TRUE;
}
sizeof (struct zone_fstab));
return;
case RT_NET:
global_scope = TRUE;
}
sizeof (struct zone_nwiftab));
return;
case RT_DEVICE:
global_scope = TRUE;
}
sizeof (struct zone_devtab));
return;
case RT_RCTL:
global_scope = TRUE;
}
sizeof (struct zone_rctltab));
return;
case RT_ATTR:
global_scope = TRUE;
}
sizeof (struct zone_attrtab));
return;
case RT_DATASET:
global_scope = TRUE;
}
sizeof (struct zone_dstab));
return;
default:
return;
}
}
/*
* Network "addresses" can be one of the following forms:
* <IPv4 address>
* <IPv4 address>/<prefix length>
* <IPv6 address>/<prefix length>
* <host name>
* <host name>/<prefix length>
* In other words, the "/" followed by a prefix length is allowed but not
* required for IPv4 addresses and host names, and required for IPv6 addresses.
* If a prefix length is given, it must be in the allowable range: 0 to 32 for
* IPv4 addresses and host names, 0 to 128 for IPv6 addresses.
* Host names must start with an alpha-numeric character, and all subsequent
* characters must be either alpha-numeric or "-".
*/
static int
{
int prefixlen, i;
/*
* Copy the part before any '/' into part1 or copy the whole
* thing if there is no '/'.
*/
*slashp = '\0';
*slashp = '/';
} else {
}
"require /prefix-length suffix."), address);
return (Z_ERR);
}
"prefix lengths must be 0 - 128."), address);
return (Z_ERR);
}
return (Z_OK);
}
/* At this point, any /prefix must be for IPv4. */
"prefix lengths must be 0 - 32."), address);
return (Z_ERR);
}
}
return (Z_OK);
/* address may also be a host name */
part1);
return (Z_ERR);
}
for (i = 1; part1[i]; i++)
"network address syntax"), part1);
return (Z_ERR);
}
return (Z_OK);
}
static int
{
return (Z_OK);
"logical interface name not allowed"), ifname);
return (Z_ERR);
}
static boolean_t
valid_fs_type(const char *type)
{
/*
* Is this a valid path component?
*/
return (B_FALSE);
/*
* Make sure a bad value for "type" doesn't make
*/
return (B_FALSE);
/*
* More detailed verification happens later by zoneadm(1m).
*/
return (B_TRUE);
}
void
{
char *prop_id;
if (zone_is_read_only(CMD_SET))
return;
if (global_scope) {
if (prop_type == PT_ZONENAME) {
} else if (prop_type == PT_ZONEPATH) {
} else if (prop_type == PT_AUTOBOOT) {
} else {
"from the global scope."));
return;
}
} else {
}
/*
* A nasty expression but not that complicated:
* 1. fs options are simple or list (tested below)
* 2. rctl value's are complex or list (tested below)
* Anything else should be simple.
*/
return;
}
if (prop_type == PT_UNKNOWN) {
return;
}
/*
* Special case: the user can change the zone name prior to 'create';
* if the zone already exists, we fall through letting initialize()
* and the rest of the logic run.
*/
return;
}
return;
}
return;
switch (res_type) {
case RT_ZONENAME:
/*
* Use prop_id instead of 'zone' here, since we're
* reporting a problem about the *new* zonename.
*/
} else {
}
return;
case RT_ZONEPATH:
if (state_atleast(ZONE_STATE_INSTALLED)) {
return;
}
return;
}
else
return;
case RT_AUTOBOOT:
} else {
return;
}
else
return;
case RT_POOL:
else
return;
case RT_FS:
switch (prop_type) {
case PT_DIR:
sizeof (in_progress_fstab.zone_fs_dir));
return;
case PT_SPECIAL:
sizeof (in_progress_fstab.zone_fs_special));
return;
case PT_RAW:
return;
case PT_TYPE:
if (!valid_fs_type(prop_id)) {
return;
}
sizeof (in_progress_fstab.zone_fs_type));
return;
case PT_OPTIONS:
return;
}
return;
default:
break;
}
return;
case RT_IPD:
switch (prop_type) {
case PT_DIR:
sizeof (in_progress_ipdtab.zone_fs_dir));
return;
default:
break;
}
return;
case RT_NET:
switch (prop_type) {
case PT_ADDRESS:
return;
}
sizeof (in_progress_nwiftab.zone_nwif_address));
break;
case PT_PHYSICAL:
return;
}
sizeof (in_progress_nwiftab.zone_nwif_physical));
break;
default:
TRUE);
return;
}
return;
case RT_DEVICE:
switch (prop_type) {
case PT_MATCH:
sizeof (in_progress_devtab.zone_dev_match));
break;
default:
TRUE);
return;
}
return;
case RT_RCTL:
switch (prop_type) {
case PT_NAME:
if (!zonecfg_valid_rctlname(prop_id)) {
return;
}
sizeof (in_progress_rctltab.zone_rctl_name));
break;
case PT_VALUE:
return;
}
break;
default:
TRUE);
return;
}
return;
case RT_ATTR:
switch (prop_type) {
case PT_NAME:
sizeof (in_progress_attrtab.zone_attr_name));
break;
case PT_TYPE:
sizeof (in_progress_attrtab.zone_attr_type));
break;
case PT_VALUE:
sizeof (in_progress_attrtab.zone_attr_value));
break;
default:
TRUE);
return;
}
return;
case RT_DATASET:
switch (prop_type) {
case PT_NAME:
sizeof (in_progress_dstab.zone_dataset_name));
return;
default:
break;
}
return;
default:
return;
}
}
static void
{
char *qstr;
if (*pval != '\0') {
} else if (print_notspec)
}
static void
{
char zonename[ZONENAME_MAX];
zonename);
else
}
static void
{
char zonepath[MAXPATHLEN];
zonepath);
else {
}
}
static void
{
int err;
else
}
static void
{
char pool[MAXNAMELEN];
int err;
else
}
static void
{
else
}
}
static void
{
}
static void
{
return;
if (cmd->cmd_prop_nv_pairs == 0) {
goto loopend;
}
goto loopend;
goto loopend; /* no match */
goto loopend; /* no match */
goto loopend; /* no match */
}
(void) zonecfg_endfsent(handle);
/*
* If a property n/v pair was specified, warn the user if there was
* nothing to output.
*/
}
static void
{
return;
if (cmd->cmd_prop_nv_pairs == 0) {
continue;
}
continue;
continue; /* no match */
}
(void) zonecfg_endipdent(handle);
/*
* If a property n/v pair was specified, warn the user if there was
* nothing to output.
*/
}
static void
{
}
static void
{
return;
if (cmd->cmd_prop_nv_pairs == 0) {
continue;
}
continue;
lookup.zone_nwif_physical) != 0)
continue; /* no match */
continue; /* no match */
}
(void) zonecfg_endnwifent(handle);
/*
* If a property n/v pair was specified, warn the user if there was
* nothing to output.
*/
}
static void
{
}
static void
{
return;
if (cmd->cmd_prop_nv_pairs == 0) {
continue;
}
continue;
continue; /* no match */
}
(void) zonecfg_enddevent(handle);
/*
* If a property n/v pair was specified, warn the user if there was
* nothing to output.
*/
}
static void
{
struct zone_rctlvaltab *valptr;
}
}
static void
{
return;
if (cmd->cmd_prop_nv_pairs == 0) {
}
}
(void) zonecfg_endrctlent(handle);
/*
* If a property n/v pair was specified, warn the user if there was
* nothing to output.
*/
}
static void
{
}
static void
{
return;
if (cmd->cmd_prop_nv_pairs == 0) {
continue;
}
continue;
continue; /* no match */
continue; /* no match */
continue; /* no match */
}
(void) zonecfg_endattrent(handle);
/*
* If a property n/v pair was specified, warn the user if there was
* nothing to output.
*/
}
static void
{
}
static void
{
return;
if (cmd->cmd_prop_nv_pairs == 0) {
continue;
}
continue;
lookup.zone_dataset_name) != 0)
continue; /* no match */
}
(void) zonecfg_enddsent(handle);
/*
* If a property n/v pair was specified, warn the user if there was
* nothing to output.
*/
}
void
{
bool need_to_close = FALSE;
char *pager;
return;
/* don't page error output */
if (interactive_mode) {
else
}
if (!global_scope) {
switch (resource_scope) {
case RT_FS:
break;
case RT_IPD:
break;
case RT_NET:
break;
case RT_DEVICE:
break;
case RT_RCTL:
break;
case RT_ATTR:
break;
case RT_DATASET:
break;
}
goto cleanup;
}
switch (cmd->cmd_res_type) {
case RT_UNKNOWN:
break;
case RT_ZONENAME:
break;
case RT_ZONEPATH:
break;
case RT_AUTOBOOT:
break;
case RT_POOL:
break;
case RT_FS:
break;
case RT_IPD:
break;
case RT_NET:
break;
case RT_DEVICE:
break;
case RT_RCTL:
break;
case RT_ATTR:
break;
case RT_DATASET:
break;
default:
TRUE);
}
if (need_to_close)
}
/*
* Helper function for verify-- checks that a required string property
* exists.
*/
static void
{
}
}
/*
* See the DTD for which attributes are required for which resources.
*
* This function can be called by commit_func(), which needs to save things,
* in addition to the general call from parse_and_run(), which doesn't need
* things saved. Since the parameters are standardized, we distinguish by
* having commit_func() call here with cmd->cmd_arg set to "save" to indicate
* that a save is needed.
*/
void
{
struct zone_nwiftab nwiftab;
struct zone_fstab fstab;
struct zone_attrtab attrtab;
struct zone_rctltab rctltab;
struct zone_dstab dstab;
char zonepath[MAXPATHLEN];
optind = 0;
switch (arg) {
case '?':
return;
default:
return;
}
}
return;
}
if (zone_is_read_only(CMD_VERIFY))
return;
return;
}
}
return;
}
}
(void) zonecfg_endipdent(handle);
return;
}
&ret_val);
}
(void) zonecfg_endfsent(handle);
return;
}
PT_ADDRESS, &ret_val);
PT_PHYSICAL, &ret_val);
}
(void) zonecfg_endnwifent(handle);
return;
}
&ret_val);
} else {
}
}
(void) zonecfg_endrctlent(handle);
return;
}
&ret_val);
&ret_val);
&ret_val);
}
(void) zonecfg_endattrent(handle);
return;
}
}
}
(void) zonecfg_enddsent(handle);
if (!global_scope) {
}
if (save) {
sizeof (revert_zone));
}
} else {
}
}
}
void
{
int arg;
optind = 0;
switch (arg) {
case '?':
return;
default:
return;
}
}
return;
}
if (global_scope)
global_scope = TRUE;
}
static int
validate_attr_name(char *name)
{
int i;
return (Z_INVAL);
}
for (i = 1; name[i]; i++)
"alpha-numeric characters, plus '-' and '.'."),
return (Z_INVAL);
}
return (Z_OK);
}
static int
{
char strval[MAXNAMELEN];
return (Z_OK);
return (Z_ERR);
}
return (Z_OK);
return (Z_ERR);
}
return (Z_OK);
return (Z_ERR);
}
return (Z_OK);
return (Z_ERR);
}
return (Z_ERR);
}
/*
* Helper function for end_func-- checks the existence of a given property
* and emits a message if not specified.
*/
static int
{
return (Z_ERR);
}
return (Z_OK);
}
void
{
bool validation_failed = FALSE;
struct zone_fstab tmp_fstab;
struct zone_nwiftab tmp_nwiftab;
struct zone_devtab tmp_devtab;
struct zone_rctltab tmp_rctltab;
struct zone_attrtab tmp_attrtab;
struct zone_dstab tmp_dstab;
optind = 0;
switch (arg) {
case '?':
return;
default:
return;
}
}
return;
}
if (global_scope) {
return;
}
switch (resource_scope) {
case RT_FS:
/* First make sure everything was filled in. */
}
}
}
if (validation_failed) {
return;
}
/* Make sure there isn't already one like this. */
sizeof (tmp_fstab.zone_fs_dir));
"with the %s '%s' already exists."),
return;
}
} else {
}
break;
case RT_IPD:
/* First make sure everything was filled in. */
&validation_failed) == Z_OK) {
}
}
if (validation_failed) {
return;
}
/* Make sure there isn't already one like this. */
sizeof (tmp_fstab.zone_fs_dir));
"with the %s '%s' already exists."),
return;
}
} else {
}
break;
case RT_NET:
/* First make sure everything was filled in. */
if (validation_failed) {
return;
}
/* Make sure there isn't already one like this. */
sizeof (tmp_nwiftab.zone_nwif_address));
"with the %s '%s' already exists."),
return;
}
} else {
}
break;
case RT_DEVICE:
/* First make sure everything was filled in. */
if (validation_failed) {
return;
}
/* Make sure there isn't already one like this. */
sizeof (tmp_devtab.zone_dev_match));
return;
}
} else {
}
break;
case RT_RCTL:
/* First make sure everything was filled in. */
}
if (validation_failed) {
return;
}
/* Make sure there isn't already one like this. */
sizeof (tmp_rctltab.zone_rctl_name));
"with the %s '%s' already exists."),
return;
}
} else {
}
}
break;
case RT_ATTR:
/* First make sure everything was filled in. */
Z_OK)
if (validation_failed) {
return;
}
/* Make sure there isn't already one like this. */
sizeof (tmp_attrtab.zone_attr_name));
"with the %s '%s' already exists."),
return;
}
} else {
}
break;
case RT_DATASET:
/* First make sure everything was filled in. */
gettext("not specified"));
}
if (validation_failed)
return;
/* Make sure there isn't already one like this. */
sizeof (tmp_dstab.zone_dataset_name));
"with the %s '%s' already exists."),
return;
}
} else {
}
break;
default:
TRUE);
return;
}
} else {
global_scope = TRUE;
end_op = -1;
}
}
void
{
int arg;
optind = 0;
switch (arg) {
case '?':
return;
default:
return;
}
}
return;
}
if (zone_is_read_only(CMD_COMMIT))
return;
/*
* cmd_arg normally comes from a strdup() in the lexer, and the
* whole cmd structure and its (char *) attributes are freed at
* the completion of each command, so the strdup() below is needed
* to match this and prevent a core dump from trying to free()
* something that can't be.
*/
}
}
void
{
optind = 0;
switch (arg) {
case '?':
return;
case 'F':
break;
default:
return;
}
}
return;
}
if (zone_is_read_only(CMD_REVERT))
return;
return;
}
if (!force) {
gettext("Are you sure you want to revert"));
"specified:\n%s command ignored, exiting."),
}
if (answer != 1)
return;
}
/*
* Time for a new handle: finish the old one off first
* then get a new one properly to avoid leaks.
*/
}
got_handle = FALSE;
else
}
}
void
{
int i;
return;
}
return;
}
return;
}
return;
}
return;
}
for (i = 0; i <= CMD_MAX; i++) {
longer_usage(i);
return;
}
}
/* We do not use zerr() here because we do not want its extra \n. */
}
static int
string_to_yyin(char *string)
{
return (Z_ERR);
}
return (Z_ERR);
}
return (Z_ERR);
}
return (Z_OK);
}
/* This is the back-end helper function for read_input() below. */
static int
cleanup()
{
int answer;
if (!interactive_mode && !cmd_file_mode) {
/*
* If we're not in interactive mode, and we're not in command
* file mode, then we must be in commands-from-the-command-line
* mode. As such, we can't loop back and ask for more input.
* It was OK to prompt for such things as whether or not to
* really delete a zone in the command handler called from
* yyparse() above, but "really quit?" makes no sense in this
* context. So disable prompting.
*/
}
if (!global_scope) {
if (!time_to_exit) {
/*
* Just print a simple error message in the -1 case,
* since exit_func() already handles that case, and
* EOF means we are finished anyway.
*/
gettext("Resource incomplete; really quit"));
if (answer == -1) {
return (Z_ERR);
}
if (answer != 1) {
return (Z_REPEAT);
}
} else {
}
}
/*
* Make sure we tried something and that the handle checks
* out, or we would get a false error trying to commit.
*/
return (Z_ERR);
}
/*
* need_to_commit will get set back to FALSE if the
* configuration is saved successfully.
*/
if (need_to_commit) {
if (force_exit) {
return (Z_ERR);
}
gettext("Configuration not saved; really quit"));
if (answer == -1) {
return (Z_ERR);
}
if (answer != 1) {
return (Z_REPEAT);
}
}
}
}
/*
* read_input() is the driver of this program. It is a wrapper around
* yyparse(), printing appropriate prompts when needed, checking for
* exit conditions and reacting appropriately [the latter in its cleanup()
* helper function].
*
* Like most zonecfg functions, it returns Z_OK or Z_ERR, *or* Z_REPEAT
* so do_interactive() knows that we are not really done (i.e, we asked
* the user if we should really quit and the user said no).
*/
static int
{
/*
* The prompt is "e:z> " or "e:z:r> " where e is execname, z is zone
* and r is resource_scope: 5 is for the two ":"s + "> " + terminator.
*/
/* yyin should have been set to the appropriate (FILE *) if not stdin */
for (;;) {
if (yyin_is_a_tty) {
if (newline_terminated) {
if (global_scope)
else
}
/*
* If the user hits ^C then we want to catch it and
* start over. If the user hits EOF then we want to
* bail out.
*/
continue;
}
break;
(void) string_to_yyin(line);
yyparse();
} else {
yyparse();
}
/* Bail out on an error in command file mode. */
time_to_exit = TRUE;
break;
}
return (cleanup());
}
/*
* This function is used in the zonecfg-interactive-mode scenario: it just
* calls read_input() until we are done.
*/
static int
do_interactive(void)
{
int err;
if (!read_only_mode) {
/*
* Try to set things up proactively in interactive mode, so
* that if the zone in question does not exist yet, we can
* provide the user with a clue.
*/
(void) initialize(FALSE);
}
do {
err = read_input();
return (err);
}
/*
* cmd_file is slightly more complicated, as it has to open the command file
* and set yyin appropriately. Once that is done, though, it just calls
* read_input(), and only once, since prompting is not possible.
*/
static int
{
int err;
if (using_real_file) {
/*
* zerr() prints a line number in cmd_file_mode, which we do
* not want here, so temporarily unset it.
*/
return (Z_ERR);
}
goto done;
}
goto done;
}
} else {
/*
* "-f -" is essentially the same as interactive mode,
* so treat it that way.
*/
}
/* Z_REPEAT is for interactive mode; treat it like Z_ERR here. */
done:
if (using_real_file)
return (err);
}
/*
* Since yacc is based on reading from a (FILE *) whereas what we get from
* the command line is in argv format, we need to convert when the user
* gives us commands directly from the command line. That is done here by
* concatenating the argv list into a space-separated string, writing it
* to a temp file, and rewinding the file so yyin can be set to it. Then
* we call read_input(), and only once, since prompting about whether to
* continue or quit would make no sense in this context.
*/
static int
{
char *command;
int i, err;
for (i = 0; i < argc; i++)
return (Z_ERR);
}
for (i = 1; i < argc; i++) {
}
return (err);
yyparse();
return (cleanup());
}
static char *
get_execbasename(char *execfullname)
{
char *last_slash, *execbasename;
/* guard against '/' at end of command invocation */
for (;;) {
if (last_slash == NULL) {
break;
} else {
if (*execbasename == '\0') {
*last_slash = '\0';
continue;
}
break;
}
}
return (execbasename);
}
int
{
/* This must be before anything goes to stdout. */
(void) textdomain(TEXT_DOMAIN);
if (getzoneid() != GLOBAL_ZONEID) {
execname);
}
if (argc < 2) {
}
}
switch (arg) {
case '?':
if (optopt == '?')
else
/* NOTREACHED */
case 'f':
break;
case 'z':
}
break;
default:
}
}
}
/* skip this message in one-off from command line mode */
"have write access to this zone's configuration "
"file;\ngoing into read-only mode.\n"));
} else {
}
}
/*
* This may get set back to FALSE again in cmd_file() if cmd_file_name
* is a "real" file as opposed to "-" (i.e. meaning use stdin).
*/
if (isatty(STDIN_FILENO))
ok_to_prompt = TRUE;
if (!cmd_file_mode)
err = do_interactive();
else
} else {
}
(void) del_GetLine(gl);
return (err);
}