/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" /* * 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 <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 <regex.h> #include <signal.h> #include <libtecla.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" 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 /* * 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] [ -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" static struct help helptab[] = { { CMD_ADD, "add", HELP_RES_PROPS, SHELP_ADD, }, { CMD_CANCEL, "cancel", 0, SHELP_CANCEL, }, { 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", "zonepath", "autoboot", "pool", "fs", "inherit-pkg-dir", "net", "device", "rctl", "attr", NULL }; /* These *must* match the order of the PT_ define's from zonecfg.h */ static char *prop_types[] = { "unknown", "zonepath", "autoboot", "pool", "dir", "special", "type", "options", "address", "physical", "name", "value", "match", "priv", "limit", "action", "raw", NULL }; /* 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", NULL }; static const char *add_cmds[] = { "add fs", "add inherit-pkg-dir", "add net", "add device", "add rctl", "add attr", NULL }; static const char *select_cmds[] = { "select fs", "select inherit-pkg-dir", "select net", "select device", "select rctl", "select attr", NULL }; static const char *set_cmds[] = { "set zonepath", "set autoboot", "set pool", NULL }; static const char *fs_res_scope_cmds[] = { "add options ", "cancel", "end", "exit", "help", "info", "set dir=", "set raw=", "set special=", "set type=", 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", "set name=", 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; /* 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 in main(), checked in multiple places */ static bool read_only_mode; /* set in check_if_zone_already_exists(), checked in save_it() */ static bool new_zone = FALSE; 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 GetLine *gl; /* The gl_get_line() resource object */ /* 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, "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)); 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)); } 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 -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)); 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: (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_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 */ } /* * 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, 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")); 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, "%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; } (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\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)); } 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_ZONEPATH)); (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_POOL)); (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)); } 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; if (zonecfg_check_handle(handle) != Z_OK) { if ((err = zonecfg_get_handle(zone, handle)) == Z_OK) { 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); } /* * 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 (;;) { (void) printf("%s (%s)? ", question, default_answer ? "[y]/n" : "y/[n]"); if (fgets(line, sizeof (line), stdin) == NULL || 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_state_t state_num; 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) { new_zone = TRUE; return (Z_OK); } if (zone_get_state(zone, &state_num) == Z_OK && state_num >= 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]; zone_dochandle_t tmphandle; bool force = 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, "?bFt:")) != EOF) { switch (arg) { case '?': if (optopt == '?') longer_usage(CMD_CREATE); else short_usage(CMD_CREATE); return; 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 ((err = zonecfg_get_handle(zone_template, tmphandle)) != Z_OK) { zonecfg_fini_handle(tmphandle); zone_perror(zone_template, err, TRUE); return; } zonecfg_fini_handle(handle); handle = tmphandle; if ((err = zonecfg_set_name(handle, zone)) == Z_OK) need_to_commit = TRUE; else zone_perror(zone, err, 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_rctlvaltab *valptr; int err, arg; char zonepath[MAXPATHLEN], outfile[MAXPATHLEN], pool[MAXNAMELEN]; FILE *of; boolean_t autoboot; 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 (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_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 ((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); 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; zone_state_t state_num; 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 (zone_get_state(zone, &state_num) == Z_OK && state_num >= 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: bzero(&in_progress_rctltab, sizeof (in_progress_rctltab)); return; case RT_ATTR: bzero(&in_progress_attrtab, sizeof (in_progress_attrtab)); 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; } } 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) { global_scope = FALSE; resource_scope = cmd->cmd_res_type; end_op = CMD_ADD; add_resource(cmd); } else add_property(cmd); } void delete_func(cmd_t *cmd) { int err, arg, answer; char line[ZONENAME_MAX + 128]; /* enough to ask a question */ bool force = FALSE; zone_state_t state_num; 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 (zone_get_state(zone, &state_num) == Z_OK && state_num >= ZONE_STATE_INCOMPLETE) { zerr(gettext("Zone %s not in %s state; %s not allowed."), zone, zone_state_str(ZONE_STATE_CONFIGURED), cmd_to_str(CMD_DELETE)); saw_error = TRUE; return; } if (initialize(TRUE) != Z_OK) return; if (!force) { (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_delete_index(zone)) != Z_OK) { zone_perror(zone, err, TRUE); return; } need_to_commit = FALSE; if ((err = zonecfg_destroy(zone)) != Z_OK) zone_perror(zone, err, TRUE); /* * Time for a new handle: finish the old one off first * then get a new one properly to avoid leaks. */ 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); fstab->zone_fs_dir[0] = '\0'; fstab->zone_fs_special[0] = '\0'; fstab->zone_fs_type[0] = '\0'; fstab->zone_fs_options = NULL; 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); ipdtab->zone_fs_dir[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_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); nwiftab->zone_nwif_address[0] = '\0'; nwiftab->zone_nwif_physical[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_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); devtab->zone_dev_match[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_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); rctltab->zone_rctl_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(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); attrtab->zone_attr_name[0] = '\0'; attrtab->zone_attr_type[0] = '\0'; attrtab->zone_attr_value[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(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 void remove_resource(cmd_t *cmd) { int err, type; struct zone_fstab fstab; struct zone_nwiftab nwiftab; struct zone_devtab devtab; struct zone_attrtab attrtab; struct zone_rctltab rctltab; zone_state_t state_num; if ((type = cmd->cmd_res_type) == RT_UNKNOWN) { long_usage(CMD_REMOVE, TRUE); return; } if (initialize(TRUE) != Z_OK) return; switch (type) { case RT_FS: if ((err = fill_in_fstab(cmd, &fstab, FALSE)) != Z_OK) { z_cmd_rt_perror(CMD_REMOVE, RT_FS, err, TRUE); return; } if ((err = zonecfg_delete_filesystem(handle, &fstab)) != Z_OK) z_cmd_rt_perror(CMD_REMOVE, RT_FS, err, TRUE); else need_to_commit = TRUE; zonecfg_free_fs_option_list(fstab.zone_fs_options); return; case RT_IPD: if (zone_get_state(zone, &state_num) == Z_OK && state_num >= ZONE_STATE_INSTALLED) { zerr(gettext("Zone %s already installed; %s %s not " "allowed."), zone, cmd_to_str(CMD_REMOVE), rt_to_str(RT_IPD)); return; } if ((err = fill_in_ipdtab(cmd, &fstab, FALSE)) != Z_OK) { z_cmd_rt_perror(CMD_REMOVE, RT_IPD, err, TRUE); return; } if ((err = zonecfg_delete_ipd(handle, &fstab)) != Z_OK) z_cmd_rt_perror(CMD_REMOVE, RT_IPD, err, TRUE); else need_to_commit = TRUE; return; case RT_NET: if ((err = fill_in_nwiftab(cmd, &nwiftab, FALSE)) != Z_OK) { z_cmd_rt_perror(CMD_REMOVE, RT_NET, err, TRUE); return; } if ((err = zonecfg_delete_nwif(handle, &nwiftab)) != Z_OK) z_cmd_rt_perror(CMD_REMOVE, RT_NET, err, TRUE); else need_to_commit = TRUE; return; case RT_DEVICE: if ((err = fill_in_devtab(cmd, &devtab, FALSE)) != Z_OK) { z_cmd_rt_perror(CMD_REMOVE, RT_DEVICE, err, TRUE); return; } if ((err = zonecfg_delete_dev(handle, &devtab)) != Z_OK) z_cmd_rt_perror(CMD_REMOVE, RT_DEVICE, err, TRUE); else need_to_commit = TRUE; return; case RT_RCTL: if ((err = fill_in_rctltab(cmd, &rctltab, FALSE)) != Z_OK) { z_cmd_rt_perror(CMD_REMOVE, RT_RCTL, err, TRUE); return; } if ((err = zonecfg_delete_rctl(handle, &rctltab)) != Z_OK) z_cmd_rt_perror(CMD_REMOVE, RT_RCTL, err, TRUE); else need_to_commit = TRUE; zonecfg_free_rctl_value_list(rctltab.zone_rctl_valptr); return; case RT_ATTR: if ((err = fill_in_attrtab(cmd, &attrtab, FALSE)) != Z_OK) { z_cmd_rt_perror(CMD_REMOVE, RT_ATTR, err, TRUE); return; } if ((err = zonecfg_delete_attr(handle, &attrtab)) != Z_OK) z_cmd_rt_perror(CMD_REMOVE, RT_ATTR, err, TRUE); else need_to_commit = TRUE; return; default: zone_perror(rt_to_str(type), Z_NO_RESOURCE_TYPE, TRUE); long_usage(CMD_REMOVE, TRUE); usage(FALSE, HELP_RESOURCES); return; } } static void remove_property(cmd_t *cmd) { char *prop_id; int err, res_type, prop_type; property_value_ptr_t pp; struct zone_rctlvaltab *rctlvaltab; complex_property_ptr_t cx; res_type = resource_scope; prop_type = cmd->cmd_prop_name[0]; if (res_type == RT_UNKNOWN || prop_type == PT_UNKNOWN) { long_usage(CMD_REMOVE, 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_REMOVE, TRUE); usage(FALSE, HELP_PROPS); return; } pp = cmd->cmd_property_ptr[0]; if (pp->pv_type == PROP_VAL_COMPLEX) { 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_remove_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_remove_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_REMOVE, TRUE); usage(FALSE, HELP_PROPS); return; } pp = cmd->cmd_property_ptr[0]; if (pp->pv_type != PROP_VAL_COMPLEX) { zerr(gettext("A %s value was expected here."), pvt_to_str(PROP_VAL_COMPLEX)); saw_error = TRUE; return; } if ((rctlvaltab = alloc_rctlvaltab()) == NULL) { zone_perror(zone, Z_NOMEM, TRUE); exit(Z_ERR); } for (cx = pp->pv_complex; cx != NULL; cx = cx->cp_next) { switch (cx->cp_type) { case PT_PRIV: (void) strlcpy(rctlvaltab->zone_rctlval_priv, cx->cp_value, sizeof (rctlvaltab->zone_rctlval_priv)); break; case PT_LIMIT: (void) strlcpy(rctlvaltab->zone_rctlval_limit, cx->cp_value, sizeof (rctlvaltab->zone_rctlval_limit)); break; case PT_ACTION: (void) strlcpy(rctlvaltab->zone_rctlval_action, cx->cp_value, sizeof (rctlvaltab->zone_rctlval_action)); break; default: zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE, TRUE); long_usage(CMD_ADD, TRUE); usage(FALSE, HELP_PROPS); zonecfg_free_rctl_value_list(rctlvaltab); return; } } rctlvaltab->zone_rctlval_next = NULL; err = zonecfg_remove_rctl_value(&in_progress_rctltab, rctlvaltab); if (err != Z_OK) zone_perror(pt_to_str(prop_type), err, TRUE); zonecfg_free_rctl_value_list(rctlvaltab); return; default: zone_perror(rt_to_str(res_type), Z_NO_RESOURCE_TYPE, TRUE); long_usage(CMD_REMOVE, TRUE); usage(FALSE, HELP_RESOURCES); return; } } void remove_func(cmd_t *cmd) { if (zone_is_read_only(CMD_REMOVE)) return; assert(cmd != NULL); if (global_scope) remove_resource(cmd); else remove_property(cmd); } void select_func(cmd_t *cmd) { int type, err; zone_state_t state_num; if (zone_is_read_only(CMD_SELECT)) return; assert(cmd != NULL); if (global_scope) { global_scope = FALSE; resource_scope = cmd->cmd_res_type; end_op = CMD_SELECT; } else { scope_usage(CMD_SELECT); return; } if ((type = cmd->cmd_res_type) == RT_UNKNOWN) { long_usage(CMD_SELECT, TRUE); return; } if (initialize(TRUE) != Z_OK) return; switch (type) { case RT_FS: if ((err = fill_in_fstab(cmd, &old_fstab, FALSE)) != Z_OK) { z_cmd_rt_perror(CMD_SELECT, RT_FS, err, TRUE); global_scope = TRUE; } bcopy(&old_fstab, &in_progress_fstab, sizeof (struct zone_fstab)); return; case RT_IPD: if (zone_get_state(zone, &state_num) == Z_OK && state_num >= ZONE_STATE_INCOMPLETE) { zerr(gettext("Zone %s not in %s state; %s %s not " "allowed."), zone, zone_state_str(ZONE_STATE_CONFIGURED), cmd_to_str(CMD_SELECT), rt_to_str(RT_IPD)); global_scope = TRUE; end_op = -1; return; } if ((err = fill_in_ipdtab(cmd, &old_ipdtab, FALSE)) != Z_OK) { z_cmd_rt_perror(CMD_SELECT, RT_IPD, err, TRUE); global_scope = TRUE; } bcopy(&old_ipdtab, &in_progress_ipdtab, sizeof (struct zone_fstab)); return; case RT_NET: if ((err = fill_in_nwiftab(cmd, &old_nwiftab, FALSE)) != Z_OK) { z_cmd_rt_perror(CMD_SELECT, RT_NET, err, TRUE); global_scope = TRUE; } bcopy(&old_nwiftab, &in_progress_nwiftab, sizeof (struct zone_nwiftab)); return; case RT_DEVICE: if ((err = fill_in_devtab(cmd, &old_devtab, FALSE)) != Z_OK) { z_cmd_rt_perror(CMD_SELECT, RT_DEVICE, err, TRUE); global_scope = TRUE; } bcopy(&old_devtab, &in_progress_devtab, sizeof (struct zone_devtab)); return; case RT_RCTL: if ((err = fill_in_rctltab(cmd, &old_rctltab, FALSE)) != Z_OK) { z_cmd_rt_perror(CMD_SELECT, RT_RCTL, err, TRUE); global_scope = TRUE; } bcopy(&old_rctltab, &in_progress_rctltab, sizeof (struct zone_rctltab)); return; case RT_ATTR: if ((err = fill_in_attrtab(cmd, &old_attrtab, FALSE)) != Z_OK) { z_cmd_rt_perror(CMD_SELECT, RT_ATTR, err, TRUE); global_scope = TRUE; } bcopy(&old_attrtab, &in_progress_attrtab, sizeof (struct zone_attrtab)); return; default: zone_perror(rt_to_str(type), Z_NO_RESOURCE_TYPE, TRUE); long_usage(CMD_SELECT, TRUE); usage(FALSE, HELP_RESOURCES); 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 validate_net_address_syntax(char *address) { char *slashp, part1[MAXHOSTNAMELEN]; struct in6_addr in6; struct in_addr in4; int prefixlen, i; /* * Copy the part before any '/' into part1 or copy the whole * thing if there is no '/'. */ if ((slashp = strchr(address, '/')) != NULL) { *slashp = '\0'; (void) strlcpy(part1, address, sizeof (part1)); *slashp = '/'; prefixlen = atoi(++slashp); } else { (void) strlcpy(part1, address, sizeof (part1)); } if (inet_pton(AF_INET6, part1, &in6) == 1) { if (slashp == NULL) { zerr(gettext("%s: IPv6 addresses " "require /prefix-length suffix."), address); return (Z_ERR); } if (prefixlen < 0 || prefixlen > 128) { zerr(gettext("%s: IPv6 address " "prefix lengths must be 0 - 128."), address); return (Z_ERR); } return (Z_OK); } /* At this point, any /prefix must be for IPv4. */ if (slashp != NULL) { if (prefixlen < 0 || prefixlen > 32) { zerr(gettext("%s: IPv4 address " "prefix lengths must be 0 - 32."), address); return (Z_ERR); } } if (inet_pton(AF_INET, part1, &in4) == 1) return (Z_OK); /* address may also be a host name */ if (!isalnum(part1[0])) { zerr(gettext("%s: bogus host name or network address syntax"), part1); saw_error = TRUE; usage(FALSE, HELP_NETADDR); return (Z_ERR); } for (i = 1; part1[i]; i++) if (!isalnum(part1[i]) && part1[i] != '-' && part1[i] != '.') { zerr(gettext("%s: bogus host name or " "network address syntax"), part1); saw_error = TRUE; usage(FALSE, HELP_NETADDR); return (Z_ERR); } return (Z_OK); } static int validate_net_physical_syntax(char *ifname) { if (strchr(ifname, ':') == NULL) return (Z_OK); zerr(gettext("%s: physical interface name required; " "logical interface name not allowed"), ifname); return (Z_ERR); } static boolean_t valid_fs_type(const char *type) { /* * Is this a valid path component? */ if (strlen(type) + 1 > MAXNAMELEN) return (B_FALSE); /* * Make sure a bad value for "type" doesn't make * /usr/lib/fs/<type>/mount turn into something else. */ if (strchr(type, '/') != NULL || type[0] == '\0' || strcmp(type, ".") == 0 || strcmp(type, "..") == 0) return (B_FALSE); /* * More detailed verification happens later by zoneadm(1m). */ return (B_TRUE); } void set_func(cmd_t *cmd) { char *prop_id; int err, res_type, prop_type; property_value_ptr_t pp; zone_state_t state_num; boolean_t autoboot; if (zone_is_read_only(CMD_SET)) return; assert(cmd != NULL); prop_type = cmd->cmd_prop_name[0]; if (global_scope) { if (prop_type == PT_ZONEPATH) { res_type = RT_ZONEPATH; } else if (prop_type == PT_AUTOBOOT) { res_type = RT_AUTOBOOT; } else if (prop_type == PT_POOL) { res_type = RT_POOL; } else { zerr(gettext("Cannot set a resource-specific property " "from the global scope.")); saw_error = TRUE; return; } } else { res_type = resource_scope; } pp = cmd->cmd_property_ptr[0]; /* * 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. */ if (!(res_type == RT_FS && prop_type == PT_OPTIONS) && !(res_type == RT_RCTL && prop_type == PT_VALUE) && (pp->pv_type != PROP_VAL_SIMPLE || (prop_id = pp->pv_simple) == NULL)) { zerr(gettext("A %s value was expected here."), pvt_to_str(PROP_VAL_SIMPLE)); saw_error = TRUE; return; } if (prop_type == PT_UNKNOWN) { long_usage(CMD_SET, TRUE); return; } if (initialize(TRUE) != Z_OK) return; switch (res_type) { case RT_ZONEPATH: if (zone_get_state(zone, &state_num) == Z_OK && state_num >= ZONE_STATE_INSTALLED) { zerr(gettext("Zone %s already installed; %s %s not " "allowed."), zone, cmd_to_str(CMD_SET), rt_to_str(RT_ZONEPATH)); return; } if (validate_zonepath_syntax(prop_id) != Z_OK) { saw_error = TRUE; return; } if ((err = zonecfg_set_zonepath(handle, prop_id)) != Z_OK) zone_perror(zone, err, TRUE); else need_to_commit = TRUE; return; case RT_AUTOBOOT: if (strcmp(prop_id, "true") == 0) { autoboot = B_TRUE; } else if (strcmp(prop_id, "false") == 0) { autoboot = B_FALSE; } else { zerr(gettext("%s value must be '%s' or '%s'."), pt_to_str(PT_AUTOBOOT), "true", "false"); saw_error = TRUE; return; } if ((err = zonecfg_set_autoboot(handle, autoboot)) != Z_OK) zone_perror(zone, err, TRUE); else need_to_commit = TRUE; return; case RT_POOL: if ((err = zonecfg_set_pool(handle, prop_id)) != Z_OK) zone_perror(zone, err, TRUE); else need_to_commit = TRUE; return; case RT_FS: switch (prop_type) { case PT_DIR: (void) strlcpy(in_progress_fstab.zone_fs_dir, prop_id, sizeof (in_progress_fstab.zone_fs_dir)); return; case PT_SPECIAL: (void) strlcpy(in_progress_fstab.zone_fs_special, prop_id, sizeof (in_progress_fstab.zone_fs_special)); return; case PT_RAW: (void) strlcpy(in_progress_fstab.zone_fs_raw, prop_id, sizeof (in_progress_fstab.zone_fs_raw)); return; case PT_TYPE: if (!valid_fs_type(prop_id)) { zerr(gettext("\"%s\" is not a valid %s."), prop_id, pt_to_str(PT_TYPE)); saw_error = TRUE; return; } (void) strlcpy(in_progress_fstab.zone_fs_type, prop_id, sizeof (in_progress_fstab.zone_fs_type)); return; case PT_OPTIONS: 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; } zonecfg_free_fs_option_list( in_progress_fstab.zone_fs_options); in_progress_fstab.zone_fs_options = NULL; if (!(pp->pv_type == PROP_VAL_LIST && pp->pv_list == NULL)) add_property(cmd); return; default: break; } zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE, TRUE); long_usage(CMD_SET, TRUE); usage(FALSE, HELP_PROPS); return; case RT_IPD: switch (prop_type) { case PT_DIR: (void) strlcpy(in_progress_ipdtab.zone_fs_dir, prop_id, sizeof (in_progress_ipdtab.zone_fs_dir)); return; default: break; } zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE, TRUE); long_usage(CMD_SET, TRUE); usage(FALSE, HELP_PROPS); return; case RT_NET: switch (prop_type) { case PT_ADDRESS: if (validate_net_address_syntax(prop_id) != Z_OK) { saw_error = TRUE; return; } (void) strlcpy(in_progress_nwiftab.zone_nwif_address, prop_id, sizeof (in_progress_nwiftab.zone_nwif_address)); break; case PT_PHYSICAL: if (validate_net_physical_syntax(prop_id) != Z_OK) { saw_error = TRUE; return; } (void) strlcpy(in_progress_nwiftab.zone_nwif_physical, prop_id, sizeof (in_progress_nwiftab.zone_nwif_physical)); break; default: zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE, TRUE); long_usage(CMD_SET, TRUE); usage(FALSE, HELP_PROPS); return; } return; case RT_DEVICE: switch (prop_type) { case PT_MATCH: (void) strlcpy(in_progress_devtab.zone_dev_match, prop_id, sizeof (in_progress_devtab.zone_dev_match)); break; default: zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE, TRUE); long_usage(CMD_SET, TRUE); usage(FALSE, HELP_PROPS); return; } return; case RT_RCTL: switch (prop_type) { case PT_NAME: if (!zonecfg_valid_rctlname(prop_id)) { zerr(gettext("'%s' is not a valid zone %s " "name."), prop_id, rt_to_str(RT_RCTL)); return; } (void) strlcpy(in_progress_rctltab.zone_rctl_name, prop_id, sizeof (in_progress_rctltab.zone_rctl_name)); break; case PT_VALUE: 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; } zonecfg_free_rctl_value_list( in_progress_rctltab.zone_rctl_valptr); in_progress_rctltab.zone_rctl_valptr = NULL; if (!(pp->pv_type == PROP_VAL_LIST && pp->pv_list == NULL)) add_property(cmd); break; default: zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE, TRUE); long_usage(CMD_SET, TRUE); usage(FALSE, HELP_PROPS); return; } return; case RT_ATTR: switch (prop_type) { case PT_NAME: (void) strlcpy(in_progress_attrtab.zone_attr_name, prop_id, sizeof (in_progress_attrtab.zone_attr_name)); break; case PT_TYPE: (void) strlcpy(in_progress_attrtab.zone_attr_type, prop_id, sizeof (in_progress_attrtab.zone_attr_type)); break; case PT_VALUE: (void) strlcpy(in_progress_attrtab.zone_attr_value, prop_id, sizeof (in_progress_attrtab.zone_attr_value)); break; default: zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE, TRUE); long_usage(CMD_SET, TRUE); usage(FALSE, HELP_PROPS); return; } return; default: zone_perror(rt_to_str(res_type), Z_NO_RESOURCE_TYPE, TRUE); long_usage(CMD_SET, TRUE); usage(FALSE, HELP_RESOURCES); return; } } static void output_prop(FILE *fp, int pnum, char *pval, bool print_notspec) { char *qstr; if (*pval != '\0') { qstr = quoteit(pval); (void) fprintf(fp, "\t%s: %s\n", pt_to_str(pnum), qstr); free(qstr); } else if (print_notspec) (void) fprintf(fp, "\t%s %s\n", pt_to_str(pnum), gettext("not specified")); } static void info_zonepath(zone_dochandle_t handle, FILE *fp) { char zonepath[MAXPATHLEN]; if (zonecfg_get_zonepath(handle, zonepath, sizeof (zonepath)) == Z_OK) (void) fprintf(fp, "%s: %s\n", pt_to_str(PT_ZONEPATH), zonepath); else (void) fprintf(fp, "%s %s\n", pt_to_str(PT_ZONEPATH), gettext("not specified")); } static void info_autoboot(zone_dochandle_t handle, FILE *fp) { boolean_t autoboot; int err; if ((err = zonecfg_get_autoboot(handle, &autoboot)) == Z_OK) (void) fprintf(fp, "%s: %s\n", pt_to_str(PT_AUTOBOOT), autoboot ? "true" : "false"); else zone_perror(zone, err, TRUE); } static void info_pool(zone_dochandle_t handle, FILE *fp) { char pool[MAXNAMELEN]; int err; if ((err = zonecfg_get_pool(handle, pool, sizeof (pool))) == Z_OK) (void) fprintf(fp, "%s: %s\n", pt_to_str(PT_POOL), pool); else zone_perror(zone, err, TRUE); } static void output_fs(FILE *fp, struct zone_fstab *fstab) { zone_fsopt_t *this; (void) fprintf(fp, "%s:\n", rt_to_str(RT_FS)); output_prop(fp, PT_DIR, fstab->zone_fs_dir, B_TRUE); output_prop(fp, PT_SPECIAL, fstab->zone_fs_special, B_TRUE); output_prop(fp, PT_RAW, fstab->zone_fs_raw, B_TRUE); output_prop(fp, PT_TYPE, fstab->zone_fs_type, B_TRUE); (void) fprintf(fp, "\t%s: [", pt_to_str(PT_OPTIONS)); for (this = fstab->zone_fs_options; this != NULL; this = this->zone_fsopt_next) { if (strchr(this->zone_fsopt_opt, '=')) (void) fprintf(fp, "\"%s\"", this->zone_fsopt_opt); else (void) fprintf(fp, "%s", this->zone_fsopt_opt); if (this->zone_fsopt_next != NULL) (void) fprintf(fp, ","); } (void) fprintf(fp, "]\n"); } static void output_ipd(FILE *fp, struct zone_fstab *ipdtab) { (void) fprintf(fp, "%s:\n", rt_to_str(RT_IPD)); output_prop(fp, PT_DIR, ipdtab->zone_fs_dir, B_TRUE); } static void info_fs(zone_dochandle_t handle, FILE *fp, cmd_t *cmd) { struct zone_fstab lookup, user; bool output = FALSE; if (zonecfg_setfsent(handle) != Z_OK) return; while (zonecfg_getfsent(handle, &lookup) == Z_OK) { if (cmd->cmd_prop_nv_pairs == 0) { output_fs(fp, &lookup); goto loopend; } if (fill_in_fstab(cmd, &user, TRUE) != Z_OK) goto loopend; if (strlen(user.zone_fs_dir) > 0 && strcmp(user.zone_fs_dir, lookup.zone_fs_dir) != 0) goto loopend; /* no match */ if (strlen(user.zone_fs_special) > 0 && strcmp(user.zone_fs_special, lookup.zone_fs_special) != 0) goto loopend; /* no match */ if (strlen(user.zone_fs_type) > 0 && strcmp(user.zone_fs_type, lookup.zone_fs_type) != 0) goto loopend; /* no match */ output_fs(fp, &lookup); output = TRUE; loopend: zonecfg_free_fs_option_list(lookup.zone_fs_options); } (void) zonecfg_endfsent(handle); /* * If a property n/v pair was specified, warn the user if there was * nothing to output. */ if (!output && cmd->cmd_prop_nv_pairs > 0) (void) printf(gettext("No such %s resource.\n"), rt_to_str(RT_FS)); } static void info_ipd(zone_dochandle_t handle, FILE *fp, cmd_t *cmd) { struct zone_fstab lookup, user; bool output = FALSE; if (zonecfg_setipdent(handle) != Z_OK) return; while (zonecfg_getipdent(handle, &lookup) == Z_OK) { if (cmd->cmd_prop_nv_pairs == 0) { output_ipd(fp, &lookup); continue; } if (fill_in_ipdtab(cmd, &user, TRUE) != Z_OK) continue; if (strlen(user.zone_fs_dir) > 0 && strcmp(user.zone_fs_dir, lookup.zone_fs_dir) != 0) continue; /* no match */ output_ipd(fp, &lookup); output = TRUE; } (void) zonecfg_endipdent(handle); /* * If a property n/v pair was specified, warn the user if there was * nothing to output. */ if (!output && cmd->cmd_prop_nv_pairs > 0) (void) printf(gettext("No such %s resource.\n"), rt_to_str(RT_IPD)); } static void output_net(FILE *fp, struct zone_nwiftab *nwiftab) { (void) fprintf(fp, "%s:\n", rt_to_str(RT_NET)); output_prop(fp, PT_ADDRESS, nwiftab->zone_nwif_address, B_TRUE); output_prop(fp, PT_PHYSICAL, nwiftab->zone_nwif_physical, B_TRUE); } static void info_net(zone_dochandle_t handle, FILE *fp, cmd_t *cmd) { struct zone_nwiftab lookup, user; bool output = FALSE; if (zonecfg_setnwifent(handle) != Z_OK) return; while (zonecfg_getnwifent(handle, &lookup) == Z_OK) { if (cmd->cmd_prop_nv_pairs == 0) { output_net(fp, &lookup); continue; } if (fill_in_nwiftab(cmd, &user, TRUE) != Z_OK) continue; if (strlen(user.zone_nwif_physical) > 0 && strcmp(user.zone_nwif_physical, lookup.zone_nwif_physical) != 0) continue; /* no match */ if (strlen(user.zone_nwif_address) > 0 && !zonecfg_same_net_address(user.zone_nwif_address, lookup.zone_nwif_address)) continue; /* no match */ output_net(fp, &lookup); output = TRUE; } (void) zonecfg_endnwifent(handle); /* * If a property n/v pair was specified, warn the user if there was * nothing to output. */ if (!output && cmd->cmd_prop_nv_pairs > 0) (void) printf(gettext("No such %s resource.\n"), rt_to_str(RT_NET)); } static void output_dev(FILE *fp, struct zone_devtab *devtab) { (void) fprintf(fp, "%s\n", rt_to_str(RT_DEVICE)); output_prop(fp, PT_MATCH, devtab->zone_dev_match, B_TRUE); } static void info_dev(zone_dochandle_t handle, FILE *fp, cmd_t *cmd) { struct zone_devtab lookup, user; bool output = FALSE; if (zonecfg_setdevent(handle) != Z_OK) return; while (zonecfg_getdevent(handle, &lookup) == Z_OK) { if (cmd->cmd_prop_nv_pairs == 0) { output_dev(fp, &lookup); continue; } if (fill_in_devtab(cmd, &user, TRUE) != Z_OK) continue; if (strlen(user.zone_dev_match) > 0 && strcmp(user.zone_dev_match, lookup.zone_dev_match) != 0) continue; /* no match */ output_dev(fp, &lookup); output = TRUE; } (void) zonecfg_enddevent(handle); /* * If a property n/v pair was specified, warn the user if there was * nothing to output. */ if (!output && cmd->cmd_prop_nv_pairs > 0) (void) printf(gettext("No such %s resource.\n"), rt_to_str(RT_DEVICE)); } static void output_rctl(FILE *fp, struct zone_rctltab *rctltab) { struct zone_rctlvaltab *valptr; (void) fprintf(fp, "%s:\n", rt_to_str(RT_RCTL)); output_prop(fp, PT_NAME, rctltab->zone_rctl_name, B_TRUE); for (valptr = rctltab->zone_rctl_valptr; valptr != NULL; valptr = valptr->zone_rctlval_next) { fprintf(fp, "\t%s: (%s=%s,%s=%s,%s=%s)\n", 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); } } static void info_rctl(zone_dochandle_t handle, FILE *fp, cmd_t *cmd) { struct zone_rctltab lookup, user; bool output = FALSE; if (zonecfg_setrctlent(handle) != Z_OK) return; while (zonecfg_getrctlent(handle, &lookup) == Z_OK) { if (cmd->cmd_prop_nv_pairs == 0) { output_rctl(fp, &lookup); } else if (fill_in_rctltab(cmd, &user, TRUE) == Z_OK && (strlen(user.zone_rctl_name) == 0 || strcmp(user.zone_rctl_name, lookup.zone_rctl_name) == 0)) { output_rctl(fp, &lookup); output = TRUE; } zonecfg_free_rctl_value_list(lookup.zone_rctl_valptr); } (void) zonecfg_endrctlent(handle); /* * If a property n/v pair was specified, warn the user if there was * nothing to output. */ if (!output && cmd->cmd_prop_nv_pairs > 0) (void) printf(gettext("No such %s resource.\n"), rt_to_str(RT_RCTL)); } static void output_attr(FILE *fp, struct zone_attrtab *attrtab) { (void) fprintf(fp, "%s:\n", rt_to_str(RT_ATTR)); output_prop(fp, PT_NAME, attrtab->zone_attr_name, B_TRUE); output_prop(fp, PT_TYPE, attrtab->zone_attr_type, B_TRUE); output_prop(fp, PT_VALUE, attrtab->zone_attr_value, B_TRUE); } static void info_attr(zone_dochandle_t handle, FILE *fp, cmd_t *cmd) { struct zone_attrtab lookup, user; bool output = FALSE; if (zonecfg_setattrent(handle) != Z_OK) return; while (zonecfg_getattrent(handle, &lookup) == Z_OK) { if (cmd->cmd_prop_nv_pairs == 0) { output_attr(fp, &lookup); continue; } if (fill_in_attrtab(cmd, &user, TRUE) != Z_OK) continue; if (strlen(user.zone_attr_name) > 0 && strcmp(user.zone_attr_name, lookup.zone_attr_name) != 0) continue; /* no match */ if (strlen(user.zone_attr_type) > 0 && strcmp(user.zone_attr_type, lookup.zone_attr_type) != 0) continue; /* no match */ if (strlen(user.zone_attr_value) > 0 && strcmp(user.zone_attr_value, lookup.zone_attr_value) != 0) continue; /* no match */ output_attr(fp, &lookup); output = TRUE; } (void) zonecfg_endattrent(handle); /* * If a property n/v pair was specified, warn the user if there was * nothing to output. */ if (!output && cmd->cmd_prop_nv_pairs > 0) (void) printf(gettext("No such %s resource.\n"), rt_to_str(RT_ATTR)); } void info_func(cmd_t *cmd) { FILE *fp = stdout; bool need_to_close = FALSE; char *pager; assert(cmd != NULL); if (initialize(TRUE) != Z_OK) return; /* don't page error output */ if (interactive_mode) { if ((pager = getenv("PAGER")) == NULL) pager = PAGER; if ((fp = popen(pager, "w")) != NULL) need_to_close = TRUE; else fp = stdout; setbuf(fp, NULL); } if (!global_scope) { switch (resource_scope) { case RT_FS: output_fs(fp, &in_progress_fstab); break; case RT_IPD: output_ipd(fp, &in_progress_ipdtab); break; case RT_NET: output_net(fp, &in_progress_nwiftab); break; case RT_DEVICE: output_dev(fp, &in_progress_devtab); break; case RT_RCTL: output_rctl(fp, &in_progress_rctltab); break; case RT_ATTR: output_attr(fp, &in_progress_attrtab); break; } goto cleanup; } switch (cmd->cmd_res_type) { case RT_UNKNOWN: info_zonepath(handle, fp); info_autoboot(handle, fp); info_pool(handle, fp); info_ipd(handle, fp, cmd); info_fs(handle, fp, cmd); info_net(handle, fp, cmd); info_dev(handle, fp, cmd); info_rctl(handle, fp, cmd); info_attr(handle, fp, cmd); break; case RT_ZONEPATH: info_zonepath(handle, fp); break; case RT_AUTOBOOT: info_autoboot(handle, fp); break; case RT_POOL: info_pool(handle, fp); break; case RT_FS: info_fs(handle, fp, cmd); break; case RT_IPD: info_ipd(handle, fp, cmd); break; case RT_NET: info_net(handle, fp, cmd); break; case RT_DEVICE: info_dev(handle, fp, cmd); break; case RT_RCTL: info_rctl(handle, fp, cmd); break; case RT_ATTR: info_attr(handle, fp, cmd); break; default: zone_perror(rt_to_str(cmd->cmd_res_type), Z_NO_RESOURCE_TYPE, TRUE); } cleanup: if (need_to_close) (void) pclose(fp); } static int save_it(char *zonepath) { int err; if (new_zone) { err = zonecfg_add_index(zone, zonepath); if (err != Z_OK) { zone_perror(zone, err, TRUE); return (err); } new_zone = FALSE; } if ((err = zonecfg_save(handle)) == Z_OK) need_to_commit = FALSE; return (err); } /* * 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 verify_func(cmd_t *cmd) { struct zone_nwiftab nwiftab; struct zone_fstab fstab; struct zone_attrtab attrtab; struct zone_rctltab rctltab; char zonepath[MAXPATHLEN]; int err, ret_val = Z_OK, arg; bool save = FALSE; optind = 0; if ((arg = getopt(cmd->cmd_argc, cmd->cmd_argv, "?")) != EOF) { switch (arg) { case '?': longer_usage(CMD_VERIFY); return; default: short_usage(CMD_VERIFY); return; } } if (optind > cmd->cmd_argc) { short_usage(CMD_VERIFY); return; } if (zone_is_read_only(CMD_VERIFY)) return; assert(cmd != NULL); if (cmd->cmd_argc > 0 && (strcmp(cmd->cmd_argv[0], "save") == 0)) save = TRUE; if (initialize(TRUE) != Z_OK) return; if (zonecfg_get_zonepath(handle, zonepath, sizeof (zonepath)) != Z_OK) { zerr("%s %s", pt_to_str(PT_ZONEPATH), gettext("not specified")); ret_val = Z_REQD_RESOURCE_MISSING; saw_error = TRUE; } if (strlen(zonepath) == 0) { zerr("%s %s", pt_to_str(PT_ZONEPATH), gettext("cannot be empty.")); ret_val = Z_REQD_RESOURCE_MISSING; saw_error = TRUE; } if ((err = zonecfg_setipdent(handle)) != Z_OK) { zone_perror(zone, err, TRUE); return; } while (zonecfg_getipdent(handle, &fstab) == Z_OK) { if (strlen(fstab.zone_fs_dir) == 0) { zerr("%s: %s %s", rt_to_str(RT_IPD), pt_to_str(PT_DIR), gettext("not specified")); saw_error = TRUE; if (ret_val == Z_OK) ret_val = Z_REQD_PROPERTY_MISSING; } } (void) zonecfg_endipdent(handle); if ((err = zonecfg_setfsent(handle)) != Z_OK) { zone_perror(zone, err, TRUE); return; } while (zonecfg_getfsent(handle, &fstab) == Z_OK) { if (strlen(fstab.zone_fs_dir) == 0) { zerr("%s: %s %s", rt_to_str(RT_FS), pt_to_str(PT_DIR), gettext("not specified")); saw_error = TRUE; if (ret_val == Z_OK) ret_val = Z_REQD_PROPERTY_MISSING; } if (strlen(fstab.zone_fs_special) == 0) { zerr("%s: %s %s", rt_to_str(RT_FS), pt_to_str(PT_SPECIAL), gettext("not specified")); saw_error = TRUE; if (ret_val == Z_OK) ret_val = Z_REQD_PROPERTY_MISSING; } if (strlen(fstab.zone_fs_type) == 0) { zerr("%s: %s %s", rt_to_str(RT_FS), pt_to_str(PT_TYPE), gettext("not specified")); saw_error = TRUE; if (ret_val == Z_OK) ret_val = Z_REQD_PROPERTY_MISSING; } zonecfg_free_fs_option_list(fstab.zone_fs_options); } (void) zonecfg_endfsent(handle); if ((err = zonecfg_setnwifent(handle)) != Z_OK) { zone_perror(zone, err, TRUE); return; } while (zonecfg_getnwifent(handle, &nwiftab) == Z_OK) { if (strlen(nwiftab.zone_nwif_address) == 0) { zerr("%s: %s %s", rt_to_str(RT_NET), pt_to_str(PT_ADDRESS), gettext("not specified")); saw_error = TRUE; if (ret_val == Z_OK) ret_val = Z_REQD_PROPERTY_MISSING; } if (strlen(nwiftab.zone_nwif_physical) == 0) { zerr("%s: %s %s", rt_to_str(RT_NET), pt_to_str(PT_PHYSICAL), gettext("not specified")); saw_error = TRUE; if (ret_val == Z_OK) ret_val = Z_REQD_PROPERTY_MISSING; } } (void) zonecfg_endnwifent(handle); if ((err = zonecfg_setrctlent(handle)) != Z_OK) { zone_perror(zone, err, TRUE); return; } while (zonecfg_getrctlent(handle, &rctltab) == Z_OK) { if (strlen(rctltab.zone_rctl_name) == 0) { zerr("%s: %s %s", rt_to_str(RT_RCTL), pt_to_str(PT_NAME), gettext("not specified")); saw_error = TRUE; if (ret_val == Z_OK) ret_val = Z_REQD_PROPERTY_MISSING; } if (rctltab.zone_rctl_valptr == NULL) { zerr(gettext("%s: no %s specified"), rt_to_str(RT_RCTL), pt_to_str(PT_VALUE)); saw_error = TRUE; if (ret_val == Z_OK) ret_val = Z_REQD_PROPERTY_MISSING; } else { zonecfg_free_rctl_value_list(rctltab.zone_rctl_valptr); } } (void) zonecfg_endrctlent(handle); if ((err = zonecfg_setattrent(handle)) != Z_OK) { zone_perror(zone, err, TRUE); return; } while (zonecfg_getattrent(handle, &attrtab) == Z_OK) { if (strlen(attrtab.zone_attr_name) == 0) { zerr("%s: %s %s", rt_to_str(RT_ATTR), pt_to_str(PT_NAME), gettext("not specified")); saw_error = TRUE; if (ret_val == Z_OK) ret_val = Z_REQD_PROPERTY_MISSING; } if (strlen(attrtab.zone_attr_type) == 0) { zerr("%s: %s %s", rt_to_str(RT_ATTR), pt_to_str(PT_TYPE), gettext("not specified")); saw_error = TRUE; if (ret_val == Z_OK) ret_val = Z_REQD_PROPERTY_MISSING; } if (strlen(attrtab.zone_attr_value) == 0) { zerr("%s: %s %s", rt_to_str(RT_ATTR), pt_to_str(PT_VALUE), gettext("not specified")); saw_error = TRUE; if (ret_val == Z_OK) ret_val = Z_REQD_PROPERTY_MISSING; } } (void) zonecfg_endattrent(handle); if (!global_scope) { zerr(gettext("resource specification incomplete")); saw_error = TRUE; if (ret_val == Z_OK) ret_val = Z_INSUFFICIENT_SPEC; } if (save) { if (ret_val == Z_OK) ret_val = save_it(zonepath); else zerr("zone %s %s", zone, gettext("failed to verify")); } if (ret_val != Z_OK) zone_perror(zone, ret_val, TRUE); } void cancel_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_CANCEL); return; default: short_usage(CMD_CANCEL); return; } } if (optind != cmd->cmd_argc) { short_usage(CMD_CANCEL); return; } if (global_scope) scope_usage(CMD_CANCEL); global_scope = TRUE; zonecfg_free_fs_option_list(in_progress_fstab.zone_fs_options); bzero(&in_progress_fstab, sizeof (in_progress_fstab)); bzero(&in_progress_nwiftab, sizeof (in_progress_nwiftab)); bzero(&in_progress_devtab, sizeof (in_progress_devtab)); zonecfg_free_rctl_value_list(in_progress_rctltab.zone_rctl_valptr); bzero(&in_progress_rctltab, sizeof (in_progress_rctltab)); bzero(&in_progress_attrtab, sizeof (in_progress_attrtab)); } static int validate_attr_name(char *name) { int i; if (!isalnum(name[0])) { zerr(gettext("Invalid %s %s %s: must start with an alpha-" "numeric character."), rt_to_str(RT_ATTR), pt_to_str(PT_NAME), name); return (Z_INVAL); } for (i = 1; name[i]; i++) if (!isalnum(name[i]) && name[i] != '-' && name[i] != '.') { zerr(gettext("Invalid %s %s %s: can only contain " "alpha-numeric characters, plus '-' and '.'."), rt_to_str(RT_ATTR), pt_to_str(PT_NAME), name); return (Z_INVAL); } return (Z_OK); } static int validate_attr_type_val(struct zone_attrtab *attrtab) { boolean_t boolval; int64_t intval; char strval[MAXNAMELEN]; uint64_t uintval; if (strcmp(attrtab->zone_attr_type, "boolean") == 0) { if (zonecfg_get_attr_boolean(attrtab, &boolval) == Z_OK) return (Z_OK); zerr(gettext("invalid %s value for %s=%s"), rt_to_str(RT_ATTR), pt_to_str(PT_TYPE), "boolean"); return (Z_ERR); } if (strcmp(attrtab->zone_attr_type, "int") == 0) { if (zonecfg_get_attr_int(attrtab, &intval) == Z_OK) return (Z_OK); zerr(gettext("invalid %s value for %s=%s"), rt_to_str(RT_ATTR), pt_to_str(PT_TYPE), "int"); return (Z_ERR); } if (strcmp(attrtab->zone_attr_type, "string") == 0) { if (zonecfg_get_attr_string(attrtab, strval, sizeof (strval)) == Z_OK) return (Z_OK); zerr(gettext("invalid %s value for %s=%s"), rt_to_str(RT_ATTR), pt_to_str(PT_TYPE), "string"); return (Z_ERR); } if (strcmp(attrtab->zone_attr_type, "uint") == 0) { if (zonecfg_get_attr_uint(attrtab, &uintval) == Z_OK) return (Z_OK); zerr(gettext("invalid %s value for %s=%s"), rt_to_str(RT_ATTR), pt_to_str(PT_TYPE), "uint"); return (Z_ERR); } zerr(gettext("invalid %s %s '%s'"), rt_to_str(RT_ATTR), pt_to_str(PT_TYPE), attrtab->zone_attr_type); return (Z_ERR); } void end_func(cmd_t *cmd) { 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; int err, arg; assert(cmd != NULL); optind = 0; if ((arg = getopt(cmd->cmd_argc, cmd->cmd_argv, "?")) != EOF) { switch (arg) { case '?': longer_usage(CMD_END); return; default: short_usage(CMD_END); return; } } if (optind != cmd->cmd_argc) { short_usage(CMD_END); return; } if (global_scope) { scope_usage(CMD_END); return; } assert(end_op == CMD_ADD || end_op == CMD_SELECT); switch (resource_scope) { case RT_FS: /* First make sure everything was filled in. */ if (strlen(in_progress_fstab.zone_fs_dir) == 0) { zerr("dir %s", gettext("not specified")); saw_error = TRUE; validation_failed = TRUE; } else if (in_progress_fstab.zone_fs_dir[0] != '/') { zerr("dir %s %s", in_progress_fstab.zone_fs_dir, gettext("is not an absolute path.")); saw_error = TRUE; validation_failed = TRUE; } if (strlen(in_progress_fstab.zone_fs_special) == 0) { zerr("special %s", gettext("not specified")); saw_error = TRUE; validation_failed = TRUE; } if (in_progress_fstab.zone_fs_raw[0] != '\0' && in_progress_fstab.zone_fs_raw[0] != '/') { zerr("raw device %s %s", in_progress_fstab.zone_fs_raw, gettext("is not an absolute path.")); saw_error = TRUE; validation_failed = TRUE; } if (strlen(in_progress_fstab.zone_fs_type) == 0) { zerr("type %s", gettext("not specified")); saw_error = TRUE; validation_failed = TRUE; } if (validation_failed) return; if (end_op == CMD_ADD) { /* Make sure there isn't already one like this. */ bzero(&tmp_fstab, sizeof (tmp_fstab)); (void) strlcpy(tmp_fstab.zone_fs_dir, in_progress_fstab.zone_fs_dir, sizeof (tmp_fstab.zone_fs_dir)); err = zonecfg_lookup_filesystem(handle, &tmp_fstab); zonecfg_free_fs_option_list(tmp_fstab.zone_fs_options); if (err == Z_OK) { zerr(gettext("A %s resource " "with the %s '%s' already exists."), rt_to_str(RT_FS), pt_to_str(PT_DIR), in_progress_fstab.zone_fs_dir); saw_error = TRUE; return; } err = zonecfg_add_filesystem(handle, &in_progress_fstab); } else { err = zonecfg_modify_filesystem(handle, &old_fstab, &in_progress_fstab); } zonecfg_free_fs_option_list(in_progress_fstab.zone_fs_options); in_progress_fstab.zone_fs_options = NULL; break; case RT_IPD: /* First make sure everything was filled in. */ if (strlen(in_progress_ipdtab.zone_fs_dir) == 0) { zerr("dir %s", gettext("not specified")); saw_error = TRUE; validation_failed = TRUE; } else if (in_progress_ipdtab.zone_fs_dir[0] != '/') { zerr("dir %s %s", in_progress_ipdtab.zone_fs_dir, gettext("is not an absolute path.")); saw_error = TRUE; validation_failed = TRUE; } if (validation_failed) return; if (end_op == CMD_ADD) { /* Make sure there isn't already one like this. */ bzero(&tmp_fstab, sizeof (tmp_fstab)); (void) strlcpy(tmp_fstab.zone_fs_dir, in_progress_ipdtab.zone_fs_dir, sizeof (tmp_fstab.zone_fs_dir)); err = zonecfg_lookup_ipd(handle, &tmp_fstab); if (err == Z_OK) { zerr(gettext("An %s resource " "with the %s '%s' already exists."), rt_to_str(RT_IPD), pt_to_str(PT_DIR), in_progress_ipdtab.zone_fs_dir); saw_error = TRUE; return; } err = zonecfg_add_ipd(handle, &in_progress_ipdtab); } else { err = zonecfg_modify_ipd(handle, &old_ipdtab, &in_progress_ipdtab); } break; case RT_NET: /* First make sure everything was filled in. */ if (strlen(in_progress_nwiftab.zone_nwif_physical) == 0) { zerr("physical %s", gettext("not specified")); saw_error = TRUE; validation_failed = TRUE; } if (strlen(in_progress_nwiftab.zone_nwif_address) == 0) { zerr("address %s", gettext("not specified")); saw_error = TRUE; validation_failed = TRUE; } if (validation_failed) return; if (end_op == CMD_ADD) { /* Make sure there isn't already one like this. */ bzero(&tmp_nwiftab, sizeof (tmp_nwiftab)); (void) strlcpy(tmp_nwiftab.zone_nwif_address, in_progress_nwiftab.zone_nwif_address, sizeof (tmp_nwiftab.zone_nwif_address)); if (zonecfg_lookup_nwif(handle, &tmp_nwiftab) == Z_OK) { zerr(gettext("A %s resource " "with the %s '%s' already exists."), rt_to_str(RT_NET), pt_to_str(PT_ADDRESS), in_progress_nwiftab.zone_nwif_address); saw_error = TRUE; return; } err = zonecfg_add_nwif(handle, &in_progress_nwiftab); } else { err = zonecfg_modify_nwif(handle, &old_nwiftab, &in_progress_nwiftab); } break; case RT_DEVICE: /* First make sure everything was filled in. */ if (strlen(in_progress_devtab.zone_dev_match) == 0) { zerr("match %s", gettext("not specified")); saw_error = TRUE; validation_failed = TRUE; } if (validation_failed) return; if (end_op == CMD_ADD) { /* Make sure there isn't already one like this. */ (void) strlcpy(tmp_devtab.zone_dev_match, in_progress_devtab.zone_dev_match, sizeof (tmp_devtab.zone_dev_match)); if (zonecfg_lookup_dev(handle, &tmp_devtab) == Z_OK) { zerr(gettext("A %s resource with the %s '%s' " "already exists."), rt_to_str(RT_DEVICE), pt_to_str(PT_MATCH), in_progress_devtab.zone_dev_match); saw_error = TRUE; return; } err = zonecfg_add_dev(handle, &in_progress_devtab); } else { err = zonecfg_modify_dev(handle, &old_devtab, &in_progress_devtab); } break; case RT_RCTL: /* First make sure everything was filled in. */ if (strlen(in_progress_rctltab.zone_rctl_name) == 0) { zerr("name %s", gettext("not specified")); saw_error = TRUE; validation_failed = TRUE; } if (in_progress_rctltab.zone_rctl_valptr == NULL) { zerr(gettext("no %s specified"), pt_to_str(PT_VALUE)); saw_error = TRUE; validation_failed = TRUE; } if (validation_failed) return; if (end_op == CMD_ADD) { /* Make sure there isn't already one like this. */ (void) strlcpy(tmp_rctltab.zone_rctl_name, in_progress_rctltab.zone_rctl_name, sizeof (tmp_rctltab.zone_rctl_name)); tmp_rctltab.zone_rctl_valptr = NULL; err = zonecfg_lookup_rctl(handle, &tmp_rctltab); zonecfg_free_rctl_value_list( tmp_rctltab.zone_rctl_valptr); if (err == Z_OK) { zerr(gettext("A %s resource " "with the %s '%s' already exists."), rt_to_str(RT_RCTL), pt_to_str(PT_NAME), in_progress_rctltab.zone_rctl_name); saw_error = TRUE; return; } err = zonecfg_add_rctl(handle, &in_progress_rctltab); } else { err = zonecfg_modify_rctl(handle, &old_rctltab, &in_progress_rctltab); } if (err == Z_OK) { zonecfg_free_rctl_value_list( in_progress_rctltab.zone_rctl_valptr); in_progress_rctltab.zone_rctl_valptr = NULL; } break; case RT_ATTR: /* First make sure everything was filled in. */ if (strlen(in_progress_attrtab.zone_attr_name) == 0) { zerr("name %s", gettext("not specified")); saw_error = TRUE; validation_failed = TRUE; } if (strlen(in_progress_attrtab.zone_attr_type) == 0) { zerr("type %s", gettext("not specified")); saw_error = TRUE; validation_failed = TRUE; } if (strlen(in_progress_attrtab.zone_attr_value) == 0) { zerr("value %s", gettext("not specified")); saw_error = TRUE; validation_failed = TRUE; } if (validate_attr_name(in_progress_attrtab.zone_attr_name) != Z_OK) { saw_error = TRUE; validation_failed = TRUE; } if (validate_attr_type_val(&in_progress_attrtab) != Z_OK) { saw_error = TRUE; validation_failed = TRUE; } if (validation_failed) return; if (end_op == CMD_ADD) { /* Make sure there isn't already one like this. */ bzero(&tmp_attrtab, sizeof (tmp_attrtab)); (void) strlcpy(tmp_attrtab.zone_attr_name, in_progress_attrtab.zone_attr_name, sizeof (tmp_attrtab.zone_attr_name)); if (zonecfg_lookup_attr(handle, &tmp_attrtab) == Z_OK) { zerr(gettext("An %s resource " "with the %s '%s' already exists."), rt_to_str(RT_ATTR), pt_to_str(PT_NAME), in_progress_attrtab.zone_attr_name); saw_error = TRUE; return; } err = zonecfg_add_attr(handle, &in_progress_attrtab); } else { err = zonecfg_modify_attr(handle, &old_attrtab, &in_progress_attrtab); } break; default: zone_perror(rt_to_str(resource_scope), Z_NO_RESOURCE_TYPE, TRUE); saw_error = TRUE; return; } if (err != Z_OK) { zone_perror(zone, err, TRUE); } else { need_to_commit = TRUE; global_scope = TRUE; end_op = -1; } } void commit_func(cmd_t *cmd) { int arg; optind = 0; if ((arg = getopt(cmd->cmd_argc, cmd->cmd_argv, "?")) != EOF) { switch (arg) { case '?': longer_usage(CMD_COMMIT); return; default: short_usage(CMD_COMMIT); return; } } if (optind != cmd->cmd_argc) { short_usage(CMD_COMMIT); return; } if (zone_is_read_only(CMD_COMMIT)) return; assert(cmd != NULL); cmd->cmd_argc = 1; /* * 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. */ if ((cmd->cmd_argv[0] = strdup("save")) == NULL) { zone_perror(zone, Z_NOMEM, TRUE); exit(Z_ERR); } cmd->cmd_argv[1] = NULL; verify_func(cmd); } void revert_func(cmd_t *cmd) { char line[128]; /* enough to ask a question */ bool force = FALSE; int err, arg, answer; optind = 0; while ((arg = getopt(cmd->cmd_argc, cmd->cmd_argv, "?F")) != EOF) { switch (arg) { case '?': longer_usage(CMD_REVERT); return; case 'F': force = TRUE; break; default: short_usage(CMD_REVERT); return; } } if (optind != cmd->cmd_argc) { short_usage(CMD_REVERT); return; } if (zone_is_read_only(CMD_REVERT)) return; if (zonecfg_check_handle(handle) != Z_OK) { zerr(gettext("No changes to revert.")); saw_error = TRUE; return; } if (!force) { (void) snprintf(line, sizeof (line), gettext("Are you sure you want to revert")); 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_REVERT)); exit(Z_ERR); } if (answer != 1) return; } /* * Time for a new handle: finish the old one off first * then get a new one properly to avoid leaks. */ 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) { saw_error = TRUE; got_handle = FALSE; if (err == Z_NO_ZONE) zerr(gettext("%s: no such saved zone to revert to."), zone); else zone_perror(zone, err, TRUE); } } void help_func(cmd_t *cmd) { int i; assert(cmd != NULL); if (cmd->cmd_argc == 0) { usage(TRUE, global_scope ? HELP_SUBCMDS : HELP_RES_SCOPE); return; } if (strcmp(cmd->cmd_argv[0], "usage") == 0) { usage(TRUE, HELP_USAGE); return; } if (strcmp(cmd->cmd_argv[0], "commands") == 0) { usage(TRUE, HELP_SUBCMDS); return; } if (strcmp(cmd->cmd_argv[0], "syntax") == 0) { usage(TRUE, HELP_SYNTAX | HELP_RES_PROPS); return; } if (strcmp(cmd->cmd_argv[0], "-?") == 0) { longer_usage(CMD_HELP); return; } for (i = 0; i <= CMD_MAX; i++) { if (strcmp(cmd->cmd_argv[0], cmd_to_str(i)) == 0) { longer_usage(i); return; } } /* We do not use zerr() here because we do not want its extra \n. */ (void) fprintf(stderr, gettext("Unknown help subject %s. "), cmd->cmd_argv[0]); usage(FALSE, HELP_META); } static int string_to_yyin(char *string) { if ((yyin = tmpfile()) == NULL) { zone_perror(execname, Z_TEMP_FILE, TRUE); return (Z_ERR); } if (fwrite(string, strlen(string), 1, yyin) != 1) { zone_perror(execname, Z_TEMP_FILE, TRUE); return (Z_ERR); } if (fseek(yyin, 0, SEEK_SET) != 0) { zone_perror(execname, Z_TEMP_FILE, TRUE); return (Z_ERR); } return (Z_OK); } /* This is the back-end helper function for read_input() below. */ static int cleanup() { int answer; cmd_t *cmd; 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. */ ok_to_prompt = FALSE; } 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. */ answer = ask_yesno(FALSE, gettext("Resource incomplete; really quit")); if (answer == -1) { zerr(gettext("Resource incomplete.")); return (Z_ERR); } if (answer != 1) { yyin = stdin; return (Z_REPEAT); } } else { saw_error = TRUE; } } /* * Make sure we tried something and that the handle checks * out, or we would get a false error trying to commit. */ if (need_to_commit && zonecfg_check_handle(handle) == Z_OK) { if ((cmd = alloc_cmd()) == NULL) { zone_perror(zone, Z_NOMEM, TRUE); return (Z_ERR); } cmd->cmd_argc = 0; cmd->cmd_argv[0] = NULL; commit_func(cmd); free_cmd(cmd); /* * need_to_commit will get set back to FALSE if the * configuration is saved successfully. */ if (need_to_commit) { if (force_exit) { zerr(gettext("Configuration not saved.")); return (Z_ERR); } answer = ask_yesno(FALSE, gettext("Configuration not saved; really quit")); if (answer == -1) { zerr(gettext("Configuration not saved.")); return (Z_ERR); } if (answer != 1) { time_to_exit = FALSE; yyin = stdin; return (Z_REPEAT); } } } return ((need_to_commit || saw_error) ? Z_ERR : Z_OK); } /* * 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 read_input() { bool yyin_is_a_tty = isatty(fileno(yyin)); /* * 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. */ char prompt[MAXPATHLEN + ZONENAME_MAX + MAX_RT_STRLEN + 5], *line; /* yyin should have been set to the appropriate (FILE *) if not stdin */ newline_terminated = TRUE; for (;;) { if (yyin_is_a_tty) { if (newline_terminated) { if (global_scope) (void) snprintf(prompt, sizeof (prompt), "%s:%s> ", execname, zone); else (void) snprintf(prompt, sizeof (prompt), "%s:%s:%s> ", execname, zone, rt_to_str(resource_scope)); } /* * 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. */ line = gl_get_line(gl, prompt, NULL, -1); if (gl_return_status(gl) == GLR_SIGNAL) { gl_abandon_line(gl); continue; } if (line == NULL) break; (void) string_to_yyin(line); while (!feof(yyin)) yyparse(); } else { yyparse(); } /* Bail out on an error in command file mode. */ if (saw_error && cmd_file_mode && !interactive_mode) time_to_exit = TRUE; if (time_to_exit || (!yyin_is_a_tty && feof(yyin))) 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; interactive_mode = TRUE; 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(); while (err == Z_REPEAT); 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 cmd_file(char *file) { FILE *infile; int err; struct stat statbuf; bool using_real_file = (strcmp(file, "-") != 0); if (using_real_file) { /* * zerr() prints a line number in cmd_file_mode, which we do * not want here, so temporarily unset it. */ cmd_file_mode = FALSE; if ((infile = fopen(file, "r")) == NULL) { zerr(gettext("could not open file %s: %s"), file, strerror(errno)); return (Z_ERR); } if ((err = fstat(fileno(infile), &statbuf)) != 0) { zerr(gettext("could not stat file %s: %s"), file, strerror(errno)); err = Z_ERR; goto done; } if (!S_ISREG(statbuf.st_mode)) { zerr(gettext("%s is not a regular file."), file); err = Z_ERR; goto done; } yyin = infile; cmd_file_mode = TRUE; ok_to_prompt = FALSE; } else { /* * "-f -" is essentially the same as interactive mode, * so treat it that way. */ interactive_mode = TRUE; } /* Z_REPEAT is for interactive mode; treat it like Z_ERR here. */ if ((err = read_input()) == Z_REPEAT) err = Z_ERR; done: if (using_real_file) (void) fclose(infile); 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 one_command_at_a_time(int argc, char *argv[]) { char *command; size_t len = 2; /* terminal \n\0 */ int i, err; for (i = 0; i < argc; i++) len += strlen(argv[i]) + 1; if ((command = malloc(len)) == NULL) { zone_perror(execname, Z_NOMEM, TRUE); return (Z_ERR); } (void) strlcpy(command, argv[0], len); for (i = 1; i < argc; i++) { (void) strlcat(command, " ", len); (void) strlcat(command, argv[i], len); } (void) strlcat(command, "\n", len); err = string_to_yyin(command); free(command); if (err != Z_OK) return (err); while (!feof(yyin)) yyparse(); return (cleanup()); } static char * get_execbasename(char *execfullname) { char *last_slash, *execbasename; /* guard against '/' at end of command invocation */ for (;;) { last_slash = strrchr(execfullname, '/'); if (last_slash == NULL) { execbasename = execfullname; break; } else { execbasename = last_slash + 1; if (*execbasename == '\0') { *last_slash = '\0'; continue; } break; } } return (execbasename); } static void validate_zone_name() { regex_t reg; char *locale = NULL, locale_buf[MAXPATHLEN]; if (strcmp(zone, GLOBAL_ZONENAME) == 0) goto err; /* * The regex(5) functions below are locale-sensitive, so save the * user's locale, then set it to "C" for the regex's, and restore * it afterwards. */ if ((locale = setlocale(LC_ALL, NULL)) != NULL) { (void) strlcpy(locale_buf, locale, sizeof (locale_buf)); locale = locale_buf; } (void) setlocale(LC_ALL, "C"); if (regcomp(®, "^" ZONENAME_REGEXP "$", REG_EXTENDED|REG_NOSUB) != 0) goto err; if (regexec(®, zone, (size_t)0, NULL, 0) != 0) goto err; regfree(®); (void) setlocale(LC_ALL, locale); return; err: (void) setlocale(LC_ALL, locale); zone_perror(zone, Z_BOGUS_ZONE_NAME, TRUE); usage(FALSE, HELP_SYNTAX); exit(Z_USAGE); } int main(int argc, char *argv[]) { int err, arg; /* This must be before anything goes to stdout. */ setbuf(stdout, NULL); saw_error = FALSE; cmd_file_mode = FALSE; execname = get_execbasename(argv[0]); (void) setlocale(LC_ALL, ""); (void) textdomain(TEXT_DOMAIN); if (getzoneid() != GLOBAL_ZONEID) { zerr(gettext("%s can only be run from the global zone."), execname); exit(Z_ERR); } if (argc < 2) { usage(FALSE, HELP_USAGE | HELP_SUBCMDS); exit(Z_USAGE); } if (strcmp(argv[1], cmd_to_str(CMD_HELP)) == 0) { (void) one_command_at_a_time(argc - 1, &(argv[1])); exit(Z_OK); } zone = NULL; while ((arg = getopt(argc, argv, "?f:z:")) != EOF) { switch (arg) { case '?': if (optopt == '?') usage(TRUE, HELP_USAGE | HELP_SUBCMDS); else usage(FALSE, HELP_USAGE); exit(Z_USAGE); /* NOTREACHED */ case 'f': cmd_file_name = optarg; cmd_file_mode = TRUE; break; case 'z': zone = optarg; break; default: usage(FALSE, HELP_USAGE); exit(Z_USAGE); } } if (optind > argc || zone == NULL) { usage(FALSE, HELP_USAGE); exit(Z_USAGE); } validate_zone_name(); if (zonecfg_access(zone, W_OK) == Z_OK) { read_only_mode = FALSE; } else { read_only_mode = TRUE; /* skip this message in one-off from command line mode */ if (optind == argc) (void) fprintf(stderr, gettext("WARNING: you do not " "have write access to this zone's configuration " "file;\ngoing into read-only mode.\n")); } if ((handle = zonecfg_init_handle()) == NULL) { zone_perror(execname, Z_NOMEM, TRUE); exit(Z_ERR); } /* * 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 ((gl = new_GetLine(MAX_LINE_LEN, MAX_CMD_HIST)) == NULL) exit(Z_ERR); if (gl_customize_completion(gl, NULL, cmd_cpl_fn) != 0) exit(Z_ERR); (void) sigset(SIGINT, SIG_IGN); if (optind == argc) { if (!cmd_file_mode) err = do_interactive(); else err = cmd_file(cmd_file_name); } else { err = one_command_at_a_time(argc - optind, &(argv[optind])); } zonecfg_fini_handle(handle); (void) del_GetLine(gl); return (err); }