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