xref: /illumos-gate/usr/src/cmd/zonecfg/zonecfg.c (revision 814a60b13c0ad90e5d2edfd29a7a84bbf416cc1a)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 
23 /*
24  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
25  * Use is subject to license terms.
26  */
27 
28 #pragma ident	"%Z%%M%	%I%	%E% SMI"
29 
30 /*
31  * zonecfg is a lex/yacc based command interpreter used to manage zone
32  * configurations.  The lexer (see zonecfg_lex.l) builds up tokens, which
33  * the grammar (see zonecfg_grammar.y) builds up into commands, some of
34  * which takes resources and/or properties as arguments.  See the block
35  * comments near the end of zonecfg_grammar.y for how the data structures
36  * which keep track of these resources and properties are built up.
37  *
38  * The resource/property data structures are inserted into a command
39  * structure (see zonecfg.h), which also keeps track of command names,
40  * miscellaneous arguments, and function handlers.  The grammar selects
41  * the appropriate function handler, each of which takes a pointer to a
42  * command structure as its sole argument, and invokes it.  The grammar
43  * itself is "entered" (a la the Matrix) by yyparse(), which is called
44  * from read_input(), our main driving function.  That in turn is called
45  * by one of do_interactive(), cmd_file() or one_command_at_a_time(), each
46  * of which is called from main() depending on how the program was invoked.
47  *
48  * The rest of this module consists of the various function handlers and
49  * their helper functions.  Some of these functions, particularly the
50  * X_to_str() functions, which maps command, resource and property numbers
51  * to strings, are used quite liberally, as doing so results in a better
52  * program w/rt I18N, reducing the need for translation notes.
53  */
54 
55 #include <sys/mntent.h>
56 #include <sys/varargs.h>
57 #include <sys/sysmacros.h>
58 
59 #include <errno.h>
60 #include <strings.h>
61 #include <unistd.h>
62 #include <ctype.h>
63 #include <stdlib.h>
64 #include <assert.h>
65 #include <sys/stat.h>
66 #include <zone.h>
67 #include <arpa/inet.h>
68 #include <netdb.h>
69 #include <locale.h>
70 #include <libintl.h>
71 #include <alloca.h>
72 #include <regex.h>
73 #include <signal.h>
74 #include <libtecla.h>
75 
76 #include <libzonecfg.h>
77 #include "zonecfg.h"
78 
79 #if !defined(TEXT_DOMAIN)		/* should be defined by cc -D */
80 #define	TEXT_DOMAIN	"SYS_TEST"	/* Use this only if it wasn't */
81 #endif
82 
83 #define	PAGER	"/usr/bin/more"
84 
85 struct help {
86 	uint_t	cmd_num;
87 	char	*cmd_name;
88 	uint_t	flags;
89 	char	*short_usage;
90 };
91 
92 extern int yyparse(void);
93 extern int lex_lineno;
94 
95 #define	MAX_LINE_LEN	1024
96 #define	MAX_CMD_HIST	1024
97 
98 /*
99  * Each SHELP_ should be a simple string.
100  */
101 
102 #define	SHELP_ADD	"add <resource-type>\n\t(global scope)\n" \
103 	"add <property-name> <property-value>\n\t(resource scope)"
104 #define	SHELP_CANCEL	"cancel"
105 #define	SHELP_COMMIT	"commit"
106 #define	SHELP_CREATE	"create [-F] [ -b | -t <template> ]"
107 #define	SHELP_DELETE	"delete [-F]"
108 #define	SHELP_END	"end"
109 #define	SHELP_EXIT	"exit [-F]"
110 #define	SHELP_EXPORT	"export [-f output-file]"
111 #define	SHELP_HELP	"help [commands] [syntax] [usage] [<command-name>]"
112 #define	SHELP_INFO	"info [<resource-type> [property-name=property-value]*]"
113 #define	SHELP_REMOVE	"remove <resource-type> { <property-name>=<property-" \
114 	"value> }\n\t(global scope)\nremove <property-name>=<property-value>" \
115 	"\n\t(resource scope)"
116 #define	SHELP_REVERT	"revert [-F]"
117 #define	SHELP_SELECT	"select <resource-type> { <property-name>=" \
118 	"<property-value> }"
119 #define	SHELP_SET	"set <property-name>=<property-value>"
120 #define	SHELP_VERIFY	"verify"
121 
122 static struct help helptab[] = {
123 	{ CMD_ADD,	"add",		HELP_RES_PROPS,	SHELP_ADD, },
124 	{ CMD_CANCEL,	"cancel",	0,		SHELP_CANCEL, },
125 	{ CMD_COMMIT,	"commit",	0,		SHELP_COMMIT, },
126 	{ CMD_CREATE,	"create",	0,		SHELP_CREATE, },
127 	{ CMD_DELETE,	"delete",	0,		SHELP_DELETE, },
128 	{ CMD_END,	"end",		0,		SHELP_END, },
129 	{ CMD_EXIT,	"exit",		0,		SHELP_EXIT, },
130 	{ CMD_EXPORT,	"export",	0,		SHELP_EXPORT, },
131 	{ CMD_HELP,	"help",		0,		SHELP_HELP },
132 	{ CMD_INFO,	"info",		HELP_RES_PROPS,	SHELP_INFO, },
133 	{ CMD_REMOVE,	"remove",	HELP_RES_PROPS,	SHELP_REMOVE, },
134 	{ CMD_REVERT,	"revert",	0,		SHELP_REVERT, },
135 	{ CMD_SELECT,	"select",	HELP_RES_PROPS,	SHELP_SELECT, },
136 	{ CMD_SET,	"set",		HELP_PROPS,	SHELP_SET, },
137 	{ CMD_VERIFY,	"verify",	0,		SHELP_VERIFY, },
138 	{ 0 },
139 };
140 
141 #define	MAX_RT_STRLEN	16
142 
143 /* These *must* match the order of the RT_ define's from zonecfg.h */
144 static char *res_types[] = {
145 	"unknown",
146 	"zonepath",
147 	"autoboot",
148 	"pool",
149 	"fs",
150 	"inherit-pkg-dir",
151 	"net",
152 	"device",
153 	"rctl",
154 	"attr",
155 	NULL
156 };
157 
158 /* These *must* match the order of the PT_ define's from zonecfg.h */
159 static char *prop_types[] = {
160 	"unknown",
161 	"zonepath",
162 	"autoboot",
163 	"pool",
164 	"dir",
165 	"special",
166 	"type",
167 	"options",
168 	"address",
169 	"physical",
170 	"name",
171 	"value",
172 	"match",
173 	"priv",
174 	"limit",
175 	"action",
176 	"raw",
177 	NULL
178 };
179 
180 /* These *must* match the order of the PT_ define's from zonecfg.h */
181 static char *prop_val_types[] = {
182 	"simple",
183 	"complex",
184 	"list",
185 };
186 
187 /*
188  * The various _cmds[] lists below are for command tab-completion.
189  */
190 
191 /*
192  * remove has a space afterwards because it has qualifiers; the other commands
193  * that have qualifiers (add, select and set) don't need a space here because
194  * they have their own _cmds[] lists below.
195  */
196 static const char *global_scope_cmds[] = {
197 	"add",
198 	"commit",
199 	"create",
200 	"delete",
201 	"exit",
202 	"export",
203 	"help",
204 	"info",
205 	"remove ",
206 	"revert",
207 	"select",
208 	"set",
209 	"verify",
210 	NULL
211 };
212 
213 static const char *add_cmds[] = {
214 	"add fs",
215 	"add inherit-pkg-dir",
216 	"add net",
217 	"add device",
218 	"add rctl",
219 	"add attr",
220 	NULL
221 };
222 
223 static const char *select_cmds[] = {
224 	"select fs",
225 	"select inherit-pkg-dir",
226 	"select net",
227 	"select device",
228 	"select rctl",
229 	"select attr",
230 	NULL
231 };
232 
233 static const char *set_cmds[] = {
234 	"set zonepath",
235 	"set autoboot",
236 	"set pool",
237 	NULL
238 };
239 
240 static const char *fs_res_scope_cmds[] = {
241 	"add options ",
242 	"cancel",
243 	"end",
244 	"exit",
245 	"help",
246 	"info",
247 	"set dir=",
248 	"set raw=",
249 	"set special=",
250 	"set type=",
251 	NULL
252 };
253 
254 static const char *net_res_scope_cmds[] = {
255 	"cancel",
256 	"end",
257 	"exit",
258 	"help",
259 	"info",
260 	"set address=",
261 	"set physical=",
262 	NULL
263 };
264 
265 static const char *ipd_res_scope_cmds[] = {
266 	"cancel",
267 	"end",
268 	"exit",
269 	"help",
270 	"info",
271 	"set dir=",
272 	NULL
273 };
274 
275 static const char *device_res_scope_cmds[] = {
276 	"cancel",
277 	"end",
278 	"exit",
279 	"help",
280 	"info",
281 	"set match=",
282 	NULL
283 };
284 
285 static const char *attr_res_scope_cmds[] = {
286 	"cancel",
287 	"end",
288 	"exit",
289 	"help",
290 	"info",
291 	"set name=",
292 	"set type=",
293 	"set value=",
294 	NULL
295 };
296 
297 static const char *rctl_res_scope_cmds[] = {
298 	"add value ",
299 	"cancel",
300 	"end",
301 	"exit",
302 	"help",
303 	"info",
304 	"set name=",
305 	NULL
306 };
307 
308 /* Global variables */
309 
310 /* set early in main(), never modified thereafter, used all over the place */
311 static char *execname;
312 
313 /* set in main(), used all over the place */
314 static zone_dochandle_t handle;
315 
316 /* used all over the place */
317 static char *zone;
318 
319 /* set in modifying functions, checked in read_input() */
320 static bool need_to_commit = FALSE;
321 bool saw_error;
322 
323 /* set in yacc parser, checked in read_input() */
324 bool newline_terminated;
325 
326 /* set in main(), checked in lex error handler */
327 bool cmd_file_mode;
328 
329 /* set in exit_func(), checked in read_input() */
330 static bool time_to_exit = FALSE, force_exit = FALSE;
331 
332 /* used in short_usage() and zerr() */
333 static char *cmd_file_name = NULL;
334 
335 /* checked in read_input() and other places */
336 static bool ok_to_prompt = FALSE;
337 
338 /* set and checked in initialize() */
339 static bool got_handle = FALSE;
340 
341 /* initialized in do_interactive(), checked in initialize() */
342 static bool interactive_mode;
343 
344 /* set in main(), checked in multiple places */
345 static bool read_only_mode;
346 
347 /* set in check_if_zone_already_exists(), checked in save_it() */
348 static bool new_zone = FALSE;
349 
350 static bool global_scope = TRUE; /* scope is outer/global or inner/resource */
351 static int resource_scope;	/* should be in the RT_ list from zonecfg.h */
352 static int end_op = -1;		/* operation on end is either add or modify */
353 
354 int num_prop_vals;		/* for grammar */
355 
356 /*
357  * These are for keeping track of resources as they are specified as part of
358  * the multi-step process.  They should be initialized by add_resource() or
359  * select_func() and filled in by add_property() or set_func().
360  */
361 static struct zone_fstab	old_fstab, in_progress_fstab;
362 static struct zone_fstab	old_ipdtab, in_progress_ipdtab;
363 static struct zone_nwiftab	old_nwiftab, in_progress_nwiftab;
364 static struct zone_devtab	old_devtab, in_progress_devtab;
365 static struct zone_rctltab	old_rctltab, in_progress_rctltab;
366 static struct zone_attrtab	old_attrtab, in_progress_attrtab;
367 
368 static GetLine *gl;	/* The gl_get_line() resource object */
369 
370 /* Functions begin here */
371 
372 static bool
373 initial_match(const char *line1, const char *line2, int word_end)
374 {
375 	if (word_end <= 0)
376 		return (TRUE);
377 	return (strncmp(line1, line2, word_end) == 0);
378 }
379 
380 static int
381 add_stuff(WordCompletion *cpl, const char *line1, const char **list,
382     int word_end)
383 {
384 	int i, err;
385 
386 	for (i = 0; list[i] != NULL; i++) {
387 		if (initial_match(line1, list[i], word_end)) {
388 			err = cpl_add_completion(cpl, line1, 0, word_end,
389 			    list[i] + word_end, "", "");
390 			if (err != 0)
391 				return (err);
392 		}
393 	}
394 	return (0);
395 }
396 
397 static
398 /* ARGSUSED */
399 CPL_MATCH_FN(cmd_cpl_fn)
400 {
401 	if (global_scope) {
402 		/*
403 		 * The MAX/MIN tests below are to make sure we have at least
404 		 * enough characters to distinguish from other prefixes (MAX)
405 		 * but only check MIN(what we have, what we're checking).
406 		 */
407 		if (strncmp(line, "add ", MAX(MIN(word_end, 4), 1)) == 0)
408 			return (add_stuff(cpl, line, add_cmds, word_end));
409 		if (strncmp(line, "select ", MAX(MIN(word_end, 7), 3)) == 0)
410 			return (add_stuff(cpl, line, select_cmds, word_end));
411 		if (strncmp(line, "set ", MAX(MIN(word_end, 4), 3)) == 0)
412 			return (add_stuff(cpl, line, set_cmds, word_end));
413 		return (add_stuff(cpl, line, global_scope_cmds, word_end));
414 	}
415 	switch (resource_scope) {
416 	case RT_FS:
417 		return (add_stuff(cpl, line, fs_res_scope_cmds, word_end));
418 	case RT_IPD:
419 		return (add_stuff(cpl, line, ipd_res_scope_cmds, word_end));
420 	case RT_NET:
421 		return (add_stuff(cpl, line, net_res_scope_cmds, word_end));
422 	case RT_DEVICE:
423 		return (add_stuff(cpl, line, device_res_scope_cmds, word_end));
424 	case RT_RCTL:
425 		return (add_stuff(cpl, line, rctl_res_scope_cmds, word_end));
426 	case RT_ATTR:
427 		return (add_stuff(cpl, line, attr_res_scope_cmds, word_end));
428 	}
429 	return (0);
430 }
431 
432 /*
433  * For the main CMD_func() functions below, several of them call getopt()
434  * then check optind against argc to make sure an extra parameter was not
435  * passed in.  The reason this is not caught in the grammar is that the
436  * grammar just checks for a miscellaneous TOKEN, which is *expected* to
437  * be "-F" (for example), but could be anything.  So (for example) this
438  * check will prevent "create bogus".
439  */
440 
441 cmd_t *
442 alloc_cmd(void)
443 {
444 	return (calloc(1, sizeof (cmd_t)));
445 }
446 
447 void
448 free_cmd(cmd_t *cmd)
449 {
450 	int i;
451 
452 	for (i = 0; i < MAX_EQ_PROP_PAIRS; i++)
453 		if (cmd->cmd_property_ptr[i] != NULL) {
454 			property_value_ptr_t pp = cmd->cmd_property_ptr[i];
455 
456 			switch (pp->pv_type) {
457 			case PROP_VAL_SIMPLE:
458 				free(pp->pv_simple);
459 				break;
460 			case PROP_VAL_COMPLEX:
461 				free_complex(pp->pv_complex);
462 				break;
463 			case PROP_VAL_LIST:
464 				free_list(pp->pv_list);
465 				break;
466 			}
467 		}
468 	for (i = 0; i < cmd->cmd_argc; i++)
469 		free(cmd->cmd_argv[i]);
470 	free(cmd);
471 }
472 
473 complex_property_ptr_t
474 alloc_complex(void)
475 {
476 	return (calloc(1, sizeof (complex_property_t)));
477 }
478 
479 void
480 free_complex(complex_property_ptr_t complex)
481 {
482 	if (complex == NULL)
483 		return;
484 	free_complex(complex->cp_next);
485 	if (complex->cp_value != NULL)
486 		free(complex->cp_value);
487 	free(complex);
488 }
489 
490 list_property_ptr_t
491 alloc_list(void)
492 {
493 	return (calloc(1, sizeof (list_property_t)));
494 }
495 
496 void
497 free_list(list_property_ptr_t list)
498 {
499 	if (list == NULL)
500 		return;
501 	if (list->lp_simple != NULL)
502 		free(list->lp_simple);
503 	free_complex(list->lp_complex);
504 	free_list(list->lp_next);
505 	free(list);
506 }
507 
508 void
509 free_outer_list(list_property_ptr_t list)
510 {
511 	if (list == NULL)
512 		return;
513 	free_outer_list(list->lp_next);
514 	free(list);
515 }
516 
517 static struct zone_rctlvaltab *
518 alloc_rctlvaltab(void)
519 {
520 	return (calloc(1, sizeof (struct zone_rctlvaltab)));
521 }
522 
523 static char *
524 rt_to_str(int res_type)
525 {
526 	assert(res_type >= RT_MIN && res_type <= RT_MAX);
527 	return (res_types[res_type]);
528 }
529 
530 static char *
531 pt_to_str(int prop_type)
532 {
533 	assert(prop_type >= PT_MIN && prop_type <= PT_MAX);
534 	return (prop_types[prop_type]);
535 }
536 
537 static char *
538 pvt_to_str(int pv_type)
539 {
540 	assert(pv_type >= PROP_VAL_MIN && pv_type <= PROP_VAL_MAX);
541 	return (prop_val_types[pv_type]);
542 }
543 
544 static char *
545 cmd_to_str(int cmd_num)
546 {
547 	assert(cmd_num >= CMD_MIN && cmd_num <= CMD_MAX);
548 	return (helptab[cmd_num].cmd_name);
549 }
550 
551 /*
552  * This is a separate function rather than a set of define's because of the
553  * gettext() wrapping.
554  */
555 
556 /*
557  * TRANSLATION_NOTE
558  * Each string below should have \t follow \n whenever needed; the
559  * initial \t and the terminal \n will be provided by the calling function.
560  */
561 
562 static char *
563 long_help(int cmd_num)
564 {
565 	static char line[1024];	/* arbitrary large amount */
566 
567 	assert(cmd_num >= CMD_MIN && cmd_num <= CMD_MAX);
568 	switch (cmd_num) {
569 		case CMD_HELP:
570 			return (gettext("Prints help message."));
571 		case CMD_CREATE:
572 			(void) snprintf(line, sizeof (line),
573 			    gettext("Creates a configuration for the "
574 			    "specified zone.  %s should be\n\tused to "
575 			    "begin configuring a new zone.  If overwriting an "
576 			    "existing\n\tconfiguration, the -F flag can be "
577 			    "used to force the action.  If\n\t-t template is "
578 			    "given, creates a configuration identical to the\n"
579 			    "\tspecified template, except that the zone name "
580 			    "is changed from\n\ttemplate to zonename.  '%s -b' "
581 			    "results in a blank configuration.\n\t'%s' with no "
582 			    "arguments applies the Sun default settings."),
583 			    cmd_to_str(CMD_CREATE), cmd_to_str(CMD_CREATE),
584 			    cmd_to_str(CMD_CREATE));
585 			return (line);
586 		case CMD_EXIT:
587 			return (gettext("Exits the program.  The -F flag can "
588 			    "be used to force the action."));
589 		case CMD_EXPORT:
590 			return (gettext("Prints configuration to standard "
591 			    "output, or to output-file if\n\tspecified, in "
592 			    "a form suitable for use in a command-file."));
593 		case CMD_ADD:
594 			return (gettext("Add specified resource to "
595 			    "configuration."));
596 		case CMD_DELETE:
597 			return (gettext("Deletes the specified zone.  The -F "
598 			    "flag can be used to force the\n\taction."));
599 		case CMD_REMOVE:
600 			return (gettext("Remove specified resource from "
601 			    "configuration.  Note that the curly\n\tbraces "
602 			    "('{', '}') mean one or more of whatever "
603 			    "is between them."));
604 		case CMD_SELECT:
605 			(void) snprintf(line, sizeof (line),
606 			    gettext("Selects a resource to modify.  "
607 			    "Resource modification is completed\n\twith the "
608 			    "command \"%s\".  The property name/value pairs "
609 			    "must uniquely\n\tidentify a resource.  Note that "
610 			    "the curly braces ('{', '}') mean one\n\tor more "
611 			    "of whatever is between them."),
612 			    cmd_to_str(CMD_END));
613 			return (line);
614 		case CMD_SET:
615 			return (gettext("Sets property values."));
616 		case CMD_INFO:
617 			return (gettext("Displays information about the "
618 			    "current configuration.  If resource\n\ttype is "
619 			    "specified, displays only information about "
620 			    "resources of\n\tthe relevant type.  If resource "
621 			    "id is specified, displays only\n\tinformation "
622 			    "about that resource."));
623 		case CMD_VERIFY:
624 			return (gettext("Verifies current configuration "
625 			    "for correctness (some resource types\n\thave "
626 			    "required properties)."));
627 		case CMD_COMMIT:
628 			(void) snprintf(line, sizeof (line),
629 			    gettext("Commits current configuration.  "
630 			    "Configuration must be committed to\n\tbe used by "
631 			    "%s.  Until the configuration is committed, "
632 			    "changes \n\tcan be removed with the %s "
633 			    "command.  This operation is\n\tattempted "
634 			    "automatically upon completion of a %s "
635 			    "session."), "zoneadm", cmd_to_str(CMD_REVERT),
636 			    "zonecfg");
637 			return (line);
638 		case CMD_REVERT:
639 			return (gettext("Reverts configuration back to the "
640 			    "last committed state.  The -F flag\n\tcan be "
641 			    "used to force the action."));
642 		case CMD_CANCEL:
643 			return (gettext("Cancels resource/property "
644 			    "specification."));
645 		case CMD_END:
646 			return (gettext("Ends resource/property "
647 			    "specification."));
648 	}
649 	/* NOTREACHED */
650 	return (NULL);
651 }
652 
653 /*
654  * Called with verbose TRUE when help is explicitly requested, FALSE for
655  * unexpected errors.
656  */
657 
658 void
659 usage(bool verbose, uint_t flags)
660 {
661 	FILE *fp = verbose ? stdout : stderr, *newfp;
662 	bool need_to_close = FALSE;
663 	char *pager;
664 	int i;
665 
666 	/* don't page error output */
667 	if (verbose && interactive_mode) {
668 		if ((pager = getenv("PAGER")) == NULL)
669 			pager = PAGER;
670 		if ((newfp = popen(pager, "w")) != NULL) {
671 			need_to_close = TRUE;
672 			fp = newfp;
673 		}
674 	}
675 	if (flags & HELP_META) {
676 		(void) fprintf(fp, gettext("More help is available for the "
677 		    "following:\n"));
678 		(void) fprintf(fp, "\n\tcommands ('%s commands')\n",
679 		    cmd_to_str(CMD_HELP));
680 		(void) fprintf(fp, "\tsyntax ('%s syntax')\n",
681 		    cmd_to_str(CMD_HELP));
682 		(void) fprintf(fp, "\tusage ('%s usage')\n\n",
683 		    cmd_to_str(CMD_HELP));
684 		(void) fprintf(fp, gettext("You may also obtain help on any "
685 		    "command by typing '%s <command-name>.'\n"),
686 		    cmd_to_str(CMD_HELP));
687 	}
688 	if (flags & HELP_RES_SCOPE) {
689 		switch (resource_scope) {
690 		case RT_FS:
691 			(void) fprintf(fp, gettext("The '%s' resource scope is "
692 			    "used to configure a file-system.\n"),
693 			    rt_to_str(resource_scope));
694 			(void) fprintf(fp, gettext("Valid commands:\n"));
695 			(void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
696 			    pt_to_str(PT_DIR), gettext("<path>"));
697 			(void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
698 			    pt_to_str(PT_SPECIAL), gettext("<path>"));
699 			(void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
700 			    pt_to_str(PT_RAW), gettext("<raw-device>"));
701 			(void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
702 			    pt_to_str(PT_TYPE), gettext("<file-system type>"));
703 			(void) fprintf(fp, "\t%s %s %s\n", cmd_to_str(CMD_ADD),
704 			    pt_to_str(PT_OPTIONS),
705 			    gettext("<file-system options>"));
706 			(void) fprintf(fp, gettext("Consult the file-system "
707 			    "specific manual page, such as mount_ufs(1M), "
708 			    "for\ndetails about file-system options.  Note "
709 			    "that any file-system options with an\nembedded "
710 			    "'=' character must be enclosed in double quotes, "
711 			    /*CSTYLED*/
712 			    "such as \"%s=5\".\n"), MNTOPT_RETRY);
713 			break;
714 		case RT_IPD:
715 			(void) fprintf(fp, gettext("The '%s' resource scope is "
716 			    "used to configure a directory\ninherited from the "
717 			    "global zone into a non-global zone in read-only "
718 			    "mode.\n"), rt_to_str(resource_scope));
719 			(void) fprintf(fp, gettext("Valid commands:\n"));
720 			(void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
721 			    pt_to_str(PT_DIR), gettext("<path>"));
722 			break;
723 		case RT_NET:
724 			(void) fprintf(fp, gettext("The '%s' resource scope is "
725 			    "used to configure a network interface.\n"),
726 			    rt_to_str(resource_scope));
727 			(void) fprintf(fp, gettext("Valid commands:\n"));
728 			(void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
729 			    pt_to_str(PT_ADDRESS), gettext("<IP-address>"));
730 			(void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
731 			    pt_to_str(PT_PHYSICAL), gettext("<interface>"));
732 			(void) fprintf(fp, gettext("See ifconfig(1M) for "
733 			    "details of the <interface> string.\n"));
734 			break;
735 		case RT_DEVICE:
736 			(void) fprintf(fp, gettext("The '%s' resource scope is "
737 			    "used to configure a device node.\n"),
738 			    rt_to_str(resource_scope));
739 			(void) fprintf(fp, gettext("Valid commands:\n"));
740 			(void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
741 			    pt_to_str(PT_MATCH), gettext("<device-path>"));
742 			break;
743 		case RT_RCTL:
744 			(void) fprintf(fp, gettext("The '%s' resource scope is "
745 			    "used to configure a resource control.\n"),
746 			    rt_to_str(resource_scope));
747 			(void) fprintf(fp, gettext("Valid commands:\n"));
748 			(void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
749 			    pt_to_str(PT_NAME), gettext("<string>"));
750 			(void) fprintf(fp, "\t%s %s (%s=%s,%s=%s,%s=%s)\n",
751 			    cmd_to_str(CMD_ADD), pt_to_str(PT_VALUE),
752 			    pt_to_str(PT_PRIV), gettext("<priv-value>"),
753 			    pt_to_str(PT_LIMIT), gettext("<number>"),
754 			    pt_to_str(PT_ACTION), gettext("<action-value>"));
755 			(void) fprintf(fp, "%s\n\t%s := privileged\n"
756 			    "\t%s := none | deny\n", gettext("Where"),
757 			    gettext("<priv-value>"), gettext("<action-value>"));
758 			break;
759 		case RT_ATTR:
760 			(void) fprintf(fp, gettext("The '%s' resource scope is "
761 			    "used to configure a generic attribute.\n"),
762 			    rt_to_str(resource_scope));
763 			(void) fprintf(fp, gettext("Valid commands:\n"));
764 			(void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
765 			    pt_to_str(PT_NAME), gettext("<name>"));
766 			(void) fprintf(fp, "\t%s %s=boolean\n",
767 			    cmd_to_str(CMD_SET), pt_to_str(PT_TYPE));
768 			(void) fprintf(fp, "\t%s %s=true | false\n",
769 			    cmd_to_str(CMD_SET), pt_to_str(PT_VALUE));
770 			(void) fprintf(fp, gettext("or\n"));
771 			(void) fprintf(fp, "\t%s %s=int\n", cmd_to_str(CMD_SET),
772 			    pt_to_str(PT_TYPE));
773 			(void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
774 			    pt_to_str(PT_VALUE), gettext("<integer>"));
775 			(void) fprintf(fp, gettext("or\n"));
776 			(void) fprintf(fp, "\t%s %s=string\n",
777 			    cmd_to_str(CMD_SET), pt_to_str(PT_TYPE));
778 			(void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
779 			    pt_to_str(PT_VALUE), gettext("<string>"));
780 			(void) fprintf(fp, gettext("or\n"));
781 			(void) fprintf(fp, "\t%s %s=uint\n",
782 			    cmd_to_str(CMD_SET), pt_to_str(PT_TYPE));
783 			(void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
784 			    pt_to_str(PT_VALUE), gettext("<unsigned integer>"));
785 			break;
786 		}
787 		(void) fprintf(fp, gettext("And from any resource scope, you "
788 		    "can:\n"));
789 		(void) fprintf(fp, "\t%s\t%s\n", cmd_to_str(CMD_END),
790 		    gettext("(to conclude this operation)"));
791 		(void) fprintf(fp, "\t%s\t%s\n", cmd_to_str(CMD_CANCEL),
792 		    gettext("(to cancel this operation)"));
793 		(void) fprintf(fp, "\t%s\t%s\n", cmd_to_str(CMD_EXIT),
794 		    gettext("(to exit the zonecfg utility)"));
795 	}
796 	if (flags & HELP_USAGE) {
797 		(void) fprintf(fp, "%s:\t%s %s\n", gettext("usage"),
798 		    execname, cmd_to_str(CMD_HELP));
799 		(void) fprintf(fp, "\t%s -z <zone>\t\t\t(%s)\n",
800 		    execname, gettext("interactive"));
801 		(void) fprintf(fp, "\t%s -z <zone> <command>\n", execname);
802 		(void) fprintf(fp, "\t%s -z <zone> -f <command-file>\n",
803 		    execname);
804 	}
805 	if (flags & HELP_SUBCMDS) {
806 		(void) fprintf(fp, "%s:\n\n", gettext("Commands"));
807 		for (i = 0; i <= CMD_MAX; i++) {
808 			(void) fprintf(fp, "%s\n", helptab[i].short_usage);
809 			if (verbose)
810 				(void) fprintf(fp, "\t%s\n\n", long_help(i));
811 		}
812 	}
813 	if (flags & HELP_SYNTAX) {
814 		if (!verbose)
815 			(void) fprintf(fp, "\n");
816 		(void) fprintf(fp, "<zone> := [A-Za-z0-9][A-Za-z0-9_.-]*\n");
817 		(void) fprintf(fp, gettext("\t(except the reserved words "
818 		    "'%s' and anything starting with '%s')\n"), "global",
819 		    "SUNW");
820 		(void) fprintf(fp,
821 		    gettext("\tName must be less than %d characters.\n"),
822 		    ZONENAME_MAX);
823 		if (verbose)
824 			(void) fprintf(fp, "\n");
825 	}
826 	if (flags & HELP_NETADDR) {
827 		(void) fprintf(fp, gettext("\n<net-addr> :="));
828 		(void) fprintf(fp,
829 		    gettext("\t<IPv4-address>[/<IPv4-prefix-length>] |\n"));
830 		(void) fprintf(fp,
831 		    gettext("\t\t<IPv6-address>/<IPv6-prefix-length> |\n"));
832 		(void) fprintf(fp,
833 		    gettext("\t\t<hostname>[/<IPv4-prefix-length>]\n"));
834 		(void) fprintf(fp, gettext("See inet(3SOCKET) for IPv4 and "
835 		    "IPv6 address syntax.\n"));
836 		(void) fprintf(fp, gettext("<IPv4-prefix-length> := [0-32]\n"));
837 		(void) fprintf(fp,
838 		    gettext("<IPv6-prefix-length> := [0-128]\n"));
839 		(void) fprintf(fp,
840 		    gettext("<hostname> := [A-Za-z0-9][A-Za-z0-9-.]*\n"));
841 	}
842 	if (flags & HELP_RESOURCES) {
843 		(void) fprintf(fp, "<%s> := %s | %s | %s | %s | %s | %s\n\n",
844 		    gettext("resource type"), rt_to_str(RT_FS),
845 		    rt_to_str(RT_IPD), rt_to_str(RT_NET), rt_to_str(RT_DEVICE),
846 		    rt_to_str(RT_RCTL), rt_to_str(RT_ATTR));
847 	}
848 	if (flags & HELP_PROPS) {
849 		(void) fprintf(fp, gettext("For resource type ... there are "
850 		    "property types ...:\n"));
851 		(void) fprintf(fp, "\t%s\t%s\n", gettext("(global)"),
852 		    pt_to_str(PT_ZONEPATH));
853 		(void) fprintf(fp, "\t%s\t%s\n", gettext("(global)"),
854 		    pt_to_str(PT_AUTOBOOT));
855 		(void) fprintf(fp, "\t%s\t%s\n", gettext("(global)"),
856 		    pt_to_str(PT_POOL));
857 		(void) fprintf(fp, "\t%s\t\t%s, %s, %s, %s\n", rt_to_str(RT_FS),
858 		    pt_to_str(PT_DIR), pt_to_str(PT_SPECIAL),
859 		    pt_to_str(PT_RAW), pt_to_str(PT_TYPE),
860 		    pt_to_str(PT_OPTIONS));
861 		(void) fprintf(fp, "\t%s\t%s\n", rt_to_str(RT_IPD),
862 		    pt_to_str(PT_DIR));
863 		(void) fprintf(fp, "\t%s\t\t%s, %s\n", rt_to_str(RT_NET),
864 		    pt_to_str(PT_ADDRESS), pt_to_str(PT_PHYSICAL));
865 		(void) fprintf(fp, "\t%s\t\t%s\n", rt_to_str(RT_DEVICE),
866 		    pt_to_str(PT_MATCH));
867 		(void) fprintf(fp, "\t%s\t\t%s, %s\n", rt_to_str(RT_RCTL),
868 		    pt_to_str(PT_NAME), pt_to_str(PT_VALUE));
869 		(void) fprintf(fp, "\t%s\t\t%s, %s, %s\n", rt_to_str(RT_ATTR),
870 		    pt_to_str(PT_NAME), pt_to_str(PT_TYPE),
871 		    pt_to_str(PT_VALUE));
872 	}
873 	if (need_to_close)
874 		(void) pclose(fp);
875 }
876 
877 /* PRINTFLIKE1 */
878 static void
879 zerr(const char *fmt, ...)
880 {
881 	va_list alist;
882 	static int last_lineno;
883 
884 	/* lex_lineno has already been incremented in the lexer; compensate */
885 	if (cmd_file_mode && lex_lineno > last_lineno) {
886 		if (strcmp(cmd_file_name, "-") == 0)
887 			(void) fprintf(stderr, gettext("On line %d:\n"),
888 			    lex_lineno - 1);
889 		else
890 			(void) fprintf(stderr, gettext("On line %d of %s:\n"),
891 			    lex_lineno - 1, cmd_file_name);
892 		last_lineno = lex_lineno;
893 	}
894 	va_start(alist, fmt);
895 	(void) vfprintf(stderr, fmt, alist);
896 	(void) fprintf(stderr, "\n");
897 	va_end(alist);
898 }
899 
900 static void
901 zone_perror(char *prefix, int err, bool set_saw)
902 {
903 	zerr("%s: %s", prefix, zonecfg_strerror(err));
904 	if (set_saw)
905 		saw_error = TRUE;
906 }
907 
908 /*
909  * zone_perror() expects a single string, but for remove and select
910  * we have both the command and the resource type, so this wrapper
911  * function serves the same purpose in a slightly different way.
912  */
913 
914 static void
915 z_cmd_rt_perror(int cmd_num, int res_num, int err, bool set_saw)
916 {
917 	zerr("%s %s: %s", cmd_to_str(cmd_num), rt_to_str(res_num),
918 	    zonecfg_strerror(err));
919 	if (set_saw)
920 		saw_error = TRUE;
921 }
922 
923 /* returns Z_OK if successful, Z_foo from <libzonecfg.h> otherwise */
924 static int
925 initialize(bool handle_expected)
926 {
927 	int err;
928 
929 	if (zonecfg_check_handle(handle) != Z_OK) {
930 		if ((err = zonecfg_get_handle(zone, handle)) == Z_OK) {
931 			got_handle = TRUE;
932 		} else {
933 			zone_perror(zone, err, handle_expected || got_handle);
934 			if (err == Z_NO_ZONE && !got_handle &&
935 			    interactive_mode && !read_only_mode)
936 				(void) printf(gettext("Use '%s' to begin "
937 				    "configuring a new zone.\n"),
938 				    cmd_to_str(CMD_CREATE));
939 			return (err);
940 		}
941 	}
942 	return (Z_OK);
943 }
944 
945 /*
946  * short_usage() is for bad syntax: getopt() issues, too many arguments, etc.
947  */
948 
949 void
950 short_usage(int command)
951 {
952 	/* lex_lineno has already been incremented in the lexer; compensate */
953 	if (cmd_file_mode) {
954 		if (strcmp(cmd_file_name, "-") == 0)
955 			(void) fprintf(stderr,
956 			    gettext("syntax error on line %d\n"),
957 			    lex_lineno - 1);
958 		else
959 			(void) fprintf(stderr,
960 			    gettext("syntax error on line %d of %s\n"),
961 			    lex_lineno - 1, cmd_file_name);
962 	}
963 	(void) fprintf(stderr, "%s:\n%s\n", gettext("usage"),
964 	    helptab[command].short_usage);
965 	saw_error = TRUE;
966 }
967 
968 /*
969  * long_usage() is for bad semantics: e.g., wrong property type for a given
970  * resource type.  It is also used by longer_usage() below.
971  */
972 
973 void
974 long_usage(uint_t cmd_num, bool set_saw)
975 {
976 	(void) fprintf(set_saw ? stderr : stdout, "%s:\n%s\n", gettext("usage"),
977 	    helptab[cmd_num].short_usage);
978 	(void) fprintf(set_saw ? stderr : stdout, "\t%s\n", long_help(cmd_num));
979 	if (set_saw)
980 		saw_error = TRUE;
981 }
982 
983 /*
984  * longer_usage() is for 'help foo' and 'foo -?': call long_usage() and also
985  * any extra usage() flags as appropriate for whatever command.
986  */
987 
988 void
989 longer_usage(uint_t cmd_num)
990 {
991 	long_usage(cmd_num, FALSE);
992 	if (helptab[cmd_num].flags != 0) {
993 		(void) printf("\n");
994 		usage(TRUE, helptab[cmd_num].flags);
995 	}
996 }
997 
998 /*
999  * scope_usage() is simply used when a command is called from the wrong scope.
1000  */
1001 
1002 static void
1003 scope_usage(uint_t cmd_num)
1004 {
1005 	zerr(gettext("The %s command only makes sense in the %s scope."),
1006 	    cmd_to_str(cmd_num),
1007 	    global_scope ?  gettext("resource") : gettext("global"));
1008 	saw_error = TRUE;
1009 }
1010 
1011 /*
1012  * On input, TRUE => yes, FALSE => no.
1013  * On return, TRUE => 1, FALSE => no, could not ask => -1.
1014  */
1015 
1016 static int
1017 ask_yesno(bool default_answer, const char *question)
1018 {
1019 	char line[64];	/* should be enough to answer yes or no */
1020 
1021 	if (!ok_to_prompt) {
1022 		saw_error = TRUE;
1023 		return (-1);
1024 	}
1025 	for (;;) {
1026 		(void) printf("%s (%s)? ", question,
1027 		    default_answer ? "[y]/n" : "y/[n]");
1028 		if (fgets(line, sizeof (line), stdin) == NULL ||
1029 		    line[0] == '\n')
1030 			return (default_answer ? 1 : 0);
1031 		if (tolower(line[0]) == 'y')
1032 			return (1);
1033 		if (tolower(line[0]) == 'n')
1034 			return (0);
1035 	}
1036 }
1037 
1038 /*
1039  * Prints warning if zone already exists.
1040  * In interactive mode, prompts if we should continue anyway and returns Z_OK
1041  * if so, Z_ERR if not.  In non-interactive mode, exits with Z_ERR.
1042  *
1043  * Note that if a zone exists and its state is >= INSTALLED, an error message
1044  * will be printed and this function will return Z_ERR regardless of mode.
1045  */
1046 
1047 static int
1048 check_if_zone_already_exists(bool force)
1049 {
1050 	char line[ZONENAME_MAX + 128];	/* enough to ask a question */
1051 	zone_state_t state_num;
1052 	zone_dochandle_t tmphandle;
1053 	int res, answer;
1054 
1055 	if ((tmphandle = zonecfg_init_handle()) == NULL) {
1056 		zone_perror(execname, Z_NOMEM, TRUE);
1057 		exit(Z_ERR);
1058 	}
1059 	res = zonecfg_get_handle(zone, tmphandle);
1060 	zonecfg_fini_handle(tmphandle);
1061 	if (res != Z_OK) {
1062 		new_zone = TRUE;
1063 		return (Z_OK);
1064 	}
1065 	if (zone_get_state(zone, &state_num) == Z_OK &&
1066 	    state_num >= ZONE_STATE_INSTALLED) {
1067 		zerr(gettext("Zone %s already installed; %s not allowed."),
1068 		    zone, cmd_to_str(CMD_CREATE));
1069 		return (Z_ERR);
1070 	}
1071 
1072 	if (force) {
1073 		(void) printf(gettext("Zone %s already exists; overwriting.\n"),
1074 		    zone);
1075 		return (Z_OK);
1076 	}
1077 	(void) snprintf(line, sizeof (line),
1078 	    gettext("Zone %s already exists; %s anyway"), zone,
1079 	    cmd_to_str(CMD_CREATE));
1080 	if ((answer = ask_yesno(FALSE, line)) == -1) {
1081 		zerr(gettext("Zone exists, input not from terminal and -F not "
1082 		    "specified:\n%s command ignored, exiting."),
1083 		    cmd_to_str(CMD_CREATE));
1084 		exit(Z_ERR);
1085 	}
1086 	return (answer == 1 ? Z_OK : Z_ERR);
1087 }
1088 
1089 static bool
1090 zone_is_read_only(int cmd_num)
1091 {
1092 	if (strncmp(zone, "SUNW", 4) == 0) {
1093 		zerr(gettext("%s: zones beginning with SUNW are read-only."),
1094 		    zone);
1095 		saw_error = TRUE;
1096 		return (TRUE);
1097 	}
1098 	if (read_only_mode) {
1099 		zerr(gettext("%s: cannot %s in read-only mode."), zone,
1100 		    cmd_to_str(cmd_num));
1101 		saw_error = TRUE;
1102 		return (TRUE);
1103 	}
1104 	return (FALSE);
1105 }
1106 
1107 /*
1108  * Create a new configuration.
1109  */
1110 void
1111 create_func(cmd_t *cmd)
1112 {
1113 	int err, arg;
1114 	char zone_template[ZONENAME_MAX];
1115 	zone_dochandle_t tmphandle;
1116 	bool force = FALSE;
1117 
1118 	assert(cmd != NULL);
1119 
1120 	/* This is the default if no arguments are given. */
1121 	(void) strlcpy(zone_template, "SUNWdefault", sizeof (zone_template));
1122 
1123 	optind = 0;
1124 	while ((arg = getopt(cmd->cmd_argc, cmd->cmd_argv, "?bFt:")) != EOF) {
1125 		switch (arg) {
1126 		case '?':
1127 			if (optopt == '?')
1128 				longer_usage(CMD_CREATE);
1129 			else
1130 				short_usage(CMD_CREATE);
1131 			return;
1132 		case 'b':
1133 			(void) strlcpy(zone_template, "SUNWblank",
1134 			    sizeof (zone_template));
1135 			break;
1136 		case 'F':
1137 			force = TRUE;
1138 			break;
1139 		case 't':
1140 			(void) strlcpy(zone_template, optarg,
1141 			    sizeof (zone_template));
1142 			break;
1143 		default:
1144 			short_usage(CMD_CREATE);
1145 			return;
1146 		}
1147 	}
1148 	if (optind != cmd->cmd_argc) {
1149 		short_usage(CMD_CREATE);
1150 		return;
1151 	}
1152 
1153 	if (zone_is_read_only(CMD_CREATE))
1154 		return;
1155 
1156 	if (check_if_zone_already_exists(force) != Z_OK)
1157 		return;
1158 
1159 	/*
1160 	 * Get a temporary handle first.  If that fails, the old handle
1161 	 * will not be lost.  Then finish whichever one we don't need,
1162 	 * to avoid leaks.  Then get the handle for zone_template, and
1163 	 * set the name to zone: this "copy, rename" method is how
1164 	 * create -[b|t] works.
1165 	 */
1166 	if ((tmphandle = zonecfg_init_handle()) == NULL) {
1167 		zone_perror(execname, Z_NOMEM, TRUE);
1168 		exit(Z_ERR);
1169 	}
1170 	if ((err = zonecfg_get_handle(zone_template, tmphandle)) != Z_OK) {
1171 		zonecfg_fini_handle(tmphandle);
1172 		zone_perror(zone_template, err, TRUE);
1173 		return;
1174 	}
1175 	zonecfg_fini_handle(handle);
1176 	handle = tmphandle;
1177 	if ((err = zonecfg_set_name(handle, zone)) == Z_OK)
1178 		need_to_commit = TRUE;
1179 	else
1180 		zone_perror(zone, err, TRUE);
1181 }
1182 
1183 /*
1184  * This malloc()'s memory, which must be freed by the caller.
1185  */
1186 static char *
1187 quoteit(char *instr)
1188 {
1189 	char *outstr;
1190 	size_t outstrsize = strlen(instr) + 3;	/* 2 quotes + '\0' */
1191 
1192 	if ((outstr = malloc(outstrsize)) == NULL) {
1193 		zone_perror(zone, Z_NOMEM, FALSE);
1194 		exit(Z_ERR);
1195 	}
1196 	if (strchr(instr, ' ') == NULL) {
1197 		(void) strlcpy(outstr, instr, outstrsize);
1198 		return (outstr);
1199 	}
1200 	(void) snprintf(outstr, outstrsize, "\"%s\"", instr);
1201 	return (outstr);
1202 }
1203 
1204 static void
1205 export_prop(FILE *of, int prop_num, char *prop_id)
1206 {
1207 	char *quote_str;
1208 
1209 	if (strlen(prop_id) == 0)
1210 		return;
1211 	quote_str = quoteit(prop_id);
1212 	(void) fprintf(of, "%s %s=%s\n", cmd_to_str(CMD_SET),
1213 	    pt_to_str(prop_num), quote_str);
1214 	free(quote_str);
1215 }
1216 
1217 void
1218 export_func(cmd_t *cmd)
1219 {
1220 	struct zone_nwiftab nwiftab;
1221 	struct zone_fstab fstab;
1222 	struct zone_devtab devtab;
1223 	struct zone_attrtab attrtab;
1224 	struct zone_rctltab rctltab;
1225 	struct zone_rctlvaltab *valptr;
1226 	int err, arg;
1227 	char zonepath[MAXPATHLEN], outfile[MAXPATHLEN], pool[MAXNAMELEN];
1228 	FILE *of;
1229 	boolean_t autoboot;
1230 	bool need_to_close = FALSE;
1231 
1232 	assert(cmd != NULL);
1233 
1234 	outfile[0] = '\0';
1235 	optind = 0;
1236 	while ((arg = getopt(cmd->cmd_argc, cmd->cmd_argv, "?f:")) != EOF) {
1237 		switch (arg) {
1238 		case '?':
1239 			if (optopt == '?')
1240 				longer_usage(CMD_EXPORT);
1241 			else
1242 				short_usage(CMD_EXPORT);
1243 			return;
1244 		case 'f':
1245 			(void) strlcpy(outfile, optarg, sizeof (outfile));
1246 			break;
1247 		default:
1248 			short_usage(CMD_EXPORT);
1249 			return;
1250 		}
1251 	}
1252 	if (optind != cmd->cmd_argc) {
1253 		short_usage(CMD_EXPORT);
1254 		return;
1255 	}
1256 	if (strlen(outfile) == 0) {
1257 		of = stdout;
1258 	} else {
1259 		if ((of = fopen(outfile, "w")) == NULL) {
1260 			zerr(gettext("opening file %s: %s"),
1261 			    outfile, strerror(errno));
1262 			goto done;
1263 		}
1264 		setbuf(of, NULL);
1265 		need_to_close = TRUE;
1266 	}
1267 
1268 	if ((err = initialize(TRUE)) != Z_OK)
1269 		goto done;
1270 
1271 	(void) fprintf(of, "%s -b\n", cmd_to_str(CMD_CREATE));
1272 
1273 	if (zonecfg_get_zonepath(handle, zonepath, sizeof (zonepath)) == Z_OK &&
1274 	    strlen(zonepath) > 0)
1275 		(void) fprintf(of, "%s %s=%s\n", cmd_to_str(CMD_SET),
1276 		    pt_to_str(PT_ZONEPATH), zonepath);
1277 
1278 	if (zonecfg_get_autoboot(handle, &autoboot) == Z_OK)
1279 		(void) fprintf(of, "%s %s=%s\n", cmd_to_str(CMD_SET),
1280 		    pt_to_str(PT_AUTOBOOT), autoboot ? "true" : "false");
1281 
1282 	if (zonecfg_get_pool(handle, pool, sizeof (pool)) == Z_OK &&
1283 	    strlen(pool) > 0)
1284 		(void) fprintf(of, "%s %s=%s\n", cmd_to_str(CMD_SET),
1285 		    pt_to_str(PT_POOL), pool);
1286 
1287 	if ((err = zonecfg_setipdent(handle)) != Z_OK) {
1288 		zone_perror(zone, err, FALSE);
1289 		goto done;
1290 	}
1291 	while (zonecfg_getipdent(handle, &fstab) == Z_OK) {
1292 		(void) fprintf(of, "%s %s\n", cmd_to_str(CMD_ADD),
1293 		    rt_to_str(RT_IPD));
1294 		export_prop(of, PT_DIR, fstab.zone_fs_dir);
1295 		(void) fprintf(of, "%s\n", cmd_to_str(CMD_END));
1296 	}
1297 	(void) zonecfg_endipdent(handle);
1298 
1299 	if ((err = zonecfg_setfsent(handle)) != Z_OK) {
1300 		zone_perror(zone, err, FALSE);
1301 		goto done;
1302 	}
1303 	while (zonecfg_getfsent(handle, &fstab) == Z_OK) {
1304 		zone_fsopt_t *optptr;
1305 
1306 		(void) fprintf(of, "%s %s\n", cmd_to_str(CMD_ADD),
1307 		    rt_to_str(RT_FS));
1308 		export_prop(of, PT_DIR, fstab.zone_fs_dir);
1309 		export_prop(of, PT_SPECIAL, fstab.zone_fs_special);
1310 		export_prop(of, PT_RAW, fstab.zone_fs_raw);
1311 		export_prop(of, PT_TYPE, fstab.zone_fs_type);
1312 		for (optptr = fstab.zone_fs_options; optptr != NULL;
1313 		    optptr = optptr->zone_fsopt_next) {
1314 			/*
1315 			 * Simple property values with embedded equal signs
1316 			 * need to be quoted to prevent the lexer from
1317 			 * mis-parsing them as complex name=value pairs.
1318 			 */
1319 			if (strchr(optptr->zone_fsopt_opt, '='))
1320 				(void) fprintf(of, "%s %s \"%s\"\n",
1321 				    cmd_to_str(CMD_ADD),
1322 				    pt_to_str(PT_OPTIONS),
1323 				    optptr->zone_fsopt_opt);
1324 			else
1325 				(void) fprintf(of, "%s %s %s\n",
1326 				    cmd_to_str(CMD_ADD),
1327 				    pt_to_str(PT_OPTIONS),
1328 				    optptr->zone_fsopt_opt);
1329 		}
1330 		(void) fprintf(of, "%s\n", cmd_to_str(CMD_END));
1331 		zonecfg_free_fs_option_list(fstab.zone_fs_options);
1332 	}
1333 	(void) zonecfg_endfsent(handle);
1334 
1335 	if ((err = zonecfg_setnwifent(handle)) != Z_OK) {
1336 		zone_perror(zone, err, FALSE);
1337 		goto done;
1338 	}
1339 	while (zonecfg_getnwifent(handle, &nwiftab) == Z_OK) {
1340 		(void) fprintf(of, "%s %s\n", cmd_to_str(CMD_ADD),
1341 		    rt_to_str(RT_NET));
1342 		export_prop(of, PT_ADDRESS, nwiftab.zone_nwif_address);
1343 		export_prop(of, PT_PHYSICAL, nwiftab.zone_nwif_physical);
1344 		(void) fprintf(of, "%s\n", cmd_to_str(CMD_END));
1345 	}
1346 	(void) zonecfg_endnwifent(handle);
1347 
1348 	if ((err = zonecfg_setdevent(handle)) != Z_OK) {
1349 		zone_perror(zone, err, FALSE);
1350 		goto done;
1351 	}
1352 	while (zonecfg_getdevent(handle, &devtab) == Z_OK) {
1353 		(void) fprintf(of, "%s %s\n", cmd_to_str(CMD_ADD),
1354 		    rt_to_str(RT_DEVICE));
1355 		export_prop(of, PT_MATCH, devtab.zone_dev_match);
1356 		(void) fprintf(of, "%s\n", cmd_to_str(CMD_END));
1357 	}
1358 	(void) zonecfg_enddevent(handle);
1359 
1360 	if ((err = zonecfg_setrctlent(handle)) != Z_OK) {
1361 		zone_perror(zone, err, FALSE);
1362 		goto done;
1363 	}
1364 	while (zonecfg_getrctlent(handle, &rctltab) == Z_OK) {
1365 		(void) fprintf(of, "%s rctl\n", cmd_to_str(CMD_ADD));
1366 		export_prop(of, PT_NAME, rctltab.zone_rctl_name);
1367 		for (valptr = rctltab.zone_rctl_valptr; valptr != NULL;
1368 		    valptr = valptr->zone_rctlval_next) {
1369 			fprintf(of, "%s %s (%s=%s,%s=%s,%s=%s)\n",
1370 			    cmd_to_str(CMD_ADD), pt_to_str(PT_VALUE),
1371 			    pt_to_str(PT_PRIV), valptr->zone_rctlval_priv,
1372 			    pt_to_str(PT_LIMIT), valptr->zone_rctlval_limit,
1373 			    pt_to_str(PT_ACTION), valptr->zone_rctlval_action);
1374 		}
1375 		(void) fprintf(of, "%s\n", cmd_to_str(CMD_END));
1376 		zonecfg_free_rctl_value_list(rctltab.zone_rctl_valptr);
1377 	}
1378 	(void) zonecfg_endrctlent(handle);
1379 
1380 	if ((err = zonecfg_setattrent(handle)) != Z_OK) {
1381 		zone_perror(zone, err, FALSE);
1382 		goto done;
1383 	}
1384 	while (zonecfg_getattrent(handle, &attrtab) == Z_OK) {
1385 		(void) fprintf(of, "%s %s\n", cmd_to_str(CMD_ADD),
1386 		    rt_to_str(RT_ATTR));
1387 		export_prop(of, PT_NAME, attrtab.zone_attr_name);
1388 		export_prop(of, PT_TYPE, attrtab.zone_attr_type);
1389 		export_prop(of, PT_VALUE, attrtab.zone_attr_value);
1390 		(void) fprintf(of, "%s\n", cmd_to_str(CMD_END));
1391 	}
1392 	(void) zonecfg_endattrent(handle);
1393 
1394 done:
1395 	if (need_to_close)
1396 		(void) fclose(of);
1397 }
1398 
1399 void
1400 exit_func(cmd_t *cmd)
1401 {
1402 	int arg, answer;
1403 
1404 	optind = 0;
1405 	while ((arg = getopt(cmd->cmd_argc, cmd->cmd_argv, "?F")) != EOF) {
1406 		switch (arg) {
1407 		case '?':
1408 			longer_usage(CMD_EXIT);
1409 			return;
1410 		case 'F':
1411 			force_exit = TRUE;
1412 			break;
1413 		default:
1414 			short_usage(CMD_EXIT);
1415 			return;
1416 		}
1417 	}
1418 	if (optind < cmd->cmd_argc) {
1419 		short_usage(CMD_EXIT);
1420 		return;
1421 	}
1422 
1423 	if (global_scope || force_exit) {
1424 		time_to_exit = TRUE;
1425 		return;
1426 	}
1427 
1428 	answer = ask_yesno(FALSE, "Resource incomplete; really quit");
1429 	if (answer == -1) {
1430 		zerr(gettext("Resource incomplete, input "
1431 		    "not from terminal and -F not specified:\n%s command "
1432 		    "ignored, but exiting anyway."), cmd_to_str(CMD_EXIT));
1433 		exit(Z_ERR);
1434 	} else if (answer == 1) {
1435 		time_to_exit = TRUE;
1436 	}
1437 	/* (answer == 0) => just return */
1438 }
1439 
1440 static int
1441 validate_zonepath_syntax(char *path)
1442 {
1443 	if (path[0] != '/') {
1444 		zerr(gettext("%s is not an absolute path."), path);
1445 		return (Z_ERR);
1446 	}
1447 	if (strcmp(path, "/") == 0) {
1448 		zerr(gettext("/ is not allowed as a %s."),
1449 		    pt_to_str(PT_ZONEPATH));
1450 		return (Z_ERR);
1451 	}
1452 	return (Z_OK);
1453 }
1454 
1455 static void
1456 add_resource(cmd_t *cmd)
1457 {
1458 	int type;
1459 	zone_state_t state_num;
1460 
1461 	if ((type = cmd->cmd_res_type) == RT_UNKNOWN) {
1462 		long_usage(CMD_ADD, TRUE);
1463 		goto bad;
1464 	}
1465 
1466 	switch (type) {
1467 	case RT_FS:
1468 		bzero(&in_progress_fstab, sizeof (in_progress_fstab));
1469 		return;
1470 	case RT_IPD:
1471 		if (zone_get_state(zone, &state_num) == Z_OK &&
1472 		    state_num >= ZONE_STATE_INSTALLED) {
1473 			zerr(gettext("Zone %s already installed; %s %s not "
1474 			    "allowed."), zone, cmd_to_str(CMD_ADD),
1475 			    rt_to_str(RT_IPD));
1476 			goto bad;
1477 		}
1478 		bzero(&in_progress_ipdtab, sizeof (in_progress_ipdtab));
1479 		return;
1480 	case RT_NET:
1481 		bzero(&in_progress_nwiftab, sizeof (in_progress_nwiftab));
1482 		return;
1483 	case RT_DEVICE:
1484 		bzero(&in_progress_devtab, sizeof (in_progress_devtab));
1485 		return;
1486 	case RT_RCTL:
1487 		bzero(&in_progress_rctltab, sizeof (in_progress_rctltab));
1488 		return;
1489 	case RT_ATTR:
1490 		bzero(&in_progress_attrtab, sizeof (in_progress_attrtab));
1491 		return;
1492 	default:
1493 		zone_perror(rt_to_str(type), Z_NO_RESOURCE_TYPE, TRUE);
1494 		long_usage(CMD_ADD, TRUE);
1495 		usage(FALSE, HELP_RESOURCES);
1496 	}
1497 bad:
1498 	global_scope = TRUE;
1499 	end_op = -1;
1500 }
1501 
1502 static void
1503 do_complex_rctl_val(complex_property_ptr_t cp)
1504 {
1505 	struct zone_rctlvaltab *rctlvaltab;
1506 	complex_property_ptr_t cx;
1507 	bool seen_priv = FALSE, seen_limit = FALSE, seen_action = FALSE;
1508 	rctlblk_t *rctlblk;
1509 	int err;
1510 
1511 	if ((rctlvaltab = alloc_rctlvaltab()) == NULL) {
1512 		zone_perror(zone, Z_NOMEM, TRUE);
1513 		exit(Z_ERR);
1514 	}
1515 	for (cx = cp; cx != NULL; cx = cx->cp_next) {
1516 		switch (cx->cp_type) {
1517 		case PT_PRIV:
1518 			if (seen_priv) {
1519 				zerr(gettext("%s already specified"),
1520 				    pt_to_str(PT_PRIV));
1521 				goto bad;
1522 			}
1523 			(void) strlcpy(rctlvaltab->zone_rctlval_priv,
1524 			    cx->cp_value,
1525 			    sizeof (rctlvaltab->zone_rctlval_priv));
1526 			seen_priv = TRUE;
1527 			break;
1528 		case PT_LIMIT:
1529 			if (seen_limit) {
1530 				zerr(gettext("%s already specified"),
1531 				    pt_to_str(PT_LIMIT));
1532 				goto bad;
1533 			}
1534 			(void) strlcpy(rctlvaltab->zone_rctlval_limit,
1535 			    cx->cp_value,
1536 			    sizeof (rctlvaltab->zone_rctlval_limit));
1537 			seen_limit = TRUE;
1538 			break;
1539 		case PT_ACTION:
1540 			if (seen_action) {
1541 				zerr(gettext("%s already specified"),
1542 				    pt_to_str(PT_ACTION));
1543 				goto bad;
1544 			}
1545 			(void) strlcpy(rctlvaltab->zone_rctlval_action,
1546 			    cx->cp_value,
1547 			    sizeof (rctlvaltab->zone_rctlval_action));
1548 			seen_action = TRUE;
1549 			break;
1550 		default:
1551 			zone_perror(pt_to_str(PT_VALUE),
1552 			    Z_NO_PROPERTY_TYPE, TRUE);
1553 			long_usage(CMD_ADD, TRUE);
1554 			usage(FALSE, HELP_PROPS);
1555 			zonecfg_free_rctl_value_list(rctlvaltab);
1556 			return;
1557 		}
1558 	}
1559 	if (!seen_priv)
1560 		zerr(gettext("%s not specified"), pt_to_str(PT_PRIV));
1561 	if (!seen_limit)
1562 		zerr(gettext("%s not specified"), pt_to_str(PT_LIMIT));
1563 	if (!seen_action)
1564 		zerr(gettext("%s not specified"), pt_to_str(PT_ACTION));
1565 	if (!seen_priv || !seen_limit || !seen_action)
1566 		goto bad;
1567 	rctlvaltab->zone_rctlval_next = NULL;
1568 	rctlblk = alloca(rctlblk_size());
1569 	/*
1570 	 * Make sure the rctl value looks roughly correct; we won't know if
1571 	 * it's truly OK until we verify the configuration on the target
1572 	 * system.
1573 	 */
1574 	if (zonecfg_construct_rctlblk(rctlvaltab, rctlblk) != Z_OK ||
1575 	    !zonecfg_valid_rctlblk(rctlblk)) {
1576 		zerr(gettext("Invalid %s %s specification"), rt_to_str(RT_RCTL),
1577 		    pt_to_str(PT_VALUE));
1578 		goto bad;
1579 	}
1580 	err = zonecfg_add_rctl_value(&in_progress_rctltab, rctlvaltab);
1581 	if (err != Z_OK)
1582 		zone_perror(pt_to_str(PT_VALUE), err, TRUE);
1583 	return;
1584 
1585 bad:
1586 	zonecfg_free_rctl_value_list(rctlvaltab);
1587 }
1588 
1589 static void
1590 add_property(cmd_t *cmd)
1591 {
1592 	char *prop_id;
1593 	int err, res_type, prop_type;
1594 	property_value_ptr_t pp;
1595 	list_property_ptr_t l;
1596 
1597 	res_type = resource_scope;
1598 	prop_type = cmd->cmd_prop_name[0];
1599 	if (res_type == RT_UNKNOWN || prop_type == PT_UNKNOWN) {
1600 		long_usage(CMD_ADD, TRUE);
1601 		return;
1602 	}
1603 
1604 	if (cmd->cmd_prop_nv_pairs != 1) {
1605 		long_usage(CMD_ADD, TRUE);
1606 		return;
1607 	}
1608 
1609 	if (initialize(TRUE) != Z_OK)
1610 		return;
1611 
1612 	switch (res_type) {
1613 	case RT_FS:
1614 		if (prop_type != PT_OPTIONS) {
1615 			zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE,
1616 			    TRUE);
1617 			long_usage(CMD_ADD, TRUE);
1618 			usage(FALSE, HELP_PROPS);
1619 			return;
1620 		}
1621 		pp = cmd->cmd_property_ptr[0];
1622 		if (pp->pv_type != PROP_VAL_SIMPLE &&
1623 		    pp->pv_type != PROP_VAL_LIST) {
1624 			zerr(gettext("A %s or %s value was expected here."),
1625 			    pvt_to_str(PROP_VAL_SIMPLE),
1626 			    pvt_to_str(PROP_VAL_LIST));
1627 			saw_error = TRUE;
1628 			return;
1629 		}
1630 		if (pp->pv_type == PROP_VAL_SIMPLE) {
1631 			if (pp->pv_simple == NULL) {
1632 				long_usage(CMD_ADD, TRUE);
1633 				return;
1634 			}
1635 			prop_id = pp->pv_simple;
1636 			err = zonecfg_add_fs_option(&in_progress_fstab,
1637 			    prop_id);
1638 			if (err != Z_OK)
1639 				zone_perror(pt_to_str(prop_type), err, TRUE);
1640 		} else {
1641 			list_property_ptr_t list;
1642 
1643 			for (list = pp->pv_list; list != NULL;
1644 			    list = list->lp_next) {
1645 				prop_id = list->lp_simple;
1646 				if (prop_id == NULL)
1647 					break;
1648 				err = zonecfg_add_fs_option(
1649 				    &in_progress_fstab, prop_id);
1650 				if (err != Z_OK)
1651 					zone_perror(pt_to_str(prop_type), err,
1652 					    TRUE);
1653 			}
1654 		}
1655 		return;
1656 	case RT_RCTL:
1657 		if (prop_type != PT_VALUE) {
1658 			zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE,
1659 			    TRUE);
1660 			long_usage(CMD_ADD, TRUE);
1661 			usage(FALSE, HELP_PROPS);
1662 			return;
1663 		}
1664 		pp = cmd->cmd_property_ptr[0];
1665 		if (pp->pv_type != PROP_VAL_COMPLEX &&
1666 		    pp->pv_type != PROP_VAL_LIST) {
1667 			zerr(gettext("A %s or %s value was expected here."),
1668 			    pvt_to_str(PROP_VAL_COMPLEX),
1669 			    pvt_to_str(PROP_VAL_LIST));
1670 			saw_error = TRUE;
1671 			return;
1672 		}
1673 		if (pp->pv_type == PROP_VAL_COMPLEX) {
1674 			do_complex_rctl_val(pp->pv_complex);
1675 			return;
1676 		}
1677 		for (l = pp->pv_list; l != NULL; l = l->lp_next)
1678 			do_complex_rctl_val(l->lp_complex);
1679 		return;
1680 	default:
1681 		zone_perror(rt_to_str(res_type), Z_NO_RESOURCE_TYPE, TRUE);
1682 		long_usage(CMD_ADD, TRUE);
1683 		usage(FALSE, HELP_RESOURCES);
1684 		return;
1685 	}
1686 }
1687 
1688 void
1689 add_func(cmd_t *cmd)
1690 {
1691 	int arg;
1692 
1693 	assert(cmd != NULL);
1694 
1695 	optind = 0;
1696 	if ((arg = getopt(cmd->cmd_argc, cmd->cmd_argv, "?")) != EOF) {
1697 		switch (arg) {
1698 		case '?':
1699 			longer_usage(CMD_ADD);
1700 			return;
1701 		default:
1702 			short_usage(CMD_ADD);
1703 			return;
1704 		}
1705 	}
1706 	if (optind != cmd->cmd_argc) {
1707 		short_usage(CMD_ADD);
1708 		return;
1709 	}
1710 
1711 	if (zone_is_read_only(CMD_ADD))
1712 		return;
1713 
1714 	if (initialize(TRUE) != Z_OK)
1715 		return;
1716 	if (global_scope) {
1717 		global_scope = FALSE;
1718 		resource_scope = cmd->cmd_res_type;
1719 		end_op = CMD_ADD;
1720 		add_resource(cmd);
1721 	} else
1722 		add_property(cmd);
1723 }
1724 
1725 void
1726 delete_func(cmd_t *cmd)
1727 {
1728 	int err, arg, answer;
1729 	char line[ZONENAME_MAX + 128];	/* enough to ask a question */
1730 	bool force = FALSE;
1731 	zone_state_t state_num;
1732 
1733 	optind = 0;
1734 	while ((arg = getopt(cmd->cmd_argc, cmd->cmd_argv, "?F")) != EOF) {
1735 		switch (arg) {
1736 		case '?':
1737 			longer_usage(CMD_DELETE);
1738 			return;
1739 		case 'F':
1740 			force = TRUE;
1741 			break;
1742 		default:
1743 			short_usage(CMD_DELETE);
1744 			return;
1745 		}
1746 	}
1747 	if (optind != cmd->cmd_argc) {
1748 		short_usage(CMD_DELETE);
1749 		return;
1750 	}
1751 
1752 	if (zone_is_read_only(CMD_DELETE))
1753 		return;
1754 
1755 	if (zone_get_state(zone, &state_num) == Z_OK &&
1756 	    state_num >= ZONE_STATE_INCOMPLETE) {
1757 		zerr(gettext("Zone %s not in %s state; %s not allowed."),
1758 		    zone, zone_state_str(ZONE_STATE_CONFIGURED),
1759 		    cmd_to_str(CMD_DELETE));
1760 		saw_error = TRUE;
1761 		return;
1762 	}
1763 
1764 	if (initialize(TRUE) != Z_OK)
1765 		return;
1766 
1767 	if (!force) {
1768 		(void) snprintf(line, sizeof (line),
1769 		    gettext("Are you sure you want to delete zone %s"), zone);
1770 		if ((answer = ask_yesno(FALSE, line)) == -1) {
1771 			zerr(gettext("Input not from "
1772 			    "terminal and -F not specified:\n%s command "
1773 			    "ignored, exiting."), cmd_to_str(CMD_DELETE));
1774 			exit(Z_ERR);
1775 		}
1776 		if (answer != 1)
1777 			return;
1778 	}
1779 
1780 	if ((err = zonecfg_delete_index(zone)) != Z_OK) {
1781 		zone_perror(zone, err, TRUE);
1782 		return;
1783 	}
1784 
1785 	need_to_commit = FALSE;
1786 	if ((err = zonecfg_destroy(zone)) != Z_OK)
1787 		zone_perror(zone, err, TRUE);
1788 
1789 	/*
1790 	 * Time for a new handle: finish the old one off first
1791 	 * then get a new one properly to avoid leaks.
1792 	 */
1793 	zonecfg_fini_handle(handle);
1794 	if ((handle = zonecfg_init_handle()) == NULL) {
1795 		zone_perror(execname, Z_NOMEM, TRUE);
1796 		exit(Z_ERR);
1797 	}
1798 	if ((err = zonecfg_get_handle(zone, handle)) != Z_OK) {
1799 		/* If there was no zone before, that's OK */
1800 		if (err != Z_NO_ZONE)
1801 			zone_perror(zone, err, TRUE);
1802 		got_handle = FALSE;
1803 	}
1804 }
1805 
1806 static int
1807 fill_in_fstab(cmd_t *cmd, struct zone_fstab *fstab, bool fill_in_only)
1808 {
1809 	int err, i;
1810 	property_value_ptr_t pp;
1811 
1812 	if ((err = initialize(TRUE)) != Z_OK)
1813 		return (err);
1814 
1815 	fstab->zone_fs_dir[0] = '\0';
1816 	fstab->zone_fs_special[0] = '\0';
1817 	fstab->zone_fs_type[0] = '\0';
1818 	fstab->zone_fs_options = NULL;
1819 	for (i = 0; i < cmd->cmd_prop_nv_pairs; i++) {
1820 		pp = cmd->cmd_property_ptr[i];
1821 		if (pp->pv_type != PROP_VAL_SIMPLE || pp->pv_simple == NULL) {
1822 			zerr(gettext("A simple value was expected here."));
1823 			saw_error = TRUE;
1824 			return (Z_INSUFFICIENT_SPEC);
1825 		}
1826 		switch (cmd->cmd_prop_name[i]) {
1827 		case PT_DIR:
1828 			(void) strlcpy(fstab->zone_fs_dir, pp->pv_simple,
1829 			    sizeof (fstab->zone_fs_dir));
1830 			break;
1831 		case PT_SPECIAL:
1832 			(void) strlcpy(fstab->zone_fs_special, pp->pv_simple,
1833 			    sizeof (fstab->zone_fs_special));
1834 			break;
1835 		case PT_RAW:
1836 			(void) strlcpy(fstab->zone_fs_raw, pp->pv_simple,
1837 			    sizeof (fstab->zone_fs_raw));
1838 			break;
1839 		case PT_TYPE:
1840 			(void) strlcpy(fstab->zone_fs_type, pp->pv_simple,
1841 			    sizeof (fstab->zone_fs_type));
1842 			break;
1843 		default:
1844 			zone_perror(pt_to_str(cmd->cmd_prop_name[i]),
1845 			    Z_NO_PROPERTY_TYPE, TRUE);
1846 			return (Z_INSUFFICIENT_SPEC);
1847 		}
1848 	}
1849 	if (fill_in_only)
1850 		return (Z_OK);
1851 	return (zonecfg_lookup_filesystem(handle, fstab));
1852 }
1853 
1854 static int
1855 fill_in_ipdtab(cmd_t *cmd, struct zone_fstab *ipdtab, bool fill_in_only)
1856 {
1857 	int err, i;
1858 	property_value_ptr_t pp;
1859 
1860 	if ((err = initialize(TRUE)) != Z_OK)
1861 		return (err);
1862 
1863 	ipdtab->zone_fs_dir[0] = '\0';
1864 	for (i = 0; i < cmd->cmd_prop_nv_pairs; i++) {
1865 		pp = cmd->cmd_property_ptr[i];
1866 		if (pp->pv_type != PROP_VAL_SIMPLE || pp->pv_simple == NULL) {
1867 			zerr(gettext("A simple value was expected here."));
1868 			saw_error = TRUE;
1869 			return (Z_INSUFFICIENT_SPEC);
1870 		}
1871 		switch (cmd->cmd_prop_name[i]) {
1872 		case PT_DIR:
1873 			(void) strlcpy(ipdtab->zone_fs_dir, pp->pv_simple,
1874 			    sizeof (ipdtab->zone_fs_dir));
1875 			break;
1876 		default:
1877 			zone_perror(pt_to_str(cmd->cmd_prop_name[i]),
1878 			    Z_NO_PROPERTY_TYPE, TRUE);
1879 			return (Z_INSUFFICIENT_SPEC);
1880 		}
1881 	}
1882 	if (fill_in_only)
1883 		return (Z_OK);
1884 	return (zonecfg_lookup_ipd(handle, ipdtab));
1885 }
1886 
1887 static int
1888 fill_in_nwiftab(cmd_t *cmd, struct zone_nwiftab *nwiftab, bool fill_in_only)
1889 {
1890 	int err, i;
1891 	property_value_ptr_t pp;
1892 
1893 	if ((err = initialize(TRUE)) != Z_OK)
1894 		return (err);
1895 
1896 	nwiftab->zone_nwif_address[0] = '\0';
1897 	nwiftab->zone_nwif_physical[0] = '\0';
1898 	for (i = 0; i < cmd->cmd_prop_nv_pairs; i++) {
1899 		pp = cmd->cmd_property_ptr[i];
1900 		if (pp->pv_type != PROP_VAL_SIMPLE || pp->pv_simple == NULL) {
1901 			zerr(gettext("A simple value was expected here."));
1902 			saw_error = TRUE;
1903 			return (Z_INSUFFICIENT_SPEC);
1904 		}
1905 		switch (cmd->cmd_prop_name[i]) {
1906 		case PT_ADDRESS:
1907 			(void) strlcpy(nwiftab->zone_nwif_address,
1908 			    pp->pv_simple, sizeof (nwiftab->zone_nwif_address));
1909 			break;
1910 		case PT_PHYSICAL:
1911 			(void) strlcpy(nwiftab->zone_nwif_physical,
1912 			    pp->pv_simple,
1913 			    sizeof (nwiftab->zone_nwif_physical));
1914 			break;
1915 		default:
1916 			zone_perror(pt_to_str(cmd->cmd_prop_name[i]),
1917 			    Z_NO_PROPERTY_TYPE, TRUE);
1918 			return (Z_INSUFFICIENT_SPEC);
1919 		}
1920 	}
1921 	if (fill_in_only)
1922 		return (Z_OK);
1923 	err = zonecfg_lookup_nwif(handle, nwiftab);
1924 	return (err);
1925 }
1926 
1927 static int
1928 fill_in_devtab(cmd_t *cmd, struct zone_devtab *devtab, bool fill_in_only)
1929 {
1930 	int err, i;
1931 	property_value_ptr_t pp;
1932 
1933 	if ((err = initialize(TRUE)) != Z_OK)
1934 		return (err);
1935 
1936 	devtab->zone_dev_match[0] = '\0';
1937 	for (i = 0; i < cmd->cmd_prop_nv_pairs; i++) {
1938 		pp = cmd->cmd_property_ptr[i];
1939 		if (pp->pv_type != PROP_VAL_SIMPLE || pp->pv_simple == NULL) {
1940 			zerr(gettext("A simple value was expected here."));
1941 			saw_error = TRUE;
1942 			return (Z_INSUFFICIENT_SPEC);
1943 		}
1944 		switch (cmd->cmd_prop_name[i]) {
1945 		case PT_MATCH:
1946 			(void) strlcpy(devtab->zone_dev_match, pp->pv_simple,
1947 			    sizeof (devtab->zone_dev_match));
1948 			break;
1949 		default:
1950 			zone_perror(pt_to_str(cmd->cmd_prop_name[i]),
1951 			    Z_NO_PROPERTY_TYPE, TRUE);
1952 			return (Z_INSUFFICIENT_SPEC);
1953 		}
1954 	}
1955 	if (fill_in_only)
1956 		return (Z_OK);
1957 	err = zonecfg_lookup_dev(handle, devtab);
1958 	return (err);
1959 }
1960 
1961 static int
1962 fill_in_rctltab(cmd_t *cmd, struct zone_rctltab *rctltab, bool fill_in_only)
1963 {
1964 	int err, i;
1965 	property_value_ptr_t pp;
1966 
1967 	if ((err = initialize(TRUE)) != Z_OK)
1968 		return (err);
1969 
1970 	rctltab->zone_rctl_name[0] = '\0';
1971 	for (i = 0; i < cmd->cmd_prop_nv_pairs; i++) {
1972 		pp = cmd->cmd_property_ptr[i];
1973 		if (pp->pv_type != PROP_VAL_SIMPLE || pp->pv_simple == NULL) {
1974 			zerr(gettext("A simple value was expected here."));
1975 			saw_error = TRUE;
1976 			return (Z_INSUFFICIENT_SPEC);
1977 		}
1978 		switch (cmd->cmd_prop_name[i]) {
1979 		case PT_NAME:
1980 			(void) strlcpy(rctltab->zone_rctl_name, pp->pv_simple,
1981 			    sizeof (rctltab->zone_rctl_name));
1982 			break;
1983 		default:
1984 			zone_perror(pt_to_str(cmd->cmd_prop_name[i]),
1985 			    Z_NO_PROPERTY_TYPE, TRUE);
1986 			return (Z_INSUFFICIENT_SPEC);
1987 		}
1988 	}
1989 	if (fill_in_only)
1990 		return (Z_OK);
1991 	err = zonecfg_lookup_rctl(handle, rctltab);
1992 	return (err);
1993 }
1994 
1995 static int
1996 fill_in_attrtab(cmd_t *cmd, struct zone_attrtab *attrtab, bool fill_in_only)
1997 {
1998 	int err, i;
1999 	property_value_ptr_t pp;
2000 
2001 	if ((err = initialize(TRUE)) != Z_OK)
2002 		return (err);
2003 
2004 	attrtab->zone_attr_name[0] = '\0';
2005 	attrtab->zone_attr_type[0] = '\0';
2006 	attrtab->zone_attr_value[0] = '\0';
2007 	for (i = 0; i < cmd->cmd_prop_nv_pairs; i++) {
2008 		pp = cmd->cmd_property_ptr[i];
2009 		if (pp->pv_type != PROP_VAL_SIMPLE || pp->pv_simple == NULL) {
2010 			zerr(gettext("A simple value was expected here."));
2011 			saw_error = TRUE;
2012 			return (Z_INSUFFICIENT_SPEC);
2013 		}
2014 		switch (cmd->cmd_prop_name[i]) {
2015 		case PT_NAME:
2016 			(void) strlcpy(attrtab->zone_attr_name, pp->pv_simple,
2017 			    sizeof (attrtab->zone_attr_name));
2018 			break;
2019 		case PT_TYPE:
2020 			(void) strlcpy(attrtab->zone_attr_type, pp->pv_simple,
2021 			    sizeof (attrtab->zone_attr_type));
2022 			break;
2023 		case PT_VALUE:
2024 			(void) strlcpy(attrtab->zone_attr_value, pp->pv_simple,
2025 			    sizeof (attrtab->zone_attr_value));
2026 			break;
2027 		default:
2028 			zone_perror(pt_to_str(cmd->cmd_prop_name[i]),
2029 			    Z_NO_PROPERTY_TYPE, TRUE);
2030 			return (Z_INSUFFICIENT_SPEC);
2031 		}
2032 	}
2033 	if (fill_in_only)
2034 		return (Z_OK);
2035 	err = zonecfg_lookup_attr(handle, attrtab);
2036 	return (err);
2037 }
2038 
2039 static void
2040 remove_resource(cmd_t *cmd)
2041 {
2042 	int err, type;
2043 	struct zone_fstab fstab;
2044 	struct zone_nwiftab nwiftab;
2045 	struct zone_devtab devtab;
2046 	struct zone_attrtab attrtab;
2047 	struct zone_rctltab rctltab;
2048 	zone_state_t state_num;
2049 
2050 	if ((type = cmd->cmd_res_type) == RT_UNKNOWN) {
2051 		long_usage(CMD_REMOVE, TRUE);
2052 		return;
2053 	}
2054 
2055 	if (initialize(TRUE) != Z_OK)
2056 		return;
2057 
2058 	switch (type) {
2059 	case RT_FS:
2060 		if ((err = fill_in_fstab(cmd, &fstab, FALSE)) != Z_OK) {
2061 			z_cmd_rt_perror(CMD_REMOVE, RT_FS, err, TRUE);
2062 			return;
2063 		}
2064 		if ((err = zonecfg_delete_filesystem(handle, &fstab)) != Z_OK)
2065 			z_cmd_rt_perror(CMD_REMOVE, RT_FS, err, TRUE);
2066 		else
2067 			need_to_commit = TRUE;
2068 		zonecfg_free_fs_option_list(fstab.zone_fs_options);
2069 		return;
2070 	case RT_IPD:
2071 		if (zone_get_state(zone, &state_num) == Z_OK &&
2072 		    state_num >= ZONE_STATE_INSTALLED) {
2073 			zerr(gettext("Zone %s already installed; %s %s not "
2074 			    "allowed."), zone, cmd_to_str(CMD_REMOVE),
2075 			    rt_to_str(RT_IPD));
2076 			return;
2077 		}
2078 		if ((err = fill_in_ipdtab(cmd, &fstab, FALSE)) != Z_OK) {
2079 			z_cmd_rt_perror(CMD_REMOVE, RT_IPD, err, TRUE);
2080 			return;
2081 		}
2082 		if ((err = zonecfg_delete_ipd(handle, &fstab)) != Z_OK)
2083 			z_cmd_rt_perror(CMD_REMOVE, RT_IPD, err, TRUE);
2084 		else
2085 			need_to_commit = TRUE;
2086 		return;
2087 	case RT_NET:
2088 		if ((err = fill_in_nwiftab(cmd, &nwiftab, FALSE)) != Z_OK) {
2089 			z_cmd_rt_perror(CMD_REMOVE, RT_NET, err, TRUE);
2090 			return;
2091 		}
2092 		if ((err = zonecfg_delete_nwif(handle, &nwiftab)) != Z_OK)
2093 			z_cmd_rt_perror(CMD_REMOVE, RT_NET, err, TRUE);
2094 		else
2095 			need_to_commit = TRUE;
2096 		return;
2097 	case RT_DEVICE:
2098 		if ((err = fill_in_devtab(cmd, &devtab, FALSE)) != Z_OK) {
2099 			z_cmd_rt_perror(CMD_REMOVE, RT_DEVICE, err, TRUE);
2100 			return;
2101 		}
2102 		if ((err = zonecfg_delete_dev(handle, &devtab)) != Z_OK)
2103 			z_cmd_rt_perror(CMD_REMOVE, RT_DEVICE, err, TRUE);
2104 		else
2105 			need_to_commit = TRUE;
2106 		return;
2107 	case RT_RCTL:
2108 		if ((err = fill_in_rctltab(cmd, &rctltab, FALSE)) != Z_OK) {
2109 			z_cmd_rt_perror(CMD_REMOVE, RT_RCTL, err, TRUE);
2110 			return;
2111 		}
2112 		if ((err = zonecfg_delete_rctl(handle, &rctltab)) != Z_OK)
2113 			z_cmd_rt_perror(CMD_REMOVE, RT_RCTL, err, TRUE);
2114 		else
2115 			need_to_commit = TRUE;
2116 		zonecfg_free_rctl_value_list(rctltab.zone_rctl_valptr);
2117 		return;
2118 	case RT_ATTR:
2119 		if ((err = fill_in_attrtab(cmd, &attrtab, FALSE)) != Z_OK) {
2120 			z_cmd_rt_perror(CMD_REMOVE, RT_ATTR, err, TRUE);
2121 			return;
2122 		}
2123 		if ((err = zonecfg_delete_attr(handle, &attrtab)) != Z_OK)
2124 			z_cmd_rt_perror(CMD_REMOVE, RT_ATTR, err, TRUE);
2125 		else
2126 			need_to_commit = TRUE;
2127 		return;
2128 	default:
2129 		zone_perror(rt_to_str(type), Z_NO_RESOURCE_TYPE, TRUE);
2130 		long_usage(CMD_REMOVE, TRUE);
2131 		usage(FALSE, HELP_RESOURCES);
2132 		return;
2133 	}
2134 }
2135 
2136 static void
2137 remove_property(cmd_t *cmd)
2138 {
2139 	char *prop_id;
2140 	int err, res_type, prop_type;
2141 	property_value_ptr_t pp;
2142 	struct zone_rctlvaltab *rctlvaltab;
2143 	complex_property_ptr_t cx;
2144 
2145 	res_type = resource_scope;
2146 	prop_type = cmd->cmd_prop_name[0];
2147 	if (res_type == RT_UNKNOWN || prop_type == PT_UNKNOWN) {
2148 		long_usage(CMD_REMOVE, TRUE);
2149 		return;
2150 	}
2151 
2152 	if (cmd->cmd_prop_nv_pairs != 1) {
2153 		long_usage(CMD_ADD, TRUE);
2154 		return;
2155 	}
2156 
2157 	if (initialize(TRUE) != Z_OK)
2158 		return;
2159 
2160 	switch (res_type) {
2161 	case RT_FS:
2162 		if (prop_type != PT_OPTIONS) {
2163 			zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE,
2164 			    TRUE);
2165 			long_usage(CMD_REMOVE, TRUE);
2166 			usage(FALSE, HELP_PROPS);
2167 			return;
2168 		}
2169 		pp = cmd->cmd_property_ptr[0];
2170 		if (pp->pv_type == PROP_VAL_COMPLEX) {
2171 			zerr(gettext("A %s or %s value was expected here."),
2172 			    pvt_to_str(PROP_VAL_SIMPLE),
2173 			    pvt_to_str(PROP_VAL_LIST));
2174 			saw_error = TRUE;
2175 			return;
2176 		}
2177 		if (pp->pv_type == PROP_VAL_SIMPLE) {
2178 			if (pp->pv_simple == NULL) {
2179 				long_usage(CMD_ADD, TRUE);
2180 				return;
2181 			}
2182 			prop_id = pp->pv_simple;
2183 			err = zonecfg_remove_fs_option(&in_progress_fstab,
2184 			    prop_id);
2185 			if (err != Z_OK)
2186 				zone_perror(pt_to_str(prop_type), err, TRUE);
2187 		} else {
2188 			list_property_ptr_t list;
2189 
2190 			for (list = pp->pv_list; list != NULL;
2191 			    list = list->lp_next) {
2192 				prop_id = list->lp_simple;
2193 				if (prop_id == NULL)
2194 					break;
2195 				err = zonecfg_remove_fs_option(
2196 				    &in_progress_fstab, prop_id);
2197 				if (err != Z_OK)
2198 					zone_perror(pt_to_str(prop_type), err,
2199 					    TRUE);
2200 			}
2201 		}
2202 		return;
2203 	case RT_RCTL:
2204 		if (prop_type != PT_VALUE) {
2205 			zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE,
2206 			    TRUE);
2207 			long_usage(CMD_REMOVE, TRUE);
2208 			usage(FALSE, HELP_PROPS);
2209 			return;
2210 		}
2211 		pp = cmd->cmd_property_ptr[0];
2212 		if (pp->pv_type != PROP_VAL_COMPLEX) {
2213 			zerr(gettext("A %s value was expected here."),
2214 			    pvt_to_str(PROP_VAL_COMPLEX));
2215 			saw_error = TRUE;
2216 			return;
2217 		}
2218 		if ((rctlvaltab = alloc_rctlvaltab()) == NULL) {
2219 			zone_perror(zone, Z_NOMEM, TRUE);
2220 			exit(Z_ERR);
2221 		}
2222 		for (cx = pp->pv_complex; cx != NULL; cx = cx->cp_next) {
2223 			switch (cx->cp_type) {
2224 			case PT_PRIV:
2225 				(void) strlcpy(rctlvaltab->zone_rctlval_priv,
2226 				    cx->cp_value,
2227 				    sizeof (rctlvaltab->zone_rctlval_priv));
2228 				break;
2229 			case PT_LIMIT:
2230 				(void) strlcpy(rctlvaltab->zone_rctlval_limit,
2231 				    cx->cp_value,
2232 				    sizeof (rctlvaltab->zone_rctlval_limit));
2233 				break;
2234 			case PT_ACTION:
2235 				(void) strlcpy(rctlvaltab->zone_rctlval_action,
2236 				    cx->cp_value,
2237 				    sizeof (rctlvaltab->zone_rctlval_action));
2238 				break;
2239 			default:
2240 				zone_perror(pt_to_str(prop_type),
2241 				    Z_NO_PROPERTY_TYPE, TRUE);
2242 				long_usage(CMD_ADD, TRUE);
2243 				usage(FALSE, HELP_PROPS);
2244 				zonecfg_free_rctl_value_list(rctlvaltab);
2245 				return;
2246 			}
2247 		}
2248 		rctlvaltab->zone_rctlval_next = NULL;
2249 		err = zonecfg_remove_rctl_value(&in_progress_rctltab,
2250 		    rctlvaltab);
2251 		if (err != Z_OK)
2252 			zone_perror(pt_to_str(prop_type), err, TRUE);
2253 		zonecfg_free_rctl_value_list(rctlvaltab);
2254 		return;
2255 	default:
2256 		zone_perror(rt_to_str(res_type), Z_NO_RESOURCE_TYPE, TRUE);
2257 		long_usage(CMD_REMOVE, TRUE);
2258 		usage(FALSE, HELP_RESOURCES);
2259 		return;
2260 	}
2261 }
2262 
2263 void
2264 remove_func(cmd_t *cmd)
2265 {
2266 	if (zone_is_read_only(CMD_REMOVE))
2267 		return;
2268 
2269 	assert(cmd != NULL);
2270 
2271 	if (global_scope)
2272 		remove_resource(cmd);
2273 	else
2274 		remove_property(cmd);
2275 }
2276 
2277 void
2278 select_func(cmd_t *cmd)
2279 {
2280 	int type, err;
2281 	zone_state_t state_num;
2282 
2283 	if (zone_is_read_only(CMD_SELECT))
2284 		return;
2285 
2286 	assert(cmd != NULL);
2287 
2288 	if (global_scope) {
2289 		global_scope = FALSE;
2290 		resource_scope = cmd->cmd_res_type;
2291 		end_op = CMD_SELECT;
2292 	} else {
2293 		scope_usage(CMD_SELECT);
2294 		return;
2295 	}
2296 
2297 	if ((type = cmd->cmd_res_type) == RT_UNKNOWN) {
2298 		long_usage(CMD_SELECT, TRUE);
2299 		return;
2300 	}
2301 
2302 	if (initialize(TRUE) != Z_OK)
2303 		return;
2304 
2305 	switch (type) {
2306 	case RT_FS:
2307 		if ((err = fill_in_fstab(cmd, &old_fstab, FALSE)) != Z_OK) {
2308 			z_cmd_rt_perror(CMD_SELECT, RT_FS, err, TRUE);
2309 			global_scope = TRUE;
2310 		}
2311 		bcopy(&old_fstab, &in_progress_fstab,
2312 		    sizeof (struct zone_fstab));
2313 		return;
2314 	case RT_IPD:
2315 		if (zone_get_state(zone, &state_num) == Z_OK &&
2316 		    state_num >= ZONE_STATE_INCOMPLETE) {
2317 			zerr(gettext("Zone %s not in %s state; %s %s not "
2318 			    "allowed."), zone,
2319 			    zone_state_str(ZONE_STATE_CONFIGURED),
2320 			    cmd_to_str(CMD_SELECT), rt_to_str(RT_IPD));
2321 			global_scope = TRUE;
2322 			end_op = -1;
2323 			return;
2324 		}
2325 		if ((err = fill_in_ipdtab(cmd, &old_ipdtab, FALSE)) != Z_OK) {
2326 			z_cmd_rt_perror(CMD_SELECT, RT_IPD, err, TRUE);
2327 			global_scope = TRUE;
2328 		}
2329 		bcopy(&old_ipdtab, &in_progress_ipdtab,
2330 		    sizeof (struct zone_fstab));
2331 		return;
2332 	case RT_NET:
2333 		if ((err = fill_in_nwiftab(cmd, &old_nwiftab, FALSE)) != Z_OK) {
2334 			z_cmd_rt_perror(CMD_SELECT, RT_NET, err, TRUE);
2335 			global_scope = TRUE;
2336 		}
2337 		bcopy(&old_nwiftab, &in_progress_nwiftab,
2338 		    sizeof (struct zone_nwiftab));
2339 		return;
2340 	case RT_DEVICE:
2341 		if ((err = fill_in_devtab(cmd, &old_devtab, FALSE)) != Z_OK) {
2342 			z_cmd_rt_perror(CMD_SELECT, RT_DEVICE, err, TRUE);
2343 			global_scope = TRUE;
2344 		}
2345 		bcopy(&old_devtab, &in_progress_devtab,
2346 		    sizeof (struct zone_devtab));
2347 		return;
2348 	case RT_RCTL:
2349 		if ((err = fill_in_rctltab(cmd, &old_rctltab, FALSE)) != Z_OK) {
2350 			z_cmd_rt_perror(CMD_SELECT, RT_RCTL, err, TRUE);
2351 			global_scope = TRUE;
2352 		}
2353 		bcopy(&old_rctltab, &in_progress_rctltab,
2354 		    sizeof (struct zone_rctltab));
2355 		return;
2356 	case RT_ATTR:
2357 		if ((err = fill_in_attrtab(cmd, &old_attrtab, FALSE)) != Z_OK) {
2358 			z_cmd_rt_perror(CMD_SELECT, RT_ATTR, err, TRUE);
2359 			global_scope = TRUE;
2360 		}
2361 		bcopy(&old_attrtab, &in_progress_attrtab,
2362 		    sizeof (struct zone_attrtab));
2363 		return;
2364 	default:
2365 		zone_perror(rt_to_str(type), Z_NO_RESOURCE_TYPE, TRUE);
2366 		long_usage(CMD_SELECT, TRUE);
2367 		usage(FALSE, HELP_RESOURCES);
2368 		return;
2369 	}
2370 }
2371 
2372 /*
2373  * Network "addresses" can be one of the following forms:
2374  *	<IPv4 address>
2375  *	<IPv4 address>/<prefix length>
2376  *	<IPv6 address>/<prefix length>
2377  *	<host name>
2378  *	<host name>/<prefix length>
2379  * In other words, the "/" followed by a prefix length is allowed but not
2380  * required for IPv4 addresses and host names, and required for IPv6 addresses.
2381  * If a prefix length is given, it must be in the allowable range: 0 to 32 for
2382  * IPv4 addresses and host names, 0 to 128 for IPv6 addresses.
2383  * Host names must start with an alpha-numeric character, and all subsequent
2384  * characters must be either alpha-numeric or "-".
2385  */
2386 
2387 static int
2388 validate_net_address_syntax(char *address)
2389 {
2390 	char *slashp, part1[MAXHOSTNAMELEN];
2391 	struct in6_addr in6;
2392 	struct in_addr in4;
2393 	int prefixlen, i;
2394 
2395 	/*
2396 	 * Copy the part before any '/' into part1 or copy the whole
2397 	 * thing if there is no '/'.
2398 	 */
2399 	if ((slashp = strchr(address, '/')) != NULL) {
2400 		*slashp = '\0';
2401 		(void) strlcpy(part1, address, sizeof (part1));
2402 		*slashp = '/';
2403 		prefixlen = atoi(++slashp);
2404 	} else {
2405 		(void) strlcpy(part1, address, sizeof (part1));
2406 	}
2407 
2408 	if (inet_pton(AF_INET6, part1, &in6) == 1) {
2409 		if (slashp == NULL) {
2410 			zerr(gettext("%s: IPv6 addresses "
2411 			    "require /prefix-length suffix."), address);
2412 			return (Z_ERR);
2413 		}
2414 		if (prefixlen < 0 || prefixlen > 128) {
2415 			zerr(gettext("%s: IPv6 address "
2416 			    "prefix lengths must be 0 - 128."), address);
2417 			return (Z_ERR);
2418 		}
2419 		return (Z_OK);
2420 	}
2421 
2422 	/* At this point, any /prefix must be for IPv4. */
2423 	if (slashp != NULL) {
2424 		if (prefixlen < 0 || prefixlen > 32) {
2425 			zerr(gettext("%s: IPv4 address "
2426 			    "prefix lengths must be 0 - 32."), address);
2427 			return (Z_ERR);
2428 		}
2429 	}
2430 	if (inet_pton(AF_INET, part1, &in4) == 1)
2431 		return (Z_OK);
2432 
2433 	/* address may also be a host name */
2434 	if (!isalnum(part1[0])) {
2435 		zerr(gettext("%s: bogus host name or network address syntax"),
2436 		    part1);
2437 		saw_error = TRUE;
2438 		usage(FALSE, HELP_NETADDR);
2439 		return (Z_ERR);
2440 	}
2441 	for (i = 1; part1[i]; i++)
2442 		if (!isalnum(part1[i]) && part1[i] != '-' && part1[i] != '.') {
2443 			zerr(gettext("%s: bogus host name or "
2444 			    "network address syntax"), part1);
2445 			saw_error = TRUE;
2446 			usage(FALSE, HELP_NETADDR);
2447 			return (Z_ERR);
2448 		}
2449 	return (Z_OK);
2450 }
2451 
2452 static int
2453 validate_net_physical_syntax(char *ifname)
2454 {
2455 	if (strchr(ifname, ':') == NULL)
2456 		return (Z_OK);
2457 	zerr(gettext("%s: physical interface name required; "
2458 	    "logical interface name not allowed"), ifname);
2459 	return (Z_ERR);
2460 }
2461 
2462 static boolean_t
2463 valid_fs_type(const char *type)
2464 {
2465 	/*
2466 	 * Is this a valid path component?
2467 	 */
2468 	if (strlen(type) + 1 > MAXNAMELEN)
2469 		return (B_FALSE);
2470 	/*
2471 	 * Make sure a bad value for "type" doesn't make
2472 	 * /usr/lib/fs/<type>/mount turn into something else.
2473 	 */
2474 	if (strchr(type, '/') != NULL || type[0] == '\0' ||
2475 	    strcmp(type, ".") == 0 || strcmp(type, "..") == 0)
2476 	    return (B_FALSE);
2477 	/*
2478 	 * More detailed verification happens later by zoneadm(1m).
2479 	 */
2480 	return (B_TRUE);
2481 }
2482 
2483 void
2484 set_func(cmd_t *cmd)
2485 {
2486 	char *prop_id;
2487 	int err, res_type, prop_type;
2488 	property_value_ptr_t pp;
2489 	zone_state_t state_num;
2490 	boolean_t autoboot;
2491 
2492 	if (zone_is_read_only(CMD_SET))
2493 		return;
2494 
2495 	assert(cmd != NULL);
2496 
2497 	prop_type = cmd->cmd_prop_name[0];
2498 	if (global_scope) {
2499 		if (prop_type == PT_ZONEPATH) {
2500 			res_type = RT_ZONEPATH;
2501 		} else if (prop_type == PT_AUTOBOOT) {
2502 			res_type = RT_AUTOBOOT;
2503 		} else if (prop_type == PT_POOL) {
2504 			res_type = RT_POOL;
2505 		} else {
2506 			zerr(gettext("Cannot set a resource-specific property "
2507 			    "from the global scope."));
2508 			saw_error = TRUE;
2509 			return;
2510 		}
2511 	} else {
2512 		res_type = resource_scope;
2513 	}
2514 
2515 	pp = cmd->cmd_property_ptr[0];
2516 	/*
2517 	 * A nasty expression but not that complicated:
2518 	 * 1. fs options are simple or list (tested below)
2519 	 * 2. rctl value's are complex or list (tested below)
2520 	 * Anything else should be simple.
2521 	 */
2522 	if (!(res_type == RT_FS && prop_type == PT_OPTIONS) &&
2523 	    !(res_type == RT_RCTL && prop_type == PT_VALUE) &&
2524 	    (pp->pv_type != PROP_VAL_SIMPLE ||
2525 	    (prop_id = pp->pv_simple) == NULL)) {
2526 		zerr(gettext("A %s value was expected here."),
2527 		    pvt_to_str(PROP_VAL_SIMPLE));
2528 		saw_error = TRUE;
2529 		return;
2530 	}
2531 	if (prop_type == PT_UNKNOWN) {
2532 		long_usage(CMD_SET, TRUE);
2533 		return;
2534 	}
2535 
2536 	if (initialize(TRUE) != Z_OK)
2537 		return;
2538 
2539 	switch (res_type) {
2540 	case RT_ZONEPATH:
2541 		if (zone_get_state(zone, &state_num) == Z_OK &&
2542 		    state_num >= ZONE_STATE_INSTALLED) {
2543 			zerr(gettext("Zone %s already installed; %s %s not "
2544 			    "allowed."), zone, cmd_to_str(CMD_SET),
2545 			    rt_to_str(RT_ZONEPATH));
2546 			return;
2547 		}
2548 		if (validate_zonepath_syntax(prop_id) != Z_OK) {
2549 			saw_error = TRUE;
2550 			return;
2551 		}
2552 		if ((err = zonecfg_set_zonepath(handle, prop_id)) != Z_OK)
2553 			zone_perror(zone, err, TRUE);
2554 		else
2555 			need_to_commit = TRUE;
2556 		return;
2557 	case RT_AUTOBOOT:
2558 		if (strcmp(prop_id, "true") == 0) {
2559 			autoboot = B_TRUE;
2560 		} else if (strcmp(prop_id, "false") == 0) {
2561 			autoboot = B_FALSE;
2562 		} else {
2563 			zerr(gettext("%s value must be '%s' or '%s'."),
2564 			    pt_to_str(PT_AUTOBOOT), "true", "false");
2565 			saw_error = TRUE;
2566 			return;
2567 		}
2568 		if ((err = zonecfg_set_autoboot(handle, autoboot)) != Z_OK)
2569 			zone_perror(zone, err, TRUE);
2570 		else
2571 			need_to_commit = TRUE;
2572 		return;
2573 	case RT_POOL:
2574 		if ((err = zonecfg_set_pool(handle, prop_id)) != Z_OK)
2575 			zone_perror(zone, err, TRUE);
2576 		else
2577 			need_to_commit = TRUE;
2578 		return;
2579 	case RT_FS:
2580 		switch (prop_type) {
2581 		case PT_DIR:
2582 			(void) strlcpy(in_progress_fstab.zone_fs_dir, prop_id,
2583 			    sizeof (in_progress_fstab.zone_fs_dir));
2584 			return;
2585 		case PT_SPECIAL:
2586 			(void) strlcpy(in_progress_fstab.zone_fs_special,
2587 			    prop_id,
2588 			    sizeof (in_progress_fstab.zone_fs_special));
2589 			return;
2590 		case PT_RAW:
2591 			(void) strlcpy(in_progress_fstab.zone_fs_raw,
2592 			    prop_id, sizeof (in_progress_fstab.zone_fs_raw));
2593 			return;
2594 		case PT_TYPE:
2595 			if (!valid_fs_type(prop_id)) {
2596 				zerr(gettext("\"%s\" is not a valid %s."),
2597 				    prop_id, pt_to_str(PT_TYPE));
2598 				saw_error = TRUE;
2599 				return;
2600 			}
2601 			(void) strlcpy(in_progress_fstab.zone_fs_type, prop_id,
2602 			    sizeof (in_progress_fstab.zone_fs_type));
2603 			return;
2604 		case PT_OPTIONS:
2605 			if (pp->pv_type != PROP_VAL_SIMPLE &&
2606 			    pp->pv_type != PROP_VAL_LIST) {
2607 				zerr(gettext("A %s or %s value was expected "
2608 				    "here."), pvt_to_str(PROP_VAL_SIMPLE),
2609 				    pvt_to_str(PROP_VAL_LIST));
2610 				saw_error = TRUE;
2611 				return;
2612 			}
2613 			zonecfg_free_fs_option_list(
2614 			    in_progress_fstab.zone_fs_options);
2615 			in_progress_fstab.zone_fs_options = NULL;
2616 			if (!(pp->pv_type == PROP_VAL_LIST &&
2617 			    pp->pv_list == NULL))
2618 				add_property(cmd);
2619 			return;
2620 		default:
2621 			break;
2622 		}
2623 		zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE, TRUE);
2624 		long_usage(CMD_SET, TRUE);
2625 		usage(FALSE, HELP_PROPS);
2626 		return;
2627 	case RT_IPD:
2628 		switch (prop_type) {
2629 		case PT_DIR:
2630 			(void) strlcpy(in_progress_ipdtab.zone_fs_dir, prop_id,
2631 			    sizeof (in_progress_ipdtab.zone_fs_dir));
2632 			return;
2633 		default:
2634 			break;
2635 		}
2636 		zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE, TRUE);
2637 		long_usage(CMD_SET, TRUE);
2638 		usage(FALSE, HELP_PROPS);
2639 		return;
2640 	case RT_NET:
2641 		switch (prop_type) {
2642 		case PT_ADDRESS:
2643 			if (validate_net_address_syntax(prop_id) != Z_OK) {
2644 				saw_error = TRUE;
2645 				return;
2646 			}
2647 			(void) strlcpy(in_progress_nwiftab.zone_nwif_address,
2648 			    prop_id,
2649 			    sizeof (in_progress_nwiftab.zone_nwif_address));
2650 			break;
2651 		case PT_PHYSICAL:
2652 			if (validate_net_physical_syntax(prop_id) != Z_OK) {
2653 				saw_error = TRUE;
2654 				return;
2655 			}
2656 			(void) strlcpy(in_progress_nwiftab.zone_nwif_physical,
2657 			    prop_id,
2658 			    sizeof (in_progress_nwiftab.zone_nwif_physical));
2659 			break;
2660 		default:
2661 			zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE,
2662 			    TRUE);
2663 			long_usage(CMD_SET, TRUE);
2664 			usage(FALSE, HELP_PROPS);
2665 			return;
2666 		}
2667 		return;
2668 	case RT_DEVICE:
2669 		switch (prop_type) {
2670 		case PT_MATCH:
2671 			(void) strlcpy(in_progress_devtab.zone_dev_match,
2672 			    prop_id,
2673 			    sizeof (in_progress_devtab.zone_dev_match));
2674 			break;
2675 		default:
2676 			zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE,
2677 			    TRUE);
2678 			long_usage(CMD_SET, TRUE);
2679 			usage(FALSE, HELP_PROPS);
2680 			return;
2681 		}
2682 		return;
2683 	case RT_RCTL:
2684 		switch (prop_type) {
2685 		case PT_NAME:
2686 			if (!zonecfg_valid_rctlname(prop_id)) {
2687 				zerr(gettext("'%s' is not a valid zone %s "
2688 				    "name."), prop_id, rt_to_str(RT_RCTL));
2689 				return;
2690 			}
2691 			(void) strlcpy(in_progress_rctltab.zone_rctl_name,
2692 			    prop_id,
2693 			    sizeof (in_progress_rctltab.zone_rctl_name));
2694 			break;
2695 		case PT_VALUE:
2696 			if (pp->pv_type != PROP_VAL_COMPLEX &&
2697 			    pp->pv_type != PROP_VAL_LIST) {
2698 				zerr(gettext("A %s or %s value was expected "
2699 				    "here."), pvt_to_str(PROP_VAL_COMPLEX),
2700 				    pvt_to_str(PROP_VAL_LIST));
2701 				saw_error = TRUE;
2702 				return;
2703 			}
2704 			zonecfg_free_rctl_value_list(
2705 			    in_progress_rctltab.zone_rctl_valptr);
2706 			in_progress_rctltab.zone_rctl_valptr = NULL;
2707 			if (!(pp->pv_type == PROP_VAL_LIST &&
2708 			    pp->pv_list == NULL))
2709 				add_property(cmd);
2710 			break;
2711 		default:
2712 			zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE,
2713 			    TRUE);
2714 			long_usage(CMD_SET, TRUE);
2715 			usage(FALSE, HELP_PROPS);
2716 			return;
2717 		}
2718 		return;
2719 	case RT_ATTR:
2720 		switch (prop_type) {
2721 		case PT_NAME:
2722 			(void) strlcpy(in_progress_attrtab.zone_attr_name,
2723 			    prop_id,
2724 			    sizeof (in_progress_attrtab.zone_attr_name));
2725 			break;
2726 		case PT_TYPE:
2727 			(void) strlcpy(in_progress_attrtab.zone_attr_type,
2728 			    prop_id,
2729 			    sizeof (in_progress_attrtab.zone_attr_type));
2730 			break;
2731 		case PT_VALUE:
2732 			(void) strlcpy(in_progress_attrtab.zone_attr_value,
2733 			    prop_id,
2734 			    sizeof (in_progress_attrtab.zone_attr_value));
2735 			break;
2736 		default:
2737 			zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE,
2738 			    TRUE);
2739 			long_usage(CMD_SET, TRUE);
2740 			usage(FALSE, HELP_PROPS);
2741 			return;
2742 		}
2743 		return;
2744 	default:
2745 		zone_perror(rt_to_str(res_type), Z_NO_RESOURCE_TYPE, TRUE);
2746 		long_usage(CMD_SET, TRUE);
2747 		usage(FALSE, HELP_RESOURCES);
2748 		return;
2749 	}
2750 }
2751 
2752 static void
2753 output_prop(FILE *fp, int pnum, char *pval, bool print_notspec)
2754 {
2755 	char *qstr;
2756 
2757 	if (*pval != '\0') {
2758 		qstr = quoteit(pval);
2759 		(void) fprintf(fp, "\t%s: %s\n", pt_to_str(pnum), qstr);
2760 		free(qstr);
2761 	} else if (print_notspec)
2762 		(void) fprintf(fp, "\t%s %s\n", pt_to_str(pnum),
2763 		    gettext("not specified"));
2764 }
2765 
2766 static void
2767 info_zonepath(zone_dochandle_t handle, FILE *fp)
2768 {
2769 	char zonepath[MAXPATHLEN];
2770 
2771 	if (zonecfg_get_zonepath(handle, zonepath, sizeof (zonepath)) == Z_OK)
2772 		(void) fprintf(fp, "%s: %s\n", pt_to_str(PT_ZONEPATH),
2773 		    zonepath);
2774 	else
2775 		(void) fprintf(fp, "%s %s\n", pt_to_str(PT_ZONEPATH),
2776 		    gettext("not specified"));
2777 }
2778 
2779 static void
2780 info_autoboot(zone_dochandle_t handle, FILE *fp)
2781 {
2782 	boolean_t autoboot;
2783 	int err;
2784 
2785 	if ((err = zonecfg_get_autoboot(handle, &autoboot)) == Z_OK)
2786 		(void) fprintf(fp, "%s: %s\n", pt_to_str(PT_AUTOBOOT),
2787 		    autoboot ? "true" : "false");
2788 	else
2789 		zone_perror(zone, err, TRUE);
2790 }
2791 
2792 static void
2793 info_pool(zone_dochandle_t handle, FILE *fp)
2794 {
2795 	char pool[MAXNAMELEN];
2796 	int err;
2797 
2798 	if ((err = zonecfg_get_pool(handle, pool, sizeof (pool))) == Z_OK)
2799 		(void) fprintf(fp, "%s: %s\n", pt_to_str(PT_POOL), pool);
2800 	else
2801 		zone_perror(zone, err, TRUE);
2802 }
2803 
2804 static void
2805 output_fs(FILE *fp, struct zone_fstab *fstab)
2806 {
2807 	zone_fsopt_t *this;
2808 
2809 	(void) fprintf(fp, "%s:\n", rt_to_str(RT_FS));
2810 	output_prop(fp, PT_DIR, fstab->zone_fs_dir, B_TRUE);
2811 	output_prop(fp, PT_SPECIAL, fstab->zone_fs_special, B_TRUE);
2812 	output_prop(fp, PT_RAW, fstab->zone_fs_raw, B_TRUE);
2813 	output_prop(fp, PT_TYPE, fstab->zone_fs_type, B_TRUE);
2814 	(void) fprintf(fp, "\t%s: [", pt_to_str(PT_OPTIONS));
2815 	for (this = fstab->zone_fs_options; this != NULL;
2816 	    this = this->zone_fsopt_next) {
2817 		if (strchr(this->zone_fsopt_opt, '='))
2818 			(void) fprintf(fp, "\"%s\"", this->zone_fsopt_opt);
2819 		else
2820 			(void) fprintf(fp, "%s", this->zone_fsopt_opt);
2821 		if (this->zone_fsopt_next != NULL)
2822 			(void) fprintf(fp, ",");
2823 	}
2824 	(void) fprintf(fp, "]\n");
2825 }
2826 
2827 static void
2828 output_ipd(FILE *fp, struct zone_fstab *ipdtab)
2829 {
2830 	(void) fprintf(fp, "%s:\n", rt_to_str(RT_IPD));
2831 	output_prop(fp, PT_DIR, ipdtab->zone_fs_dir, B_TRUE);
2832 }
2833 
2834 static void
2835 info_fs(zone_dochandle_t handle, FILE *fp, cmd_t *cmd)
2836 {
2837 	struct zone_fstab lookup, user;
2838 	bool output = FALSE;
2839 
2840 	if (zonecfg_setfsent(handle) != Z_OK)
2841 		return;
2842 	while (zonecfg_getfsent(handle, &lookup) == Z_OK) {
2843 		if (cmd->cmd_prop_nv_pairs == 0) {
2844 			output_fs(fp, &lookup);
2845 			goto loopend;
2846 		}
2847 		if (fill_in_fstab(cmd, &user, TRUE) != Z_OK)
2848 			goto loopend;
2849 		if (strlen(user.zone_fs_dir) > 0 &&
2850 		    strcmp(user.zone_fs_dir, lookup.zone_fs_dir) != 0)
2851 			goto loopend;	/* no match */
2852 		if (strlen(user.zone_fs_special) > 0 &&
2853 		    strcmp(user.zone_fs_special, lookup.zone_fs_special) != 0)
2854 			goto loopend;	/* no match */
2855 		if (strlen(user.zone_fs_type) > 0 &&
2856 		    strcmp(user.zone_fs_type, lookup.zone_fs_type) != 0)
2857 			goto loopend;	/* no match */
2858 		output_fs(fp, &lookup);
2859 		output = TRUE;
2860 loopend:
2861 		zonecfg_free_fs_option_list(lookup.zone_fs_options);
2862 	}
2863 	(void) zonecfg_endfsent(handle);
2864 	/*
2865 	 * If a property n/v pair was specified, warn the user if there was
2866 	 * nothing to output.
2867 	 */
2868 	if (!output && cmd->cmd_prop_nv_pairs > 0)
2869 		(void) printf(gettext("No such %s resource.\n"),
2870 		    rt_to_str(RT_FS));
2871 }
2872 
2873 static void
2874 info_ipd(zone_dochandle_t handle, FILE *fp, cmd_t *cmd)
2875 {
2876 	struct zone_fstab lookup, user;
2877 	bool output = FALSE;
2878 
2879 	if (zonecfg_setipdent(handle) != Z_OK)
2880 		return;
2881 	while (zonecfg_getipdent(handle, &lookup) == Z_OK) {
2882 		if (cmd->cmd_prop_nv_pairs == 0) {
2883 			output_ipd(fp, &lookup);
2884 			continue;
2885 		}
2886 		if (fill_in_ipdtab(cmd, &user, TRUE) != Z_OK)
2887 			continue;
2888 		if (strlen(user.zone_fs_dir) > 0 &&
2889 		    strcmp(user.zone_fs_dir, lookup.zone_fs_dir) != 0)
2890 			continue;	/* no match */
2891 		output_ipd(fp, &lookup);
2892 		output = TRUE;
2893 	}
2894 	(void) zonecfg_endipdent(handle);
2895 	/*
2896 	 * If a property n/v pair was specified, warn the user if there was
2897 	 * nothing to output.
2898 	 */
2899 	if (!output && cmd->cmd_prop_nv_pairs > 0)
2900 		(void) printf(gettext("No such %s resource.\n"),
2901 		    rt_to_str(RT_IPD));
2902 }
2903 
2904 static void
2905 output_net(FILE *fp, struct zone_nwiftab *nwiftab)
2906 {
2907 	(void) fprintf(fp, "%s:\n", rt_to_str(RT_NET));
2908 	output_prop(fp, PT_ADDRESS, nwiftab->zone_nwif_address, B_TRUE);
2909 	output_prop(fp, PT_PHYSICAL, nwiftab->zone_nwif_physical, B_TRUE);
2910 }
2911 
2912 static void
2913 info_net(zone_dochandle_t handle, FILE *fp, cmd_t *cmd)
2914 {
2915 	struct zone_nwiftab lookup, user;
2916 	bool output = FALSE;
2917 
2918 	if (zonecfg_setnwifent(handle) != Z_OK)
2919 		return;
2920 	while (zonecfg_getnwifent(handle, &lookup) == Z_OK) {
2921 		if (cmd->cmd_prop_nv_pairs == 0) {
2922 			output_net(fp, &lookup);
2923 			continue;
2924 		}
2925 		if (fill_in_nwiftab(cmd, &user, TRUE) != Z_OK)
2926 			continue;
2927 		if (strlen(user.zone_nwif_physical) > 0 &&
2928 		    strcmp(user.zone_nwif_physical,
2929 		    lookup.zone_nwif_physical) != 0)
2930 			continue;	/* no match */
2931 		if (strlen(user.zone_nwif_address) > 0 &&
2932 		    !zonecfg_same_net_address(user.zone_nwif_address,
2933 		    lookup.zone_nwif_address))
2934 			continue;	/* no match */
2935 		output_net(fp, &lookup);
2936 		output = TRUE;
2937 	}
2938 	(void) zonecfg_endnwifent(handle);
2939 	/*
2940 	 * If a property n/v pair was specified, warn the user if there was
2941 	 * nothing to output.
2942 	 */
2943 	if (!output && cmd->cmd_prop_nv_pairs > 0)
2944 		(void) printf(gettext("No such %s resource.\n"),
2945 		    rt_to_str(RT_NET));
2946 }
2947 
2948 static void
2949 output_dev(FILE *fp, struct zone_devtab *devtab)
2950 {
2951 	(void) fprintf(fp, "%s\n", rt_to_str(RT_DEVICE));
2952 	output_prop(fp, PT_MATCH, devtab->zone_dev_match, B_TRUE);
2953 }
2954 
2955 static void
2956 info_dev(zone_dochandle_t handle, FILE *fp, cmd_t *cmd)
2957 {
2958 	struct zone_devtab lookup, user;
2959 	bool output = FALSE;
2960 
2961 	if (zonecfg_setdevent(handle) != Z_OK)
2962 		return;
2963 	while (zonecfg_getdevent(handle, &lookup) == Z_OK) {
2964 		if (cmd->cmd_prop_nv_pairs == 0) {
2965 			output_dev(fp, &lookup);
2966 			continue;
2967 		}
2968 		if (fill_in_devtab(cmd, &user, TRUE) != Z_OK)
2969 			continue;
2970 		if (strlen(user.zone_dev_match) > 0 &&
2971 		    strcmp(user.zone_dev_match, lookup.zone_dev_match) != 0)
2972 			continue;	/* no match */
2973 		output_dev(fp, &lookup);
2974 		output = TRUE;
2975 	}
2976 	(void) zonecfg_enddevent(handle);
2977 	/*
2978 	 * If a property n/v pair was specified, warn the user if there was
2979 	 * nothing to output.
2980 	 */
2981 	if (!output && cmd->cmd_prop_nv_pairs > 0)
2982 		(void) printf(gettext("No such %s resource.\n"),
2983 		    rt_to_str(RT_DEVICE));
2984 }
2985 
2986 static void
2987 output_rctl(FILE *fp, struct zone_rctltab *rctltab)
2988 {
2989 	struct zone_rctlvaltab *valptr;
2990 
2991 	(void) fprintf(fp, "%s:\n", rt_to_str(RT_RCTL));
2992 	output_prop(fp, PT_NAME, rctltab->zone_rctl_name, B_TRUE);
2993 	for (valptr = rctltab->zone_rctl_valptr; valptr != NULL;
2994 	    valptr = valptr->zone_rctlval_next) {
2995 		fprintf(fp, "\t%s: (%s=%s,%s=%s,%s=%s)\n",
2996 		    pt_to_str(PT_VALUE),
2997 		    pt_to_str(PT_PRIV), valptr->zone_rctlval_priv,
2998 		    pt_to_str(PT_LIMIT), valptr->zone_rctlval_limit,
2999 		    pt_to_str(PT_ACTION), valptr->zone_rctlval_action);
3000 	}
3001 }
3002 
3003 static void
3004 info_rctl(zone_dochandle_t handle, FILE *fp, cmd_t *cmd)
3005 {
3006 	struct zone_rctltab lookup, user;
3007 	bool output = FALSE;
3008 
3009 	if (zonecfg_setrctlent(handle) != Z_OK)
3010 		return;
3011 	while (zonecfg_getrctlent(handle, &lookup) == Z_OK) {
3012 		if (cmd->cmd_prop_nv_pairs == 0) {
3013 			output_rctl(fp, &lookup);
3014 		} else if (fill_in_rctltab(cmd, &user, TRUE) == Z_OK &&
3015 		    (strlen(user.zone_rctl_name) == 0 ||
3016 		    strcmp(user.zone_rctl_name, lookup.zone_rctl_name) == 0)) {
3017 			output_rctl(fp, &lookup);
3018 			output = TRUE;
3019 		}
3020 		zonecfg_free_rctl_value_list(lookup.zone_rctl_valptr);
3021 	}
3022 	(void) zonecfg_endrctlent(handle);
3023 	/*
3024 	 * If a property n/v pair was specified, warn the user if there was
3025 	 * nothing to output.
3026 	 */
3027 	if (!output && cmd->cmd_prop_nv_pairs > 0)
3028 		(void) printf(gettext("No such %s resource.\n"),
3029 		    rt_to_str(RT_RCTL));
3030 }
3031 
3032 static void
3033 output_attr(FILE *fp, struct zone_attrtab *attrtab)
3034 {
3035 	(void) fprintf(fp, "%s:\n", rt_to_str(RT_ATTR));
3036 	output_prop(fp, PT_NAME, attrtab->zone_attr_name, B_TRUE);
3037 	output_prop(fp, PT_TYPE, attrtab->zone_attr_type, B_TRUE);
3038 	output_prop(fp, PT_VALUE, attrtab->zone_attr_value, B_TRUE);
3039 }
3040 
3041 static void
3042 info_attr(zone_dochandle_t handle, FILE *fp, cmd_t *cmd)
3043 {
3044 	struct zone_attrtab lookup, user;
3045 	bool output = FALSE;
3046 
3047 	if (zonecfg_setattrent(handle) != Z_OK)
3048 		return;
3049 	while (zonecfg_getattrent(handle, &lookup) == Z_OK) {
3050 		if (cmd->cmd_prop_nv_pairs == 0) {
3051 			output_attr(fp, &lookup);
3052 			continue;
3053 		}
3054 		if (fill_in_attrtab(cmd, &user, TRUE) != Z_OK)
3055 			continue;
3056 		if (strlen(user.zone_attr_name) > 0 &&
3057 		    strcmp(user.zone_attr_name, lookup.zone_attr_name) != 0)
3058 			continue;	/* no match */
3059 		if (strlen(user.zone_attr_type) > 0 &&
3060 		    strcmp(user.zone_attr_type, lookup.zone_attr_type) != 0)
3061 			continue;	/* no match */
3062 		if (strlen(user.zone_attr_value) > 0 &&
3063 		    strcmp(user.zone_attr_value, lookup.zone_attr_value) != 0)
3064 			continue;	/* no match */
3065 		output_attr(fp, &lookup);
3066 		output = TRUE;
3067 	}
3068 	(void) zonecfg_endattrent(handle);
3069 	/*
3070 	 * If a property n/v pair was specified, warn the user if there was
3071 	 * nothing to output.
3072 	 */
3073 	if (!output && cmd->cmd_prop_nv_pairs > 0)
3074 		(void) printf(gettext("No such %s resource.\n"),
3075 		    rt_to_str(RT_ATTR));
3076 }
3077 
3078 void
3079 info_func(cmd_t *cmd)
3080 {
3081 	FILE *fp = stdout;
3082 	bool need_to_close = FALSE;
3083 	char *pager;
3084 
3085 	assert(cmd != NULL);
3086 
3087 	if (initialize(TRUE) != Z_OK)
3088 		return;
3089 
3090 	/* don't page error output */
3091 	if (interactive_mode) {
3092 		if ((pager = getenv("PAGER")) == NULL)
3093 			pager = PAGER;
3094 		if ((fp = popen(pager, "w")) != NULL)
3095 			need_to_close = TRUE;
3096 		else
3097 			fp = stdout;
3098 		setbuf(fp, NULL);
3099 	}
3100 
3101 	if (!global_scope) {
3102 		switch (resource_scope) {
3103 		case RT_FS:
3104 			output_fs(fp, &in_progress_fstab);
3105 			break;
3106 		case RT_IPD:
3107 			output_ipd(fp, &in_progress_ipdtab);
3108 			break;
3109 		case RT_NET:
3110 			output_net(fp, &in_progress_nwiftab);
3111 			break;
3112 		case RT_DEVICE:
3113 			output_dev(fp, &in_progress_devtab);
3114 			break;
3115 		case RT_RCTL:
3116 			output_rctl(fp, &in_progress_rctltab);
3117 			break;
3118 		case RT_ATTR:
3119 			output_attr(fp, &in_progress_attrtab);
3120 			break;
3121 		}
3122 		goto cleanup;
3123 	}
3124 
3125 	switch (cmd->cmd_res_type) {
3126 	case RT_UNKNOWN:
3127 		info_zonepath(handle, fp);
3128 		info_autoboot(handle, fp);
3129 		info_pool(handle, fp);
3130 		info_ipd(handle, fp, cmd);
3131 		info_fs(handle, fp, cmd);
3132 		info_net(handle, fp, cmd);
3133 		info_dev(handle, fp, cmd);
3134 		info_rctl(handle, fp, cmd);
3135 		info_attr(handle, fp, cmd);
3136 		break;
3137 	case RT_ZONEPATH:
3138 		info_zonepath(handle, fp);
3139 		break;
3140 	case RT_AUTOBOOT:
3141 		info_autoboot(handle, fp);
3142 		break;
3143 	case RT_POOL:
3144 		info_pool(handle, fp);
3145 		break;
3146 	case RT_FS:
3147 		info_fs(handle, fp, cmd);
3148 		break;
3149 	case RT_IPD:
3150 		info_ipd(handle, fp, cmd);
3151 		break;
3152 	case RT_NET:
3153 		info_net(handle, fp, cmd);
3154 		break;
3155 	case RT_DEVICE:
3156 		info_dev(handle, fp, cmd);
3157 		break;
3158 	case RT_RCTL:
3159 		info_rctl(handle, fp, cmd);
3160 		break;
3161 	case RT_ATTR:
3162 		info_attr(handle, fp, cmd);
3163 		break;
3164 	default:
3165 		zone_perror(rt_to_str(cmd->cmd_res_type), Z_NO_RESOURCE_TYPE,
3166 		    TRUE);
3167 	}
3168 
3169 cleanup:
3170 	if (need_to_close)
3171 		(void) pclose(fp);
3172 }
3173 
3174 static int
3175 save_it(char *zonepath)
3176 {
3177 	int err;
3178 
3179 	if (new_zone) {
3180 		err = zonecfg_add_index(zone, zonepath);
3181 		if (err != Z_OK) {
3182 			zone_perror(zone, err, TRUE);
3183 			return (err);
3184 		}
3185 		new_zone = FALSE;
3186 	}
3187 	if ((err = zonecfg_save(handle)) == Z_OK)
3188 		need_to_commit = FALSE;
3189 	return (err);
3190 }
3191 
3192 /*
3193  * See the DTD for which attributes are required for which resources.
3194  *
3195  * This function can be called by commit_func(), which needs to save things,
3196  * in addition to the general call from parse_and_run(), which doesn't need
3197  * things saved.  Since the parameters are standardized, we distinguish by
3198  * having commit_func() call here with cmd->cmd_arg set to "save" to indicate
3199  * that a save is needed.
3200  */
3201 void
3202 verify_func(cmd_t *cmd)
3203 {
3204 	struct zone_nwiftab nwiftab;
3205 	struct zone_fstab fstab;
3206 	struct zone_attrtab attrtab;
3207 	struct zone_rctltab rctltab;
3208 	char zonepath[MAXPATHLEN];
3209 	int err, ret_val = Z_OK, arg;
3210 	bool save = FALSE;
3211 
3212 	optind = 0;
3213 	if ((arg = getopt(cmd->cmd_argc, cmd->cmd_argv, "?")) != EOF) {
3214 		switch (arg) {
3215 		case '?':
3216 			longer_usage(CMD_VERIFY);
3217 			return;
3218 		default:
3219 			short_usage(CMD_VERIFY);
3220 			return;
3221 		}
3222 	}
3223 	if (optind > cmd->cmd_argc) {
3224 		short_usage(CMD_VERIFY);
3225 		return;
3226 	}
3227 
3228 	if (zone_is_read_only(CMD_VERIFY))
3229 		return;
3230 
3231 	assert(cmd != NULL);
3232 
3233 	if (cmd->cmd_argc > 0 && (strcmp(cmd->cmd_argv[0], "save") == 0))
3234 		save = TRUE;
3235 	if (initialize(TRUE) != Z_OK)
3236 		return;
3237 
3238 	if (zonecfg_get_zonepath(handle, zonepath, sizeof (zonepath)) != Z_OK) {
3239 		zerr("%s %s", pt_to_str(PT_ZONEPATH), gettext("not specified"));
3240 		ret_val = Z_REQD_RESOURCE_MISSING;
3241 		saw_error = TRUE;
3242 	}
3243 	if (strlen(zonepath) == 0) {
3244 		zerr("%s %s", pt_to_str(PT_ZONEPATH),
3245 		    gettext("cannot be empty."));
3246 		ret_val = Z_REQD_RESOURCE_MISSING;
3247 		saw_error = TRUE;
3248 	}
3249 
3250 	if ((err = zonecfg_setipdent(handle)) != Z_OK) {
3251 		zone_perror(zone, err, TRUE);
3252 		return;
3253 	}
3254 	while (zonecfg_getipdent(handle, &fstab) == Z_OK) {
3255 		if (strlen(fstab.zone_fs_dir) == 0) {
3256 			zerr("%s: %s %s", rt_to_str(RT_IPD), pt_to_str(PT_DIR),
3257 			    gettext("not specified"));
3258 			saw_error = TRUE;
3259 			if (ret_val == Z_OK)
3260 				ret_val = Z_REQD_PROPERTY_MISSING;
3261 		}
3262 	}
3263 	(void) zonecfg_endipdent(handle);
3264 
3265 	if ((err = zonecfg_setfsent(handle)) != Z_OK) {
3266 		zone_perror(zone, err, TRUE);
3267 		return;
3268 	}
3269 	while (zonecfg_getfsent(handle, &fstab) == Z_OK) {
3270 		if (strlen(fstab.zone_fs_dir) == 0) {
3271 			zerr("%s: %s %s", rt_to_str(RT_FS), pt_to_str(PT_DIR),
3272 			    gettext("not specified"));
3273 			saw_error = TRUE;
3274 			if (ret_val == Z_OK)
3275 				ret_val = Z_REQD_PROPERTY_MISSING;
3276 		}
3277 		if (strlen(fstab.zone_fs_special) == 0) {
3278 			zerr("%s: %s %s", rt_to_str(RT_FS),
3279 			    pt_to_str(PT_SPECIAL), gettext("not specified"));
3280 			saw_error = TRUE;
3281 			if (ret_val == Z_OK)
3282 				ret_val = Z_REQD_PROPERTY_MISSING;
3283 		}
3284 		if (strlen(fstab.zone_fs_type) == 0) {
3285 			zerr("%s: %s %s", rt_to_str(RT_FS), pt_to_str(PT_TYPE),
3286 			    gettext("not specified"));
3287 			saw_error = TRUE;
3288 			if (ret_val == Z_OK)
3289 				ret_val = Z_REQD_PROPERTY_MISSING;
3290 		}
3291 		zonecfg_free_fs_option_list(fstab.zone_fs_options);
3292 	}
3293 	(void) zonecfg_endfsent(handle);
3294 
3295 	if ((err = zonecfg_setnwifent(handle)) != Z_OK) {
3296 		zone_perror(zone, err, TRUE);
3297 		return;
3298 	}
3299 	while (zonecfg_getnwifent(handle, &nwiftab) == Z_OK) {
3300 		if (strlen(nwiftab.zone_nwif_address) == 0) {
3301 			zerr("%s: %s %s", rt_to_str(RT_NET),
3302 			    pt_to_str(PT_ADDRESS), gettext("not specified"));
3303 			saw_error = TRUE;
3304 			if (ret_val == Z_OK)
3305 				ret_val = Z_REQD_PROPERTY_MISSING;
3306 		}
3307 		if (strlen(nwiftab.zone_nwif_physical) == 0) {
3308 			zerr("%s: %s %s", rt_to_str(RT_NET),
3309 			    pt_to_str(PT_PHYSICAL), gettext("not specified"));
3310 			saw_error = TRUE;
3311 			if (ret_val == Z_OK)
3312 				ret_val = Z_REQD_PROPERTY_MISSING;
3313 		}
3314 	}
3315 	(void) zonecfg_endnwifent(handle);
3316 
3317 	if ((err = zonecfg_setrctlent(handle)) != Z_OK) {
3318 		zone_perror(zone, err, TRUE);
3319 		return;
3320 	}
3321 	while (zonecfg_getrctlent(handle, &rctltab) == Z_OK) {
3322 		if (strlen(rctltab.zone_rctl_name) == 0) {
3323 			zerr("%s: %s %s", rt_to_str(RT_RCTL),
3324 			    pt_to_str(PT_NAME), gettext("not specified"));
3325 			saw_error = TRUE;
3326 			if (ret_val == Z_OK)
3327 				ret_val = Z_REQD_PROPERTY_MISSING;
3328 		}
3329 		if (rctltab.zone_rctl_valptr == NULL) {
3330 			zerr(gettext("%s: no %s specified"),
3331 			    rt_to_str(RT_RCTL), pt_to_str(PT_VALUE));
3332 			saw_error = TRUE;
3333 			if (ret_val == Z_OK)
3334 				ret_val = Z_REQD_PROPERTY_MISSING;
3335 		} else {
3336 			zonecfg_free_rctl_value_list(rctltab.zone_rctl_valptr);
3337 		}
3338 	}
3339 	(void) zonecfg_endrctlent(handle);
3340 
3341 	if ((err = zonecfg_setattrent(handle)) != Z_OK) {
3342 		zone_perror(zone, err, TRUE);
3343 		return;
3344 	}
3345 	while (zonecfg_getattrent(handle, &attrtab) == Z_OK) {
3346 		if (strlen(attrtab.zone_attr_name) == 0) {
3347 			zerr("%s: %s %s", rt_to_str(RT_ATTR),
3348 			    pt_to_str(PT_NAME), gettext("not specified"));
3349 			saw_error = TRUE;
3350 			if (ret_val == Z_OK)
3351 				ret_val = Z_REQD_PROPERTY_MISSING;
3352 		}
3353 		if (strlen(attrtab.zone_attr_type) == 0) {
3354 			zerr("%s: %s %s", rt_to_str(RT_ATTR),
3355 			    pt_to_str(PT_TYPE), gettext("not specified"));
3356 			saw_error = TRUE;
3357 			if (ret_val == Z_OK)
3358 				ret_val = Z_REQD_PROPERTY_MISSING;
3359 		}
3360 		if (strlen(attrtab.zone_attr_value) == 0) {
3361 			zerr("%s: %s %s", rt_to_str(RT_ATTR),
3362 			    pt_to_str(PT_VALUE), gettext("not specified"));
3363 			saw_error = TRUE;
3364 			if (ret_val == Z_OK)
3365 				ret_val = Z_REQD_PROPERTY_MISSING;
3366 		}
3367 	}
3368 	(void) zonecfg_endattrent(handle);
3369 
3370 	if (!global_scope) {
3371 		zerr(gettext("resource specification incomplete"));
3372 		saw_error = TRUE;
3373 		if (ret_val == Z_OK)
3374 			ret_val = Z_INSUFFICIENT_SPEC;
3375 	}
3376 
3377 	if (save) {
3378 		if (ret_val == Z_OK)
3379 			ret_val = save_it(zonepath);
3380 		else
3381 			zerr("zone %s %s", zone, gettext("failed to verify"));
3382 	}
3383 	if (ret_val != Z_OK)
3384 		zone_perror(zone, ret_val, TRUE);
3385 }
3386 
3387 void
3388 cancel_func(cmd_t *cmd)
3389 {
3390 	int arg;
3391 
3392 	assert(cmd != NULL);
3393 
3394 	optind = 0;
3395 	if ((arg = getopt(cmd->cmd_argc, cmd->cmd_argv, "?")) != EOF) {
3396 		switch (arg) {
3397 		case '?':
3398 			longer_usage(CMD_CANCEL);
3399 			return;
3400 		default:
3401 			short_usage(CMD_CANCEL);
3402 			return;
3403 		}
3404 	}
3405 	if (optind != cmd->cmd_argc) {
3406 		short_usage(CMD_CANCEL);
3407 		return;
3408 	}
3409 
3410 	if (global_scope)
3411 		scope_usage(CMD_CANCEL);
3412 	global_scope = TRUE;
3413 	zonecfg_free_fs_option_list(in_progress_fstab.zone_fs_options);
3414 	bzero(&in_progress_fstab, sizeof (in_progress_fstab));
3415 	bzero(&in_progress_nwiftab, sizeof (in_progress_nwiftab));
3416 	bzero(&in_progress_devtab, sizeof (in_progress_devtab));
3417 	zonecfg_free_rctl_value_list(in_progress_rctltab.zone_rctl_valptr);
3418 	bzero(&in_progress_rctltab, sizeof (in_progress_rctltab));
3419 	bzero(&in_progress_attrtab, sizeof (in_progress_attrtab));
3420 }
3421 
3422 static int
3423 validate_attr_name(char *name)
3424 {
3425 	int i;
3426 
3427 	if (!isalnum(name[0])) {
3428 		zerr(gettext("Invalid %s %s %s: must start with an alpha-"
3429 		    "numeric character."), rt_to_str(RT_ATTR),
3430 		    pt_to_str(PT_NAME), name);
3431 		return (Z_INVAL);
3432 	}
3433 	for (i = 1; name[i]; i++)
3434 		if (!isalnum(name[i]) && name[i] != '-' && name[i] != '.') {
3435 			zerr(gettext("Invalid %s %s %s: can only contain "
3436 			    "alpha-numeric characters, plus '-' and '.'."),
3437 			    rt_to_str(RT_ATTR), pt_to_str(PT_NAME), name);
3438 			return (Z_INVAL);
3439 		}
3440 	return (Z_OK);
3441 }
3442 
3443 static int
3444 validate_attr_type_val(struct zone_attrtab *attrtab)
3445 {
3446 	boolean_t boolval;
3447 	int64_t intval;
3448 	char strval[MAXNAMELEN];
3449 	uint64_t uintval;
3450 
3451 	if (strcmp(attrtab->zone_attr_type, "boolean") == 0) {
3452 		if (zonecfg_get_attr_boolean(attrtab, &boolval) == Z_OK)
3453 			return (Z_OK);
3454 		zerr(gettext("invalid %s value for %s=%s"),
3455 		    rt_to_str(RT_ATTR), pt_to_str(PT_TYPE), "boolean");
3456 		return (Z_ERR);
3457 	}
3458 
3459 	if (strcmp(attrtab->zone_attr_type, "int") == 0) {
3460 		if (zonecfg_get_attr_int(attrtab, &intval) == Z_OK)
3461 			return (Z_OK);
3462 		zerr(gettext("invalid %s value for %s=%s"),
3463 		    rt_to_str(RT_ATTR), pt_to_str(PT_TYPE), "int");
3464 		return (Z_ERR);
3465 	}
3466 
3467 	if (strcmp(attrtab->zone_attr_type, "string") == 0) {
3468 		if (zonecfg_get_attr_string(attrtab, strval,
3469 		    sizeof (strval)) == Z_OK)
3470 			return (Z_OK);
3471 		zerr(gettext("invalid %s value for %s=%s"),
3472 		    rt_to_str(RT_ATTR), pt_to_str(PT_TYPE), "string");
3473 		return (Z_ERR);
3474 	}
3475 
3476 	if (strcmp(attrtab->zone_attr_type, "uint") == 0) {
3477 		if (zonecfg_get_attr_uint(attrtab, &uintval) == Z_OK)
3478 			return (Z_OK);
3479 		zerr(gettext("invalid %s value for %s=%s"),
3480 		    rt_to_str(RT_ATTR), pt_to_str(PT_TYPE), "uint");
3481 		return (Z_ERR);
3482 	}
3483 
3484 	zerr(gettext("invalid %s %s '%s'"), rt_to_str(RT_ATTR),
3485 	    pt_to_str(PT_TYPE), attrtab->zone_attr_type);
3486 	return (Z_ERR);
3487 }
3488 
3489 void
3490 end_func(cmd_t *cmd)
3491 {
3492 	bool validation_failed = FALSE;
3493 	struct zone_fstab tmp_fstab;
3494 	struct zone_nwiftab tmp_nwiftab;
3495 	struct zone_devtab tmp_devtab;
3496 	struct zone_rctltab tmp_rctltab;
3497 	struct zone_attrtab tmp_attrtab;
3498 	int err, arg;
3499 
3500 	assert(cmd != NULL);
3501 
3502 	optind = 0;
3503 	if ((arg = getopt(cmd->cmd_argc, cmd->cmd_argv, "?")) != EOF) {
3504 		switch (arg) {
3505 		case '?':
3506 			longer_usage(CMD_END);
3507 			return;
3508 		default:
3509 			short_usage(CMD_END);
3510 			return;
3511 		}
3512 	}
3513 	if (optind != cmd->cmd_argc) {
3514 		short_usage(CMD_END);
3515 		return;
3516 	}
3517 
3518 	if (global_scope) {
3519 		scope_usage(CMD_END);
3520 		return;
3521 	}
3522 
3523 	assert(end_op == CMD_ADD || end_op == CMD_SELECT);
3524 
3525 	switch (resource_scope) {
3526 	case RT_FS:
3527 		/* First make sure everything was filled in. */
3528 		if (strlen(in_progress_fstab.zone_fs_dir) == 0) {
3529 			zerr("dir %s", gettext("not specified"));
3530 			saw_error = TRUE;
3531 			validation_failed = TRUE;
3532 		} else if (in_progress_fstab.zone_fs_dir[0] != '/') {
3533 			zerr("dir %s %s", in_progress_fstab.zone_fs_dir,
3534 			    gettext("is not an absolute path."));
3535 			saw_error = TRUE;
3536 			validation_failed = TRUE;
3537 		}
3538 		if (strlen(in_progress_fstab.zone_fs_special) == 0) {
3539 			zerr("special %s", gettext("not specified"));
3540 			saw_error = TRUE;
3541 			validation_failed = TRUE;
3542 		}
3543 		if (in_progress_fstab.zone_fs_raw[0] != '\0' &&
3544 		    in_progress_fstab.zone_fs_raw[0] != '/') {
3545 			zerr("raw device %s %s",
3546 			    in_progress_fstab.zone_fs_raw,
3547 			    gettext("is not an absolute path."));
3548 			saw_error = TRUE;
3549 			validation_failed = TRUE;
3550 		}
3551 		if (strlen(in_progress_fstab.zone_fs_type) == 0) {
3552 			zerr("type %s", gettext("not specified"));
3553 			saw_error = TRUE;
3554 			validation_failed = TRUE;
3555 		}
3556 		if (validation_failed)
3557 			return;
3558 		if (end_op == CMD_ADD) {
3559 			/* Make sure there isn't already one like this. */
3560 			bzero(&tmp_fstab, sizeof (tmp_fstab));
3561 			(void) strlcpy(tmp_fstab.zone_fs_dir,
3562 			    in_progress_fstab.zone_fs_dir,
3563 			    sizeof (tmp_fstab.zone_fs_dir));
3564 			err = zonecfg_lookup_filesystem(handle, &tmp_fstab);
3565 			zonecfg_free_fs_option_list(tmp_fstab.zone_fs_options);
3566 			if (err == Z_OK) {
3567 				zerr(gettext("A %s resource "
3568 				    "with the %s '%s' already exists."),
3569 				    rt_to_str(RT_FS), pt_to_str(PT_DIR),
3570 				    in_progress_fstab.zone_fs_dir);
3571 				saw_error = TRUE;
3572 				return;
3573 			}
3574 			err = zonecfg_add_filesystem(handle,
3575 			    &in_progress_fstab);
3576 		} else {
3577 			err = zonecfg_modify_filesystem(handle, &old_fstab,
3578 			    &in_progress_fstab);
3579 		}
3580 		zonecfg_free_fs_option_list(in_progress_fstab.zone_fs_options);
3581 		in_progress_fstab.zone_fs_options = NULL;
3582 		break;
3583 	case RT_IPD:
3584 		/* First make sure everything was filled in. */
3585 		if (strlen(in_progress_ipdtab.zone_fs_dir) == 0) {
3586 			zerr("dir %s", gettext("not specified"));
3587 			saw_error = TRUE;
3588 			validation_failed = TRUE;
3589 		} else if (in_progress_ipdtab.zone_fs_dir[0] != '/') {
3590 			zerr("dir %s %s", in_progress_ipdtab.zone_fs_dir,
3591 			    gettext("is not an absolute path."));
3592 			saw_error = TRUE;
3593 			validation_failed = TRUE;
3594 		}
3595 		if (validation_failed)
3596 			return;
3597 		if (end_op == CMD_ADD) {
3598 			/* Make sure there isn't already one like this. */
3599 			bzero(&tmp_fstab, sizeof (tmp_fstab));
3600 			(void) strlcpy(tmp_fstab.zone_fs_dir,
3601 			    in_progress_ipdtab.zone_fs_dir,
3602 			    sizeof (tmp_fstab.zone_fs_dir));
3603 			err = zonecfg_lookup_ipd(handle, &tmp_fstab);
3604 			if (err == Z_OK) {
3605 				zerr(gettext("An %s resource "
3606 				    "with the %s '%s' already exists."),
3607 				    rt_to_str(RT_IPD), pt_to_str(PT_DIR),
3608 				    in_progress_ipdtab.zone_fs_dir);
3609 				saw_error = TRUE;
3610 				return;
3611 			}
3612 			err = zonecfg_add_ipd(handle, &in_progress_ipdtab);
3613 		} else {
3614 			err = zonecfg_modify_ipd(handle, &old_ipdtab,
3615 			    &in_progress_ipdtab);
3616 		}
3617 		break;
3618 	case RT_NET:
3619 		/* First make sure everything was filled in. */
3620 		if (strlen(in_progress_nwiftab.zone_nwif_physical) == 0) {
3621 			zerr("physical %s", gettext("not specified"));
3622 			saw_error = TRUE;
3623 			validation_failed = TRUE;
3624 		}
3625 		if (strlen(in_progress_nwiftab.zone_nwif_address) == 0) {
3626 			zerr("address %s", gettext("not specified"));
3627 			saw_error = TRUE;
3628 			validation_failed = TRUE;
3629 		}
3630 		if (validation_failed)
3631 			return;
3632 		if (end_op == CMD_ADD) {
3633 			/* Make sure there isn't already one like this. */
3634 			bzero(&tmp_nwiftab, sizeof (tmp_nwiftab));
3635 			(void) strlcpy(tmp_nwiftab.zone_nwif_address,
3636 			    in_progress_nwiftab.zone_nwif_address,
3637 			    sizeof (tmp_nwiftab.zone_nwif_address));
3638 			if (zonecfg_lookup_nwif(handle, &tmp_nwiftab) == Z_OK) {
3639 				zerr(gettext("A %s resource "
3640 				    "with the %s '%s' already exists."),
3641 				    rt_to_str(RT_NET), pt_to_str(PT_ADDRESS),
3642 				    in_progress_nwiftab.zone_nwif_address);
3643 				saw_error = TRUE;
3644 				return;
3645 			}
3646 			err = zonecfg_add_nwif(handle, &in_progress_nwiftab);
3647 		} else {
3648 			err = zonecfg_modify_nwif(handle, &old_nwiftab,
3649 			    &in_progress_nwiftab);
3650 		}
3651 		break;
3652 	case RT_DEVICE:
3653 		/* First make sure everything was filled in. */
3654 		if (strlen(in_progress_devtab.zone_dev_match) == 0) {
3655 			zerr("match %s", gettext("not specified"));
3656 			saw_error = TRUE;
3657 			validation_failed = TRUE;
3658 		}
3659 		if (validation_failed)
3660 			return;
3661 		if (end_op == CMD_ADD) {
3662 			/* Make sure there isn't already one like this. */
3663 			(void) strlcpy(tmp_devtab.zone_dev_match,
3664 			    in_progress_devtab.zone_dev_match,
3665 			    sizeof (tmp_devtab.zone_dev_match));
3666 			if (zonecfg_lookup_dev(handle, &tmp_devtab) == Z_OK) {
3667 				zerr(gettext("A %s resource with the %s '%s' "
3668 				    "already exists."), rt_to_str(RT_DEVICE),
3669 				    pt_to_str(PT_MATCH),
3670 				    in_progress_devtab.zone_dev_match);
3671 				saw_error = TRUE;
3672 				return;
3673 			}
3674 			err = zonecfg_add_dev(handle, &in_progress_devtab);
3675 		} else {
3676 			err = zonecfg_modify_dev(handle, &old_devtab,
3677 			    &in_progress_devtab);
3678 		}
3679 		break;
3680 	case RT_RCTL:
3681 		/* First make sure everything was filled in. */
3682 		if (strlen(in_progress_rctltab.zone_rctl_name) == 0) {
3683 			zerr("name %s", gettext("not specified"));
3684 			saw_error = TRUE;
3685 			validation_failed = TRUE;
3686 		}
3687 		if (in_progress_rctltab.zone_rctl_valptr == NULL) {
3688 			zerr(gettext("no %s specified"), pt_to_str(PT_VALUE));
3689 			saw_error = TRUE;
3690 			validation_failed = TRUE;
3691 		}
3692 		if (validation_failed)
3693 			return;
3694 		if (end_op == CMD_ADD) {
3695 			/* Make sure there isn't already one like this. */
3696 			(void) strlcpy(tmp_rctltab.zone_rctl_name,
3697 			    in_progress_rctltab.zone_rctl_name,
3698 			    sizeof (tmp_rctltab.zone_rctl_name));
3699 			tmp_rctltab.zone_rctl_valptr = NULL;
3700 			err = zonecfg_lookup_rctl(handle, &tmp_rctltab);
3701 			zonecfg_free_rctl_value_list(
3702 			    tmp_rctltab.zone_rctl_valptr);
3703 			if (err == Z_OK) {
3704 				zerr(gettext("A %s resource "
3705 				    "with the %s '%s' already exists."),
3706 				    rt_to_str(RT_RCTL), pt_to_str(PT_NAME),
3707 				    in_progress_rctltab.zone_rctl_name);
3708 				saw_error = TRUE;
3709 				return;
3710 			}
3711 			err = zonecfg_add_rctl(handle, &in_progress_rctltab);
3712 		} else {
3713 			err = zonecfg_modify_rctl(handle, &old_rctltab,
3714 			    &in_progress_rctltab);
3715 		}
3716 		if (err == Z_OK) {
3717 			zonecfg_free_rctl_value_list(
3718 			    in_progress_rctltab.zone_rctl_valptr);
3719 			in_progress_rctltab.zone_rctl_valptr = NULL;
3720 		}
3721 		break;
3722 	case RT_ATTR:
3723 		/* First make sure everything was filled in. */
3724 		if (strlen(in_progress_attrtab.zone_attr_name) == 0) {
3725 			zerr("name %s", gettext("not specified"));
3726 			saw_error = TRUE;
3727 			validation_failed = TRUE;
3728 		}
3729 		if (strlen(in_progress_attrtab.zone_attr_type) == 0) {
3730 			zerr("type %s", gettext("not specified"));
3731 			saw_error = TRUE;
3732 			validation_failed = TRUE;
3733 		}
3734 		if (strlen(in_progress_attrtab.zone_attr_value) == 0) {
3735 			zerr("value %s", gettext("not specified"));
3736 			saw_error = TRUE;
3737 			validation_failed = TRUE;
3738 		}
3739 		if (validate_attr_name(in_progress_attrtab.zone_attr_name) !=
3740 		    Z_OK) {
3741 			saw_error = TRUE;
3742 			validation_failed = TRUE;
3743 		}
3744 		if (validate_attr_type_val(&in_progress_attrtab) != Z_OK) {
3745 			saw_error = TRUE;
3746 			validation_failed = TRUE;
3747 		}
3748 		if (validation_failed)
3749 			return;
3750 		if (end_op == CMD_ADD) {
3751 			/* Make sure there isn't already one like this. */
3752 			bzero(&tmp_attrtab, sizeof (tmp_attrtab));
3753 			(void) strlcpy(tmp_attrtab.zone_attr_name,
3754 			    in_progress_attrtab.zone_attr_name,
3755 			    sizeof (tmp_attrtab.zone_attr_name));
3756 			if (zonecfg_lookup_attr(handle, &tmp_attrtab) == Z_OK) {
3757 				zerr(gettext("An %s resource "
3758 				    "with the %s '%s' already exists."),
3759 				    rt_to_str(RT_ATTR), pt_to_str(PT_NAME),
3760 				    in_progress_attrtab.zone_attr_name);
3761 				saw_error = TRUE;
3762 				return;
3763 			}
3764 			err = zonecfg_add_attr(handle, &in_progress_attrtab);
3765 		} else {
3766 			err = zonecfg_modify_attr(handle, &old_attrtab,
3767 			    &in_progress_attrtab);
3768 		}
3769 		break;
3770 	default:
3771 		zone_perror(rt_to_str(resource_scope), Z_NO_RESOURCE_TYPE,
3772 		    TRUE);
3773 		saw_error = TRUE;
3774 		return;
3775 	}
3776 
3777 	if (err != Z_OK) {
3778 		zone_perror(zone, err, TRUE);
3779 	} else {
3780 		need_to_commit = TRUE;
3781 		global_scope = TRUE;
3782 		end_op = -1;
3783 	}
3784 }
3785 
3786 void
3787 commit_func(cmd_t *cmd)
3788 {
3789 	int arg;
3790 
3791 	optind = 0;
3792 	if ((arg = getopt(cmd->cmd_argc, cmd->cmd_argv, "?")) != EOF) {
3793 		switch (arg) {
3794 		case '?':
3795 			longer_usage(CMD_COMMIT);
3796 			return;
3797 		default:
3798 			short_usage(CMD_COMMIT);
3799 			return;
3800 		}
3801 	}
3802 	if (optind != cmd->cmd_argc) {
3803 		short_usage(CMD_COMMIT);
3804 		return;
3805 	}
3806 
3807 	if (zone_is_read_only(CMD_COMMIT))
3808 		return;
3809 
3810 	assert(cmd != NULL);
3811 
3812 	cmd->cmd_argc = 1;
3813 	/*
3814 	 * cmd_arg normally comes from a strdup() in the lexer, and the
3815 	 * whole cmd structure and its (char *) attributes are freed at
3816 	 * the completion of each command, so the strdup() below is needed
3817 	 * to match this and prevent a core dump from trying to free()
3818 	 * something that can't be.
3819 	 */
3820 	if ((cmd->cmd_argv[0] = strdup("save")) == NULL) {
3821 		zone_perror(zone, Z_NOMEM, TRUE);
3822 		exit(Z_ERR);
3823 	}
3824 	cmd->cmd_argv[1] = NULL;
3825 	verify_func(cmd);
3826 }
3827 
3828 void
3829 revert_func(cmd_t *cmd)
3830 {
3831 	char line[128];	/* enough to ask a question */
3832 	bool force = FALSE;
3833 	int err, arg, answer;
3834 
3835 	optind = 0;
3836 	while ((arg = getopt(cmd->cmd_argc, cmd->cmd_argv, "?F")) != EOF) {
3837 		switch (arg) {
3838 		case '?':
3839 			longer_usage(CMD_REVERT);
3840 			return;
3841 		case 'F':
3842 			force = TRUE;
3843 			break;
3844 		default:
3845 			short_usage(CMD_REVERT);
3846 			return;
3847 		}
3848 	}
3849 	if (optind != cmd->cmd_argc) {
3850 		short_usage(CMD_REVERT);
3851 		return;
3852 	}
3853 
3854 	if (zone_is_read_only(CMD_REVERT))
3855 		return;
3856 
3857 	if (zonecfg_check_handle(handle) != Z_OK) {
3858 		zerr(gettext("No changes to revert."));
3859 		saw_error = TRUE;
3860 		return;
3861 	}
3862 
3863 	if (!force) {
3864 		(void) snprintf(line, sizeof (line),
3865 		    gettext("Are you sure you want to revert"));
3866 		if ((answer = ask_yesno(FALSE, line)) == -1) {
3867 			zerr(gettext("Input not from terminal and -F not "
3868 			    "specified:\n%s command ignored, exiting."),
3869 			    cmd_to_str(CMD_REVERT));
3870 			exit(Z_ERR);
3871 		}
3872 		if (answer != 1)
3873 			return;
3874 	}
3875 
3876 	/*
3877 	 * Time for a new handle: finish the old one off first
3878 	 * then get a new one properly to avoid leaks.
3879 	 */
3880 	zonecfg_fini_handle(handle);
3881 	if ((handle = zonecfg_init_handle()) == NULL) {
3882 		zone_perror(execname, Z_NOMEM, TRUE);
3883 		exit(Z_ERR);
3884 	}
3885 	if ((err = zonecfg_get_handle(zone, handle)) != Z_OK) {
3886 		saw_error = TRUE;
3887 		got_handle = FALSE;
3888 		if (err == Z_NO_ZONE)
3889 			zerr(gettext("%s: no such saved zone to revert to."),
3890 			    zone);
3891 		else
3892 			zone_perror(zone, err, TRUE);
3893 	}
3894 }
3895 
3896 void
3897 help_func(cmd_t *cmd)
3898 {
3899 	int i;
3900 
3901 	assert(cmd != NULL);
3902 
3903 	if (cmd->cmd_argc == 0) {
3904 		usage(TRUE, global_scope ? HELP_SUBCMDS : HELP_RES_SCOPE);
3905 		return;
3906 	}
3907 	if (strcmp(cmd->cmd_argv[0], "usage") == 0) {
3908 		usage(TRUE, HELP_USAGE);
3909 		return;
3910 	}
3911 	if (strcmp(cmd->cmd_argv[0], "commands") == 0) {
3912 		usage(TRUE, HELP_SUBCMDS);
3913 		return;
3914 	}
3915 	if (strcmp(cmd->cmd_argv[0], "syntax") == 0) {
3916 		usage(TRUE, HELP_SYNTAX | HELP_RES_PROPS);
3917 		return;
3918 	}
3919 	if (strcmp(cmd->cmd_argv[0], "-?") == 0) {
3920 		longer_usage(CMD_HELP);
3921 		return;
3922 	}
3923 
3924 	for (i = 0; i <= CMD_MAX; i++) {
3925 		if (strcmp(cmd->cmd_argv[0], cmd_to_str(i)) == 0) {
3926 			longer_usage(i);
3927 			return;
3928 		}
3929 	}
3930 	/* We do not use zerr() here because we do not want its extra \n. */
3931 	(void) fprintf(stderr, gettext("Unknown help subject %s.  "),
3932 	    cmd->cmd_argv[0]);
3933 	usage(FALSE, HELP_META);
3934 }
3935 
3936 static int
3937 string_to_yyin(char *string)
3938 {
3939 	if ((yyin = tmpfile()) == NULL) {
3940 		zone_perror(execname, Z_TEMP_FILE, TRUE);
3941 		return (Z_ERR);
3942 	}
3943 	if (fwrite(string, strlen(string), 1, yyin) != 1) {
3944 		zone_perror(execname, Z_TEMP_FILE, TRUE);
3945 		return (Z_ERR);
3946 	}
3947 	if (fseek(yyin, 0, SEEK_SET) != 0) {
3948 		zone_perror(execname, Z_TEMP_FILE, TRUE);
3949 		return (Z_ERR);
3950 	}
3951 	return (Z_OK);
3952 }
3953 
3954 /* This is the back-end helper function for read_input() below. */
3955 
3956 static int
3957 cleanup()
3958 {
3959 	int answer;
3960 	cmd_t *cmd;
3961 
3962 	if (!interactive_mode && !cmd_file_mode) {
3963 		/*
3964 		 * If we're not in interactive mode, and we're not in command
3965 		 * file mode, then we must be in commands-from-the-command-line
3966 		 * mode.  As such, we can't loop back and ask for more input.
3967 		 * It was OK to prompt for such things as whether or not to
3968 		 * really delete a zone in the command handler called from
3969 		 * yyparse() above, but "really quit?" makes no sense in this
3970 		 * context.  So disable prompting.
3971 		 */
3972 		ok_to_prompt = FALSE;
3973 	}
3974 	if (!global_scope) {
3975 		if (!time_to_exit) {
3976 			/*
3977 			 * Just print a simple error message in the -1 case,
3978 			 * since exit_func() already handles that case, and
3979 			 * EOF means we are finished anyway.
3980 			 */
3981 			answer = ask_yesno(FALSE,
3982 			    gettext("Resource incomplete; really quit"));
3983 			if (answer == -1) {
3984 				zerr(gettext("Resource incomplete."));
3985 				return (Z_ERR);
3986 			}
3987 			if (answer != 1) {
3988 				yyin = stdin;
3989 				return (Z_REPEAT);
3990 			}
3991 		} else {
3992 			saw_error = TRUE;
3993 		}
3994 	}
3995 	/*
3996 	 * Make sure we tried something and that the handle checks
3997 	 * out, or we would get a false error trying to commit.
3998 	 */
3999 	if (need_to_commit && zonecfg_check_handle(handle) == Z_OK) {
4000 		if ((cmd = alloc_cmd()) == NULL) {
4001 			zone_perror(zone, Z_NOMEM, TRUE);
4002 			return (Z_ERR);
4003 		}
4004 		cmd->cmd_argc = 0;
4005 		cmd->cmd_argv[0] = NULL;
4006 		commit_func(cmd);
4007 		free_cmd(cmd);
4008 		/*
4009 		 * need_to_commit will get set back to FALSE if the
4010 		 * configuration is saved successfully.
4011 		 */
4012 		if (need_to_commit) {
4013 			if (force_exit) {
4014 				zerr(gettext("Configuration not saved."));
4015 				return (Z_ERR);
4016 			}
4017 			answer = ask_yesno(FALSE,
4018 			    gettext("Configuration not saved; really quit"));
4019 			if (answer == -1) {
4020 				zerr(gettext("Configuration not saved."));
4021 				return (Z_ERR);
4022 			}
4023 			if (answer != 1) {
4024 				time_to_exit = FALSE;
4025 				yyin = stdin;
4026 				return (Z_REPEAT);
4027 			}
4028 		}
4029 	}
4030 	return ((need_to_commit || saw_error) ? Z_ERR : Z_OK);
4031 }
4032 
4033 /*
4034  * read_input() is the driver of this program.  It is a wrapper around
4035  * yyparse(), printing appropriate prompts when needed, checking for
4036  * exit conditions and reacting appropriately [the latter in its cleanup()
4037  * helper function].
4038  *
4039  * Like most zonecfg functions, it returns Z_OK or Z_ERR, *or* Z_REPEAT
4040  * so do_interactive() knows that we are not really done (i.e, we asked
4041  * the user if we should really quit and the user said no).
4042  */
4043 static int
4044 read_input()
4045 {
4046 	bool yyin_is_a_tty = isatty(fileno(yyin));
4047 	/*
4048 	 * The prompt is "e:z> " or "e:z:r> " where e is execname, z is zone
4049 	 * and r is resource_scope: 5 is for the two ":"s + "> " + terminator.
4050 	 */
4051 	char prompt[MAXPATHLEN + ZONENAME_MAX + MAX_RT_STRLEN + 5], *line;
4052 
4053 	/* yyin should have been set to the appropriate (FILE *) if not stdin */
4054 	newline_terminated = TRUE;
4055 	for (;;) {
4056 		if (yyin_is_a_tty) {
4057 			if (newline_terminated) {
4058 				if (global_scope)
4059 					(void) snprintf(prompt, sizeof (prompt),
4060 					    "%s:%s> ", execname, zone);
4061 				else
4062 					(void) snprintf(prompt, sizeof (prompt),
4063 					    "%s:%s:%s> ", execname, zone,
4064 					    rt_to_str(resource_scope));
4065 			}
4066 			/*
4067 			 * If the user hits ^C then we want to catch it and
4068 			 * start over.  If the user hits EOF then we want to
4069 			 * bail out.
4070 			 */
4071 			line = gl_get_line(gl, prompt, NULL, -1);
4072 			if (gl_return_status(gl) == GLR_SIGNAL) {
4073 				gl_abandon_line(gl);
4074 				continue;
4075 			}
4076 			if (line == NULL)
4077 				break;
4078 			(void) string_to_yyin(line);
4079 			while (!feof(yyin))
4080 				yyparse();
4081 		} else {
4082 			yyparse();
4083 		}
4084 		/* Bail out on an error in command file mode. */
4085 		if (saw_error && cmd_file_mode && !interactive_mode)
4086 			time_to_exit = TRUE;
4087 		if (time_to_exit || (!yyin_is_a_tty && feof(yyin)))
4088 			break;
4089 	}
4090 	return (cleanup());
4091 }
4092 
4093 /*
4094  * This function is used in the zonecfg-interactive-mode scenario: it just
4095  * calls read_input() until we are done.
4096  */
4097 
4098 static int
4099 do_interactive(void)
4100 {
4101 	int err;
4102 
4103 	interactive_mode = TRUE;
4104 	if (!read_only_mode) {
4105 		/*
4106 		 * Try to set things up proactively in interactive mode, so
4107 		 * that if the zone in question does not exist yet, we can
4108 		 * provide the user with a clue.
4109 		 */
4110 		(void) initialize(FALSE);
4111 	}
4112 	do
4113 		err = read_input();
4114 	while (err == Z_REPEAT);
4115 	return (err);
4116 }
4117 
4118 /*
4119  * cmd_file is slightly more complicated, as it has to open the command file
4120  * and set yyin appropriately.  Once that is done, though, it just calls
4121  * read_input(), and only once, since prompting is not possible.
4122  */
4123 
4124 static int
4125 cmd_file(char *file)
4126 {
4127 	FILE *infile;
4128 	int err;
4129 	struct stat statbuf;
4130 	bool using_real_file = (strcmp(file, "-") != 0);
4131 
4132 	if (using_real_file) {
4133 		/*
4134 		 * zerr() prints a line number in cmd_file_mode, which we do
4135 		 * not want here, so temporarily unset it.
4136 		 */
4137 		cmd_file_mode = FALSE;
4138 		if ((infile = fopen(file, "r")) == NULL) {
4139 			zerr(gettext("could not open file %s: %s"),
4140 			    file, strerror(errno));
4141 			return (Z_ERR);
4142 		}
4143 		if ((err = fstat(fileno(infile), &statbuf)) != 0) {
4144 			zerr(gettext("could not stat file %s: %s"),
4145 			    file, strerror(errno));
4146 			err = Z_ERR;
4147 			goto done;
4148 		}
4149 		if (!S_ISREG(statbuf.st_mode)) {
4150 			zerr(gettext("%s is not a regular file."), file);
4151 			err = Z_ERR;
4152 			goto done;
4153 		}
4154 		yyin = infile;
4155 		cmd_file_mode = TRUE;
4156 		ok_to_prompt = FALSE;
4157 	} else {
4158 		/*
4159 		 * "-f -" is essentially the same as interactive mode,
4160 		 * so treat it that way.
4161 		 */
4162 		interactive_mode = TRUE;
4163 	}
4164 	/* Z_REPEAT is for interactive mode; treat it like Z_ERR here. */
4165 	if ((err = read_input()) == Z_REPEAT)
4166 		err = Z_ERR;
4167 done:
4168 	if (using_real_file)
4169 		(void) fclose(infile);
4170 	return (err);
4171 }
4172 
4173 /*
4174  * Since yacc is based on reading from a (FILE *) whereas what we get from
4175  * the command line is in argv format, we need to convert when the user
4176  * gives us commands directly from the command line.  That is done here by
4177  * concatenating the argv list into a space-separated string, writing it
4178  * to a temp file, and rewinding the file so yyin can be set to it.  Then
4179  * we call read_input(), and only once, since prompting about whether to
4180  * continue or quit would make no sense in this context.
4181  */
4182 
4183 static int
4184 one_command_at_a_time(int argc, char *argv[])
4185 {
4186 	char *command;
4187 	size_t len = 2; /* terminal \n\0 */
4188 	int i, err;
4189 
4190 	for (i = 0; i < argc; i++)
4191 		len += strlen(argv[i]) + 1;
4192 	if ((command = malloc(len)) == NULL) {
4193 		zone_perror(execname, Z_NOMEM, TRUE);
4194 		return (Z_ERR);
4195 	}
4196 	(void) strlcpy(command, argv[0], len);
4197 	for (i = 1; i < argc; i++) {
4198 		(void) strlcat(command, " ", len);
4199 		(void) strlcat(command, argv[i], len);
4200 	}
4201 	(void) strlcat(command, "\n", len);
4202 	err = string_to_yyin(command);
4203 	free(command);
4204 	if (err != Z_OK)
4205 		return (err);
4206 	while (!feof(yyin))
4207 		yyparse();
4208 	return (cleanup());
4209 }
4210 
4211 static char *
4212 get_execbasename(char *execfullname)
4213 {
4214 	char *last_slash, *execbasename;
4215 
4216 	/* guard against '/' at end of command invocation */
4217 	for (;;) {
4218 		last_slash = strrchr(execfullname, '/');
4219 		if (last_slash == NULL) {
4220 			execbasename = execfullname;
4221 			break;
4222 		} else {
4223 			execbasename = last_slash + 1;
4224 			if (*execbasename == '\0') {
4225 				*last_slash = '\0';
4226 				continue;
4227 			}
4228 			break;
4229 		}
4230 	}
4231 	return (execbasename);
4232 }
4233 
4234 static void
4235 validate_zone_name()
4236 {
4237 	regex_t reg;
4238 	char *locale = NULL, locale_buf[MAXPATHLEN];
4239 
4240 	if (strcmp(zone, GLOBAL_ZONENAME) == 0)
4241 		goto err;
4242 
4243 	/*
4244 	 * The regex(5) functions below are locale-sensitive, so save the
4245 	 * user's locale, then set it to "C" for the regex's, and restore
4246 	 * it afterwards.
4247 	 */
4248 	if ((locale = setlocale(LC_ALL, NULL)) != NULL) {
4249 		(void) strlcpy(locale_buf, locale, sizeof (locale_buf));
4250 		locale = locale_buf;
4251 	}
4252 	(void) setlocale(LC_ALL, "C");
4253 	if (regcomp(&reg, "^" ZONENAME_REGEXP "$", REG_EXTENDED|REG_NOSUB) != 0)
4254 		goto err;
4255 
4256 	if (regexec(&reg, zone, (size_t)0, NULL, 0) != 0)
4257 		goto err;
4258 
4259 	regfree(&reg);
4260 	(void) setlocale(LC_ALL, locale);
4261 	return;
4262 
4263 err:
4264 	(void) setlocale(LC_ALL, locale);
4265 	zone_perror(zone, Z_BOGUS_ZONE_NAME, TRUE);
4266 	usage(FALSE, HELP_SYNTAX);
4267 	exit(Z_USAGE);
4268 }
4269 
4270 int
4271 main(int argc, char *argv[])
4272 {
4273 	int err, arg;
4274 
4275 	/* This must be before anything goes to stdout. */
4276 	setbuf(stdout, NULL);
4277 
4278 	saw_error = FALSE;
4279 	cmd_file_mode = FALSE;
4280 	execname = get_execbasename(argv[0]);
4281 
4282 	(void) setlocale(LC_ALL, "");
4283 	(void) textdomain(TEXT_DOMAIN);
4284 
4285 	if (getzoneid() != GLOBAL_ZONEID) {
4286 		zerr(gettext("%s can only be run from the global zone."),
4287 		    execname);
4288 		exit(Z_ERR);
4289 	}
4290 
4291 	if (argc < 2) {
4292 		usage(FALSE, HELP_USAGE | HELP_SUBCMDS);
4293 		exit(Z_USAGE);
4294 	}
4295 	if (strcmp(argv[1], cmd_to_str(CMD_HELP)) == 0) {
4296 		(void) one_command_at_a_time(argc - 1, &(argv[1]));
4297 		exit(Z_OK);
4298 	}
4299 
4300 	zone = NULL;
4301 	while ((arg = getopt(argc, argv, "?f:z:")) != EOF) {
4302 		switch (arg) {
4303 		case '?':
4304 			if (optopt == '?')
4305 				usage(TRUE, HELP_USAGE | HELP_SUBCMDS);
4306 			else
4307 				usage(FALSE, HELP_USAGE);
4308 			exit(Z_USAGE);
4309 			/* NOTREACHED */
4310 		case 'f':
4311 			cmd_file_name = optarg;
4312 			cmd_file_mode = TRUE;
4313 			break;
4314 		case 'z':
4315 			zone = optarg;
4316 			break;
4317 		default:
4318 			usage(FALSE, HELP_USAGE);
4319 			exit(Z_USAGE);
4320 		}
4321 	}
4322 
4323 	if (optind > argc || zone == NULL) {
4324 		usage(FALSE, HELP_USAGE);
4325 		exit(Z_USAGE);
4326 	}
4327 
4328 	validate_zone_name();
4329 	if (zonecfg_access(zone, W_OK) == Z_OK) {
4330 		read_only_mode = FALSE;
4331 	} else {
4332 		read_only_mode = TRUE;
4333 		/* skip this message in one-off from command line mode */
4334 		if (optind == argc)
4335 			(void) fprintf(stderr, gettext("WARNING: you do not "
4336 			    "have write access to this zone's configuration "
4337 			    "file;\ngoing into read-only mode.\n"));
4338 	}
4339 
4340 	if ((handle = zonecfg_init_handle()) == NULL) {
4341 		zone_perror(execname, Z_NOMEM, TRUE);
4342 		exit(Z_ERR);
4343 	}
4344 
4345 	/*
4346 	 * This may get set back to FALSE again in cmd_file() if cmd_file_name
4347 	 * is a "real" file as opposed to "-" (i.e. meaning use stdin).
4348 	 */
4349 	if (isatty(STDIN_FILENO))
4350 		ok_to_prompt = TRUE;
4351 	if ((gl = new_GetLine(MAX_LINE_LEN, MAX_CMD_HIST)) == NULL)
4352 		exit(Z_ERR);
4353 	if (gl_customize_completion(gl, NULL, cmd_cpl_fn) != 0)
4354 		exit(Z_ERR);
4355 	(void) sigset(SIGINT, SIG_IGN);
4356 	if (optind == argc) {
4357 		if (!cmd_file_mode)
4358 			err = do_interactive();
4359 		else
4360 			err = cmd_file(cmd_file_name);
4361 	} else {
4362 		err = one_command_at_a_time(argc - optind, &(argv[optind]));
4363 	}
4364 	zonecfg_fini_handle(handle);
4365 	(void) del_GetLine(gl);
4366 	return (err);
4367 }
4368