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