xref: /titanic_51/usr/src/cmd/zonecfg/zonecfg.c (revision bf56214c0556fa6864189c826d39dbe156bb22a0)
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 2007 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 <fcntl.h>
60 #include <strings.h>
61 #include <unistd.h>
62 #include <ctype.h>
63 #include <stdlib.h>
64 #include <assert.h>
65 #include <sys/stat.h>
66 #include <zone.h>
67 #include <arpa/inet.h>
68 #include <netdb.h>
69 #include <locale.h>
70 #include <libintl.h>
71 #include <alloca.h>
72 #include <signal.h>
73 #include <wait.h>
74 #include <libtecla.h>
75 #include <libzfs.h>
76 #include <sys/brand.h>
77 #include <libbrand.h>
78 
79 #include <libzonecfg.h>
80 #include "zonecfg.h"
81 
82 #if !defined(TEXT_DOMAIN)		/* should be defined by cc -D */
83 #define	TEXT_DOMAIN	"SYS_TEST"	/* Use this only if it wasn't */
84 #endif
85 
86 #define	PAGER	"/usr/bin/more"
87 #define	EXEC_PREFIX	"exec "
88 #define	EXEC_LEN	(strlen(EXEC_PREFIX))
89 
90 struct help {
91 	uint_t	cmd_num;
92 	char	*cmd_name;
93 	uint_t	flags;
94 	char	*short_usage;
95 };
96 
97 extern int yyparse(void);
98 extern int lex_lineno;
99 
100 #define	MAX_LINE_LEN	1024
101 #define	MAX_CMD_HIST	1024
102 #define	MAX_CMD_LEN	1024
103 
104 #define	ONE_MB		1048576
105 
106 /*
107  * Each SHELP_ should be a simple string.
108  */
109 
110 #define	SHELP_ADD	"add <resource-type>\n\t(global scope)\n" \
111 	"add <property-name> <property-value>\n\t(resource scope)"
112 #define	SHELP_CANCEL	"cancel"
113 #define	SHELP_CLEAR	"clear <property-name>"
114 #define	SHELP_COMMIT	"commit"
115 #define	SHELP_CREATE	"create [-F] [ -a <path> | -b | -t <template> ]"
116 #define	SHELP_DELETE	"delete [-F]"
117 #define	SHELP_END	"end"
118 #define	SHELP_EXIT	"exit [-F]"
119 #define	SHELP_EXPORT	"export [-f output-file]"
120 #define	SHELP_HELP	"help [commands] [syntax] [usage] [<command-name>]"
121 #define	SHELP_INFO	"info [<resource-type> [property-name=property-value]*]"
122 #define	SHELP_REMOVE	"remove [-F] <resource-type> " \
123 	"[ <property-name>=<property-value> ]*\n" \
124 	"\t(global scope)\n" \
125 	"remove <property-name> <property-value>\n" \
126 	"\t(resource scope)"
127 #define	SHELP_REVERT	"revert [-F]"
128 #define	SHELP_SELECT	"select <resource-type> { <property-name>=" \
129 	"<property-value> }"
130 #define	SHELP_SET	"set <property-name>=<property-value>"
131 #define	SHELP_VERIFY	"verify"
132 
133 static struct help helptab[] = {
134 	{ CMD_ADD,	"add",		HELP_RES_PROPS,	SHELP_ADD, },
135 	{ CMD_CANCEL,	"cancel",	0,		SHELP_CANCEL, },
136 	{ CMD_CLEAR,	"clear",	HELP_PROPS,	SHELP_CLEAR, },
137 	{ CMD_COMMIT,	"commit",	0,		SHELP_COMMIT, },
138 	{ CMD_CREATE,	"create",	0,		SHELP_CREATE, },
139 	{ CMD_DELETE,	"delete",	0,		SHELP_DELETE, },
140 	{ CMD_END,	"end",		0,		SHELP_END, },
141 	{ CMD_EXIT,	"exit",		0,		SHELP_EXIT, },
142 	{ CMD_EXPORT,	"export",	0,		SHELP_EXPORT, },
143 	{ CMD_HELP,	"help",		0,		SHELP_HELP },
144 	{ CMD_INFO,	"info",		HELP_RES_PROPS,	SHELP_INFO, },
145 	{ CMD_REMOVE,	"remove",	HELP_RES_PROPS,	SHELP_REMOVE, },
146 	{ CMD_REVERT,	"revert",	0,		SHELP_REVERT, },
147 	{ CMD_SELECT,	"select",	HELP_RES_PROPS,	SHELP_SELECT, },
148 	{ CMD_SET,	"set",		HELP_PROPS,	SHELP_SET, },
149 	{ CMD_VERIFY,	"verify",	0,		SHELP_VERIFY, },
150 	{ 0 },
151 };
152 
153 #define	MAX_RT_STRLEN	16
154 
155 /* These *must* match the order of the RT_ define's from zonecfg.h */
156 static char *res_types[] = {
157 	"unknown",
158 	"zonename",
159 	"zonepath",
160 	"autoboot",
161 	"pool",
162 	"fs",
163 	"inherit-pkg-dir",
164 	"net",
165 	"device",
166 	"rctl",
167 	"attr",
168 	"dataset",
169 	"limitpriv",
170 	"bootargs",
171 	"brand",
172 	"dedicated-cpu",
173 	"capped-memory",
174 	ALIAS_MAXLWPS,
175 	ALIAS_MAXSHMMEM,
176 	ALIAS_MAXSHMIDS,
177 	ALIAS_MAXMSGIDS,
178 	ALIAS_MAXSEMIDS,
179 	ALIAS_SHARES,
180 	"scheduling-class",
181 	"ip-type",
182 	"capped-cpu",
183 	NULL
184 };
185 
186 /* These *must* match the order of the PT_ define's from zonecfg.h */
187 static char *prop_types[] = {
188 	"unknown",
189 	"zonename",
190 	"zonepath",
191 	"autoboot",
192 	"pool",
193 	"dir",
194 	"special",
195 	"type",
196 	"options",
197 	"address",
198 	"physical",
199 	"name",
200 	"value",
201 	"match",
202 	"priv",
203 	"limit",
204 	"action",
205 	"raw",
206 	"limitpriv",
207 	"bootargs",
208 	"brand",
209 	"ncpus",
210 	"importance",
211 	"swap",
212 	"locked",
213 	ALIAS_SHARES,
214 	ALIAS_MAXLWPS,
215 	ALIAS_MAXSHMMEM,
216 	ALIAS_MAXSHMIDS,
217 	ALIAS_MAXMSGIDS,
218 	ALIAS_MAXSEMIDS,
219 	ALIAS_MAXLOCKEDMEM,
220 	ALIAS_MAXSWAP,
221 	"scheduling-class",
222 	"ip-type",
223 	NULL
224 };
225 
226 /* These *must* match the order of the PROP_VAL_ define's from zonecfg.h */
227 static char *prop_val_types[] = {
228 	"simple",
229 	"complex",
230 	"list",
231 };
232 
233 /*
234  * The various _cmds[] lists below are for command tab-completion.
235  */
236 
237 /*
238  * remove has a space afterwards because it has qualifiers; the other commands
239  * that have qualifiers (add, select, etc.) don't need a space here because
240  * they have their own _cmds[] lists below.
241  */
242 static const char *global_scope_cmds[] = {
243 	"add",
244 	"clear",
245 	"commit",
246 	"create",
247 	"delete",
248 	"exit",
249 	"export",
250 	"help",
251 	"info",
252 	"remove ",
253 	"revert",
254 	"select",
255 	"set",
256 	"verify",
257 	NULL
258 };
259 
260 static const char *add_cmds[] = {
261 	"add fs",
262 	"add inherit-pkg-dir",
263 	"add net",
264 	"add device",
265 	"add rctl",
266 	"add attr",
267 	"add dataset",
268 	"add dedicated-cpu",
269 	"add capped-cpu",
270 	"add capped-memory",
271 	NULL
272 };
273 
274 static const char *clear_cmds[] = {
275 	"clear autoboot",
276 	"clear pool",
277 	"clear limitpriv",
278 	"clear bootargs",
279 	"clear scheduling-class",
280 	"clear ip-type",
281 	"clear " ALIAS_MAXLWPS,
282 	"clear " ALIAS_MAXSHMMEM,
283 	"clear " ALIAS_MAXSHMIDS,
284 	"clear " ALIAS_MAXMSGIDS,
285 	"clear " ALIAS_MAXSEMIDS,
286 	"clear " ALIAS_SHARES,
287 	NULL
288 };
289 
290 static const char *remove_cmds[] = {
291 	"remove fs ",
292 	"remove inherit-pkg-dir ",
293 	"remove net ",
294 	"remove device ",
295 	"remove rctl ",
296 	"remove attr ",
297 	"remove dataset ",
298 	"remove dedicated-cpu ",
299 	"remove capped-cpu ",
300 	"remove capped-memory ",
301 	NULL
302 };
303 
304 static const char *select_cmds[] = {
305 	"select fs ",
306 	"select inherit-pkg-dir ",
307 	"select net ",
308 	"select device ",
309 	"select rctl ",
310 	"select attr ",
311 	"select dataset ",
312 	"select dedicated-cpu",
313 	"select capped-cpu",
314 	"select capped-memory",
315 	NULL
316 };
317 
318 static const char *set_cmds[] = {
319 	"set zonename=",
320 	"set zonepath=",
321 	"set brand=",
322 	"set autoboot=",
323 	"set pool=",
324 	"set limitpriv=",
325 	"set bootargs=",
326 	"set scheduling-class=",
327 	"set ip-type=",
328 	"set " ALIAS_MAXLWPS "=",
329 	"set " ALIAS_MAXSHMMEM "=",
330 	"set " ALIAS_MAXSHMIDS "=",
331 	"set " ALIAS_MAXMSGIDS "=",
332 	"set " ALIAS_MAXSEMIDS "=",
333 	"set " ALIAS_SHARES "=",
334 	NULL
335 };
336 
337 static const char *info_cmds[] = {
338 	"info fs ",
339 	"info inherit-pkg-dir ",
340 	"info net ",
341 	"info device ",
342 	"info rctl ",
343 	"info attr ",
344 	"info dataset ",
345 	"info capped-memory",
346 	"info dedicated-cpu",
347 	"info capped-cpu",
348 	"info zonename",
349 	"info zonepath",
350 	"info autoboot",
351 	"info pool",
352 	"info limitpriv",
353 	"info bootargs",
354 	"info brand",
355 	"info scheduling-class",
356 	"info ip-type",
357 	"info max-lwps",
358 	"info max-shm-memory",
359 	"info max-shm-ids",
360 	"info max-msg-ids",
361 	"info max-sem-ids",
362 	"info cpu-shares",
363 	NULL
364 };
365 
366 static const char *fs_res_scope_cmds[] = {
367 	"add options ",
368 	"cancel",
369 	"end",
370 	"exit",
371 	"help",
372 	"info",
373 	"remove options ",
374 	"set dir=",
375 	"set raw=",
376 	"set special=",
377 	"set type=",
378 	"clear raw",
379 	NULL
380 };
381 
382 static const char *net_res_scope_cmds[] = {
383 	"cancel",
384 	"end",
385 	"exit",
386 	"help",
387 	"info",
388 	"set address=",
389 	"set physical=",
390 	NULL
391 };
392 
393 static const char *ipd_res_scope_cmds[] = {
394 	"cancel",
395 	"end",
396 	"exit",
397 	"help",
398 	"info",
399 	"set dir=",
400 	NULL
401 };
402 
403 static const char *device_res_scope_cmds[] = {
404 	"cancel",
405 	"end",
406 	"exit",
407 	"help",
408 	"info",
409 	"set match=",
410 	NULL
411 };
412 
413 static const char *attr_res_scope_cmds[] = {
414 	"cancel",
415 	"end",
416 	"exit",
417 	"help",
418 	"info",
419 	"set name=",
420 	"set type=",
421 	"set value=",
422 	NULL
423 };
424 
425 static const char *rctl_res_scope_cmds[] = {
426 	"add value ",
427 	"cancel",
428 	"end",
429 	"exit",
430 	"help",
431 	"info",
432 	"remove value ",
433 	"set name=",
434 	NULL
435 };
436 
437 static const char *dataset_res_scope_cmds[] = {
438 	"cancel",
439 	"end",
440 	"exit",
441 	"help",
442 	"info",
443 	"set name=",
444 	NULL
445 };
446 
447 static const char *pset_res_scope_cmds[] = {
448 	"cancel",
449 	"end",
450 	"exit",
451 	"help",
452 	"info",
453 	"set ncpus=",
454 	"set importance=",
455 	"clear importance",
456 	NULL
457 };
458 
459 static const char *pcap_res_scope_cmds[] = {
460 	"cancel",
461 	"end",
462 	"exit",
463 	"help",
464 	"info",
465 	"set ncpus=",
466 	NULL
467 };
468 
469 static const char *mcap_res_scope_cmds[] = {
470 	"cancel",
471 	"end",
472 	"exit",
473 	"help",
474 	"info",
475 	"set physical=",
476 	"set swap=",
477 	"set locked=",
478 	"clear physical",
479 	"clear swap",
480 	"clear locked",
481 	NULL
482 };
483 
484 /* Global variables */
485 
486 /* set early in main(), never modified thereafter, used all over the place */
487 static char *execname;
488 
489 /* set in main(), used all over the place */
490 static zone_dochandle_t handle;
491 
492 /* used all over the place */
493 static char zone[ZONENAME_MAX];
494 static char revert_zone[ZONENAME_MAX];
495 
496 /* global brand operations */
497 static brand_handle_t brand;
498 
499 /* set in modifying functions, checked in read_input() */
500 static bool need_to_commit = FALSE;
501 bool saw_error;
502 
503 /* set in yacc parser, checked in read_input() */
504 bool newline_terminated;
505 
506 /* set in main(), checked in lex error handler */
507 bool cmd_file_mode;
508 
509 /* set in exit_func(), checked in read_input() */
510 static bool time_to_exit = FALSE, force_exit = FALSE;
511 
512 /* used in short_usage() and zerr() */
513 static char *cmd_file_name = NULL;
514 
515 /* checked in read_input() and other places */
516 static bool ok_to_prompt = FALSE;
517 
518 /* set and checked in initialize() */
519 static bool got_handle = FALSE;
520 
521 /* initialized in do_interactive(), checked in initialize() */
522 static bool interactive_mode;
523 
524 /* set if configuring the global zone */
525 static bool global_zone = FALSE;
526 
527 /* set in main(), checked in multiple places */
528 static bool read_only_mode;
529 
530 static bool global_scope = TRUE; /* scope is outer/global or inner/resource */
531 static int resource_scope;	/* should be in the RT_ list from zonecfg.h */
532 static int end_op = -1;		/* operation on end is either add or modify */
533 
534 int num_prop_vals;		/* for grammar */
535 
536 /*
537  * These are for keeping track of resources as they are specified as part of
538  * the multi-step process.  They should be initialized by add_resource() or
539  * select_func() and filled in by add_property() or set_func().
540  */
541 static struct zone_fstab	old_fstab, in_progress_fstab;
542 static struct zone_fstab	old_ipdtab, in_progress_ipdtab;
543 static struct zone_nwiftab	old_nwiftab, in_progress_nwiftab;
544 static struct zone_devtab	old_devtab, in_progress_devtab;
545 static struct zone_rctltab	old_rctltab, in_progress_rctltab;
546 static struct zone_attrtab	old_attrtab, in_progress_attrtab;
547 static struct zone_dstab	old_dstab, in_progress_dstab;
548 static struct zone_psettab	old_psettab, in_progress_psettab;
549 static struct zone_mcaptab	old_mcaptab, in_progress_mcaptab;
550 
551 static GetLine *gl;	/* The gl_get_line() resource object */
552 
553 static void bytes_to_units(char *str, char *buf, int bufsize);
554 
555 /* Functions begin here */
556 
557 static bool
558 initial_match(const char *line1, const char *line2, int word_end)
559 {
560 	if (word_end <= 0)
561 		return (TRUE);
562 	return (strncmp(line1, line2, word_end) == 0);
563 }
564 
565 static int
566 add_stuff(WordCompletion *cpl, const char *line1, const char **list,
567     int word_end)
568 {
569 	int i, err;
570 
571 	for (i = 0; list[i] != NULL; i++) {
572 		if (initial_match(line1, list[i], word_end)) {
573 			err = cpl_add_completion(cpl, line1, 0, word_end,
574 			    list[i] + word_end, "", "");
575 			if (err != 0)
576 				return (err);
577 		}
578 	}
579 	return (0);
580 }
581 
582 static
583 /* ARGSUSED */
584 CPL_MATCH_FN(cmd_cpl_fn)
585 {
586 	if (global_scope) {
587 		/*
588 		 * The MAX/MIN tests below are to make sure we have at least
589 		 * enough characters to distinguish from other prefixes (MAX)
590 		 * but only check MIN(what we have, what we're checking).
591 		 */
592 		if (strncmp(line, "add ", MAX(MIN(word_end, 4), 1)) == 0)
593 			return (add_stuff(cpl, line, add_cmds, word_end));
594 		if (strncmp(line, "clear ", MAX(MIN(word_end, 6), 2)) == 0)
595 			return (add_stuff(cpl, line, clear_cmds, word_end));
596 		if (strncmp(line, "select ", MAX(MIN(word_end, 7), 3)) == 0)
597 			return (add_stuff(cpl, line, select_cmds, word_end));
598 		if (strncmp(line, "set ", MAX(MIN(word_end, 4), 3)) == 0)
599 			return (add_stuff(cpl, line, set_cmds, word_end));
600 		if (strncmp(line, "remove ", MAX(MIN(word_end, 7), 1)) == 0)
601 			return (add_stuff(cpl, line, remove_cmds, word_end));
602 		if (strncmp(line, "info ", MAX(MIN(word_end, 5), 1)) == 0)
603 			return (add_stuff(cpl, line, info_cmds, word_end));
604 		return (add_stuff(cpl, line, global_scope_cmds, word_end));
605 	}
606 	switch (resource_scope) {
607 	case RT_FS:
608 		return (add_stuff(cpl, line, fs_res_scope_cmds, word_end));
609 	case RT_IPD:
610 		return (add_stuff(cpl, line, ipd_res_scope_cmds, word_end));
611 	case RT_NET:
612 		return (add_stuff(cpl, line, net_res_scope_cmds, word_end));
613 	case RT_DEVICE:
614 		return (add_stuff(cpl, line, device_res_scope_cmds, word_end));
615 	case RT_RCTL:
616 		return (add_stuff(cpl, line, rctl_res_scope_cmds, word_end));
617 	case RT_ATTR:
618 		return (add_stuff(cpl, line, attr_res_scope_cmds, word_end));
619 	case RT_DATASET:
620 		return (add_stuff(cpl, line, dataset_res_scope_cmds, word_end));
621 	case RT_DCPU:
622 		return (add_stuff(cpl, line, pset_res_scope_cmds, word_end));
623 	case RT_PCAP:
624 		return (add_stuff(cpl, line, pcap_res_scope_cmds, word_end));
625 	case RT_MCAP:
626 		return (add_stuff(cpl, line, mcap_res_scope_cmds, word_end));
627 	}
628 	return (0);
629 }
630 
631 /*
632  * For the main CMD_func() functions below, several of them call getopt()
633  * then check optind against argc to make sure an extra parameter was not
634  * passed in.  The reason this is not caught in the grammar is that the
635  * grammar just checks for a miscellaneous TOKEN, which is *expected* to
636  * be "-F" (for example), but could be anything.  So (for example) this
637  * check will prevent "create bogus".
638  */
639 
640 cmd_t *
641 alloc_cmd(void)
642 {
643 	return (calloc(1, sizeof (cmd_t)));
644 }
645 
646 void
647 free_cmd(cmd_t *cmd)
648 {
649 	int i;
650 
651 	for (i = 0; i < MAX_EQ_PROP_PAIRS; i++)
652 		if (cmd->cmd_property_ptr[i] != NULL) {
653 			property_value_ptr_t pp = cmd->cmd_property_ptr[i];
654 
655 			switch (pp->pv_type) {
656 			case PROP_VAL_SIMPLE:
657 				free(pp->pv_simple);
658 				break;
659 			case PROP_VAL_COMPLEX:
660 				free_complex(pp->pv_complex);
661 				break;
662 			case PROP_VAL_LIST:
663 				free_list(pp->pv_list);
664 				break;
665 			}
666 		}
667 	for (i = 0; i < cmd->cmd_argc; i++)
668 		free(cmd->cmd_argv[i]);
669 	free(cmd);
670 }
671 
672 complex_property_ptr_t
673 alloc_complex(void)
674 {
675 	return (calloc(1, sizeof (complex_property_t)));
676 }
677 
678 void
679 free_complex(complex_property_ptr_t complex)
680 {
681 	if (complex == NULL)
682 		return;
683 	free_complex(complex->cp_next);
684 	if (complex->cp_value != NULL)
685 		free(complex->cp_value);
686 	free(complex);
687 }
688 
689 list_property_ptr_t
690 alloc_list(void)
691 {
692 	return (calloc(1, sizeof (list_property_t)));
693 }
694 
695 void
696 free_list(list_property_ptr_t list)
697 {
698 	if (list == NULL)
699 		return;
700 	if (list->lp_simple != NULL)
701 		free(list->lp_simple);
702 	free_complex(list->lp_complex);
703 	free_list(list->lp_next);
704 	free(list);
705 }
706 
707 void
708 free_outer_list(list_property_ptr_t list)
709 {
710 	if (list == NULL)
711 		return;
712 	free_outer_list(list->lp_next);
713 	free(list);
714 }
715 
716 static struct zone_rctlvaltab *
717 alloc_rctlvaltab(void)
718 {
719 	return (calloc(1, sizeof (struct zone_rctlvaltab)));
720 }
721 
722 static char *
723 rt_to_str(int res_type)
724 {
725 	assert(res_type >= RT_MIN && res_type <= RT_MAX);
726 	return (res_types[res_type]);
727 }
728 
729 static char *
730 pt_to_str(int prop_type)
731 {
732 	assert(prop_type >= PT_MIN && prop_type <= PT_MAX);
733 	return (prop_types[prop_type]);
734 }
735 
736 static char *
737 pvt_to_str(int pv_type)
738 {
739 	assert(pv_type >= PROP_VAL_MIN && pv_type <= PROP_VAL_MAX);
740 	return (prop_val_types[pv_type]);
741 }
742 
743 static char *
744 cmd_to_str(int cmd_num)
745 {
746 	assert(cmd_num >= CMD_MIN && cmd_num <= CMD_MAX);
747 	return (helptab[cmd_num].cmd_name);
748 }
749 
750 /*
751  * This is a separate function rather than a set of define's because of the
752  * gettext() wrapping.
753  */
754 
755 /*
756  * TRANSLATION_NOTE
757  * Each string below should have \t follow \n whenever needed; the
758  * initial \t and the terminal \n will be provided by the calling function.
759  */
760 
761 static char *
762 long_help(int cmd_num)
763 {
764 	static char line[1024];	/* arbitrary large amount */
765 
766 	assert(cmd_num >= CMD_MIN && cmd_num <= CMD_MAX);
767 	switch (cmd_num) {
768 		case CMD_HELP:
769 			return (gettext("Prints help message."));
770 		case CMD_CREATE:
771 			(void) snprintf(line, sizeof (line),
772 			    gettext("Creates a configuration for the "
773 			    "specified zone.  %s should be\n\tused to "
774 			    "begin configuring a new zone.  If overwriting an "
775 			    "existing\n\tconfiguration, the -F flag can be "
776 			    "used to force the action.  If\n\t-t template is "
777 			    "given, creates a configuration identical to the\n"
778 			    "\tspecified template, except that the zone name "
779 			    "is changed from\n\ttemplate to zonename.  '%s -a' "
780 			    "creates a configuration from a\n\tdetached "
781 			    "zonepath.  '%s -b' results in a blank "
782 			    "configuration.\n\t'%s' with no arguments applies "
783 			    "the Sun default settings."),
784 			    cmd_to_str(CMD_CREATE), cmd_to_str(CMD_CREATE),
785 			    cmd_to_str(CMD_CREATE), cmd_to_str(CMD_CREATE));
786 			return (line);
787 		case CMD_EXIT:
788 			return (gettext("Exits the program.  The -F flag can "
789 			    "be used to force the action."));
790 		case CMD_EXPORT:
791 			return (gettext("Prints configuration to standard "
792 			    "output, or to output-file if\n\tspecified, in "
793 			    "a form suitable for use in a command-file."));
794 		case CMD_ADD:
795 			return (gettext("Add specified resource to "
796 			    "configuration."));
797 		case CMD_DELETE:
798 			return (gettext("Deletes the specified zone.  The -F "
799 			    "flag can be used to force the\n\taction."));
800 		case CMD_REMOVE:
801 			return (gettext("Remove specified resource from "
802 			    "configuration.  The -F flag can be used\n\tto "
803 			    "force the action."));
804 		case CMD_SELECT:
805 			(void) snprintf(line, sizeof (line),
806 			    gettext("Selects a resource to modify.  "
807 			    "Resource modification is completed\n\twith the "
808 			    "command \"%s\".  The property name/value pairs "
809 			    "must uniquely\n\tidentify a resource.  Note that "
810 			    "the curly braces ('{', '}') mean one\n\tor more "
811 			    "of whatever is between them."),
812 			    cmd_to_str(CMD_END));
813 			return (line);
814 		case CMD_SET:
815 			return (gettext("Sets property values."));
816 		case CMD_CLEAR:
817 			return (gettext("Clears property values."));
818 		case CMD_INFO:
819 			return (gettext("Displays information about the "
820 			    "current configuration.  If resource\n\ttype is "
821 			    "specified, displays only information about "
822 			    "resources of\n\tthe relevant type.  If resource "
823 			    "id is specified, displays only\n\tinformation "
824 			    "about that resource."));
825 		case CMD_VERIFY:
826 			return (gettext("Verifies current configuration "
827 			    "for correctness (some resource types\n\thave "
828 			    "required properties)."));
829 		case CMD_COMMIT:
830 			(void) snprintf(line, sizeof (line),
831 			    gettext("Commits current configuration.  "
832 			    "Configuration must be committed to\n\tbe used by "
833 			    "%s.  Until the configuration is committed, "
834 			    "changes \n\tcan be removed with the %s "
835 			    "command.  This operation is\n\tattempted "
836 			    "automatically upon completion of a %s "
837 			    "session."), "zoneadm", cmd_to_str(CMD_REVERT),
838 			    "zonecfg");
839 			return (line);
840 		case CMD_REVERT:
841 			return (gettext("Reverts configuration back to the "
842 			    "last committed state.  The -F flag\n\tcan be "
843 			    "used to force the action."));
844 		case CMD_CANCEL:
845 			return (gettext("Cancels resource/property "
846 			    "specification."));
847 		case CMD_END:
848 			return (gettext("Ends resource/property "
849 			    "specification."));
850 	}
851 	/* NOTREACHED */
852 	return (NULL);
853 }
854 
855 /*
856  * Called with verbose TRUE when help is explicitly requested, FALSE for
857  * unexpected errors.
858  */
859 
860 void
861 usage(bool verbose, uint_t flags)
862 {
863 	FILE *fp = verbose ? stdout : stderr, *newfp;
864 	bool need_to_close = FALSE;
865 	char *pager;
866 	int i;
867 
868 	/* don't page error output */
869 	if (verbose && interactive_mode) {
870 		if ((pager = getenv("PAGER")) == NULL)
871 			pager = PAGER;
872 		if ((newfp = popen(pager, "w")) != NULL) {
873 			need_to_close = TRUE;
874 			fp = newfp;
875 		}
876 	}
877 	if (flags & HELP_META) {
878 		(void) fprintf(fp, gettext("More help is available for the "
879 		    "following:\n"));
880 		(void) fprintf(fp, "\n\tcommands ('%s commands')\n",
881 		    cmd_to_str(CMD_HELP));
882 		(void) fprintf(fp, "\tsyntax ('%s syntax')\n",
883 		    cmd_to_str(CMD_HELP));
884 		(void) fprintf(fp, "\tusage ('%s usage')\n\n",
885 		    cmd_to_str(CMD_HELP));
886 		(void) fprintf(fp, gettext("You may also obtain help on any "
887 		    "command by typing '%s <command-name>.'\n"),
888 		    cmd_to_str(CMD_HELP));
889 	}
890 	if (flags & HELP_RES_SCOPE) {
891 		switch (resource_scope) {
892 		case RT_FS:
893 			(void) fprintf(fp, gettext("The '%s' resource scope is "
894 			    "used to configure a file-system.\n"),
895 			    rt_to_str(resource_scope));
896 			(void) fprintf(fp, gettext("Valid commands:\n"));
897 			(void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
898 			    pt_to_str(PT_DIR), gettext("<path>"));
899 			(void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
900 			    pt_to_str(PT_SPECIAL), gettext("<path>"));
901 			(void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
902 			    pt_to_str(PT_RAW), gettext("<raw-device>"));
903 			(void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
904 			    pt_to_str(PT_TYPE), gettext("<file-system type>"));
905 			(void) fprintf(fp, "\t%s %s %s\n", cmd_to_str(CMD_ADD),
906 			    pt_to_str(PT_OPTIONS),
907 			    gettext("<file-system options>"));
908 			(void) fprintf(fp, "\t%s %s %s\n",
909 			    cmd_to_str(CMD_REMOVE), pt_to_str(PT_OPTIONS),
910 			    gettext("<file-system options>"));
911 			(void) fprintf(fp, gettext("Consult the file-system "
912 			    "specific manual page, such as mount_ufs(1M), "
913 			    "for\ndetails about file-system options.  Note "
914 			    "that any file-system options with an\nembedded "
915 			    "'=' character must be enclosed in double quotes, "
916 			    /*CSTYLED*/
917 			    "such as \"%s=5\".\n"), MNTOPT_RETRY);
918 			break;
919 		case RT_IPD:
920 			(void) fprintf(fp, gettext("The '%s' resource scope is "
921 			    "used to configure a directory\ninherited from the "
922 			    "global zone into a non-global zone in read-only "
923 			    "mode.\n"), rt_to_str(resource_scope));
924 			(void) fprintf(fp, gettext("Valid commands:\n"));
925 			(void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
926 			    pt_to_str(PT_DIR), gettext("<path>"));
927 			break;
928 		case RT_NET:
929 			(void) fprintf(fp, gettext("The '%s' resource scope is "
930 			    "used to configure a network interface.\n"),
931 			    rt_to_str(resource_scope));
932 			(void) fprintf(fp, gettext("Valid commands:\n"));
933 			(void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
934 			    pt_to_str(PT_ADDRESS), gettext("<IP-address>"));
935 			(void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
936 			    pt_to_str(PT_PHYSICAL), gettext("<interface>"));
937 			(void) fprintf(fp, gettext("See ifconfig(1M) for "
938 			    "details of the <interface> string.\n"));
939 			(void) fprintf(fp, gettext("%s %s is valid if the %s "
940 			    "property is set to %s, otherwise it must not be "
941 			    "set.\n"),
942 			    cmd_to_str(CMD_SET), pt_to_str(PT_ADDRESS),
943 			    pt_to_str(PT_IPTYPE), "shared");
944 			break;
945 		case RT_DEVICE:
946 			(void) fprintf(fp, gettext("The '%s' resource scope is "
947 			    "used to configure a device node.\n"),
948 			    rt_to_str(resource_scope));
949 			(void) fprintf(fp, gettext("Valid commands:\n"));
950 			(void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
951 			    pt_to_str(PT_MATCH), gettext("<device-path>"));
952 			break;
953 		case RT_RCTL:
954 			(void) fprintf(fp, gettext("The '%s' resource scope is "
955 			    "used to configure a resource control.\n"),
956 			    rt_to_str(resource_scope));
957 			(void) fprintf(fp, gettext("Valid commands:\n"));
958 			(void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
959 			    pt_to_str(PT_NAME), gettext("<string>"));
960 			(void) fprintf(fp, "\t%s %s (%s=%s,%s=%s,%s=%s)\n",
961 			    cmd_to_str(CMD_ADD), pt_to_str(PT_VALUE),
962 			    pt_to_str(PT_PRIV), gettext("<priv-value>"),
963 			    pt_to_str(PT_LIMIT), gettext("<number>"),
964 			    pt_to_str(PT_ACTION), gettext("<action-value>"));
965 			(void) fprintf(fp, "\t%s %s (%s=%s,%s=%s,%s=%s)\n",
966 			    cmd_to_str(CMD_REMOVE), pt_to_str(PT_VALUE),
967 			    pt_to_str(PT_PRIV), gettext("<priv-value>"),
968 			    pt_to_str(PT_LIMIT), gettext("<number>"),
969 			    pt_to_str(PT_ACTION), gettext("<action-value>"));
970 			(void) fprintf(fp, "%s\n\t%s := privileged\n"
971 			    "\t%s := none | deny\n", gettext("Where"),
972 			    gettext("<priv-value>"), gettext("<action-value>"));
973 			break;
974 		case RT_ATTR:
975 			(void) fprintf(fp, gettext("The '%s' resource scope is "
976 			    "used to configure a generic attribute.\n"),
977 			    rt_to_str(resource_scope));
978 			(void) fprintf(fp, gettext("Valid commands:\n"));
979 			(void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
980 			    pt_to_str(PT_NAME), gettext("<name>"));
981 			(void) fprintf(fp, "\t%s %s=boolean\n",
982 			    cmd_to_str(CMD_SET), pt_to_str(PT_TYPE));
983 			(void) fprintf(fp, "\t%s %s=true | false\n",
984 			    cmd_to_str(CMD_SET), pt_to_str(PT_VALUE));
985 			(void) fprintf(fp, gettext("or\n"));
986 			(void) fprintf(fp, "\t%s %s=int\n", cmd_to_str(CMD_SET),
987 			    pt_to_str(PT_TYPE));
988 			(void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
989 			    pt_to_str(PT_VALUE), gettext("<integer>"));
990 			(void) fprintf(fp, gettext("or\n"));
991 			(void) fprintf(fp, "\t%s %s=string\n",
992 			    cmd_to_str(CMD_SET), pt_to_str(PT_TYPE));
993 			(void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
994 			    pt_to_str(PT_VALUE), gettext("<string>"));
995 			(void) fprintf(fp, gettext("or\n"));
996 			(void) fprintf(fp, "\t%s %s=uint\n",
997 			    cmd_to_str(CMD_SET), pt_to_str(PT_TYPE));
998 			(void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
999 			    pt_to_str(PT_VALUE), gettext("<unsigned integer>"));
1000 			break;
1001 		case RT_DATASET:
1002 			(void) fprintf(fp, gettext("The '%s' resource scope is "
1003 			    "used to export ZFS datasets.\n"),
1004 			    rt_to_str(resource_scope));
1005 			(void) fprintf(fp, gettext("Valid commands:\n"));
1006 			(void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
1007 			    pt_to_str(PT_NAME), gettext("<name>"));
1008 			break;
1009 		case RT_DCPU:
1010 			(void) fprintf(fp, gettext("The '%s' resource scope "
1011 			    "configures the 'pools' facility to dedicate\na "
1012 			    "subset of the system's processors to this zone "
1013 			    "while it is running.\n"),
1014 			    rt_to_str(resource_scope));
1015 			(void) fprintf(fp, gettext("Valid commands:\n"));
1016 			(void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
1017 			    pt_to_str(PT_NCPUS),
1018 			    gettext("<unsigned integer | range>"));
1019 			(void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
1020 			    pt_to_str(PT_IMPORTANCE),
1021 			    gettext("<unsigned integer>"));
1022 			break;
1023 		case RT_PCAP:
1024 			(void) fprintf(fp, gettext("The '%s' resource scope is "
1025 			    "used to set an upper limit (a cap) on the\n"
1026 			    "percentage of CPU that can be used by this zone.  "
1027 			    "A '%s' value of 1\ncorresponds to one cpu.  The "
1028 			    "value can be set higher than 1, up to the total\n"
1029 			    "number of CPUs on the system.  The value can "
1030 			    "also be less than 1,\nrepresenting a fraction of "
1031 			    "a cpu.\n"),
1032 			    rt_to_str(resource_scope), pt_to_str(PT_NCPUS));
1033 			(void) fprintf(fp, gettext("Valid commands:\n"));
1034 			(void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
1035 			    pt_to_str(PT_NCPUS), gettext("<unsigned decimal>"));
1036 			break;
1037 		case RT_MCAP:
1038 			(void) fprintf(fp, gettext("The '%s' resource scope is "
1039 			    "used to set an upper limit (a cap) on the\n"
1040 			    "amount of physical memory, swap space and locked "
1041 			    "memory that can be used by\nthis zone.\n"),
1042 			    rt_to_str(resource_scope));
1043 			(void) fprintf(fp, gettext("Valid commands:\n"));
1044 			(void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
1045 			    pt_to_str(PT_PHYSICAL),
1046 			    gettext("<qualified unsigned decimal>"));
1047 			(void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
1048 			    pt_to_str(PT_SWAP),
1049 			    gettext("<qualified unsigned decimal>"));
1050 			(void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
1051 			    pt_to_str(PT_LOCKED),
1052 			    gettext("<qualified unsigned decimal>"));
1053 			break;
1054 		}
1055 		(void) fprintf(fp, gettext("And from any resource scope, you "
1056 		    "can:\n"));
1057 		(void) fprintf(fp, "\t%s\t%s\n", cmd_to_str(CMD_END),
1058 		    gettext("(to conclude this operation)"));
1059 		(void) fprintf(fp, "\t%s\t%s\n", cmd_to_str(CMD_CANCEL),
1060 		    gettext("(to cancel this operation)"));
1061 		(void) fprintf(fp, "\t%s\t%s\n", cmd_to_str(CMD_EXIT),
1062 		    gettext("(to exit the zonecfg utility)"));
1063 	}
1064 	if (flags & HELP_USAGE) {
1065 		(void) fprintf(fp, "%s:\t%s %s\n", gettext("usage"),
1066 		    execname, cmd_to_str(CMD_HELP));
1067 		(void) fprintf(fp, "\t%s -z <zone>\t\t\t(%s)\n",
1068 		    execname, gettext("interactive"));
1069 		(void) fprintf(fp, "\t%s -z <zone> <command>\n", execname);
1070 		(void) fprintf(fp, "\t%s -z <zone> -f <command-file>\n",
1071 		    execname);
1072 	}
1073 	if (flags & HELP_SUBCMDS) {
1074 		(void) fprintf(fp, "%s:\n\n", gettext("Commands"));
1075 		for (i = 0; i <= CMD_MAX; i++) {
1076 			(void) fprintf(fp, "%s\n", helptab[i].short_usage);
1077 			if (verbose)
1078 				(void) fprintf(fp, "\t%s\n\n", long_help(i));
1079 		}
1080 	}
1081 	if (flags & HELP_SYNTAX) {
1082 		if (!verbose)
1083 			(void) fprintf(fp, "\n");
1084 		(void) fprintf(fp, "<zone> := [A-Za-z0-9][A-Za-z0-9_.-]*\n");
1085 		(void) fprintf(fp, gettext("\t(except the reserved words "
1086 		    "'%s' and anything starting with '%s')\n"), "global",
1087 		    "SUNW");
1088 		(void) fprintf(fp,
1089 		    gettext("\tName must be less than %d characters.\n"),
1090 		    ZONENAME_MAX);
1091 		if (verbose)
1092 			(void) fprintf(fp, "\n");
1093 	}
1094 	if (flags & HELP_NETADDR) {
1095 		(void) fprintf(fp, gettext("\n<net-addr> :="));
1096 		(void) fprintf(fp,
1097 		    gettext("\t<IPv4-address>[/<IPv4-prefix-length>] |\n"));
1098 		(void) fprintf(fp,
1099 		    gettext("\t\t<IPv6-address>/<IPv6-prefix-length> |\n"));
1100 		(void) fprintf(fp,
1101 		    gettext("\t\t<hostname>[/<IPv4-prefix-length>]\n"));
1102 		(void) fprintf(fp, gettext("See inet(3SOCKET) for IPv4 and "
1103 		    "IPv6 address syntax.\n"));
1104 		(void) fprintf(fp, gettext("<IPv4-prefix-length> := [0-32]\n"));
1105 		(void) fprintf(fp,
1106 		    gettext("<IPv6-prefix-length> := [0-128]\n"));
1107 		(void) fprintf(fp,
1108 		    gettext("<hostname> := [A-Za-z0-9][A-Za-z0-9-.]*\n"));
1109 	}
1110 	if (flags & HELP_RESOURCES) {
1111 		(void) fprintf(fp, "<%s> := %s | %s | %s | %s | %s | %s |\n\t"
1112 		    "%s | %s | %s | %s\n\n",
1113 		    gettext("resource type"), rt_to_str(RT_FS),
1114 		    rt_to_str(RT_IPD), rt_to_str(RT_NET), rt_to_str(RT_DEVICE),
1115 		    rt_to_str(RT_RCTL), rt_to_str(RT_ATTR),
1116 		    rt_to_str(RT_DATASET), rt_to_str(RT_DCPU),
1117 		    rt_to_str(RT_PCAP), rt_to_str(RT_MCAP));
1118 	}
1119 	if (flags & HELP_PROPS) {
1120 		(void) fprintf(fp, gettext("For resource type ... there are "
1121 		    "property types ...:\n"));
1122 		(void) fprintf(fp, "\t%s\t%s\n", gettext("(global)"),
1123 		    pt_to_str(PT_ZONENAME));
1124 		(void) fprintf(fp, "\t%s\t%s\n", gettext("(global)"),
1125 		    pt_to_str(PT_ZONEPATH));
1126 		(void) fprintf(fp, "\t%s\t%s\n", gettext("(global)"),
1127 		    pt_to_str(PT_BRAND));
1128 		(void) fprintf(fp, "\t%s\t%s\n", gettext("(global)"),
1129 		    pt_to_str(PT_AUTOBOOT));
1130 		(void) fprintf(fp, "\t%s\t%s\n", gettext("(global)"),
1131 		    pt_to_str(PT_BOOTARGS));
1132 		(void) fprintf(fp, "\t%s\t%s\n", gettext("(global)"),
1133 		    pt_to_str(PT_POOL));
1134 		(void) fprintf(fp, "\t%s\t%s\n", gettext("(global)"),
1135 		    pt_to_str(PT_LIMITPRIV));
1136 		(void) fprintf(fp, "\t%s\t%s\n", gettext("(global)"),
1137 		    pt_to_str(PT_SCHED));
1138 		(void) fprintf(fp, "\t%s\t%s\n", gettext("(global)"),
1139 		    pt_to_str(PT_IPTYPE));
1140 		(void) fprintf(fp, "\t%s\t%s\n", gettext("(global)"),
1141 		    pt_to_str(PT_MAXLWPS));
1142 		(void) fprintf(fp, "\t%s\t%s\n", gettext("(global)"),
1143 		    pt_to_str(PT_MAXSHMMEM));
1144 		(void) fprintf(fp, "\t%s\t%s\n", gettext("(global)"),
1145 		    pt_to_str(PT_MAXSHMIDS));
1146 		(void) fprintf(fp, "\t%s\t%s\n", gettext("(global)"),
1147 		    pt_to_str(PT_MAXMSGIDS));
1148 		(void) fprintf(fp, "\t%s\t%s\n", gettext("(global)"),
1149 		    pt_to_str(PT_MAXSEMIDS));
1150 		(void) fprintf(fp, "\t%s\t%s\n", gettext("(global)"),
1151 		    pt_to_str(PT_SHARES));
1152 		(void) fprintf(fp, "\t%s\t\t%s, %s, %s, %s\n", rt_to_str(RT_FS),
1153 		    pt_to_str(PT_DIR), pt_to_str(PT_SPECIAL),
1154 		    pt_to_str(PT_RAW), pt_to_str(PT_TYPE),
1155 		    pt_to_str(PT_OPTIONS));
1156 		(void) fprintf(fp, "\t%s\t%s\n", rt_to_str(RT_IPD),
1157 		    pt_to_str(PT_DIR));
1158 		(void) fprintf(fp, "\t%s\t\t%s, %s\n", rt_to_str(RT_NET),
1159 		    pt_to_str(PT_ADDRESS), pt_to_str(PT_PHYSICAL));
1160 		(void) fprintf(fp, "\t%s\t\t%s\n", rt_to_str(RT_DEVICE),
1161 		    pt_to_str(PT_MATCH));
1162 		(void) fprintf(fp, "\t%s\t\t%s, %s\n", rt_to_str(RT_RCTL),
1163 		    pt_to_str(PT_NAME), pt_to_str(PT_VALUE));
1164 		(void) fprintf(fp, "\t%s\t\t%s, %s, %s\n", rt_to_str(RT_ATTR),
1165 		    pt_to_str(PT_NAME), pt_to_str(PT_TYPE),
1166 		    pt_to_str(PT_VALUE));
1167 		(void) fprintf(fp, "\t%s\t\t%s\n", rt_to_str(RT_DATASET),
1168 		    pt_to_str(PT_NAME));
1169 		(void) fprintf(fp, "\t%s\t%s, %s\n", rt_to_str(RT_DCPU),
1170 		    pt_to_str(PT_NCPUS), pt_to_str(PT_IMPORTANCE));
1171 		(void) fprintf(fp, "\t%s\t%s\n", rt_to_str(RT_PCAP),
1172 		    pt_to_str(PT_NCPUS));
1173 		(void) fprintf(fp, "\t%s\t%s, %s, %s\n", rt_to_str(RT_MCAP),
1174 		    pt_to_str(PT_PHYSICAL), pt_to_str(PT_SWAP),
1175 		    pt_to_str(PT_LOCKED));
1176 	}
1177 	if (need_to_close)
1178 		(void) pclose(fp);
1179 }
1180 
1181 /* PRINTFLIKE1 */
1182 static void
1183 zerr(const char *fmt, ...)
1184 {
1185 	va_list alist;
1186 	static int last_lineno;
1187 
1188 	/* lex_lineno has already been incremented in the lexer; compensate */
1189 	if (cmd_file_mode && lex_lineno > last_lineno) {
1190 		if (strcmp(cmd_file_name, "-") == 0)
1191 			(void) fprintf(stderr, gettext("On line %d:\n"),
1192 			    lex_lineno - 1);
1193 		else
1194 			(void) fprintf(stderr, gettext("On line %d of %s:\n"),
1195 			    lex_lineno - 1, cmd_file_name);
1196 		last_lineno = lex_lineno;
1197 	}
1198 	va_start(alist, fmt);
1199 	(void) vfprintf(stderr, fmt, alist);
1200 	(void) fprintf(stderr, "\n");
1201 	va_end(alist);
1202 }
1203 
1204 static void
1205 zone_perror(char *prefix, int err, bool set_saw)
1206 {
1207 	zerr("%s: %s", prefix, zonecfg_strerror(err));
1208 	if (set_saw)
1209 		saw_error = TRUE;
1210 }
1211 
1212 /*
1213  * zone_perror() expects a single string, but for remove and select
1214  * we have both the command and the resource type, so this wrapper
1215  * function serves the same purpose in a slightly different way.
1216  */
1217 
1218 static void
1219 z_cmd_rt_perror(int cmd_num, int res_num, int err, bool set_saw)
1220 {
1221 	zerr("%s %s: %s", cmd_to_str(cmd_num), rt_to_str(res_num),
1222 	    zonecfg_strerror(err));
1223 	if (set_saw)
1224 		saw_error = TRUE;
1225 }
1226 
1227 /* returns Z_OK if successful, Z_foo from <libzonecfg.h> otherwise */
1228 static int
1229 initialize(bool handle_expected)
1230 {
1231 	int err;
1232 	char brandname[MAXNAMELEN];
1233 
1234 	if (zonecfg_check_handle(handle) != Z_OK) {
1235 		if ((err = zonecfg_get_handle(zone, handle)) == Z_OK) {
1236 			got_handle = TRUE;
1237 			if (zonecfg_get_brand(handle, brandname,
1238 			    sizeof (brandname)) != Z_OK) {
1239 				zerr("Zone %s is inconsistent: missing "
1240 				    "brand attribute", zone);
1241 				exit(Z_ERR);
1242 			}
1243 			if ((brand = brand_open(brandname)) == NULL) {
1244 				zerr("Zone %s uses non-existent brand \"%s\"."
1245 				    "  Unable to continue", zone, brandname);
1246 				exit(Z_ERR);
1247 			}
1248 		} else if (global_zone && err == Z_NO_ZONE && !got_handle &&
1249 		    !read_only_mode) {
1250 			/*
1251 			 * We implicitly create the global zone config if it
1252 			 * doesn't exist.
1253 			 */
1254 			zone_dochandle_t tmphandle;
1255 
1256 			if ((tmphandle = zonecfg_init_handle()) == NULL) {
1257 				zone_perror(execname, Z_NOMEM, TRUE);
1258 				exit(Z_ERR);
1259 			}
1260 
1261 			err = zonecfg_get_template_handle("SUNWblank", zone,
1262 			    tmphandle);
1263 
1264 			if (err != Z_OK) {
1265 				zonecfg_fini_handle(tmphandle);
1266 				zone_perror("SUNWblank", err, TRUE);
1267 				return (err);
1268 			}
1269 
1270 			need_to_commit = TRUE;
1271 			zonecfg_fini_handle(handle);
1272 			handle = tmphandle;
1273 			got_handle = TRUE;
1274 
1275 		} else {
1276 			zone_perror(zone, err, handle_expected || got_handle);
1277 			if (err == Z_NO_ZONE && !got_handle &&
1278 			    interactive_mode && !read_only_mode)
1279 				(void) printf(gettext("Use '%s' to begin "
1280 				    "configuring a new zone.\n"),
1281 				    cmd_to_str(CMD_CREATE));
1282 			return (err);
1283 		}
1284 	}
1285 	return (Z_OK);
1286 }
1287 
1288 static bool
1289 state_atleast(zone_state_t state)
1290 {
1291 	zone_state_t state_num;
1292 	int err;
1293 
1294 	if ((err = zone_get_state(zone, &state_num)) != Z_OK) {
1295 		/* all states are greater than "non-existent" */
1296 		if (err == Z_NO_ZONE)
1297 			return (B_FALSE);
1298 		zerr(gettext("Unexpectedly failed to determine state "
1299 		    "of zone %s: %s"), zone, zonecfg_strerror(err));
1300 		exit(Z_ERR);
1301 	}
1302 	return (state_num >= state);
1303 }
1304 
1305 /*
1306  * short_usage() is for bad syntax: getopt() issues, too many arguments, etc.
1307  */
1308 
1309 void
1310 short_usage(int command)
1311 {
1312 	/* lex_lineno has already been incremented in the lexer; compensate */
1313 	if (cmd_file_mode) {
1314 		if (strcmp(cmd_file_name, "-") == 0)
1315 			(void) fprintf(stderr,
1316 			    gettext("syntax error on line %d\n"),
1317 			    lex_lineno - 1);
1318 		else
1319 			(void) fprintf(stderr,
1320 			    gettext("syntax error on line %d of %s\n"),
1321 			    lex_lineno - 1, cmd_file_name);
1322 	}
1323 	(void) fprintf(stderr, "%s:\n%s\n", gettext("usage"),
1324 	    helptab[command].short_usage);
1325 	saw_error = TRUE;
1326 }
1327 
1328 /*
1329  * long_usage() is for bad semantics: e.g., wrong property type for a given
1330  * resource type.  It is also used by longer_usage() below.
1331  */
1332 
1333 void
1334 long_usage(uint_t cmd_num, bool set_saw)
1335 {
1336 	(void) fprintf(set_saw ? stderr : stdout, "%s:\n%s\n", gettext("usage"),
1337 	    helptab[cmd_num].short_usage);
1338 	(void) fprintf(set_saw ? stderr : stdout, "\t%s\n", long_help(cmd_num));
1339 	if (set_saw)
1340 		saw_error = TRUE;
1341 }
1342 
1343 /*
1344  * longer_usage() is for 'help foo' and 'foo -?': call long_usage() and also
1345  * any extra usage() flags as appropriate for whatever command.
1346  */
1347 
1348 void
1349 longer_usage(uint_t cmd_num)
1350 {
1351 	long_usage(cmd_num, FALSE);
1352 	if (helptab[cmd_num].flags != 0) {
1353 		(void) printf("\n");
1354 		usage(TRUE, helptab[cmd_num].flags);
1355 	}
1356 }
1357 
1358 /*
1359  * scope_usage() is simply used when a command is called from the wrong scope.
1360  */
1361 
1362 static void
1363 scope_usage(uint_t cmd_num)
1364 {
1365 	zerr(gettext("The %s command only makes sense in the %s scope."),
1366 	    cmd_to_str(cmd_num),
1367 	    global_scope ?  gettext("resource") : gettext("global"));
1368 	saw_error = TRUE;
1369 }
1370 
1371 /*
1372  * On input, TRUE => yes, FALSE => no.
1373  * On return, TRUE => 1, FALSE => no, could not ask => -1.
1374  */
1375 
1376 static int
1377 ask_yesno(bool default_answer, const char *question)
1378 {
1379 	char line[64];	/* should be enough to answer yes or no */
1380 
1381 	if (!ok_to_prompt) {
1382 		saw_error = TRUE;
1383 		return (-1);
1384 	}
1385 	for (;;) {
1386 		if (printf("%s (%s)? ", question,
1387 		    default_answer ? "[y]/n" : "y/[n]") < 0)
1388 			return (-1);
1389 		if (fgets(line, sizeof (line), stdin) == NULL)
1390 			return (-1);
1391 
1392 		if (line[0] == '\n')
1393 			return (default_answer ? 1 : 0);
1394 		if (tolower(line[0]) == 'y')
1395 			return (1);
1396 		if (tolower(line[0]) == 'n')
1397 			return (0);
1398 	}
1399 }
1400 
1401 /*
1402  * Prints warning if zone already exists.
1403  * In interactive mode, prompts if we should continue anyway and returns Z_OK
1404  * if so, Z_ERR if not.  In non-interactive mode, exits with Z_ERR.
1405  *
1406  * Note that if a zone exists and its state is >= INSTALLED, an error message
1407  * will be printed and this function will return Z_ERR regardless of mode.
1408  */
1409 
1410 static int
1411 check_if_zone_already_exists(bool force)
1412 {
1413 	char line[ZONENAME_MAX + 128];	/* enough to ask a question */
1414 	zone_dochandle_t tmphandle;
1415 	int res, answer;
1416 
1417 	if ((tmphandle = zonecfg_init_handle()) == NULL) {
1418 		zone_perror(execname, Z_NOMEM, TRUE);
1419 		exit(Z_ERR);
1420 	}
1421 	res = zonecfg_get_handle(zone, tmphandle);
1422 	zonecfg_fini_handle(tmphandle);
1423 	if (res != Z_OK)
1424 		return (Z_OK);
1425 
1426 	if (state_atleast(ZONE_STATE_INSTALLED)) {
1427 		zerr(gettext("Zone %s already installed; %s not allowed."),
1428 		    zone, cmd_to_str(CMD_CREATE));
1429 		return (Z_ERR);
1430 	}
1431 
1432 	if (force) {
1433 		(void) printf(gettext("Zone %s already exists; overwriting.\n"),
1434 		    zone);
1435 		return (Z_OK);
1436 	}
1437 	(void) snprintf(line, sizeof (line),
1438 	    gettext("Zone %s already exists; %s anyway"), zone,
1439 	    cmd_to_str(CMD_CREATE));
1440 	if ((answer = ask_yesno(FALSE, line)) == -1) {
1441 		zerr(gettext("Zone exists, input not from terminal and -F not "
1442 		    "specified:\n%s command ignored, exiting."),
1443 		    cmd_to_str(CMD_CREATE));
1444 		exit(Z_ERR);
1445 	}
1446 	return (answer == 1 ? Z_OK : Z_ERR);
1447 }
1448 
1449 static bool
1450 zone_is_read_only(int cmd_num)
1451 {
1452 	if (strncmp(zone, "SUNW", 4) == 0) {
1453 		zerr(gettext("%s: zones beginning with SUNW are read-only."),
1454 		    zone);
1455 		saw_error = TRUE;
1456 		return (TRUE);
1457 	}
1458 	if (read_only_mode) {
1459 		zerr(gettext("%s: cannot %s in read-only mode."), zone,
1460 		    cmd_to_str(cmd_num));
1461 		saw_error = TRUE;
1462 		return (TRUE);
1463 	}
1464 	return (FALSE);
1465 }
1466 
1467 /*
1468  * Create a new configuration.
1469  */
1470 void
1471 create_func(cmd_t *cmd)
1472 {
1473 	int err, arg;
1474 	char zone_template[ZONENAME_MAX];
1475 	char attach_path[MAXPATHLEN];
1476 	zone_dochandle_t tmphandle;
1477 	bool force = FALSE;
1478 	bool attach = FALSE;
1479 	bool arg_err = FALSE;
1480 
1481 	assert(cmd != NULL);
1482 
1483 	/* This is the default if no arguments are given. */
1484 	(void) strlcpy(zone_template, "SUNWdefault", sizeof (zone_template));
1485 
1486 	optind = 0;
1487 	while ((arg = getopt(cmd->cmd_argc, cmd->cmd_argv, "?a:bFt:"))
1488 	    != EOF) {
1489 		switch (arg) {
1490 		case '?':
1491 			if (optopt == '?')
1492 				longer_usage(CMD_CREATE);
1493 			else
1494 				short_usage(CMD_CREATE);
1495 			arg_err = TRUE;
1496 			break;
1497 		case 'a':
1498 			(void) strlcpy(attach_path, optarg,
1499 			    sizeof (attach_path));
1500 			attach = TRUE;
1501 			break;
1502 		case 'b':
1503 			(void) strlcpy(zone_template, "SUNWblank",
1504 			    sizeof (zone_template));
1505 			break;
1506 		case 'F':
1507 			force = TRUE;
1508 			break;
1509 		case 't':
1510 			(void) strlcpy(zone_template, optarg,
1511 			    sizeof (zone_template));
1512 			break;
1513 		default:
1514 			short_usage(CMD_CREATE);
1515 			arg_err = TRUE;
1516 			break;
1517 		}
1518 	}
1519 	if (arg_err)
1520 		return;
1521 
1522 	if (optind != cmd->cmd_argc) {
1523 		short_usage(CMD_CREATE);
1524 		return;
1525 	}
1526 
1527 	if (zone_is_read_only(CMD_CREATE))
1528 		return;
1529 
1530 	if (check_if_zone_already_exists(force) != Z_OK)
1531 		return;
1532 
1533 	/*
1534 	 * Get a temporary handle first.  If that fails, the old handle
1535 	 * will not be lost.  Then finish whichever one we don't need,
1536 	 * to avoid leaks.  Then get the handle for zone_template, and
1537 	 * set the name to zone: this "copy, rename" method is how
1538 	 * create -[b|t] works.
1539 	 */
1540 	if ((tmphandle = zonecfg_init_handle()) == NULL) {
1541 		zone_perror(execname, Z_NOMEM, TRUE);
1542 		exit(Z_ERR);
1543 	}
1544 
1545 	if (attach)
1546 		err = zonecfg_get_attach_handle(attach_path, zone, B_FALSE,
1547 		    tmphandle);
1548 	else
1549 		err = zonecfg_get_template_handle(zone_template, zone,
1550 		    tmphandle);
1551 
1552 	if (err != Z_OK) {
1553 		zonecfg_fini_handle(tmphandle);
1554 		if (attach && err == Z_NO_ZONE)
1555 			(void) fprintf(stderr, gettext("invalid path to "
1556 			    "detached zone\n"));
1557 		else if (attach && err == Z_INVALID_DOCUMENT)
1558 			(void) fprintf(stderr, gettext("Cannot attach to an "
1559 			    "earlier release of the operating system\n"));
1560 		else
1561 			zone_perror(zone_template, err, TRUE);
1562 		return;
1563 	}
1564 
1565 	need_to_commit = TRUE;
1566 	zonecfg_fini_handle(handle);
1567 	handle = tmphandle;
1568 	got_handle = TRUE;
1569 }
1570 
1571 /*
1572  * This malloc()'s memory, which must be freed by the caller.
1573  */
1574 static char *
1575 quoteit(char *instr)
1576 {
1577 	char *outstr;
1578 	size_t outstrsize = strlen(instr) + 3;	/* 2 quotes + '\0' */
1579 
1580 	if ((outstr = malloc(outstrsize)) == NULL) {
1581 		zone_perror(zone, Z_NOMEM, FALSE);
1582 		exit(Z_ERR);
1583 	}
1584 	if (strchr(instr, ' ') == NULL) {
1585 		(void) strlcpy(outstr, instr, outstrsize);
1586 		return (outstr);
1587 	}
1588 	(void) snprintf(outstr, outstrsize, "\"%s\"", instr);
1589 	return (outstr);
1590 }
1591 
1592 static void
1593 export_prop(FILE *of, int prop_num, char *prop_id)
1594 {
1595 	char *quote_str;
1596 
1597 	if (strlen(prop_id) == 0)
1598 		return;
1599 	quote_str = quoteit(prop_id);
1600 	(void) fprintf(of, "%s %s=%s\n", cmd_to_str(CMD_SET),
1601 	    pt_to_str(prop_num), quote_str);
1602 	free(quote_str);
1603 }
1604 
1605 void
1606 export_func(cmd_t *cmd)
1607 {
1608 	struct zone_nwiftab nwiftab;
1609 	struct zone_fstab fstab;
1610 	struct zone_devtab devtab;
1611 	struct zone_attrtab attrtab;
1612 	struct zone_rctltab rctltab;
1613 	struct zone_dstab dstab;
1614 	struct zone_psettab psettab;
1615 	struct zone_mcaptab mcaptab;
1616 	struct zone_rctlvaltab *valptr;
1617 	int err, arg;
1618 	char zonepath[MAXPATHLEN], outfile[MAXPATHLEN], pool[MAXNAMELEN];
1619 	char bootargs[BOOTARGS_MAX];
1620 	char sched[MAXNAMELEN];
1621 	char brand[MAXNAMELEN];
1622 	char *limitpriv;
1623 	FILE *of;
1624 	boolean_t autoboot;
1625 	zone_iptype_t iptype;
1626 	bool need_to_close = FALSE;
1627 	bool arg_err = FALSE;
1628 
1629 	assert(cmd != NULL);
1630 
1631 	outfile[0] = '\0';
1632 	optind = 0;
1633 	while ((arg = getopt(cmd->cmd_argc, cmd->cmd_argv, "?f:")) != EOF) {
1634 		switch (arg) {
1635 		case '?':
1636 			if (optopt == '?')
1637 				longer_usage(CMD_EXPORT);
1638 			else
1639 				short_usage(CMD_EXPORT);
1640 			arg_err = TRUE;
1641 			break;
1642 		case 'f':
1643 			(void) strlcpy(outfile, optarg, sizeof (outfile));
1644 			break;
1645 		default:
1646 			short_usage(CMD_EXPORT);
1647 			arg_err = TRUE;
1648 			break;
1649 		}
1650 	}
1651 	if (arg_err)
1652 		return;
1653 
1654 	if (optind != cmd->cmd_argc) {
1655 		short_usage(CMD_EXPORT);
1656 		return;
1657 	}
1658 	if (strlen(outfile) == 0) {
1659 		of = stdout;
1660 	} else {
1661 		if ((of = fopen(outfile, "w")) == NULL) {
1662 			zerr(gettext("opening file %s: %s"),
1663 			    outfile, strerror(errno));
1664 			goto done;
1665 		}
1666 		setbuf(of, NULL);
1667 		need_to_close = TRUE;
1668 	}
1669 
1670 	if ((err = initialize(TRUE)) != Z_OK)
1671 		goto done;
1672 
1673 	(void) fprintf(of, "%s -b\n", cmd_to_str(CMD_CREATE));
1674 
1675 	if (zonecfg_get_zonepath(handle, zonepath, sizeof (zonepath)) == Z_OK &&
1676 	    strlen(zonepath) > 0)
1677 		(void) fprintf(of, "%s %s=%s\n", cmd_to_str(CMD_SET),
1678 		    pt_to_str(PT_ZONEPATH), zonepath);
1679 
1680 	if ((zone_get_brand(zone, brand, sizeof (brand)) == Z_OK) &&
1681 	    (strcmp(brand, NATIVE_BRAND_NAME) != 0))
1682 		(void) fprintf(of, "%s %s=%s\n", cmd_to_str(CMD_SET),
1683 		    pt_to_str(PT_BRAND), brand);
1684 
1685 	if (zonecfg_get_autoboot(handle, &autoboot) == Z_OK)
1686 		(void) fprintf(of, "%s %s=%s\n", cmd_to_str(CMD_SET),
1687 		    pt_to_str(PT_AUTOBOOT), autoboot ? "true" : "false");
1688 
1689 	if (zonecfg_get_bootargs(handle, bootargs, sizeof (bootargs)) == Z_OK &&
1690 	    strlen(bootargs) > 0) {
1691 		(void) fprintf(of, "%s %s=%s\n", cmd_to_str(CMD_SET),
1692 		    pt_to_str(PT_BOOTARGS), bootargs);
1693 	}
1694 
1695 	if (zonecfg_get_pool(handle, pool, sizeof (pool)) == Z_OK &&
1696 	    strlen(pool) > 0)
1697 		(void) fprintf(of, "%s %s=%s\n", cmd_to_str(CMD_SET),
1698 		    pt_to_str(PT_POOL), pool);
1699 
1700 	if (zonecfg_get_limitpriv(handle, &limitpriv) == Z_OK &&
1701 	    strlen(limitpriv) > 0) {
1702 		(void) fprintf(of, "%s %s=%s\n", cmd_to_str(CMD_SET),
1703 		    pt_to_str(PT_LIMITPRIV), limitpriv);
1704 		free(limitpriv);
1705 	}
1706 
1707 	if (zonecfg_get_sched_class(handle, sched, sizeof (sched)) == Z_OK &&
1708 	    strlen(sched) > 0)
1709 		(void) fprintf(of, "%s %s=%s\n", cmd_to_str(CMD_SET),
1710 		    pt_to_str(PT_SCHED), sched);
1711 
1712 	if (zonecfg_get_iptype(handle, &iptype) == Z_OK) {
1713 		switch (iptype) {
1714 		case ZS_SHARED:
1715 			(void) fprintf(of, "%s %s=%s\n", cmd_to_str(CMD_SET),
1716 			    pt_to_str(PT_IPTYPE), "shared");
1717 			break;
1718 		case ZS_EXCLUSIVE:
1719 			(void) fprintf(of, "%s %s=%s\n", cmd_to_str(CMD_SET),
1720 			    pt_to_str(PT_IPTYPE), "exclusive");
1721 			break;
1722 		}
1723 	}
1724 
1725 	if ((err = zonecfg_setipdent(handle)) != Z_OK) {
1726 		zone_perror(zone, err, FALSE);
1727 		goto done;
1728 	}
1729 	while (zonecfg_getipdent(handle, &fstab) == Z_OK) {
1730 		(void) fprintf(of, "%s %s\n", cmd_to_str(CMD_ADD),
1731 		    rt_to_str(RT_IPD));
1732 		export_prop(of, PT_DIR, fstab.zone_fs_dir);
1733 		(void) fprintf(of, "%s\n", cmd_to_str(CMD_END));
1734 	}
1735 	(void) zonecfg_endipdent(handle);
1736 
1737 	if ((err = zonecfg_setfsent(handle)) != Z_OK) {
1738 		zone_perror(zone, err, FALSE);
1739 		goto done;
1740 	}
1741 	while (zonecfg_getfsent(handle, &fstab) == Z_OK) {
1742 		zone_fsopt_t *optptr;
1743 
1744 		(void) fprintf(of, "%s %s\n", cmd_to_str(CMD_ADD),
1745 		    rt_to_str(RT_FS));
1746 		export_prop(of, PT_DIR, fstab.zone_fs_dir);
1747 		export_prop(of, PT_SPECIAL, fstab.zone_fs_special);
1748 		export_prop(of, PT_RAW, fstab.zone_fs_raw);
1749 		export_prop(of, PT_TYPE, fstab.zone_fs_type);
1750 		for (optptr = fstab.zone_fs_options; optptr != NULL;
1751 		    optptr = optptr->zone_fsopt_next) {
1752 			/*
1753 			 * Simple property values with embedded equal signs
1754 			 * need to be quoted to prevent the lexer from
1755 			 * mis-parsing them as complex name=value pairs.
1756 			 */
1757 			if (strchr(optptr->zone_fsopt_opt, '='))
1758 				(void) fprintf(of, "%s %s \"%s\"\n",
1759 				    cmd_to_str(CMD_ADD),
1760 				    pt_to_str(PT_OPTIONS),
1761 				    optptr->zone_fsopt_opt);
1762 			else
1763 				(void) fprintf(of, "%s %s %s\n",
1764 				    cmd_to_str(CMD_ADD),
1765 				    pt_to_str(PT_OPTIONS),
1766 				    optptr->zone_fsopt_opt);
1767 		}
1768 		(void) fprintf(of, "%s\n", cmd_to_str(CMD_END));
1769 		zonecfg_free_fs_option_list(fstab.zone_fs_options);
1770 	}
1771 	(void) zonecfg_endfsent(handle);
1772 
1773 	if ((err = zonecfg_setnwifent(handle)) != Z_OK) {
1774 		zone_perror(zone, err, FALSE);
1775 		goto done;
1776 	}
1777 	while (zonecfg_getnwifent(handle, &nwiftab) == Z_OK) {
1778 		(void) fprintf(of, "%s %s\n", cmd_to_str(CMD_ADD),
1779 		    rt_to_str(RT_NET));
1780 		export_prop(of, PT_ADDRESS, nwiftab.zone_nwif_address);
1781 		export_prop(of, PT_PHYSICAL, nwiftab.zone_nwif_physical);
1782 		(void) fprintf(of, "%s\n", cmd_to_str(CMD_END));
1783 	}
1784 	(void) zonecfg_endnwifent(handle);
1785 
1786 	if ((err = zonecfg_setdevent(handle)) != Z_OK) {
1787 		zone_perror(zone, err, FALSE);
1788 		goto done;
1789 	}
1790 	while (zonecfg_getdevent(handle, &devtab) == Z_OK) {
1791 		(void) fprintf(of, "%s %s\n", cmd_to_str(CMD_ADD),
1792 		    rt_to_str(RT_DEVICE));
1793 		export_prop(of, PT_MATCH, devtab.zone_dev_match);
1794 		(void) fprintf(of, "%s\n", cmd_to_str(CMD_END));
1795 	}
1796 	(void) zonecfg_enddevent(handle);
1797 
1798 	if ((err = zonecfg_setrctlent(handle)) != Z_OK) {
1799 		zone_perror(zone, err, FALSE);
1800 		goto done;
1801 	}
1802 	while (zonecfg_getrctlent(handle, &rctltab) == Z_OK) {
1803 		(void) fprintf(of, "%s rctl\n", cmd_to_str(CMD_ADD));
1804 		export_prop(of, PT_NAME, rctltab.zone_rctl_name);
1805 		for (valptr = rctltab.zone_rctl_valptr; valptr != NULL;
1806 		    valptr = valptr->zone_rctlval_next) {
1807 			fprintf(of, "%s %s (%s=%s,%s=%s,%s=%s)\n",
1808 			    cmd_to_str(CMD_ADD), pt_to_str(PT_VALUE),
1809 			    pt_to_str(PT_PRIV), valptr->zone_rctlval_priv,
1810 			    pt_to_str(PT_LIMIT), valptr->zone_rctlval_limit,
1811 			    pt_to_str(PT_ACTION), valptr->zone_rctlval_action);
1812 		}
1813 		(void) fprintf(of, "%s\n", cmd_to_str(CMD_END));
1814 		zonecfg_free_rctl_value_list(rctltab.zone_rctl_valptr);
1815 	}
1816 	(void) zonecfg_endrctlent(handle);
1817 
1818 	if ((err = zonecfg_setattrent(handle)) != Z_OK) {
1819 		zone_perror(zone, err, FALSE);
1820 		goto done;
1821 	}
1822 	while (zonecfg_getattrent(handle, &attrtab) == Z_OK) {
1823 		(void) fprintf(of, "%s %s\n", cmd_to_str(CMD_ADD),
1824 		    rt_to_str(RT_ATTR));
1825 		export_prop(of, PT_NAME, attrtab.zone_attr_name);
1826 		export_prop(of, PT_TYPE, attrtab.zone_attr_type);
1827 		export_prop(of, PT_VALUE, attrtab.zone_attr_value);
1828 		(void) fprintf(of, "%s\n", cmd_to_str(CMD_END));
1829 	}
1830 	(void) zonecfg_endattrent(handle);
1831 
1832 	if ((err = zonecfg_setdsent(handle)) != Z_OK) {
1833 		zone_perror(zone, err, FALSE);
1834 		goto done;
1835 	}
1836 	while (zonecfg_getdsent(handle, &dstab) == Z_OK) {
1837 		(void) fprintf(of, "%s %s\n", cmd_to_str(CMD_ADD),
1838 		    rt_to_str(RT_DATASET));
1839 		export_prop(of, PT_NAME, dstab.zone_dataset_name);
1840 		(void) fprintf(of, "%s\n", cmd_to_str(CMD_END));
1841 	}
1842 	(void) zonecfg_enddsent(handle);
1843 
1844 	if (zonecfg_getpsetent(handle, &psettab) == Z_OK) {
1845 		(void) fprintf(of, "%s %s\n", cmd_to_str(CMD_ADD),
1846 		    rt_to_str(RT_DCPU));
1847 		if (strcmp(psettab.zone_ncpu_min, psettab.zone_ncpu_max) == 0)
1848 			(void) fprintf(of, "%s %s=%s\n", cmd_to_str(CMD_SET),
1849 			    pt_to_str(PT_NCPUS), psettab.zone_ncpu_max);
1850 		else
1851 			(void) fprintf(of, "%s %s=%s-%s\n", cmd_to_str(CMD_SET),
1852 			    pt_to_str(PT_NCPUS), psettab.zone_ncpu_min,
1853 			    psettab.zone_ncpu_max);
1854 		if (psettab.zone_importance[0] != '\0')
1855 			(void) fprintf(of, "%s %s=%s\n", cmd_to_str(CMD_SET),
1856 			    pt_to_str(PT_IMPORTANCE), psettab.zone_importance);
1857 		(void) fprintf(of, "%s\n", cmd_to_str(CMD_END));
1858 	}
1859 
1860 	if (zonecfg_getmcapent(handle, &mcaptab) == Z_OK) {
1861 		char buf[128];
1862 
1863 		(void) fprintf(of, "%s %s\n", cmd_to_str(CMD_ADD),
1864 		    rt_to_str(RT_MCAP));
1865 		bytes_to_units(mcaptab.zone_physmem_cap, buf, sizeof (buf));
1866 		(void) fprintf(of, "%s %s=%s\n", cmd_to_str(CMD_SET),
1867 		    pt_to_str(PT_PHYSICAL), buf);
1868 		(void) fprintf(of, "%s\n", cmd_to_str(CMD_END));
1869 	}
1870 
1871 	/*
1872 	 * There is nothing to export for pcap since this resource is just
1873 	 * a container for an rctl alias.
1874 	 */
1875 
1876 done:
1877 	if (need_to_close)
1878 		(void) fclose(of);
1879 }
1880 
1881 void
1882 exit_func(cmd_t *cmd)
1883 {
1884 	int arg, answer;
1885 	bool arg_err = FALSE;
1886 
1887 	optind = 0;
1888 	while ((arg = getopt(cmd->cmd_argc, cmd->cmd_argv, "?F")) != EOF) {
1889 		switch (arg) {
1890 		case '?':
1891 			longer_usage(CMD_EXIT);
1892 			arg_err = TRUE;
1893 			break;
1894 		case 'F':
1895 			force_exit = TRUE;
1896 			break;
1897 		default:
1898 			short_usage(CMD_EXIT);
1899 			arg_err = TRUE;
1900 			break;
1901 		}
1902 	}
1903 	if (arg_err)
1904 		return;
1905 
1906 	if (optind < cmd->cmd_argc) {
1907 		short_usage(CMD_EXIT);
1908 		return;
1909 	}
1910 
1911 	if (global_scope || force_exit) {
1912 		time_to_exit = TRUE;
1913 		return;
1914 	}
1915 
1916 	answer = ask_yesno(FALSE, "Resource incomplete; really quit");
1917 	if (answer == -1) {
1918 		zerr(gettext("Resource incomplete, input "
1919 		    "not from terminal and -F not specified:\n%s command "
1920 		    "ignored, but exiting anyway."), cmd_to_str(CMD_EXIT));
1921 		exit(Z_ERR);
1922 	} else if (answer == 1) {
1923 		time_to_exit = TRUE;
1924 	}
1925 	/* (answer == 0) => just return */
1926 }
1927 
1928 static int
1929 validate_zonepath_syntax(char *path)
1930 {
1931 	if (path[0] != '/') {
1932 		zerr(gettext("%s is not an absolute path."), path);
1933 		return (Z_ERR);
1934 	}
1935 	if (strcmp(path, "/") == 0) {
1936 		zerr(gettext("/ is not allowed as a %s."),
1937 		    pt_to_str(PT_ZONEPATH));
1938 		return (Z_ERR);
1939 	}
1940 	return (Z_OK);
1941 }
1942 
1943 static void
1944 add_resource(cmd_t *cmd)
1945 {
1946 	int type;
1947 	struct zone_psettab tmp_psettab;
1948 	struct zone_mcaptab tmp_mcaptab;
1949 	uint64_t tmp;
1950 	uint64_t tmp_mcap;
1951 	char pool[MAXNAMELEN];
1952 
1953 	if ((type = cmd->cmd_res_type) == RT_UNKNOWN) {
1954 		long_usage(CMD_ADD, TRUE);
1955 		goto bad;
1956 	}
1957 
1958 	switch (type) {
1959 	case RT_FS:
1960 		bzero(&in_progress_fstab, sizeof (in_progress_fstab));
1961 		return;
1962 	case RT_IPD:
1963 		if (state_atleast(ZONE_STATE_INSTALLED)) {
1964 			zerr(gettext("Zone %s already installed; %s %s not "
1965 			    "allowed."), zone, cmd_to_str(CMD_ADD),
1966 			    rt_to_str(RT_IPD));
1967 			goto bad;
1968 		}
1969 		bzero(&in_progress_ipdtab, sizeof (in_progress_ipdtab));
1970 		return;
1971 	case RT_NET:
1972 		bzero(&in_progress_nwiftab, sizeof (in_progress_nwiftab));
1973 		return;
1974 	case RT_DEVICE:
1975 		bzero(&in_progress_devtab, sizeof (in_progress_devtab));
1976 		return;
1977 	case RT_RCTL:
1978 		if (global_zone)
1979 			zerr(gettext("WARNING: Setting a global zone resource "
1980 			    "control too low could deny\nservice "
1981 			    "to even the root user; "
1982 			    "this could render the system impossible\n"
1983 			    "to administer.  Please use caution."));
1984 		bzero(&in_progress_rctltab, sizeof (in_progress_rctltab));
1985 		return;
1986 	case RT_ATTR:
1987 		bzero(&in_progress_attrtab, sizeof (in_progress_attrtab));
1988 		return;
1989 	case RT_DATASET:
1990 		bzero(&in_progress_dstab, sizeof (in_progress_dstab));
1991 		return;
1992 	case RT_DCPU:
1993 		/* Make sure there isn't already a cpu-set or cpu-cap entry. */
1994 		if (zonecfg_lookup_pset(handle, &tmp_psettab) == Z_OK) {
1995 			zerr(gettext("The %s resource already exists."),
1996 			    rt_to_str(RT_DCPU));
1997 			goto bad;
1998 		}
1999 		if (zonecfg_get_aliased_rctl(handle, ALIAS_CPUCAP, &tmp) !=
2000 		    Z_NO_ENTRY) {
2001 			zerr(gettext("The %s resource already exists."),
2002 			    rt_to_str(RT_PCAP));
2003 			goto bad;
2004 		}
2005 
2006 		/* Make sure the pool property isn't set. */
2007 		if (zonecfg_get_pool(handle, pool, sizeof (pool)) == Z_OK &&
2008 		    strlen(pool) > 0) {
2009 			zerr(gettext("The %s property is already set.  "
2010 			    "A persistent pool is incompatible with\nthe %s "
2011 			    "resource."),
2012 			    pt_to_str(PT_POOL), rt_to_str(RT_DCPU));
2013 			goto bad;
2014 		}
2015 
2016 		bzero(&in_progress_psettab, sizeof (in_progress_psettab));
2017 		return;
2018 	case RT_PCAP:
2019 		/*
2020 		 * Make sure there isn't already a cpu-set or incompatible
2021 		 * cpu-cap rctls.
2022 		 */
2023 		if (zonecfg_lookup_pset(handle, &tmp_psettab) == Z_OK) {
2024 			zerr(gettext("The %s resource already exists."),
2025 			    rt_to_str(RT_DCPU));
2026 			goto bad;
2027 		}
2028 
2029 		switch (zonecfg_get_aliased_rctl(handle, ALIAS_CPUCAP, &tmp)) {
2030 		case Z_ALIAS_DISALLOW:
2031 			zone_perror(rt_to_str(RT_PCAP), Z_ALIAS_DISALLOW,
2032 			    FALSE);
2033 			goto bad;
2034 
2035 		case Z_OK:
2036 			zerr(gettext("The %s resource already exists."),
2037 			    rt_to_str(RT_PCAP));
2038 			goto bad;
2039 
2040 		default:
2041 			break;
2042 		}
2043 		return;
2044 	case RT_MCAP:
2045 		/*
2046 		 * Make sure there isn't already a mem-cap entry or max-swap
2047 		 * or max-locked rctl.
2048 		 */
2049 		if (zonecfg_lookup_mcap(handle, &tmp_mcaptab) == Z_OK ||
2050 		    zonecfg_get_aliased_rctl(handle, ALIAS_MAXSWAP, &tmp_mcap)
2051 		    == Z_OK ||
2052 		    zonecfg_get_aliased_rctl(handle, ALIAS_MAXLOCKEDMEM,
2053 		    &tmp_mcap) == Z_OK) {
2054 			zerr(gettext("The %s resource or a related resource "
2055 			    "control already exists."), rt_to_str(RT_MCAP));
2056 			goto bad;
2057 		}
2058 		if (global_zone)
2059 			zerr(gettext("WARNING: Setting a global zone memory "
2060 			    "cap too low could deny\nservice "
2061 			    "to even the root user; "
2062 			    "this could render the system impossible\n"
2063 			    "to administer.  Please use caution."));
2064 		bzero(&in_progress_mcaptab, sizeof (in_progress_mcaptab));
2065 		return;
2066 	default:
2067 		zone_perror(rt_to_str(type), Z_NO_RESOURCE_TYPE, TRUE);
2068 		long_usage(CMD_ADD, TRUE);
2069 		usage(FALSE, HELP_RESOURCES);
2070 	}
2071 bad:
2072 	global_scope = TRUE;
2073 	end_op = -1;
2074 }
2075 
2076 static void
2077 do_complex_rctl_val(complex_property_ptr_t cp)
2078 {
2079 	struct zone_rctlvaltab *rctlvaltab;
2080 	complex_property_ptr_t cx;
2081 	bool seen_priv = FALSE, seen_limit = FALSE, seen_action = FALSE;
2082 	rctlblk_t *rctlblk;
2083 	int err;
2084 
2085 	if ((rctlvaltab = alloc_rctlvaltab()) == NULL) {
2086 		zone_perror(zone, Z_NOMEM, TRUE);
2087 		exit(Z_ERR);
2088 	}
2089 	for (cx = cp; cx != NULL; cx = cx->cp_next) {
2090 		switch (cx->cp_type) {
2091 		case PT_PRIV:
2092 			if (seen_priv) {
2093 				zerr(gettext("%s already specified"),
2094 				    pt_to_str(PT_PRIV));
2095 				goto bad;
2096 			}
2097 			(void) strlcpy(rctlvaltab->zone_rctlval_priv,
2098 			    cx->cp_value,
2099 			    sizeof (rctlvaltab->zone_rctlval_priv));
2100 			seen_priv = TRUE;
2101 			break;
2102 		case PT_LIMIT:
2103 			if (seen_limit) {
2104 				zerr(gettext("%s already specified"),
2105 				    pt_to_str(PT_LIMIT));
2106 				goto bad;
2107 			}
2108 			(void) strlcpy(rctlvaltab->zone_rctlval_limit,
2109 			    cx->cp_value,
2110 			    sizeof (rctlvaltab->zone_rctlval_limit));
2111 			seen_limit = TRUE;
2112 			break;
2113 		case PT_ACTION:
2114 			if (seen_action) {
2115 				zerr(gettext("%s already specified"),
2116 				    pt_to_str(PT_ACTION));
2117 				goto bad;
2118 			}
2119 			(void) strlcpy(rctlvaltab->zone_rctlval_action,
2120 			    cx->cp_value,
2121 			    sizeof (rctlvaltab->zone_rctlval_action));
2122 			seen_action = TRUE;
2123 			break;
2124 		default:
2125 			zone_perror(pt_to_str(PT_VALUE),
2126 			    Z_NO_PROPERTY_TYPE, TRUE);
2127 			long_usage(CMD_ADD, TRUE);
2128 			usage(FALSE, HELP_PROPS);
2129 			zonecfg_free_rctl_value_list(rctlvaltab);
2130 			return;
2131 		}
2132 	}
2133 	if (!seen_priv)
2134 		zerr(gettext("%s not specified"), pt_to_str(PT_PRIV));
2135 	if (!seen_limit)
2136 		zerr(gettext("%s not specified"), pt_to_str(PT_LIMIT));
2137 	if (!seen_action)
2138 		zerr(gettext("%s not specified"), pt_to_str(PT_ACTION));
2139 	if (!seen_priv || !seen_limit || !seen_action)
2140 		goto bad;
2141 	rctlvaltab->zone_rctlval_next = NULL;
2142 	rctlblk = alloca(rctlblk_size());
2143 	/*
2144 	 * Make sure the rctl value looks roughly correct; we won't know if
2145 	 * it's truly OK until we verify the configuration on the target
2146 	 * system.
2147 	 */
2148 	if (zonecfg_construct_rctlblk(rctlvaltab, rctlblk) != Z_OK ||
2149 	    !zonecfg_valid_rctlblk(rctlblk)) {
2150 		zerr(gettext("Invalid %s %s specification"), rt_to_str(RT_RCTL),
2151 		    pt_to_str(PT_VALUE));
2152 		goto bad;
2153 	}
2154 	err = zonecfg_add_rctl_value(&in_progress_rctltab, rctlvaltab);
2155 	if (err != Z_OK)
2156 		zone_perror(pt_to_str(PT_VALUE), err, TRUE);
2157 	return;
2158 
2159 bad:
2160 	zonecfg_free_rctl_value_list(rctlvaltab);
2161 }
2162 
2163 static void
2164 add_property(cmd_t *cmd)
2165 {
2166 	char *prop_id;
2167 	int err, res_type, prop_type;
2168 	property_value_ptr_t pp;
2169 	list_property_ptr_t l;
2170 
2171 	res_type = resource_scope;
2172 	prop_type = cmd->cmd_prop_name[0];
2173 	if (res_type == RT_UNKNOWN || prop_type == PT_UNKNOWN) {
2174 		long_usage(CMD_ADD, TRUE);
2175 		return;
2176 	}
2177 
2178 	if (cmd->cmd_prop_nv_pairs != 1) {
2179 		long_usage(CMD_ADD, TRUE);
2180 		return;
2181 	}
2182 
2183 	if (initialize(TRUE) != Z_OK)
2184 		return;
2185 
2186 	switch (res_type) {
2187 	case RT_FS:
2188 		if (prop_type != PT_OPTIONS) {
2189 			zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE,
2190 			    TRUE);
2191 			long_usage(CMD_ADD, TRUE);
2192 			usage(FALSE, HELP_PROPS);
2193 			return;
2194 		}
2195 		pp = cmd->cmd_property_ptr[0];
2196 		if (pp->pv_type != PROP_VAL_SIMPLE &&
2197 		    pp->pv_type != PROP_VAL_LIST) {
2198 			zerr(gettext("A %s or %s value was expected here."),
2199 			    pvt_to_str(PROP_VAL_SIMPLE),
2200 			    pvt_to_str(PROP_VAL_LIST));
2201 			saw_error = TRUE;
2202 			return;
2203 		}
2204 		if (pp->pv_type == PROP_VAL_SIMPLE) {
2205 			if (pp->pv_simple == NULL) {
2206 				long_usage(CMD_ADD, TRUE);
2207 				return;
2208 			}
2209 			prop_id = pp->pv_simple;
2210 			err = zonecfg_add_fs_option(&in_progress_fstab,
2211 			    prop_id);
2212 			if (err != Z_OK)
2213 				zone_perror(pt_to_str(prop_type), err, TRUE);
2214 		} else {
2215 			list_property_ptr_t list;
2216 
2217 			for (list = pp->pv_list; list != NULL;
2218 			    list = list->lp_next) {
2219 				prop_id = list->lp_simple;
2220 				if (prop_id == NULL)
2221 					break;
2222 				err = zonecfg_add_fs_option(
2223 				    &in_progress_fstab, prop_id);
2224 				if (err != Z_OK)
2225 					zone_perror(pt_to_str(prop_type), err,
2226 					    TRUE);
2227 			}
2228 		}
2229 		return;
2230 	case RT_RCTL:
2231 		if (prop_type != PT_VALUE) {
2232 			zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE,
2233 			    TRUE);
2234 			long_usage(CMD_ADD, TRUE);
2235 			usage(FALSE, HELP_PROPS);
2236 			return;
2237 		}
2238 		pp = cmd->cmd_property_ptr[0];
2239 		if (pp->pv_type != PROP_VAL_COMPLEX &&
2240 		    pp->pv_type != PROP_VAL_LIST) {
2241 			zerr(gettext("A %s or %s value was expected here."),
2242 			    pvt_to_str(PROP_VAL_COMPLEX),
2243 			    pvt_to_str(PROP_VAL_LIST));
2244 			saw_error = TRUE;
2245 			return;
2246 		}
2247 		if (pp->pv_type == PROP_VAL_COMPLEX) {
2248 			do_complex_rctl_val(pp->pv_complex);
2249 			return;
2250 		}
2251 		for (l = pp->pv_list; l != NULL; l = l->lp_next)
2252 			do_complex_rctl_val(l->lp_complex);
2253 		return;
2254 	default:
2255 		zone_perror(rt_to_str(res_type), Z_NO_RESOURCE_TYPE, TRUE);
2256 		long_usage(CMD_ADD, TRUE);
2257 		usage(FALSE, HELP_RESOURCES);
2258 		return;
2259 	}
2260 }
2261 
2262 static boolean_t
2263 gz_invalid_resource(int type)
2264 {
2265 	return (global_zone && (type == RT_FS || type == RT_IPD ||
2266 	    type == RT_NET || type == RT_DEVICE || type == RT_ATTR ||
2267 	    type == RT_DATASET));
2268 }
2269 
2270 static boolean_t
2271 gz_invalid_rt_property(int type)
2272 {
2273 	return (global_zone && (type == RT_ZONENAME || type == RT_ZONEPATH ||
2274 	    type == RT_AUTOBOOT || type == RT_LIMITPRIV ||
2275 	    type == RT_BOOTARGS || type == RT_BRAND || type == RT_SCHED ||
2276 	    type == RT_IPTYPE));
2277 }
2278 
2279 static boolean_t
2280 gz_invalid_property(int type)
2281 {
2282 	return (global_zone && (type == PT_ZONENAME || type == PT_ZONEPATH ||
2283 	    type == PT_AUTOBOOT || type == PT_LIMITPRIV ||
2284 	    type == PT_BOOTARGS || type == PT_BRAND || type == PT_SCHED ||
2285 	    type == PT_IPTYPE));
2286 }
2287 
2288 void
2289 add_func(cmd_t *cmd)
2290 {
2291 	int arg;
2292 	bool arg_err = FALSE;
2293 
2294 	assert(cmd != NULL);
2295 
2296 	optind = 0;
2297 	while ((arg = getopt(cmd->cmd_argc, cmd->cmd_argv, "?")) != EOF) {
2298 		switch (arg) {
2299 		case '?':
2300 			longer_usage(CMD_ADD);
2301 			arg_err = TRUE;
2302 			break;
2303 		default:
2304 			short_usage(CMD_ADD);
2305 			arg_err = TRUE;
2306 			break;
2307 		}
2308 	}
2309 	if (arg_err)
2310 		return;
2311 
2312 	if (optind != cmd->cmd_argc) {
2313 		short_usage(CMD_ADD);
2314 		return;
2315 	}
2316 
2317 	if (zone_is_read_only(CMD_ADD))
2318 		return;
2319 
2320 	if (initialize(TRUE) != Z_OK)
2321 		return;
2322 	if (global_scope) {
2323 		if (gz_invalid_resource(cmd->cmd_res_type)) {
2324 			zerr(gettext("Cannot add a %s resource to the "
2325 			    "global zone."), rt_to_str(cmd->cmd_res_type));
2326 			saw_error = TRUE;
2327 			return;
2328 		}
2329 
2330 		global_scope = FALSE;
2331 		resource_scope = cmd->cmd_res_type;
2332 		end_op = CMD_ADD;
2333 		add_resource(cmd);
2334 	} else
2335 		add_property(cmd);
2336 }
2337 
2338 /*
2339  * This routine has an unusual implementation, because it tries very
2340  * hard to succeed in the face of a variety of failure modes.
2341  * The most common and most vexing occurs when the index file and
2342  * the /etc/zones/<zonename.xml> file are not both present.  In
2343  * this case, delete must eradicate as much of the zone state as is left
2344  * so that the user can later create a new zone with the same name.
2345  */
2346 void
2347 delete_func(cmd_t *cmd)
2348 {
2349 	int err, arg, answer;
2350 	char line[ZONENAME_MAX + 128];	/* enough to ask a question */
2351 	bool force = FALSE;
2352 	bool arg_err = FALSE;
2353 
2354 	optind = 0;
2355 	while ((arg = getopt(cmd->cmd_argc, cmd->cmd_argv, "?F")) != EOF) {
2356 		switch (arg) {
2357 		case '?':
2358 			longer_usage(CMD_DELETE);
2359 			arg_err = TRUE;
2360 			break;
2361 		case 'F':
2362 			force = TRUE;
2363 			break;
2364 		default:
2365 			short_usage(CMD_DELETE);
2366 			arg_err = TRUE;
2367 			break;
2368 		}
2369 	}
2370 	if (arg_err)
2371 		return;
2372 
2373 	if (optind != cmd->cmd_argc) {
2374 		short_usage(CMD_DELETE);
2375 		return;
2376 	}
2377 
2378 	if (zone_is_read_only(CMD_DELETE))
2379 		return;
2380 
2381 	if (!force) {
2382 		/*
2383 		 * Initialize sets up the global called "handle" and warns the
2384 		 * user if the zone is not configured.  In force mode, we don't
2385 		 * trust that evaluation, and hence skip it.  (We don't need the
2386 		 * handle to be loaded anyway, since zonecfg_destroy is done by
2387 		 * zonename).  However, we also have to take care to emulate the
2388 		 * messages spit out by initialize; see below.
2389 		 */
2390 		if (initialize(TRUE) != Z_OK)
2391 			return;
2392 
2393 		(void) snprintf(line, sizeof (line),
2394 		    gettext("Are you sure you want to delete zone %s"), zone);
2395 		if ((answer = ask_yesno(FALSE, line)) == -1) {
2396 			zerr(gettext("Input not from terminal and -F not "
2397 			    "specified:\n%s command ignored, exiting."),
2398 			    cmd_to_str(CMD_DELETE));
2399 			exit(Z_ERR);
2400 		}
2401 		if (answer != 1)
2402 			return;
2403 	}
2404 
2405 	if ((err = zonecfg_destroy(zone, force)) != Z_OK) {
2406 		if ((err == Z_BAD_ZONE_STATE) && !force) {
2407 			zerr(gettext("Zone %s not in %s state; %s not "
2408 			    "allowed.  Use -F to force %s."),
2409 			    zone, zone_state_str(ZONE_STATE_CONFIGURED),
2410 			    cmd_to_str(CMD_DELETE), cmd_to_str(CMD_DELETE));
2411 		} else {
2412 			zone_perror(zone, err, TRUE);
2413 		}
2414 	}
2415 	need_to_commit = FALSE;
2416 
2417 	/*
2418 	 * Emulate initialize's messaging; if there wasn't a valid handle to
2419 	 * begin with, then user had typed delete (or delete -F) multiple
2420 	 * times.  So we emit a message.
2421 	 *
2422 	 * We only do this in the 'force' case because normally, initialize()
2423 	 * takes care of this for us.
2424 	 */
2425 	if (force && zonecfg_check_handle(handle) != Z_OK && interactive_mode)
2426 		(void) printf(gettext("Use '%s' to begin "
2427 		    "configuring a new zone.\n"), cmd_to_str(CMD_CREATE));
2428 
2429 	/*
2430 	 * Time for a new handle: finish the old one off first
2431 	 * then get a new one properly to avoid leaks.
2432 	 */
2433 	if (got_handle) {
2434 		zonecfg_fini_handle(handle);
2435 		if ((handle = zonecfg_init_handle()) == NULL) {
2436 			zone_perror(execname, Z_NOMEM, TRUE);
2437 			exit(Z_ERR);
2438 		}
2439 		if ((err = zonecfg_get_handle(zone, handle)) != Z_OK) {
2440 			/* If there was no zone before, that's OK */
2441 			if (err != Z_NO_ZONE)
2442 				zone_perror(zone, err, TRUE);
2443 			got_handle = FALSE;
2444 		}
2445 	}
2446 }
2447 
2448 static int
2449 fill_in_fstab(cmd_t *cmd, struct zone_fstab *fstab, bool fill_in_only)
2450 {
2451 	int err, i;
2452 	property_value_ptr_t pp;
2453 
2454 	if ((err = initialize(TRUE)) != Z_OK)
2455 		return (err);
2456 
2457 	bzero(fstab, sizeof (*fstab));
2458 	for (i = 0; i < cmd->cmd_prop_nv_pairs; i++) {
2459 		pp = cmd->cmd_property_ptr[i];
2460 		if (pp->pv_type != PROP_VAL_SIMPLE || pp->pv_simple == NULL) {
2461 			zerr(gettext("A simple value was expected here."));
2462 			saw_error = TRUE;
2463 			return (Z_INSUFFICIENT_SPEC);
2464 		}
2465 		switch (cmd->cmd_prop_name[i]) {
2466 		case PT_DIR:
2467 			(void) strlcpy(fstab->zone_fs_dir, pp->pv_simple,
2468 			    sizeof (fstab->zone_fs_dir));
2469 			break;
2470 		case PT_SPECIAL:
2471 			(void) strlcpy(fstab->zone_fs_special, pp->pv_simple,
2472 			    sizeof (fstab->zone_fs_special));
2473 			break;
2474 		case PT_RAW:
2475 			(void) strlcpy(fstab->zone_fs_raw, pp->pv_simple,
2476 			    sizeof (fstab->zone_fs_raw));
2477 			break;
2478 		case PT_TYPE:
2479 			(void) strlcpy(fstab->zone_fs_type, pp->pv_simple,
2480 			    sizeof (fstab->zone_fs_type));
2481 			break;
2482 		default:
2483 			zone_perror(pt_to_str(cmd->cmd_prop_name[i]),
2484 			    Z_NO_PROPERTY_TYPE, TRUE);
2485 			return (Z_INSUFFICIENT_SPEC);
2486 		}
2487 	}
2488 	if (fill_in_only)
2489 		return (Z_OK);
2490 	return (zonecfg_lookup_filesystem(handle, fstab));
2491 }
2492 
2493 static int
2494 fill_in_ipdtab(cmd_t *cmd, struct zone_fstab *ipdtab, bool fill_in_only)
2495 {
2496 	int err, i;
2497 	property_value_ptr_t pp;
2498 
2499 	if ((err = initialize(TRUE)) != Z_OK)
2500 		return (err);
2501 
2502 	bzero(ipdtab, sizeof (*ipdtab));
2503 	for (i = 0; i < cmd->cmd_prop_nv_pairs; i++) {
2504 		pp = cmd->cmd_property_ptr[i];
2505 		if (pp->pv_type != PROP_VAL_SIMPLE || pp->pv_simple == NULL) {
2506 			zerr(gettext("A simple value was expected here."));
2507 			saw_error = TRUE;
2508 			return (Z_INSUFFICIENT_SPEC);
2509 		}
2510 		switch (cmd->cmd_prop_name[i]) {
2511 		case PT_DIR:
2512 			(void) strlcpy(ipdtab->zone_fs_dir, pp->pv_simple,
2513 			    sizeof (ipdtab->zone_fs_dir));
2514 			break;
2515 		default:
2516 			zone_perror(pt_to_str(cmd->cmd_prop_name[i]),
2517 			    Z_NO_PROPERTY_TYPE, TRUE);
2518 			return (Z_INSUFFICIENT_SPEC);
2519 		}
2520 	}
2521 	if (fill_in_only)
2522 		return (Z_OK);
2523 	return (zonecfg_lookup_ipd(handle, ipdtab));
2524 }
2525 
2526 static int
2527 fill_in_nwiftab(cmd_t *cmd, struct zone_nwiftab *nwiftab, bool fill_in_only)
2528 {
2529 	int err, i;
2530 	property_value_ptr_t pp;
2531 
2532 	if ((err = initialize(TRUE)) != Z_OK)
2533 		return (err);
2534 
2535 	bzero(nwiftab, sizeof (*nwiftab));
2536 	for (i = 0; i < cmd->cmd_prop_nv_pairs; i++) {
2537 		pp = cmd->cmd_property_ptr[i];
2538 		if (pp->pv_type != PROP_VAL_SIMPLE || pp->pv_simple == NULL) {
2539 			zerr(gettext("A simple value was expected here."));
2540 			saw_error = TRUE;
2541 			return (Z_INSUFFICIENT_SPEC);
2542 		}
2543 		switch (cmd->cmd_prop_name[i]) {
2544 		case PT_ADDRESS:
2545 			(void) strlcpy(nwiftab->zone_nwif_address,
2546 			    pp->pv_simple, sizeof (nwiftab->zone_nwif_address));
2547 			break;
2548 		case PT_PHYSICAL:
2549 			(void) strlcpy(nwiftab->zone_nwif_physical,
2550 			    pp->pv_simple,
2551 			    sizeof (nwiftab->zone_nwif_physical));
2552 			break;
2553 		default:
2554 			zone_perror(pt_to_str(cmd->cmd_prop_name[i]),
2555 			    Z_NO_PROPERTY_TYPE, TRUE);
2556 			return (Z_INSUFFICIENT_SPEC);
2557 		}
2558 	}
2559 	if (fill_in_only)
2560 		return (Z_OK);
2561 	err = zonecfg_lookup_nwif(handle, nwiftab);
2562 	return (err);
2563 }
2564 
2565 static int
2566 fill_in_devtab(cmd_t *cmd, struct zone_devtab *devtab, bool fill_in_only)
2567 {
2568 	int err, i;
2569 	property_value_ptr_t pp;
2570 
2571 	if ((err = initialize(TRUE)) != Z_OK)
2572 		return (err);
2573 
2574 	bzero(devtab, sizeof (*devtab));
2575 	for (i = 0; i < cmd->cmd_prop_nv_pairs; i++) {
2576 		pp = cmd->cmd_property_ptr[i];
2577 		if (pp->pv_type != PROP_VAL_SIMPLE || pp->pv_simple == NULL) {
2578 			zerr(gettext("A simple value was expected here."));
2579 			saw_error = TRUE;
2580 			return (Z_INSUFFICIENT_SPEC);
2581 		}
2582 		switch (cmd->cmd_prop_name[i]) {
2583 		case PT_MATCH:
2584 			(void) strlcpy(devtab->zone_dev_match, pp->pv_simple,
2585 			    sizeof (devtab->zone_dev_match));
2586 			break;
2587 		default:
2588 			zone_perror(pt_to_str(cmd->cmd_prop_name[i]),
2589 			    Z_NO_PROPERTY_TYPE, TRUE);
2590 			return (Z_INSUFFICIENT_SPEC);
2591 		}
2592 	}
2593 	if (fill_in_only)
2594 		return (Z_OK);
2595 	err = zonecfg_lookup_dev(handle, devtab);
2596 	return (err);
2597 }
2598 
2599 static int
2600 fill_in_rctltab(cmd_t *cmd, struct zone_rctltab *rctltab, bool fill_in_only)
2601 {
2602 	int err, i;
2603 	property_value_ptr_t pp;
2604 
2605 	if ((err = initialize(TRUE)) != Z_OK)
2606 		return (err);
2607 
2608 	bzero(rctltab, sizeof (*rctltab));
2609 	for (i = 0; i < cmd->cmd_prop_nv_pairs; i++) {
2610 		pp = cmd->cmd_property_ptr[i];
2611 		if (pp->pv_type != PROP_VAL_SIMPLE || pp->pv_simple == NULL) {
2612 			zerr(gettext("A simple value was expected here."));
2613 			saw_error = TRUE;
2614 			return (Z_INSUFFICIENT_SPEC);
2615 		}
2616 		switch (cmd->cmd_prop_name[i]) {
2617 		case PT_NAME:
2618 			(void) strlcpy(rctltab->zone_rctl_name, pp->pv_simple,
2619 			    sizeof (rctltab->zone_rctl_name));
2620 			break;
2621 		default:
2622 			zone_perror(pt_to_str(cmd->cmd_prop_name[i]),
2623 			    Z_NO_PROPERTY_TYPE, TRUE);
2624 			return (Z_INSUFFICIENT_SPEC);
2625 		}
2626 	}
2627 	if (fill_in_only)
2628 		return (Z_OK);
2629 	err = zonecfg_lookup_rctl(handle, rctltab);
2630 	return (err);
2631 }
2632 
2633 static int
2634 fill_in_attrtab(cmd_t *cmd, struct zone_attrtab *attrtab, bool fill_in_only)
2635 {
2636 	int err, i;
2637 	property_value_ptr_t pp;
2638 
2639 	if ((err = initialize(TRUE)) != Z_OK)
2640 		return (err);
2641 
2642 	bzero(attrtab, sizeof (*attrtab));
2643 	for (i = 0; i < cmd->cmd_prop_nv_pairs; i++) {
2644 		pp = cmd->cmd_property_ptr[i];
2645 		if (pp->pv_type != PROP_VAL_SIMPLE || pp->pv_simple == NULL) {
2646 			zerr(gettext("A simple value was expected here."));
2647 			saw_error = TRUE;
2648 			return (Z_INSUFFICIENT_SPEC);
2649 		}
2650 		switch (cmd->cmd_prop_name[i]) {
2651 		case PT_NAME:
2652 			(void) strlcpy(attrtab->zone_attr_name, pp->pv_simple,
2653 			    sizeof (attrtab->zone_attr_name));
2654 			break;
2655 		case PT_TYPE:
2656 			(void) strlcpy(attrtab->zone_attr_type, pp->pv_simple,
2657 			    sizeof (attrtab->zone_attr_type));
2658 			break;
2659 		case PT_VALUE:
2660 			(void) strlcpy(attrtab->zone_attr_value, pp->pv_simple,
2661 			    sizeof (attrtab->zone_attr_value));
2662 			break;
2663 		default:
2664 			zone_perror(pt_to_str(cmd->cmd_prop_name[i]),
2665 			    Z_NO_PROPERTY_TYPE, TRUE);
2666 			return (Z_INSUFFICIENT_SPEC);
2667 		}
2668 	}
2669 	if (fill_in_only)
2670 		return (Z_OK);
2671 	err = zonecfg_lookup_attr(handle, attrtab);
2672 	return (err);
2673 }
2674 
2675 static int
2676 fill_in_dstab(cmd_t *cmd, struct zone_dstab *dstab, bool fill_in_only)
2677 {
2678 	int err, i;
2679 	property_value_ptr_t pp;
2680 
2681 	if ((err = initialize(TRUE)) != Z_OK)
2682 		return (err);
2683 
2684 	dstab->zone_dataset_name[0] = '\0';
2685 	for (i = 0; i < cmd->cmd_prop_nv_pairs; i++) {
2686 		pp = cmd->cmd_property_ptr[i];
2687 		if (pp->pv_type != PROP_VAL_SIMPLE || pp->pv_simple == NULL) {
2688 			zerr(gettext("A simple value was expected here."));
2689 			saw_error = TRUE;
2690 			return (Z_INSUFFICIENT_SPEC);
2691 		}
2692 		switch (cmd->cmd_prop_name[i]) {
2693 		case PT_NAME:
2694 			(void) strlcpy(dstab->zone_dataset_name, pp->pv_simple,
2695 			    sizeof (dstab->zone_dataset_name));
2696 			break;
2697 		default:
2698 			zone_perror(pt_to_str(cmd->cmd_prop_name[i]),
2699 			    Z_NO_PROPERTY_TYPE, TRUE);
2700 			return (Z_INSUFFICIENT_SPEC);
2701 		}
2702 	}
2703 	if (fill_in_only)
2704 		return (Z_OK);
2705 	return (zonecfg_lookup_ds(handle, dstab));
2706 }
2707 
2708 static void
2709 remove_aliased_rctl(int type, char *name)
2710 {
2711 	int err;
2712 	uint64_t tmp;
2713 
2714 	if ((err = zonecfg_get_aliased_rctl(handle, name, &tmp)) != Z_OK) {
2715 		zerr("%s %s: %s", cmd_to_str(CMD_CLEAR), pt_to_str(type),
2716 		    zonecfg_strerror(err));
2717 		saw_error = TRUE;
2718 		return;
2719 	}
2720 	if ((err = zonecfg_rm_aliased_rctl(handle, name)) != Z_OK) {
2721 		zerr("%s %s: %s", cmd_to_str(CMD_CLEAR), pt_to_str(type),
2722 		    zonecfg_strerror(err));
2723 		saw_error = TRUE;
2724 	} else {
2725 		need_to_commit = TRUE;
2726 	}
2727 }
2728 
2729 static boolean_t
2730 prompt_remove_resource(cmd_t *cmd, char *rsrc)
2731 {
2732 	int num;
2733 	int answer;
2734 	int arg;
2735 	boolean_t force = B_FALSE;
2736 	char prompt[128];
2737 	bool arg_err = FALSE;
2738 
2739 	optind = 0;
2740 	while ((arg = getopt(cmd->cmd_argc, cmd->cmd_argv, "F")) != EOF) {
2741 		switch (arg) {
2742 		case 'F':
2743 			force = B_TRUE;
2744 			break;
2745 		default:
2746 			arg_err = TRUE;
2747 			break;
2748 		}
2749 	}
2750 	if (arg_err)
2751 		return (B_FALSE);
2752 
2753 
2754 	num = zonecfg_num_resources(handle, rsrc);
2755 
2756 	if (num == 0) {
2757 		z_cmd_rt_perror(CMD_REMOVE, cmd->cmd_res_type, Z_NO_ENTRY,
2758 		    TRUE);
2759 		return (B_FALSE);
2760 	}
2761 	if (num > 1 && !force) {
2762 		if (!interactive_mode) {
2763 			zerr(gettext("There are multiple instances of this "
2764 			    "resource.  Either qualify the resource to\n"
2765 			    "remove a single instance or use the -F option to "
2766 			    "remove all instances."));
2767 			saw_error = TRUE;
2768 			return (B_FALSE);
2769 		}
2770 		(void) snprintf(prompt, sizeof (prompt), gettext(
2771 		    "Are you sure you want to remove ALL '%s' resources"),
2772 		    rsrc);
2773 		answer = ask_yesno(FALSE, prompt);
2774 		if (answer == -1) {
2775 			zerr(gettext("Resource incomplete."));
2776 			return (B_FALSE);
2777 		}
2778 		if (answer != 1)
2779 			return (B_FALSE);
2780 	}
2781 	return (B_TRUE);
2782 }
2783 
2784 static void
2785 remove_fs(cmd_t *cmd)
2786 {
2787 	int err;
2788 
2789 	/* traditional, qualified fs removal */
2790 	if (cmd->cmd_prop_nv_pairs > 0) {
2791 		struct zone_fstab fstab;
2792 
2793 		if ((err = fill_in_fstab(cmd, &fstab, FALSE)) != Z_OK) {
2794 			z_cmd_rt_perror(CMD_REMOVE, RT_FS, err, TRUE);
2795 			return;
2796 		}
2797 		if ((err = zonecfg_delete_filesystem(handle, &fstab)) != Z_OK)
2798 			z_cmd_rt_perror(CMD_REMOVE, RT_FS, err, TRUE);
2799 		else
2800 			need_to_commit = TRUE;
2801 		zonecfg_free_fs_option_list(fstab.zone_fs_options);
2802 		return;
2803 	}
2804 
2805 	/*
2806 	 * unqualified fs removal.  remove all fs's but prompt if more
2807 	 * than one.
2808 	 */
2809 	if (!prompt_remove_resource(cmd, "fs"))
2810 		return;
2811 
2812 	if ((err = zonecfg_del_all_resources(handle, "fs")) != Z_OK)
2813 		z_cmd_rt_perror(CMD_REMOVE, RT_FS, err, TRUE);
2814 	else
2815 		need_to_commit = TRUE;
2816 }
2817 
2818 static void
2819 remove_ipd(cmd_t *cmd)
2820 {
2821 	int err;
2822 
2823 	if (state_atleast(ZONE_STATE_INSTALLED)) {
2824 		zerr(gettext("Zone %s already installed; %s %s not allowed."),
2825 		    zone, cmd_to_str(CMD_REMOVE), rt_to_str(RT_IPD));
2826 		return;
2827 	}
2828 
2829 	/* traditional, qualified ipd removal */
2830 	if (cmd->cmd_prop_nv_pairs > 0) {
2831 		struct zone_fstab fstab;
2832 
2833 		if ((err = fill_in_ipdtab(cmd, &fstab, FALSE)) != Z_OK) {
2834 			z_cmd_rt_perror(CMD_REMOVE, RT_IPD, err, TRUE);
2835 			return;
2836 		}
2837 		if ((err = zonecfg_delete_ipd(handle, &fstab)) != Z_OK)
2838 			z_cmd_rt_perror(CMD_REMOVE, RT_IPD, err, TRUE);
2839 		else
2840 			need_to_commit = TRUE;
2841 		return;
2842 	}
2843 
2844 	/*
2845 	 * unqualified ipd removal.  remove all ipds but prompt if more
2846 	 * than one.
2847 	 */
2848 	if (!prompt_remove_resource(cmd, "inherit-pkg-dir"))
2849 		return;
2850 
2851 	if ((err = zonecfg_del_all_resources(handle, "inherit-pkg-dir"))
2852 	    != Z_OK)
2853 		z_cmd_rt_perror(CMD_REMOVE, RT_IPD, err, TRUE);
2854 	else
2855 		need_to_commit = TRUE;
2856 }
2857 
2858 static void
2859 remove_net(cmd_t *cmd)
2860 {
2861 	int err;
2862 
2863 	/* traditional, qualified net removal */
2864 	if (cmd->cmd_prop_nv_pairs > 0) {
2865 		struct zone_nwiftab nwiftab;
2866 
2867 		if ((err = fill_in_nwiftab(cmd, &nwiftab, FALSE)) != Z_OK) {
2868 			z_cmd_rt_perror(CMD_REMOVE, RT_NET, err, TRUE);
2869 			return;
2870 		}
2871 		if ((err = zonecfg_delete_nwif(handle, &nwiftab)) != Z_OK)
2872 			z_cmd_rt_perror(CMD_REMOVE, RT_NET, err, TRUE);
2873 		else
2874 			need_to_commit = TRUE;
2875 		return;
2876 	}
2877 
2878 	/*
2879 	 * unqualified net removal.  remove all nets but prompt if more
2880 	 * than one.
2881 	 */
2882 	if (!prompt_remove_resource(cmd, "net"))
2883 		return;
2884 
2885 	if ((err = zonecfg_del_all_resources(handle, "net")) != Z_OK)
2886 		z_cmd_rt_perror(CMD_REMOVE, RT_NET, err, TRUE);
2887 	else
2888 		need_to_commit = TRUE;
2889 }
2890 
2891 static void
2892 remove_device(cmd_t *cmd)
2893 {
2894 	int err;
2895 
2896 	/* traditional, qualified device removal */
2897 	if (cmd->cmd_prop_nv_pairs > 0) {
2898 		struct zone_devtab devtab;
2899 
2900 		if ((err = fill_in_devtab(cmd, &devtab, FALSE)) != Z_OK) {
2901 			z_cmd_rt_perror(CMD_REMOVE, RT_DEVICE, err, TRUE);
2902 			return;
2903 		}
2904 		if ((err = zonecfg_delete_dev(handle, &devtab)) != Z_OK)
2905 			z_cmd_rt_perror(CMD_REMOVE, RT_DEVICE, err, TRUE);
2906 		else
2907 			need_to_commit = TRUE;
2908 		return;
2909 	}
2910 
2911 	/*
2912 	 * unqualified device removal.  remove all devices but prompt if more
2913 	 * than one.
2914 	 */
2915 	if (!prompt_remove_resource(cmd, "device"))
2916 		return;
2917 
2918 	if ((err = zonecfg_del_all_resources(handle, "device")) != Z_OK)
2919 		z_cmd_rt_perror(CMD_REMOVE, RT_DEVICE, err, TRUE);
2920 	else
2921 		need_to_commit = TRUE;
2922 }
2923 
2924 static void
2925 remove_attr(cmd_t *cmd)
2926 {
2927 	int err;
2928 
2929 	/* traditional, qualified attr removal */
2930 	if (cmd->cmd_prop_nv_pairs > 0) {
2931 		struct zone_attrtab attrtab;
2932 
2933 		if ((err = fill_in_attrtab(cmd, &attrtab, FALSE)) != Z_OK) {
2934 			z_cmd_rt_perror(CMD_REMOVE, RT_ATTR, err, TRUE);
2935 			return;
2936 		}
2937 		if ((err = zonecfg_delete_attr(handle, &attrtab)) != Z_OK)
2938 			z_cmd_rt_perror(CMD_REMOVE, RT_ATTR, err, TRUE);
2939 		else
2940 			need_to_commit = TRUE;
2941 		return;
2942 	}
2943 
2944 	/*
2945 	 * unqualified attr removal.  remove all attrs but prompt if more
2946 	 * than one.
2947 	 */
2948 	if (!prompt_remove_resource(cmd, "attr"))
2949 		return;
2950 
2951 	if ((err = zonecfg_del_all_resources(handle, "attr")) != Z_OK)
2952 		z_cmd_rt_perror(CMD_REMOVE, RT_ATTR, err, TRUE);
2953 	else
2954 		need_to_commit = TRUE;
2955 }
2956 
2957 static void
2958 remove_dataset(cmd_t *cmd)
2959 {
2960 	int err;
2961 
2962 	/* traditional, qualified dataset removal */
2963 	if (cmd->cmd_prop_nv_pairs > 0) {
2964 		struct zone_dstab dstab;
2965 
2966 		if ((err = fill_in_dstab(cmd, &dstab, FALSE)) != Z_OK) {
2967 			z_cmd_rt_perror(CMD_REMOVE, RT_DATASET, err, TRUE);
2968 			return;
2969 		}
2970 		if ((err = zonecfg_delete_ds(handle, &dstab)) != Z_OK)
2971 			z_cmd_rt_perror(CMD_REMOVE, RT_DATASET, err, TRUE);
2972 		else
2973 			need_to_commit = TRUE;
2974 		return;
2975 	}
2976 
2977 	/*
2978 	 * unqualified dataset removal.  remove all datasets but prompt if more
2979 	 * than one.
2980 	 */
2981 	if (!prompt_remove_resource(cmd, "dataset"))
2982 		return;
2983 
2984 	if ((err = zonecfg_del_all_resources(handle, "dataset")) != Z_OK)
2985 		z_cmd_rt_perror(CMD_REMOVE, RT_DATASET, err, TRUE);
2986 	else
2987 		need_to_commit = TRUE;
2988 }
2989 
2990 static void
2991 remove_rctl(cmd_t *cmd)
2992 {
2993 	int err;
2994 
2995 	/* traditional, qualified rctl removal */
2996 	if (cmd->cmd_prop_nv_pairs > 0) {
2997 		struct zone_rctltab rctltab;
2998 
2999 		if ((err = fill_in_rctltab(cmd, &rctltab, FALSE)) != Z_OK) {
3000 			z_cmd_rt_perror(CMD_REMOVE, RT_RCTL, err, TRUE);
3001 			return;
3002 		}
3003 		if ((err = zonecfg_delete_rctl(handle, &rctltab)) != Z_OK)
3004 			z_cmd_rt_perror(CMD_REMOVE, RT_RCTL, err, TRUE);
3005 		else
3006 			need_to_commit = TRUE;
3007 		zonecfg_free_rctl_value_list(rctltab.zone_rctl_valptr);
3008 		return;
3009 	}
3010 
3011 	/*
3012 	 * unqualified rctl removal.  remove all rctls but prompt if more
3013 	 * than one.
3014 	 */
3015 	if (!prompt_remove_resource(cmd, "rctl"))
3016 		return;
3017 
3018 	if ((err = zonecfg_del_all_resources(handle, "rctl")) != Z_OK)
3019 		z_cmd_rt_perror(CMD_REMOVE, RT_RCTL, err, TRUE);
3020 	else
3021 		need_to_commit = TRUE;
3022 }
3023 
3024 static void
3025 remove_pset()
3026 {
3027 	int err;
3028 	struct zone_psettab psettab;
3029 
3030 	if ((err = zonecfg_lookup_pset(handle, &psettab)) != Z_OK) {
3031 		z_cmd_rt_perror(CMD_REMOVE, RT_DCPU, err, TRUE);
3032 		return;
3033 	}
3034 	if ((err = zonecfg_delete_pset(handle)) != Z_OK)
3035 		z_cmd_rt_perror(CMD_REMOVE, RT_DCPU, err, TRUE);
3036 	else
3037 		need_to_commit = TRUE;
3038 }
3039 
3040 static void
3041 remove_pcap()
3042 {
3043 	int err;
3044 	uint64_t tmp;
3045 
3046 	if (zonecfg_get_aliased_rctl(handle, ALIAS_CPUCAP, &tmp) != Z_OK) {
3047 		zerr("%s %s: %s", cmd_to_str(CMD_REMOVE), rt_to_str(RT_PCAP),
3048 		    zonecfg_strerror(Z_NO_RESOURCE_TYPE));
3049 		saw_error = TRUE;
3050 		return;
3051 	}
3052 
3053 	if ((err = zonecfg_rm_aliased_rctl(handle, ALIAS_CPUCAP)) != Z_OK)
3054 		z_cmd_rt_perror(CMD_REMOVE, RT_PCAP, err, TRUE);
3055 	else
3056 		need_to_commit = TRUE;
3057 }
3058 
3059 static void
3060 remove_mcap()
3061 {
3062 	int err, res1, res2, res3;
3063 	uint64_t tmp;
3064 	struct zone_mcaptab mcaptab;
3065 	boolean_t revert = B_FALSE;
3066 
3067 	res1 = zonecfg_lookup_mcap(handle, &mcaptab);
3068 	res2 = zonecfg_get_aliased_rctl(handle, ALIAS_MAXSWAP, &tmp);
3069 	res3 = zonecfg_get_aliased_rctl(handle, ALIAS_MAXLOCKEDMEM, &tmp);
3070 
3071 	/* if none of these exist, there is no resource to remove */
3072 	if (res1 != Z_OK && res2 != Z_OK && res3 != Z_OK) {
3073 		zerr("%s %s: %s", cmd_to_str(CMD_REMOVE), rt_to_str(RT_MCAP),
3074 		    zonecfg_strerror(Z_NO_RESOURCE_TYPE));
3075 		saw_error = TRUE;
3076 		return;
3077 	}
3078 	if (res1 == Z_OK) {
3079 		if ((err = zonecfg_delete_mcap(handle)) != Z_OK) {
3080 			z_cmd_rt_perror(CMD_REMOVE, RT_MCAP, err, TRUE);
3081 			revert = B_TRUE;
3082 		} else {
3083 			need_to_commit = TRUE;
3084 		}
3085 	}
3086 	if (res2 == Z_OK) {
3087 		if ((err = zonecfg_rm_aliased_rctl(handle, ALIAS_MAXSWAP))
3088 		    != Z_OK) {
3089 			z_cmd_rt_perror(CMD_REMOVE, RT_MCAP, err, TRUE);
3090 			revert = B_TRUE;
3091 		} else {
3092 			need_to_commit = TRUE;
3093 		}
3094 	}
3095 	if (res3 == Z_OK) {
3096 		if ((err = zonecfg_rm_aliased_rctl(handle, ALIAS_MAXLOCKEDMEM))
3097 		    != Z_OK) {
3098 			z_cmd_rt_perror(CMD_REMOVE, RT_MCAP, err, TRUE);
3099 			revert = B_TRUE;
3100 		} else {
3101 			need_to_commit = TRUE;
3102 		}
3103 	}
3104 
3105 	if (revert)
3106 		need_to_commit = FALSE;
3107 }
3108 
3109 static void
3110 remove_resource(cmd_t *cmd)
3111 {
3112 	int type;
3113 	int arg;
3114 	bool arg_err = FALSE;
3115 
3116 	if ((type = cmd->cmd_res_type) == RT_UNKNOWN) {
3117 		long_usage(CMD_REMOVE, TRUE);
3118 		return;
3119 	}
3120 
3121 	optind = 0;
3122 	while ((arg = getopt(cmd->cmd_argc, cmd->cmd_argv, "?F")) != EOF) {
3123 		switch (arg) {
3124 		case '?':
3125 			longer_usage(CMD_REMOVE);
3126 			arg_err = TRUE;
3127 			break;
3128 		case 'F':
3129 			break;
3130 		default:
3131 			short_usage(CMD_REMOVE);
3132 			arg_err = TRUE;
3133 			break;
3134 		}
3135 	}
3136 	if (arg_err)
3137 		return;
3138 
3139 	if (initialize(TRUE) != Z_OK)
3140 		return;
3141 
3142 	switch (type) {
3143 	case RT_FS:
3144 		remove_fs(cmd);
3145 		return;
3146 	case RT_IPD:
3147 		remove_ipd(cmd);
3148 		return;
3149 	case RT_NET:
3150 		remove_net(cmd);
3151 		return;
3152 	case RT_DEVICE:
3153 		remove_device(cmd);
3154 		return;
3155 	case RT_RCTL:
3156 		remove_rctl(cmd);
3157 		return;
3158 	case RT_ATTR:
3159 		remove_attr(cmd);
3160 		return;
3161 	case RT_DATASET:
3162 		remove_dataset(cmd);
3163 		return;
3164 	case RT_DCPU:
3165 		remove_pset();
3166 		return;
3167 	case RT_PCAP:
3168 		remove_pcap();
3169 		return;
3170 	case RT_MCAP:
3171 		remove_mcap();
3172 		return;
3173 	default:
3174 		zone_perror(rt_to_str(type), Z_NO_RESOURCE_TYPE, TRUE);
3175 		long_usage(CMD_REMOVE, TRUE);
3176 		usage(FALSE, HELP_RESOURCES);
3177 		return;
3178 	}
3179 }
3180 
3181 static void
3182 remove_property(cmd_t *cmd)
3183 {
3184 	char *prop_id;
3185 	int err, res_type, prop_type;
3186 	property_value_ptr_t pp;
3187 	struct zone_rctlvaltab *rctlvaltab;
3188 	complex_property_ptr_t cx;
3189 
3190 	res_type = resource_scope;
3191 	prop_type = cmd->cmd_prop_name[0];
3192 	if (res_type == RT_UNKNOWN || prop_type == PT_UNKNOWN) {
3193 		long_usage(CMD_REMOVE, TRUE);
3194 		return;
3195 	}
3196 
3197 	if (cmd->cmd_prop_nv_pairs != 1) {
3198 		long_usage(CMD_ADD, TRUE);
3199 		return;
3200 	}
3201 
3202 	if (initialize(TRUE) != Z_OK)
3203 		return;
3204 
3205 	switch (res_type) {
3206 	case RT_FS:
3207 		if (prop_type != PT_OPTIONS) {
3208 			zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE,
3209 			    TRUE);
3210 			long_usage(CMD_REMOVE, TRUE);
3211 			usage(FALSE, HELP_PROPS);
3212 			return;
3213 		}
3214 		pp = cmd->cmd_property_ptr[0];
3215 		if (pp->pv_type == PROP_VAL_COMPLEX) {
3216 			zerr(gettext("A %s or %s value was expected here."),
3217 			    pvt_to_str(PROP_VAL_SIMPLE),
3218 			    pvt_to_str(PROP_VAL_LIST));
3219 			saw_error = TRUE;
3220 			return;
3221 		}
3222 		if (pp->pv_type == PROP_VAL_SIMPLE) {
3223 			if (pp->pv_simple == NULL) {
3224 				long_usage(CMD_ADD, TRUE);
3225 				return;
3226 			}
3227 			prop_id = pp->pv_simple;
3228 			err = zonecfg_remove_fs_option(&in_progress_fstab,
3229 			    prop_id);
3230 			if (err != Z_OK)
3231 				zone_perror(pt_to_str(prop_type), err, TRUE);
3232 		} else {
3233 			list_property_ptr_t list;
3234 
3235 			for (list = pp->pv_list; list != NULL;
3236 			    list = list->lp_next) {
3237 				prop_id = list->lp_simple;
3238 				if (prop_id == NULL)
3239 					break;
3240 				err = zonecfg_remove_fs_option(
3241 				    &in_progress_fstab, prop_id);
3242 				if (err != Z_OK)
3243 					zone_perror(pt_to_str(prop_type), err,
3244 					    TRUE);
3245 			}
3246 		}
3247 		return;
3248 	case RT_RCTL:
3249 		if (prop_type != PT_VALUE) {
3250 			zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE,
3251 			    TRUE);
3252 			long_usage(CMD_REMOVE, TRUE);
3253 			usage(FALSE, HELP_PROPS);
3254 			return;
3255 		}
3256 		pp = cmd->cmd_property_ptr[0];
3257 		if (pp->pv_type != PROP_VAL_COMPLEX) {
3258 			zerr(gettext("A %s value was expected here."),
3259 			    pvt_to_str(PROP_VAL_COMPLEX));
3260 			saw_error = TRUE;
3261 			return;
3262 		}
3263 		if ((rctlvaltab = alloc_rctlvaltab()) == NULL) {
3264 			zone_perror(zone, Z_NOMEM, TRUE);
3265 			exit(Z_ERR);
3266 		}
3267 		for (cx = pp->pv_complex; cx != NULL; cx = cx->cp_next) {
3268 			switch (cx->cp_type) {
3269 			case PT_PRIV:
3270 				(void) strlcpy(rctlvaltab->zone_rctlval_priv,
3271 				    cx->cp_value,
3272 				    sizeof (rctlvaltab->zone_rctlval_priv));
3273 				break;
3274 			case PT_LIMIT:
3275 				(void) strlcpy(rctlvaltab->zone_rctlval_limit,
3276 				    cx->cp_value,
3277 				    sizeof (rctlvaltab->zone_rctlval_limit));
3278 				break;
3279 			case PT_ACTION:
3280 				(void) strlcpy(rctlvaltab->zone_rctlval_action,
3281 				    cx->cp_value,
3282 				    sizeof (rctlvaltab->zone_rctlval_action));
3283 				break;
3284 			default:
3285 				zone_perror(pt_to_str(prop_type),
3286 				    Z_NO_PROPERTY_TYPE, TRUE);
3287 				long_usage(CMD_ADD, TRUE);
3288 				usage(FALSE, HELP_PROPS);
3289 				zonecfg_free_rctl_value_list(rctlvaltab);
3290 				return;
3291 			}
3292 		}
3293 		rctlvaltab->zone_rctlval_next = NULL;
3294 		err = zonecfg_remove_rctl_value(&in_progress_rctltab,
3295 		    rctlvaltab);
3296 		if (err != Z_OK)
3297 			zone_perror(pt_to_str(prop_type), err, TRUE);
3298 		zonecfg_free_rctl_value_list(rctlvaltab);
3299 		return;
3300 	default:
3301 		zone_perror(rt_to_str(res_type), Z_NO_RESOURCE_TYPE, TRUE);
3302 		long_usage(CMD_REMOVE, TRUE);
3303 		usage(FALSE, HELP_RESOURCES);
3304 		return;
3305 	}
3306 }
3307 
3308 void
3309 remove_func(cmd_t *cmd)
3310 {
3311 	if (zone_is_read_only(CMD_REMOVE))
3312 		return;
3313 
3314 	assert(cmd != NULL);
3315 
3316 	if (global_scope) {
3317 		if (gz_invalid_resource(cmd->cmd_res_type)) {
3318 			zerr(gettext("%s is not a valid resource for the "
3319 			    "global zone."), rt_to_str(cmd->cmd_res_type));
3320 			saw_error = TRUE;
3321 			return;
3322 		}
3323 		remove_resource(cmd);
3324 	} else {
3325 		remove_property(cmd);
3326 	}
3327 }
3328 
3329 static void
3330 clear_property(cmd_t *cmd)
3331 {
3332 	int res_type, prop_type;
3333 
3334 	res_type = resource_scope;
3335 	prop_type = cmd->cmd_res_type;
3336 	if (res_type == RT_UNKNOWN || prop_type == PT_UNKNOWN) {
3337 		long_usage(CMD_CLEAR, TRUE);
3338 		return;
3339 	}
3340 
3341 	if (initialize(TRUE) != Z_OK)
3342 		return;
3343 
3344 	switch (res_type) {
3345 	case RT_FS:
3346 		if (prop_type == PT_RAW) {
3347 			in_progress_fstab.zone_fs_raw[0] = '\0';
3348 			need_to_commit = TRUE;
3349 			return;
3350 		}
3351 		break;
3352 	case RT_DCPU:
3353 		if (prop_type == PT_IMPORTANCE) {
3354 			in_progress_psettab.zone_importance[0] = '\0';
3355 			need_to_commit = TRUE;
3356 			return;
3357 		}
3358 		break;
3359 	case RT_MCAP:
3360 		switch (prop_type) {
3361 		case PT_PHYSICAL:
3362 			in_progress_mcaptab.zone_physmem_cap[0] = '\0';
3363 			need_to_commit = TRUE;
3364 			return;
3365 		case PT_SWAP:
3366 			remove_aliased_rctl(PT_SWAP, ALIAS_MAXSWAP);
3367 			return;
3368 		case PT_LOCKED:
3369 			remove_aliased_rctl(PT_LOCKED, ALIAS_MAXLOCKEDMEM);
3370 			return;
3371 		}
3372 		break;
3373 	default:
3374 		break;
3375 	}
3376 
3377 	zone_perror(pt_to_str(prop_type), Z_CLEAR_DISALLOW, TRUE);
3378 }
3379 
3380 static void
3381 clear_global(cmd_t *cmd)
3382 {
3383 	int err, type;
3384 
3385 	if ((type = cmd->cmd_res_type) == RT_UNKNOWN) {
3386 		long_usage(CMD_CLEAR, TRUE);
3387 		return;
3388 	}
3389 
3390 	if (initialize(TRUE) != Z_OK)
3391 		return;
3392 
3393 	switch (type) {
3394 	case PT_ZONENAME:
3395 		/* FALLTHRU */
3396 	case PT_ZONEPATH:
3397 		/* FALLTHRU */
3398 	case PT_BRAND:
3399 		zone_perror(pt_to_str(type), Z_CLEAR_DISALLOW, TRUE);
3400 		return;
3401 	case PT_AUTOBOOT:
3402 		/* false is default; we'll treat as equivalent to clearing */
3403 		if ((err = zonecfg_set_autoboot(handle, B_FALSE)) != Z_OK)
3404 			z_cmd_rt_perror(CMD_CLEAR, RT_AUTOBOOT, err, TRUE);
3405 		else
3406 			need_to_commit = TRUE;
3407 		return;
3408 	case PT_POOL:
3409 		if ((err = zonecfg_set_pool(handle, NULL)) != Z_OK)
3410 			z_cmd_rt_perror(CMD_CLEAR, RT_POOL, err, TRUE);
3411 		else
3412 			need_to_commit = TRUE;
3413 		return;
3414 	case PT_LIMITPRIV:
3415 		if ((err = zonecfg_set_limitpriv(handle, NULL)) != Z_OK)
3416 			z_cmd_rt_perror(CMD_CLEAR, RT_LIMITPRIV, err, TRUE);
3417 		else
3418 			need_to_commit = TRUE;
3419 		return;
3420 	case PT_BOOTARGS:
3421 		if ((err = zonecfg_set_bootargs(handle, NULL)) != Z_OK)
3422 			z_cmd_rt_perror(CMD_CLEAR, RT_BOOTARGS, err, TRUE);
3423 		else
3424 			need_to_commit = TRUE;
3425 		return;
3426 	case PT_SCHED:
3427 		if ((err = zonecfg_set_sched(handle, NULL)) != Z_OK)
3428 			z_cmd_rt_perror(CMD_CLEAR, RT_SCHED, err, TRUE);
3429 		else
3430 			need_to_commit = TRUE;
3431 		return;
3432 	case PT_IPTYPE:
3433 		/* shared is default; we'll treat as equivalent to clearing */
3434 		if ((err = zonecfg_set_iptype(handle, ZS_SHARED)) != Z_OK)
3435 			z_cmd_rt_perror(CMD_CLEAR, RT_IPTYPE, err, TRUE);
3436 		else
3437 			need_to_commit = TRUE;
3438 		return;
3439 	case PT_MAXLWPS:
3440 		remove_aliased_rctl(PT_MAXLWPS, ALIAS_MAXLWPS);
3441 		return;
3442 	case PT_MAXSHMMEM:
3443 		remove_aliased_rctl(PT_MAXSHMMEM, ALIAS_MAXSHMMEM);
3444 		return;
3445 	case PT_MAXSHMIDS:
3446 		remove_aliased_rctl(PT_MAXSHMIDS, ALIAS_MAXSHMIDS);
3447 		return;
3448 	case PT_MAXMSGIDS:
3449 		remove_aliased_rctl(PT_MAXMSGIDS, ALIAS_MAXMSGIDS);
3450 		return;
3451 	case PT_MAXSEMIDS:
3452 		remove_aliased_rctl(PT_MAXSEMIDS, ALIAS_MAXSEMIDS);
3453 		return;
3454 	case PT_SHARES:
3455 		remove_aliased_rctl(PT_SHARES, ALIAS_SHARES);
3456 		return;
3457 	default:
3458 		zone_perror(pt_to_str(type), Z_NO_PROPERTY_TYPE, TRUE);
3459 		long_usage(CMD_CLEAR, TRUE);
3460 		usage(FALSE, HELP_PROPS);
3461 		return;
3462 	}
3463 }
3464 
3465 void
3466 clear_func(cmd_t *cmd)
3467 {
3468 	if (zone_is_read_only(CMD_CLEAR))
3469 		return;
3470 
3471 	assert(cmd != NULL);
3472 
3473 	if (global_scope) {
3474 		if (gz_invalid_property(cmd->cmd_res_type)) {
3475 			zerr(gettext("%s is not a valid property for the "
3476 			    "global zone."), pt_to_str(cmd->cmd_res_type));
3477 			saw_error = TRUE;
3478 			return;
3479 		}
3480 
3481 		clear_global(cmd);
3482 	} else {
3483 		clear_property(cmd);
3484 	}
3485 }
3486 
3487 void
3488 select_func(cmd_t *cmd)
3489 {
3490 	int type, err, res;
3491 	uint64_t limit;
3492 	uint64_t tmp;
3493 
3494 	if (zone_is_read_only(CMD_SELECT))
3495 		return;
3496 
3497 	assert(cmd != NULL);
3498 
3499 	if (global_scope) {
3500 		global_scope = FALSE;
3501 		resource_scope = cmd->cmd_res_type;
3502 		end_op = CMD_SELECT;
3503 	} else {
3504 		scope_usage(CMD_SELECT);
3505 		return;
3506 	}
3507 
3508 	if ((type = cmd->cmd_res_type) == RT_UNKNOWN) {
3509 		long_usage(CMD_SELECT, TRUE);
3510 		return;
3511 	}
3512 
3513 	if (initialize(TRUE) != Z_OK)
3514 		return;
3515 
3516 	switch (type) {
3517 	case RT_FS:
3518 		if ((err = fill_in_fstab(cmd, &old_fstab, FALSE)) != Z_OK) {
3519 			z_cmd_rt_perror(CMD_SELECT, RT_FS, err, TRUE);
3520 			global_scope = TRUE;
3521 		}
3522 		bcopy(&old_fstab, &in_progress_fstab,
3523 		    sizeof (struct zone_fstab));
3524 		return;
3525 	case RT_IPD:
3526 		if (state_atleast(ZONE_STATE_INCOMPLETE)) {
3527 			zerr(gettext("Zone %s not in %s state; %s %s not "
3528 			    "allowed."), zone,
3529 			    zone_state_str(ZONE_STATE_CONFIGURED),
3530 			    cmd_to_str(CMD_SELECT), rt_to_str(RT_IPD));
3531 			global_scope = TRUE;
3532 			end_op = -1;
3533 			return;
3534 		}
3535 		if ((err = fill_in_ipdtab(cmd, &old_ipdtab, FALSE)) != Z_OK) {
3536 			z_cmd_rt_perror(CMD_SELECT, RT_IPD, err, TRUE);
3537 			global_scope = TRUE;
3538 		}
3539 		bcopy(&old_ipdtab, &in_progress_ipdtab,
3540 		    sizeof (struct zone_fstab));
3541 		return;
3542 	case RT_NET:
3543 		if ((err = fill_in_nwiftab(cmd, &old_nwiftab, FALSE)) != Z_OK) {
3544 			z_cmd_rt_perror(CMD_SELECT, RT_NET, err, TRUE);
3545 			global_scope = TRUE;
3546 		}
3547 		bcopy(&old_nwiftab, &in_progress_nwiftab,
3548 		    sizeof (struct zone_nwiftab));
3549 		return;
3550 	case RT_DEVICE:
3551 		if ((err = fill_in_devtab(cmd, &old_devtab, FALSE)) != Z_OK) {
3552 			z_cmd_rt_perror(CMD_SELECT, RT_DEVICE, err, TRUE);
3553 			global_scope = TRUE;
3554 		}
3555 		bcopy(&old_devtab, &in_progress_devtab,
3556 		    sizeof (struct zone_devtab));
3557 		return;
3558 	case RT_RCTL:
3559 		if ((err = fill_in_rctltab(cmd, &old_rctltab, FALSE)) != Z_OK) {
3560 			z_cmd_rt_perror(CMD_SELECT, RT_RCTL, err, TRUE);
3561 			global_scope = TRUE;
3562 		}
3563 		bcopy(&old_rctltab, &in_progress_rctltab,
3564 		    sizeof (struct zone_rctltab));
3565 		return;
3566 	case RT_ATTR:
3567 		if ((err = fill_in_attrtab(cmd, &old_attrtab, FALSE)) != Z_OK) {
3568 			z_cmd_rt_perror(CMD_SELECT, RT_ATTR, err, TRUE);
3569 			global_scope = TRUE;
3570 		}
3571 		bcopy(&old_attrtab, &in_progress_attrtab,
3572 		    sizeof (struct zone_attrtab));
3573 		return;
3574 	case RT_DATASET:
3575 		if ((err = fill_in_dstab(cmd, &old_dstab, FALSE)) != Z_OK) {
3576 			z_cmd_rt_perror(CMD_SELECT, RT_DATASET, err, TRUE);
3577 			global_scope = TRUE;
3578 		}
3579 		bcopy(&old_dstab, &in_progress_dstab,
3580 		    sizeof (struct zone_dstab));
3581 		return;
3582 	case RT_DCPU:
3583 		if ((err = zonecfg_lookup_pset(handle, &old_psettab)) != Z_OK) {
3584 			z_cmd_rt_perror(CMD_SELECT, RT_DCPU, err, TRUE);
3585 			global_scope = TRUE;
3586 		}
3587 		bcopy(&old_psettab, &in_progress_psettab,
3588 		    sizeof (struct zone_psettab));
3589 		return;
3590 	case RT_PCAP:
3591 		if ((err = zonecfg_get_aliased_rctl(handle, ALIAS_CPUCAP, &tmp))
3592 		    != Z_OK) {
3593 			z_cmd_rt_perror(CMD_SELECT, RT_PCAP, err, TRUE);
3594 			global_scope = TRUE;
3595 		}
3596 		return;
3597 	case RT_MCAP:
3598 		/* if none of these exist, there is no resource to select */
3599 		if ((res = zonecfg_lookup_mcap(handle, &old_mcaptab)) != Z_OK &&
3600 		    zonecfg_get_aliased_rctl(handle, ALIAS_MAXSWAP, &limit)
3601 		    != Z_OK &&
3602 		    zonecfg_get_aliased_rctl(handle, ALIAS_MAXLOCKEDMEM, &limit)
3603 		    != Z_OK) {
3604 			z_cmd_rt_perror(CMD_SELECT, RT_MCAP, Z_NO_RESOURCE_TYPE,
3605 			    TRUE);
3606 			global_scope = TRUE;
3607 		}
3608 		if (res == Z_OK)
3609 			bcopy(&old_mcaptab, &in_progress_mcaptab,
3610 			    sizeof (struct zone_mcaptab));
3611 		else
3612 			bzero(&in_progress_mcaptab,
3613 			    sizeof (in_progress_mcaptab));
3614 		return;
3615 	default:
3616 		zone_perror(rt_to_str(type), Z_NO_RESOURCE_TYPE, TRUE);
3617 		long_usage(CMD_SELECT, TRUE);
3618 		usage(FALSE, HELP_RESOURCES);
3619 		return;
3620 	}
3621 }
3622 
3623 /*
3624  * Network "addresses" can be one of the following forms:
3625  *	<IPv4 address>
3626  *	<IPv4 address>/<prefix length>
3627  *	<IPv6 address>/<prefix length>
3628  *	<host name>
3629  *	<host name>/<prefix length>
3630  * In other words, the "/" followed by a prefix length is allowed but not
3631  * required for IPv4 addresses and host names, and required for IPv6 addresses.
3632  * If a prefix length is given, it must be in the allowable range: 0 to 32 for
3633  * IPv4 addresses and host names, 0 to 128 for IPv6 addresses.
3634  * Host names must start with an alpha-numeric character, and all subsequent
3635  * characters must be either alpha-numeric or "-".
3636  */
3637 
3638 static int
3639 validate_net_address_syntax(char *address)
3640 {
3641 	char *slashp, part1[MAXHOSTNAMELEN];
3642 	struct in6_addr in6;
3643 	struct in_addr in4;
3644 	int prefixlen, i;
3645 
3646 	/*
3647 	 * Copy the part before any '/' into part1 or copy the whole
3648 	 * thing if there is no '/'.
3649 	 */
3650 	if ((slashp = strchr(address, '/')) != NULL) {
3651 		*slashp = '\0';
3652 		(void) strlcpy(part1, address, sizeof (part1));
3653 		*slashp = '/';
3654 		prefixlen = atoi(++slashp);
3655 	} else {
3656 		(void) strlcpy(part1, address, sizeof (part1));
3657 	}
3658 
3659 	if (inet_pton(AF_INET6, part1, &in6) == 1) {
3660 		if (slashp == NULL) {
3661 			zerr(gettext("%s: IPv6 addresses "
3662 			    "require /prefix-length suffix."), address);
3663 			return (Z_ERR);
3664 		}
3665 		if (prefixlen < 0 || prefixlen > 128) {
3666 			zerr(gettext("%s: IPv6 address "
3667 			    "prefix lengths must be 0 - 128."), address);
3668 			return (Z_ERR);
3669 		}
3670 		return (Z_OK);
3671 	}
3672 
3673 	/* At this point, any /prefix must be for IPv4. */
3674 	if (slashp != NULL) {
3675 		if (prefixlen < 0 || prefixlen > 32) {
3676 			zerr(gettext("%s: IPv4 address "
3677 			    "prefix lengths must be 0 - 32."), address);
3678 			return (Z_ERR);
3679 		}
3680 	}
3681 	if (inet_pton(AF_INET, part1, &in4) == 1)
3682 		return (Z_OK);
3683 
3684 	/* address may also be a host name */
3685 	if (!isalnum(part1[0])) {
3686 		zerr(gettext("%s: bogus host name or network address syntax"),
3687 		    part1);
3688 		saw_error = TRUE;
3689 		usage(FALSE, HELP_NETADDR);
3690 		return (Z_ERR);
3691 	}
3692 	for (i = 1; part1[i]; i++)
3693 		if (!isalnum(part1[i]) && part1[i] != '-' && part1[i] != '.') {
3694 			zerr(gettext("%s: bogus host name or "
3695 			    "network address syntax"), part1);
3696 			saw_error = TRUE;
3697 			usage(FALSE, HELP_NETADDR);
3698 			return (Z_ERR);
3699 		}
3700 	return (Z_OK);
3701 }
3702 
3703 static int
3704 validate_net_physical_syntax(char *ifname)
3705 {
3706 	if (strchr(ifname, ':') == NULL)
3707 		return (Z_OK);
3708 	zerr(gettext("%s: physical interface name required; "
3709 	    "logical interface name not allowed"), ifname);
3710 	return (Z_ERR);
3711 }
3712 
3713 static boolean_t
3714 valid_fs_type(const char *type)
3715 {
3716 	/*
3717 	 * Is this a valid path component?
3718 	 */
3719 	if (strlen(type) + 1 > MAXNAMELEN)
3720 		return (B_FALSE);
3721 	/*
3722 	 * Make sure a bad value for "type" doesn't make
3723 	 * /usr/lib/fs/<type>/mount turn into something else.
3724 	 */
3725 	if (strchr(type, '/') != NULL || type[0] == '\0' ||
3726 	    strcmp(type, ".") == 0 || strcmp(type, "..") == 0)
3727 		return (B_FALSE);
3728 	/*
3729 	 * More detailed verification happens later by zoneadm(1m).
3730 	 */
3731 	return (B_TRUE);
3732 }
3733 
3734 static boolean_t
3735 allow_exclusive()
3736 {
3737 	brand_handle_t	bh;
3738 	char		brand[MAXNAMELEN];
3739 	boolean_t	ret;
3740 
3741 	if (zonecfg_get_brand(handle, brand, sizeof (brand)) != Z_OK) {
3742 		zerr("%s: %s\n", zone, gettext("could not get zone brand"));
3743 		return (B_FALSE);
3744 	}
3745 	if ((bh = brand_open(brand)) == NULL) {
3746 		zerr("%s: %s\n", zone, gettext("unknown brand."));
3747 		return (B_FALSE);
3748 	}
3749 	ret = brand_allow_exclusive_ip(bh);
3750 	brand_close(bh);
3751 	if (!ret)
3752 		zerr(gettext("%s cannot be '%s' when %s is '%s'."),
3753 		    pt_to_str(PT_IPTYPE), "exclusive",
3754 		    pt_to_str(PT_BRAND), brand);
3755 	return (ret);
3756 }
3757 
3758 static void
3759 set_aliased_rctl(char *alias, int prop_type, char *s)
3760 {
3761 	uint64_t limit;
3762 	int err;
3763 	char tmp[128];
3764 
3765 	if (global_zone && strcmp(alias, ALIAS_SHARES) != 0)
3766 		zerr(gettext("WARNING: Setting a global zone resource "
3767 		    "control too low could deny\nservice "
3768 		    "to even the root user; "
3769 		    "this could render the system impossible\n"
3770 		    "to administer.  Please use caution."));
3771 
3772 	/* convert memory based properties */
3773 	if (prop_type == PT_MAXSHMMEM) {
3774 		if (!zonecfg_valid_memlimit(s, &limit)) {
3775 			zerr(gettext("A non-negative number with a required "
3776 			    "scale suffix (K, M, G or T) was expected\nhere."));
3777 			saw_error = TRUE;
3778 			return;
3779 		}
3780 
3781 		(void) snprintf(tmp, sizeof (tmp), "%llu", limit);
3782 		s = tmp;
3783 	}
3784 
3785 	if (!zonecfg_aliased_rctl_ok(handle, alias)) {
3786 		zone_perror(pt_to_str(prop_type), Z_ALIAS_DISALLOW, FALSE);
3787 		saw_error = TRUE;
3788 	} else if (!zonecfg_valid_alias_limit(alias, s, &limit)) {
3789 		zerr(gettext("%s property is out of range."),
3790 		    pt_to_str(prop_type));
3791 		saw_error = TRUE;
3792 	} else if ((err = zonecfg_set_aliased_rctl(handle, alias, limit))
3793 	    != Z_OK) {
3794 		zone_perror(zone, err, TRUE);
3795 		saw_error = TRUE;
3796 	} else {
3797 		need_to_commit = TRUE;
3798 	}
3799 }
3800 
3801 void
3802 set_func(cmd_t *cmd)
3803 {
3804 	char *prop_id;
3805 	int arg, err, res_type, prop_type;
3806 	property_value_ptr_t pp;
3807 	boolean_t autoboot;
3808 	zone_iptype_t iptype;
3809 	boolean_t force_set = FALSE;
3810 	size_t physmem_size = sizeof (in_progress_mcaptab.zone_physmem_cap);
3811 	uint64_t mem_cap, mem_limit;
3812 	float cap;
3813 	char *unitp;
3814 	struct zone_psettab tmp_psettab;
3815 	bool arg_err = FALSE;
3816 
3817 	if (zone_is_read_only(CMD_SET))
3818 		return;
3819 
3820 	assert(cmd != NULL);
3821 
3822 	optind = opterr = 0;
3823 	while ((arg = getopt(cmd->cmd_argc, cmd->cmd_argv, "F")) != EOF) {
3824 		switch (arg) {
3825 		case 'F':
3826 			force_set = TRUE;
3827 			break;
3828 		default:
3829 			if (optopt == '?')
3830 				longer_usage(CMD_SET);
3831 			else
3832 				short_usage(CMD_SET);
3833 			arg_err = TRUE;
3834 			break;
3835 		}
3836 	}
3837 	if (arg_err)
3838 		return;
3839 
3840 	prop_type = cmd->cmd_prop_name[0];
3841 	if (global_scope) {
3842 		if (gz_invalid_property(prop_type)) {
3843 			zerr(gettext("%s is not a valid property for the "
3844 			    "global zone."), pt_to_str(prop_type));
3845 			saw_error = TRUE;
3846 			return;
3847 		}
3848 
3849 		if (prop_type == PT_ZONENAME) {
3850 			res_type = RT_ZONENAME;
3851 		} else if (prop_type == PT_ZONEPATH) {
3852 			res_type = RT_ZONEPATH;
3853 		} else if (prop_type == PT_AUTOBOOT) {
3854 			res_type = RT_AUTOBOOT;
3855 		} else if (prop_type == PT_BRAND) {
3856 			res_type = RT_BRAND;
3857 		} else if (prop_type == PT_POOL) {
3858 			res_type = RT_POOL;
3859 		} else if (prop_type == PT_LIMITPRIV) {
3860 			res_type = RT_LIMITPRIV;
3861 		} else if (prop_type == PT_BOOTARGS) {
3862 			res_type = RT_BOOTARGS;
3863 		} else if (prop_type == PT_SCHED) {
3864 			res_type = RT_SCHED;
3865 		} else if (prop_type == PT_IPTYPE) {
3866 			res_type = RT_IPTYPE;
3867 		} else if (prop_type == PT_MAXLWPS) {
3868 			res_type = RT_MAXLWPS;
3869 		} else if (prop_type == PT_MAXSHMMEM) {
3870 			res_type = RT_MAXSHMMEM;
3871 		} else if (prop_type == PT_MAXSHMIDS) {
3872 			res_type = RT_MAXSHMIDS;
3873 		} else if (prop_type == PT_MAXMSGIDS) {
3874 			res_type = RT_MAXMSGIDS;
3875 		} else if (prop_type == PT_MAXSEMIDS) {
3876 			res_type = RT_MAXSEMIDS;
3877 		} else if (prop_type == PT_SHARES) {
3878 			res_type = RT_SHARES;
3879 		} else {
3880 			zerr(gettext("Cannot set a resource-specific property "
3881 			    "from the global scope."));
3882 			saw_error = TRUE;
3883 			return;
3884 		}
3885 	} else {
3886 		res_type = resource_scope;
3887 	}
3888 
3889 	if (force_set) {
3890 		if (res_type != RT_ZONEPATH) {
3891 			zerr(gettext("Only zonepath setting can be forced."));
3892 			saw_error = TRUE;
3893 			return;
3894 		}
3895 		if (!zonecfg_in_alt_root()) {
3896 			zerr(gettext("Zonepath is changeable only in an "
3897 			    "alternate root."));
3898 			saw_error = TRUE;
3899 			return;
3900 		}
3901 	}
3902 
3903 	pp = cmd->cmd_property_ptr[0];
3904 	/*
3905 	 * A nasty expression but not that complicated:
3906 	 * 1. fs options are simple or list (tested below)
3907 	 * 2. rctl value's are complex or list (tested below)
3908 	 * Anything else should be simple.
3909 	 */
3910 	if (!(res_type == RT_FS && prop_type == PT_OPTIONS) &&
3911 	    !(res_type == RT_RCTL && prop_type == PT_VALUE) &&
3912 	    (pp->pv_type != PROP_VAL_SIMPLE ||
3913 	    (prop_id = pp->pv_simple) == NULL)) {
3914 		zerr(gettext("A %s value was expected here."),
3915 		    pvt_to_str(PROP_VAL_SIMPLE));
3916 		saw_error = TRUE;
3917 		return;
3918 	}
3919 	if (prop_type == PT_UNKNOWN) {
3920 		long_usage(CMD_SET, TRUE);
3921 		return;
3922 	}
3923 
3924 	/*
3925 	 * Special case: the user can change the zone name prior to 'create';
3926 	 * if the zone already exists, we fall through letting initialize()
3927 	 * and the rest of the logic run.
3928 	 */
3929 	if (res_type == RT_ZONENAME && got_handle == FALSE &&
3930 	    !state_atleast(ZONE_STATE_CONFIGURED)) {
3931 		if ((err = zonecfg_validate_zonename(prop_id)) != Z_OK) {
3932 			zone_perror(prop_id, err, TRUE);
3933 			usage(FALSE, HELP_SYNTAX);
3934 			return;
3935 		}
3936 		(void) strlcpy(zone, prop_id, sizeof (zone));
3937 		return;
3938 	}
3939 
3940 	if (initialize(TRUE) != Z_OK)
3941 		return;
3942 
3943 	switch (res_type) {
3944 	case RT_ZONENAME:
3945 		if ((err = zonecfg_set_name(handle, prop_id)) != Z_OK) {
3946 			/*
3947 			 * Use prop_id instead of 'zone' here, since we're
3948 			 * reporting a problem about the *new* zonename.
3949 			 */
3950 			zone_perror(prop_id, err, TRUE);
3951 			usage(FALSE, HELP_SYNTAX);
3952 		} else {
3953 			need_to_commit = TRUE;
3954 			(void) strlcpy(zone, prop_id, sizeof (zone));
3955 		}
3956 		return;
3957 	case RT_ZONEPATH:
3958 		if (!force_set && state_atleast(ZONE_STATE_INSTALLED)) {
3959 			zerr(gettext("Zone %s already installed; %s %s not "
3960 			    "allowed."), zone, cmd_to_str(CMD_SET),
3961 			    rt_to_str(RT_ZONEPATH));
3962 			return;
3963 		}
3964 		if (validate_zonepath_syntax(prop_id) != Z_OK) {
3965 			saw_error = TRUE;
3966 			return;
3967 		}
3968 		if ((err = zonecfg_set_zonepath(handle, prop_id)) != Z_OK)
3969 			zone_perror(zone, err, TRUE);
3970 		else
3971 			need_to_commit = TRUE;
3972 		return;
3973 	case RT_BRAND:
3974 		if (state_atleast(ZONE_STATE_INSTALLED)) {
3975 			zerr(gettext("Zone %s already installed; %s %s not "
3976 			    "allowed."), zone, cmd_to_str(CMD_SET),
3977 			    rt_to_str(RT_BRAND));
3978 			return;
3979 		}
3980 		if ((err = zonecfg_set_brand(handle, prop_id)) != Z_OK)
3981 			zone_perror(zone, err, TRUE);
3982 		else
3983 			need_to_commit = TRUE;
3984 		return;
3985 	case RT_AUTOBOOT:
3986 		if (strcmp(prop_id, "true") == 0) {
3987 			autoboot = B_TRUE;
3988 		} else if (strcmp(prop_id, "false") == 0) {
3989 			autoboot = B_FALSE;
3990 		} else {
3991 			zerr(gettext("%s value must be '%s' or '%s'."),
3992 			    pt_to_str(PT_AUTOBOOT), "true", "false");
3993 			saw_error = TRUE;
3994 			return;
3995 		}
3996 		if ((err = zonecfg_set_autoboot(handle, autoboot)) != Z_OK)
3997 			zone_perror(zone, err, TRUE);
3998 		else
3999 			need_to_commit = TRUE;
4000 		return;
4001 	case RT_POOL:
4002 		/* don't allow use of the reserved temporary pool names */
4003 		if (strncmp("SUNW", prop_id, 4) == 0) {
4004 			zerr(gettext("pool names starting with SUNW are "
4005 			    "reserved."));
4006 			saw_error = TRUE;
4007 			return;
4008 		}
4009 
4010 		/* can't set pool if dedicated-cpu exists */
4011 		if (zonecfg_lookup_pset(handle, &tmp_psettab) == Z_OK) {
4012 			zerr(gettext("The %s resource already exists.  "
4013 			    "A persistent pool is incompatible\nwith the %s "
4014 			    "resource."), rt_to_str(RT_DCPU),
4015 			    rt_to_str(RT_DCPU));
4016 			saw_error = TRUE;
4017 			return;
4018 		}
4019 
4020 		if ((err = zonecfg_set_pool(handle, prop_id)) != Z_OK)
4021 			zone_perror(zone, err, TRUE);
4022 		else
4023 			need_to_commit = TRUE;
4024 		return;
4025 	case RT_LIMITPRIV:
4026 		if ((err = zonecfg_set_limitpriv(handle, prop_id)) != Z_OK)
4027 			zone_perror(zone, err, TRUE);
4028 		else
4029 			need_to_commit = TRUE;
4030 		return;
4031 	case RT_BOOTARGS:
4032 		if ((err = zonecfg_set_bootargs(handle, prop_id)) != Z_OK)
4033 			zone_perror(zone, err, TRUE);
4034 		else
4035 			need_to_commit = TRUE;
4036 		return;
4037 	case RT_SCHED:
4038 		if ((err = zonecfg_set_sched(handle, prop_id)) != Z_OK)
4039 			zone_perror(zone, err, TRUE);
4040 		else
4041 			need_to_commit = TRUE;
4042 		return;
4043 	case RT_IPTYPE:
4044 		if (strcmp(prop_id, "shared") == 0) {
4045 			iptype = ZS_SHARED;
4046 		} else if (strcmp(prop_id, "exclusive") == 0) {
4047 			iptype = ZS_EXCLUSIVE;
4048 		} else {
4049 			zerr(gettext("%s value must be '%s' or '%s'."),
4050 			    pt_to_str(PT_IPTYPE), "shared", "exclusive");
4051 			saw_error = TRUE;
4052 			return;
4053 		}
4054 		if (iptype == ZS_EXCLUSIVE && !allow_exclusive()) {
4055 			saw_error = TRUE;
4056 			return;
4057 		}
4058 		if ((err = zonecfg_set_iptype(handle, iptype)) != Z_OK)
4059 			zone_perror(zone, err, TRUE);
4060 		else
4061 			need_to_commit = TRUE;
4062 		return;
4063 	case RT_MAXLWPS:
4064 		set_aliased_rctl(ALIAS_MAXLWPS, prop_type, prop_id);
4065 		return;
4066 	case RT_MAXSHMMEM:
4067 		set_aliased_rctl(ALIAS_MAXSHMMEM, prop_type, prop_id);
4068 		return;
4069 	case RT_MAXSHMIDS:
4070 		set_aliased_rctl(ALIAS_MAXSHMIDS, prop_type, prop_id);
4071 		return;
4072 	case RT_MAXMSGIDS:
4073 		set_aliased_rctl(ALIAS_MAXMSGIDS, prop_type, prop_id);
4074 		return;
4075 	case RT_MAXSEMIDS:
4076 		set_aliased_rctl(ALIAS_MAXSEMIDS, prop_type, prop_id);
4077 		return;
4078 	case RT_SHARES:
4079 		set_aliased_rctl(ALIAS_SHARES, prop_type, prop_id);
4080 		return;
4081 	case RT_FS:
4082 		switch (prop_type) {
4083 		case PT_DIR:
4084 			(void) strlcpy(in_progress_fstab.zone_fs_dir, prop_id,
4085 			    sizeof (in_progress_fstab.zone_fs_dir));
4086 			return;
4087 		case PT_SPECIAL:
4088 			(void) strlcpy(in_progress_fstab.zone_fs_special,
4089 			    prop_id,
4090 			    sizeof (in_progress_fstab.zone_fs_special));
4091 			return;
4092 		case PT_RAW:
4093 			(void) strlcpy(in_progress_fstab.zone_fs_raw,
4094 			    prop_id, sizeof (in_progress_fstab.zone_fs_raw));
4095 			return;
4096 		case PT_TYPE:
4097 			if (!valid_fs_type(prop_id)) {
4098 				zerr(gettext("\"%s\" is not a valid %s."),
4099 				    prop_id, pt_to_str(PT_TYPE));
4100 				saw_error = TRUE;
4101 				return;
4102 			}
4103 			(void) strlcpy(in_progress_fstab.zone_fs_type, prop_id,
4104 			    sizeof (in_progress_fstab.zone_fs_type));
4105 			return;
4106 		case PT_OPTIONS:
4107 			if (pp->pv_type != PROP_VAL_SIMPLE &&
4108 			    pp->pv_type != PROP_VAL_LIST) {
4109 				zerr(gettext("A %s or %s value was expected "
4110 				    "here."), pvt_to_str(PROP_VAL_SIMPLE),
4111 				    pvt_to_str(PROP_VAL_LIST));
4112 				saw_error = TRUE;
4113 				return;
4114 			}
4115 			zonecfg_free_fs_option_list(
4116 			    in_progress_fstab.zone_fs_options);
4117 			in_progress_fstab.zone_fs_options = NULL;
4118 			if (!(pp->pv_type == PROP_VAL_LIST &&
4119 			    pp->pv_list == NULL))
4120 				add_property(cmd);
4121 			return;
4122 		default:
4123 			break;
4124 		}
4125 		zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE, TRUE);
4126 		long_usage(CMD_SET, TRUE);
4127 		usage(FALSE, HELP_PROPS);
4128 		return;
4129 	case RT_IPD:
4130 		switch (prop_type) {
4131 		case PT_DIR:
4132 			(void) strlcpy(in_progress_ipdtab.zone_fs_dir, prop_id,
4133 			    sizeof (in_progress_ipdtab.zone_fs_dir));
4134 			return;
4135 		default:
4136 			break;
4137 		}
4138 		zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE, TRUE);
4139 		long_usage(CMD_SET, TRUE);
4140 		usage(FALSE, HELP_PROPS);
4141 		return;
4142 	case RT_NET:
4143 		switch (prop_type) {
4144 		case PT_ADDRESS:
4145 			if (validate_net_address_syntax(prop_id) != Z_OK) {
4146 				saw_error = TRUE;
4147 				return;
4148 			}
4149 			(void) strlcpy(in_progress_nwiftab.zone_nwif_address,
4150 			    prop_id,
4151 			    sizeof (in_progress_nwiftab.zone_nwif_address));
4152 			break;
4153 		case PT_PHYSICAL:
4154 			if (validate_net_physical_syntax(prop_id) != Z_OK) {
4155 				saw_error = TRUE;
4156 				return;
4157 			}
4158 			(void) strlcpy(in_progress_nwiftab.zone_nwif_physical,
4159 			    prop_id,
4160 			    sizeof (in_progress_nwiftab.zone_nwif_physical));
4161 			break;
4162 		default:
4163 			zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE,
4164 			    TRUE);
4165 			long_usage(CMD_SET, TRUE);
4166 			usage(FALSE, HELP_PROPS);
4167 			return;
4168 		}
4169 		return;
4170 	case RT_DEVICE:
4171 		switch (prop_type) {
4172 		case PT_MATCH:
4173 			(void) strlcpy(in_progress_devtab.zone_dev_match,
4174 			    prop_id,
4175 			    sizeof (in_progress_devtab.zone_dev_match));
4176 			break;
4177 		default:
4178 			zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE,
4179 			    TRUE);
4180 			long_usage(CMD_SET, TRUE);
4181 			usage(FALSE, HELP_PROPS);
4182 			return;
4183 		}
4184 		return;
4185 	case RT_RCTL:
4186 		switch (prop_type) {
4187 		case PT_NAME:
4188 			if (!zonecfg_valid_rctlname(prop_id)) {
4189 				zerr(gettext("'%s' is not a valid zone %s "
4190 				    "name."), prop_id, rt_to_str(RT_RCTL));
4191 				return;
4192 			}
4193 			(void) strlcpy(in_progress_rctltab.zone_rctl_name,
4194 			    prop_id,
4195 			    sizeof (in_progress_rctltab.zone_rctl_name));
4196 			break;
4197 		case PT_VALUE:
4198 			if (pp->pv_type != PROP_VAL_COMPLEX &&
4199 			    pp->pv_type != PROP_VAL_LIST) {
4200 				zerr(gettext("A %s or %s value was expected "
4201 				    "here."), pvt_to_str(PROP_VAL_COMPLEX),
4202 				    pvt_to_str(PROP_VAL_LIST));
4203 				saw_error = TRUE;
4204 				return;
4205 			}
4206 			zonecfg_free_rctl_value_list(
4207 			    in_progress_rctltab.zone_rctl_valptr);
4208 			in_progress_rctltab.zone_rctl_valptr = NULL;
4209 			if (!(pp->pv_type == PROP_VAL_LIST &&
4210 			    pp->pv_list == NULL))
4211 				add_property(cmd);
4212 			break;
4213 		default:
4214 			zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE,
4215 			    TRUE);
4216 			long_usage(CMD_SET, TRUE);
4217 			usage(FALSE, HELP_PROPS);
4218 			return;
4219 		}
4220 		return;
4221 	case RT_ATTR:
4222 		switch (prop_type) {
4223 		case PT_NAME:
4224 			(void) strlcpy(in_progress_attrtab.zone_attr_name,
4225 			    prop_id,
4226 			    sizeof (in_progress_attrtab.zone_attr_name));
4227 			break;
4228 		case PT_TYPE:
4229 			(void) strlcpy(in_progress_attrtab.zone_attr_type,
4230 			    prop_id,
4231 			    sizeof (in_progress_attrtab.zone_attr_type));
4232 			break;
4233 		case PT_VALUE:
4234 			(void) strlcpy(in_progress_attrtab.zone_attr_value,
4235 			    prop_id,
4236 			    sizeof (in_progress_attrtab.zone_attr_value));
4237 			break;
4238 		default:
4239 			zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE,
4240 			    TRUE);
4241 			long_usage(CMD_SET, TRUE);
4242 			usage(FALSE, HELP_PROPS);
4243 			return;
4244 		}
4245 		return;
4246 	case RT_DATASET:
4247 		switch (prop_type) {
4248 		case PT_NAME:
4249 			(void) strlcpy(in_progress_dstab.zone_dataset_name,
4250 			    prop_id,
4251 			    sizeof (in_progress_dstab.zone_dataset_name));
4252 			return;
4253 		default:
4254 			break;
4255 		}
4256 		zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE, TRUE);
4257 		long_usage(CMD_SET, TRUE);
4258 		usage(FALSE, HELP_PROPS);
4259 		return;
4260 	case RT_DCPU:
4261 		switch (prop_type) {
4262 		char *lowp, *highp;
4263 
4264 		case PT_NCPUS:
4265 			lowp = prop_id;
4266 			if ((highp = strchr(prop_id, '-')) != NULL)
4267 				*highp++ = '\0';
4268 			else
4269 				highp = lowp;
4270 
4271 			/* Make sure the input makes sense. */
4272 			if (!zonecfg_valid_ncpus(lowp, highp)) {
4273 				zerr(gettext("%s property is out of range."),
4274 				    pt_to_str(PT_NCPUS));
4275 				saw_error = TRUE;
4276 				return;
4277 			}
4278 
4279 			(void) strlcpy(
4280 			    in_progress_psettab.zone_ncpu_min, lowp,
4281 			    sizeof (in_progress_psettab.zone_ncpu_min));
4282 			(void) strlcpy(
4283 			    in_progress_psettab.zone_ncpu_max, highp,
4284 			    sizeof (in_progress_psettab.zone_ncpu_max));
4285 			return;
4286 		case PT_IMPORTANCE:
4287 			/* Make sure the value makes sense. */
4288 			if (!zonecfg_valid_importance(prop_id)) {
4289 				zerr(gettext("%s property is out of range."),
4290 				    pt_to_str(PT_IMPORTANCE));
4291 				saw_error = TRUE;
4292 				return;
4293 			}
4294 
4295 			(void) strlcpy(in_progress_psettab.zone_importance,
4296 			    prop_id,
4297 			    sizeof (in_progress_psettab.zone_importance));
4298 			return;
4299 		default:
4300 			break;
4301 		}
4302 		zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE, TRUE);
4303 		long_usage(CMD_SET, TRUE);
4304 		usage(FALSE, HELP_PROPS);
4305 		return;
4306 	case RT_PCAP:
4307 		if (prop_type != PT_NCPUS) {
4308 			zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE,
4309 			    TRUE);
4310 			long_usage(CMD_SET, TRUE);
4311 			usage(FALSE, HELP_PROPS);
4312 			return;
4313 		}
4314 
4315 		/*
4316 		 * We already checked that an rctl alias is allowed in
4317 		 * the add_resource() function.
4318 		 */
4319 
4320 		if ((cap = strtof(prop_id, &unitp)) <= 0 || *unitp != '\0' ||
4321 		    (int)(cap * 100) < 1) {
4322 			zerr(gettext("%s property is out of range."),
4323 			    pt_to_str(PT_NCPUS));
4324 			saw_error = TRUE;
4325 			return;
4326 		}
4327 
4328 		if ((err = zonecfg_set_aliased_rctl(handle, ALIAS_CPUCAP,
4329 		    (int)(cap * 100))) != Z_OK)
4330 			zone_perror(zone, err, TRUE);
4331 		else
4332 			need_to_commit = TRUE;
4333 		return;
4334 	case RT_MCAP:
4335 		switch (prop_type) {
4336 		case PT_PHYSICAL:
4337 			if (!zonecfg_valid_memlimit(prop_id, &mem_cap)) {
4338 				zerr(gettext("A positive number with a "
4339 				    "required scale suffix (K, M, G or T) was "
4340 				    "expected here."));
4341 				saw_error = TRUE;
4342 			} else if (mem_cap < ONE_MB) {
4343 				zerr(gettext("%s value is too small.  It must "
4344 				    "be at least 1M."), pt_to_str(PT_PHYSICAL));
4345 				saw_error = TRUE;
4346 			} else {
4347 				snprintf(in_progress_mcaptab.zone_physmem_cap,
4348 				    physmem_size, "%llu", mem_cap);
4349 			}
4350 			break;
4351 		case PT_SWAP:
4352 			/*
4353 			 * We have to check if an rctl is allowed here since
4354 			 * there might already be a rctl defined that blocks
4355 			 * the alias.
4356 			 */
4357 			if (!zonecfg_aliased_rctl_ok(handle, ALIAS_MAXSWAP)) {
4358 				zone_perror(pt_to_str(PT_MAXSWAP),
4359 				    Z_ALIAS_DISALLOW, FALSE);
4360 				saw_error = TRUE;
4361 				return;
4362 			}
4363 
4364 			if (global_zone)
4365 				mem_limit = ONE_MB * 100;
4366 			else
4367 				mem_limit = ONE_MB * 50;
4368 
4369 			if (!zonecfg_valid_memlimit(prop_id, &mem_cap)) {
4370 				zerr(gettext("A positive number with a "
4371 				    "required scale suffix (K, M, G or T) was "
4372 				    "expected here."));
4373 				saw_error = TRUE;
4374 			} else if (mem_cap < mem_limit) {
4375 				char buf[128];
4376 
4377 				(void) snprintf(buf, sizeof (buf), "%llu",
4378 				    mem_limit);
4379 				bytes_to_units(buf, buf, sizeof (buf));
4380 				zerr(gettext("%s value is too small.  It must "
4381 				    "be at least %s."), pt_to_str(PT_SWAP),
4382 				    buf);
4383 				saw_error = TRUE;
4384 			} else {
4385 				if ((err = zonecfg_set_aliased_rctl(handle,
4386 				    ALIAS_MAXSWAP, mem_cap)) != Z_OK)
4387 					zone_perror(zone, err, TRUE);
4388 				else
4389 					need_to_commit = TRUE;
4390 			}
4391 			break;
4392 		case PT_LOCKED:
4393 			/*
4394 			 * We have to check if an rctl is allowed here since
4395 			 * there might already be a rctl defined that blocks
4396 			 * the alias.
4397 			 */
4398 			if (!zonecfg_aliased_rctl_ok(handle,
4399 			    ALIAS_MAXLOCKEDMEM)) {
4400 				zone_perror(pt_to_str(PT_LOCKED),
4401 				    Z_ALIAS_DISALLOW, FALSE);
4402 				saw_error = TRUE;
4403 				return;
4404 			}
4405 
4406 			if (!zonecfg_valid_memlimit(prop_id, &mem_cap)) {
4407 				zerr(gettext("A non-negative number with a "
4408 				    "required scale suffix (K, M, G or T) was "
4409 				    "expected\nhere."));
4410 				saw_error = TRUE;
4411 			} else {
4412 				if ((err = zonecfg_set_aliased_rctl(handle,
4413 				    ALIAS_MAXLOCKEDMEM, mem_cap)) != Z_OK)
4414 					zone_perror(zone, err, TRUE);
4415 				else
4416 					need_to_commit = TRUE;
4417 			}
4418 			break;
4419 		default:
4420 			zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE,
4421 			    TRUE);
4422 			long_usage(CMD_SET, TRUE);
4423 			usage(FALSE, HELP_PROPS);
4424 			return;
4425 		}
4426 
4427 		return;
4428 	default:
4429 		zone_perror(rt_to_str(res_type), Z_NO_RESOURCE_TYPE, TRUE);
4430 		long_usage(CMD_SET, TRUE);
4431 		usage(FALSE, HELP_RESOURCES);
4432 		return;
4433 	}
4434 }
4435 
4436 static void
4437 output_prop(FILE *fp, int pnum, char *pval, bool print_notspec)
4438 {
4439 	char *qstr;
4440 
4441 	if (*pval != '\0') {
4442 		qstr = quoteit(pval);
4443 		if (pnum == PT_SWAP || pnum == PT_LOCKED)
4444 			(void) fprintf(fp, "\t[%s: %s]\n", pt_to_str(pnum),
4445 			    qstr);
4446 		else
4447 			(void) fprintf(fp, "\t%s: %s\n", pt_to_str(pnum), qstr);
4448 		free(qstr);
4449 	} else if (print_notspec)
4450 		(void) fprintf(fp, gettext("\t%s not specified\n"),
4451 		    pt_to_str(pnum));
4452 }
4453 
4454 static void
4455 info_zonename(zone_dochandle_t handle, FILE *fp)
4456 {
4457 	char zonename[ZONENAME_MAX];
4458 
4459 	if (zonecfg_get_name(handle, zonename, sizeof (zonename)) == Z_OK)
4460 		(void) fprintf(fp, "%s: %s\n", pt_to_str(PT_ZONENAME),
4461 		    zonename);
4462 	else
4463 		(void) fprintf(fp, gettext("%s not specified\n"),
4464 		    pt_to_str(PT_ZONENAME));
4465 }
4466 
4467 static void
4468 info_zonepath(zone_dochandle_t handle, FILE *fp)
4469 {
4470 	char zonepath[MAXPATHLEN];
4471 
4472 	if (zonecfg_get_zonepath(handle, zonepath, sizeof (zonepath)) == Z_OK)
4473 		(void) fprintf(fp, "%s: %s\n", pt_to_str(PT_ZONEPATH),
4474 		    zonepath);
4475 	else {
4476 		(void) fprintf(fp, gettext("%s not specified\n"),
4477 		    pt_to_str(PT_ZONEPATH));
4478 	}
4479 }
4480 
4481 static void
4482 info_brand(zone_dochandle_t handle, FILE *fp)
4483 {
4484 	char brand[MAXNAMELEN];
4485 
4486 	if (zonecfg_get_brand(handle, brand, sizeof (brand)) == Z_OK)
4487 		(void) fprintf(fp, "%s: %s\n", pt_to_str(PT_BRAND),
4488 		    brand);
4489 	else
4490 		(void) fprintf(fp, "%s %s\n", pt_to_str(PT_BRAND),
4491 		    gettext("not specified"));
4492 }
4493 
4494 static void
4495 info_autoboot(zone_dochandle_t handle, FILE *fp)
4496 {
4497 	boolean_t autoboot;
4498 	int err;
4499 
4500 	if ((err = zonecfg_get_autoboot(handle, &autoboot)) == Z_OK)
4501 		(void) fprintf(fp, "%s: %s\n", pt_to_str(PT_AUTOBOOT),
4502 		    autoboot ? "true" : "false");
4503 	else
4504 		zone_perror(zone, err, TRUE);
4505 }
4506 
4507 static void
4508 info_pool(zone_dochandle_t handle, FILE *fp)
4509 {
4510 	char pool[MAXNAMELEN];
4511 	int err;
4512 
4513 	if ((err = zonecfg_get_pool(handle, pool, sizeof (pool))) == Z_OK)
4514 		(void) fprintf(fp, "%s: %s\n", pt_to_str(PT_POOL), pool);
4515 	else
4516 		zone_perror(zone, err, TRUE);
4517 }
4518 
4519 static void
4520 info_limitpriv(zone_dochandle_t handle, FILE *fp)
4521 {
4522 	char *limitpriv;
4523 	int err;
4524 
4525 	if ((err = zonecfg_get_limitpriv(handle, &limitpriv)) == Z_OK) {
4526 		(void) fprintf(fp, "%s: %s\n", pt_to_str(PT_LIMITPRIV),
4527 		    limitpriv);
4528 		free(limitpriv);
4529 	} else {
4530 		zone_perror(zone, err, TRUE);
4531 	}
4532 }
4533 
4534 static void
4535 info_bootargs(zone_dochandle_t handle, FILE *fp)
4536 {
4537 	char bootargs[BOOTARGS_MAX];
4538 	int err;
4539 
4540 	if ((err = zonecfg_get_bootargs(handle, bootargs,
4541 	    sizeof (bootargs))) == Z_OK) {
4542 		(void) fprintf(fp, "%s: %s\n", pt_to_str(PT_BOOTARGS),
4543 		    bootargs);
4544 	} else {
4545 		zone_perror(zone, err, TRUE);
4546 	}
4547 }
4548 
4549 static void
4550 info_sched(zone_dochandle_t handle, FILE *fp)
4551 {
4552 	char sched[MAXNAMELEN];
4553 	int err;
4554 
4555 	if ((err = zonecfg_get_sched_class(handle, sched, sizeof (sched)))
4556 	    == Z_OK) {
4557 		(void) fprintf(fp, "%s: %s\n", pt_to_str(PT_SCHED), sched);
4558 	} else {
4559 		zone_perror(zone, err, TRUE);
4560 	}
4561 }
4562 
4563 static void
4564 info_iptype(zone_dochandle_t handle, FILE *fp)
4565 {
4566 	zone_iptype_t iptype;
4567 	int err;
4568 
4569 	if ((err = zonecfg_get_iptype(handle, &iptype)) == Z_OK) {
4570 		switch (iptype) {
4571 		case ZS_SHARED:
4572 			(void) fprintf(fp, "%s: %s\n", pt_to_str(PT_IPTYPE),
4573 			    "shared");
4574 			break;
4575 		case ZS_EXCLUSIVE:
4576 			(void) fprintf(fp, "%s: %s\n", pt_to_str(PT_IPTYPE),
4577 			    "exclusive");
4578 			break;
4579 		}
4580 	} else {
4581 		zone_perror(zone, err, TRUE);
4582 	}
4583 }
4584 
4585 static void
4586 output_fs(FILE *fp, struct zone_fstab *fstab)
4587 {
4588 	zone_fsopt_t *this;
4589 
4590 	(void) fprintf(fp, "%s:\n", rt_to_str(RT_FS));
4591 	output_prop(fp, PT_DIR, fstab->zone_fs_dir, B_TRUE);
4592 	output_prop(fp, PT_SPECIAL, fstab->zone_fs_special, B_TRUE);
4593 	output_prop(fp, PT_RAW, fstab->zone_fs_raw, B_TRUE);
4594 	output_prop(fp, PT_TYPE, fstab->zone_fs_type, B_TRUE);
4595 	(void) fprintf(fp, "\t%s: [", pt_to_str(PT_OPTIONS));
4596 	for (this = fstab->zone_fs_options; this != NULL;
4597 	    this = this->zone_fsopt_next) {
4598 		if (strchr(this->zone_fsopt_opt, '='))
4599 			(void) fprintf(fp, "\"%s\"", this->zone_fsopt_opt);
4600 		else
4601 			(void) fprintf(fp, "%s", this->zone_fsopt_opt);
4602 		if (this->zone_fsopt_next != NULL)
4603 			(void) fprintf(fp, ",");
4604 	}
4605 	(void) fprintf(fp, "]\n");
4606 }
4607 
4608 static void
4609 output_ipd(FILE *fp, struct zone_fstab *ipdtab)
4610 {
4611 	(void) fprintf(fp, "%s:\n", rt_to_str(RT_IPD));
4612 	output_prop(fp, PT_DIR, ipdtab->zone_fs_dir, B_TRUE);
4613 }
4614 
4615 static void
4616 info_fs(zone_dochandle_t handle, FILE *fp, cmd_t *cmd)
4617 {
4618 	struct zone_fstab lookup, user;
4619 	bool output = FALSE;
4620 
4621 	if (zonecfg_setfsent(handle) != Z_OK)
4622 		return;
4623 	while (zonecfg_getfsent(handle, &lookup) == Z_OK) {
4624 		if (cmd->cmd_prop_nv_pairs == 0) {
4625 			output_fs(fp, &lookup);
4626 			goto loopend;
4627 		}
4628 		if (fill_in_fstab(cmd, &user, TRUE) != Z_OK)
4629 			goto loopend;
4630 		if (strlen(user.zone_fs_dir) > 0 &&
4631 		    strcmp(user.zone_fs_dir, lookup.zone_fs_dir) != 0)
4632 			goto loopend;	/* no match */
4633 		if (strlen(user.zone_fs_special) > 0 &&
4634 		    strcmp(user.zone_fs_special, lookup.zone_fs_special) != 0)
4635 			goto loopend;	/* no match */
4636 		if (strlen(user.zone_fs_type) > 0 &&
4637 		    strcmp(user.zone_fs_type, lookup.zone_fs_type) != 0)
4638 			goto loopend;	/* no match */
4639 		output_fs(fp, &lookup);
4640 		output = TRUE;
4641 loopend:
4642 		zonecfg_free_fs_option_list(lookup.zone_fs_options);
4643 	}
4644 	(void) zonecfg_endfsent(handle);
4645 	/*
4646 	 * If a property n/v pair was specified, warn the user if there was
4647 	 * nothing to output.
4648 	 */
4649 	if (!output && cmd->cmd_prop_nv_pairs > 0)
4650 		(void) printf(gettext("No such %s resource.\n"),
4651 		    rt_to_str(RT_FS));
4652 }
4653 
4654 static void
4655 info_ipd(zone_dochandle_t handle, FILE *fp, cmd_t *cmd)
4656 {
4657 	struct zone_fstab lookup, user;
4658 	bool output = FALSE;
4659 
4660 	if (zonecfg_setipdent(handle) != Z_OK)
4661 		return;
4662 	while (zonecfg_getipdent(handle, &lookup) == Z_OK) {
4663 		if (cmd->cmd_prop_nv_pairs == 0) {
4664 			output_ipd(fp, &lookup);
4665 			continue;
4666 		}
4667 		if (fill_in_ipdtab(cmd, &user, TRUE) != Z_OK)
4668 			continue;
4669 		if (strlen(user.zone_fs_dir) > 0 &&
4670 		    strcmp(user.zone_fs_dir, lookup.zone_fs_dir) != 0)
4671 			continue;	/* no match */
4672 		output_ipd(fp, &lookup);
4673 		output = TRUE;
4674 	}
4675 	(void) zonecfg_endipdent(handle);
4676 	/*
4677 	 * If a property n/v pair was specified, warn the user if there was
4678 	 * nothing to output.
4679 	 */
4680 	if (!output && cmd->cmd_prop_nv_pairs > 0)
4681 		(void) printf(gettext("No such %s resource.\n"),
4682 		    rt_to_str(RT_IPD));
4683 }
4684 
4685 static void
4686 output_net(FILE *fp, struct zone_nwiftab *nwiftab)
4687 {
4688 	(void) fprintf(fp, "%s:\n", rt_to_str(RT_NET));
4689 	output_prop(fp, PT_ADDRESS, nwiftab->zone_nwif_address, B_TRUE);
4690 	output_prop(fp, PT_PHYSICAL, nwiftab->zone_nwif_physical, B_TRUE);
4691 }
4692 
4693 static void
4694 info_net(zone_dochandle_t handle, FILE *fp, cmd_t *cmd)
4695 {
4696 	struct zone_nwiftab lookup, user;
4697 	bool output = FALSE;
4698 
4699 	if (zonecfg_setnwifent(handle) != Z_OK)
4700 		return;
4701 	while (zonecfg_getnwifent(handle, &lookup) == Z_OK) {
4702 		if (cmd->cmd_prop_nv_pairs == 0) {
4703 			output_net(fp, &lookup);
4704 			continue;
4705 		}
4706 		if (fill_in_nwiftab(cmd, &user, TRUE) != Z_OK)
4707 			continue;
4708 		if (strlen(user.zone_nwif_physical) > 0 &&
4709 		    strcmp(user.zone_nwif_physical,
4710 		    lookup.zone_nwif_physical) != 0)
4711 			continue;	/* no match */
4712 		/* If present make sure it matches */
4713 		if (strlen(user.zone_nwif_address) > 0 &&
4714 		    !zonecfg_same_net_address(user.zone_nwif_address,
4715 		    lookup.zone_nwif_address))
4716 			continue;	/* no match */
4717 		output_net(fp, &lookup);
4718 		output = TRUE;
4719 	}
4720 	(void) zonecfg_endnwifent(handle);
4721 	/*
4722 	 * If a property n/v pair was specified, warn the user if there was
4723 	 * nothing to output.
4724 	 */
4725 	if (!output && cmd->cmd_prop_nv_pairs > 0)
4726 		(void) printf(gettext("No such %s resource.\n"),
4727 		    rt_to_str(RT_NET));
4728 }
4729 
4730 static void
4731 output_dev(FILE *fp, struct zone_devtab *devtab)
4732 {
4733 	(void) fprintf(fp, "%s:\n", rt_to_str(RT_DEVICE));
4734 	output_prop(fp, PT_MATCH, devtab->zone_dev_match, B_TRUE);
4735 }
4736 
4737 static void
4738 info_dev(zone_dochandle_t handle, FILE *fp, cmd_t *cmd)
4739 {
4740 	struct zone_devtab lookup, user;
4741 	bool output = FALSE;
4742 
4743 	if (zonecfg_setdevent(handle) != Z_OK)
4744 		return;
4745 	while (zonecfg_getdevent(handle, &lookup) == Z_OK) {
4746 		if (cmd->cmd_prop_nv_pairs == 0) {
4747 			output_dev(fp, &lookup);
4748 			continue;
4749 		}
4750 		if (fill_in_devtab(cmd, &user, TRUE) != Z_OK)
4751 			continue;
4752 		if (strlen(user.zone_dev_match) > 0 &&
4753 		    strcmp(user.zone_dev_match, lookup.zone_dev_match) != 0)
4754 			continue;	/* no match */
4755 		output_dev(fp, &lookup);
4756 		output = TRUE;
4757 	}
4758 	(void) zonecfg_enddevent(handle);
4759 	/*
4760 	 * If a property n/v pair was specified, warn the user if there was
4761 	 * nothing to output.
4762 	 */
4763 	if (!output && cmd->cmd_prop_nv_pairs > 0)
4764 		(void) printf(gettext("No such %s resource.\n"),
4765 		    rt_to_str(RT_DEVICE));
4766 }
4767 
4768 static void
4769 output_rctl(FILE *fp, struct zone_rctltab *rctltab)
4770 {
4771 	struct zone_rctlvaltab *valptr;
4772 
4773 	(void) fprintf(fp, "%s:\n", rt_to_str(RT_RCTL));
4774 	output_prop(fp, PT_NAME, rctltab->zone_rctl_name, B_TRUE);
4775 	for (valptr = rctltab->zone_rctl_valptr; valptr != NULL;
4776 	    valptr = valptr->zone_rctlval_next) {
4777 		fprintf(fp, "\t%s: (%s=%s,%s=%s,%s=%s)\n",
4778 		    pt_to_str(PT_VALUE),
4779 		    pt_to_str(PT_PRIV), valptr->zone_rctlval_priv,
4780 		    pt_to_str(PT_LIMIT), valptr->zone_rctlval_limit,
4781 		    pt_to_str(PT_ACTION), valptr->zone_rctlval_action);
4782 	}
4783 }
4784 
4785 static void
4786 info_rctl(zone_dochandle_t handle, FILE *fp, cmd_t *cmd)
4787 {
4788 	struct zone_rctltab lookup, user;
4789 	bool output = FALSE;
4790 
4791 	if (zonecfg_setrctlent(handle) != Z_OK)
4792 		return;
4793 	while (zonecfg_getrctlent(handle, &lookup) == Z_OK) {
4794 		if (cmd->cmd_prop_nv_pairs == 0) {
4795 			output_rctl(fp, &lookup);
4796 		} else if (fill_in_rctltab(cmd, &user, TRUE) == Z_OK &&
4797 		    (strlen(user.zone_rctl_name) == 0 ||
4798 		    strcmp(user.zone_rctl_name, lookup.zone_rctl_name) == 0)) {
4799 			output_rctl(fp, &lookup);
4800 			output = TRUE;
4801 		}
4802 		zonecfg_free_rctl_value_list(lookup.zone_rctl_valptr);
4803 	}
4804 	(void) zonecfg_endrctlent(handle);
4805 	/*
4806 	 * If a property n/v pair was specified, warn the user if there was
4807 	 * nothing to output.
4808 	 */
4809 	if (!output && cmd->cmd_prop_nv_pairs > 0)
4810 		(void) printf(gettext("No such %s resource.\n"),
4811 		    rt_to_str(RT_RCTL));
4812 }
4813 
4814 static void
4815 output_attr(FILE *fp, struct zone_attrtab *attrtab)
4816 {
4817 	(void) fprintf(fp, "%s:\n", rt_to_str(RT_ATTR));
4818 	output_prop(fp, PT_NAME, attrtab->zone_attr_name, B_TRUE);
4819 	output_prop(fp, PT_TYPE, attrtab->zone_attr_type, B_TRUE);
4820 	output_prop(fp, PT_VALUE, attrtab->zone_attr_value, B_TRUE);
4821 }
4822 
4823 static void
4824 info_attr(zone_dochandle_t handle, FILE *fp, cmd_t *cmd)
4825 {
4826 	struct zone_attrtab lookup, user;
4827 	bool output = FALSE;
4828 
4829 	if (zonecfg_setattrent(handle) != Z_OK)
4830 		return;
4831 	while (zonecfg_getattrent(handle, &lookup) == Z_OK) {
4832 		if (cmd->cmd_prop_nv_pairs == 0) {
4833 			output_attr(fp, &lookup);
4834 			continue;
4835 		}
4836 		if (fill_in_attrtab(cmd, &user, TRUE) != Z_OK)
4837 			continue;
4838 		if (strlen(user.zone_attr_name) > 0 &&
4839 		    strcmp(user.zone_attr_name, lookup.zone_attr_name) != 0)
4840 			continue;	/* no match */
4841 		if (strlen(user.zone_attr_type) > 0 &&
4842 		    strcmp(user.zone_attr_type, lookup.zone_attr_type) != 0)
4843 			continue;	/* no match */
4844 		if (strlen(user.zone_attr_value) > 0 &&
4845 		    strcmp(user.zone_attr_value, lookup.zone_attr_value) != 0)
4846 			continue;	/* no match */
4847 		output_attr(fp, &lookup);
4848 		output = TRUE;
4849 	}
4850 	(void) zonecfg_endattrent(handle);
4851 	/*
4852 	 * If a property n/v pair was specified, warn the user if there was
4853 	 * nothing to output.
4854 	 */
4855 	if (!output && cmd->cmd_prop_nv_pairs > 0)
4856 		(void) printf(gettext("No such %s resource.\n"),
4857 		    rt_to_str(RT_ATTR));
4858 }
4859 
4860 static void
4861 output_ds(FILE *fp, struct zone_dstab *dstab)
4862 {
4863 	(void) fprintf(fp, "%s:\n", rt_to_str(RT_DATASET));
4864 	output_prop(fp, PT_NAME, dstab->zone_dataset_name, B_TRUE);
4865 }
4866 
4867 static void
4868 info_ds(zone_dochandle_t handle, FILE *fp, cmd_t *cmd)
4869 {
4870 	struct zone_dstab lookup, user;
4871 	bool output = FALSE;
4872 
4873 	if (zonecfg_setdsent(handle) != Z_OK)
4874 		return;
4875 	while (zonecfg_getdsent(handle, &lookup) == Z_OK) {
4876 		if (cmd->cmd_prop_nv_pairs == 0) {
4877 			output_ds(fp, &lookup);
4878 			continue;
4879 		}
4880 		if (fill_in_dstab(cmd, &user, TRUE) != Z_OK)
4881 			continue;
4882 		if (strlen(user.zone_dataset_name) > 0 &&
4883 		    strcmp(user.zone_dataset_name,
4884 		    lookup.zone_dataset_name) != 0)
4885 			continue;	/* no match */
4886 		output_ds(fp, &lookup);
4887 		output = TRUE;
4888 	}
4889 	(void) zonecfg_enddsent(handle);
4890 	/*
4891 	 * If a property n/v pair was specified, warn the user if there was
4892 	 * nothing to output.
4893 	 */
4894 	if (!output && cmd->cmd_prop_nv_pairs > 0)
4895 		(void) printf(gettext("No such %s resource.\n"),
4896 		    rt_to_str(RT_DATASET));
4897 }
4898 
4899 static void
4900 output_pset(FILE *fp, struct zone_psettab *psettab)
4901 {
4902 	(void) fprintf(fp, "%s:\n", rt_to_str(RT_DCPU));
4903 	if (strcmp(psettab->zone_ncpu_min, psettab->zone_ncpu_max) == 0)
4904 		(void) fprintf(fp, "\t%s: %s\n", pt_to_str(PT_NCPUS),
4905 		    psettab->zone_ncpu_max);
4906 	else
4907 		(void) fprintf(fp, "\t%s: %s-%s\n", pt_to_str(PT_NCPUS),
4908 		    psettab->zone_ncpu_min, psettab->zone_ncpu_max);
4909 	if (psettab->zone_importance[0] != '\0')
4910 		(void) fprintf(fp, "\t%s: %s\n", pt_to_str(PT_IMPORTANCE),
4911 		    psettab->zone_importance);
4912 }
4913 
4914 static void
4915 info_pset(zone_dochandle_t handle, FILE *fp)
4916 {
4917 	struct zone_psettab lookup;
4918 
4919 	if (zonecfg_getpsetent(handle, &lookup) == Z_OK)
4920 		output_pset(fp, &lookup);
4921 }
4922 
4923 static void
4924 output_pcap(FILE *fp)
4925 {
4926 	uint64_t cap;
4927 
4928 	if (zonecfg_get_aliased_rctl(handle, ALIAS_CPUCAP, &cap) == Z_OK) {
4929 		float scaled = (float)cap / 100;
4930 		(void) fprintf(fp, "%s:\n", rt_to_str(RT_PCAP));
4931 		(void) fprintf(fp, "\t[%s: %.2f]\n", pt_to_str(PT_NCPUS),
4932 		    scaled);
4933 	}
4934 }
4935 
4936 static void
4937 info_pcap(FILE *fp)
4938 {
4939 	output_pcap(fp);
4940 }
4941 
4942 
4943 static void
4944 info_aliased_rctl(zone_dochandle_t handle, FILE *fp, char *alias)
4945 {
4946 	uint64_t limit;
4947 
4948 	if (zonecfg_get_aliased_rctl(handle, alias, &limit) == Z_OK) {
4949 		/* convert memory based properties */
4950 		if (strcmp(alias, ALIAS_MAXSHMMEM) == 0) {
4951 			char buf[128];
4952 
4953 			(void) snprintf(buf, sizeof (buf), "%llu", limit);
4954 			bytes_to_units(buf, buf, sizeof (buf));
4955 			(void) fprintf(fp, "[%s: %s]\n", alias, buf);
4956 			return;
4957 		}
4958 
4959 		(void) fprintf(fp, "[%s: %llu]\n", alias, limit);
4960 	}
4961 }
4962 
4963 static void
4964 bytes_to_units(char *str, char *buf, int bufsize)
4965 {
4966 	unsigned long long num;
4967 	unsigned long long save = 0;
4968 	char *units = "BKMGT";
4969 	char *up = units;
4970 
4971 	num = strtoll(str, NULL, 10);
4972 
4973 	if (num < 1024) {
4974 		(void) snprintf(buf, bufsize, "%llu", num);
4975 		return;
4976 	}
4977 
4978 	while ((num >= 1024) && (*up != 'T')) {
4979 		up++; /* next unit of measurement */
4980 		save = num;
4981 		num = (num + 512) >> 10;
4982 	}
4983 
4984 	/* check if we should output a fraction.  snprintf will round for us */
4985 	if (save % 1024 != 0 && ((save >> 10) < 10))
4986 		(void) snprintf(buf, bufsize, "%2.1f%c", ((float)save / 1024),
4987 		    *up);
4988 	else
4989 		(void) snprintf(buf, bufsize, "%llu%c", num, *up);
4990 }
4991 
4992 static void
4993 output_mcap(FILE *fp, struct zone_mcaptab *mcaptab, int showswap,
4994     uint64_t maxswap, int showlocked, uint64_t maxlocked)
4995 {
4996 	char buf[128];
4997 
4998 	(void) fprintf(fp, "%s:\n", rt_to_str(RT_MCAP));
4999 	if (mcaptab->zone_physmem_cap[0] != '\0') {
5000 		bytes_to_units(mcaptab->zone_physmem_cap, buf, sizeof (buf));
5001 		output_prop(fp, PT_PHYSICAL, buf, B_TRUE);
5002 	}
5003 
5004 	if (showswap == Z_OK) {
5005 		(void) snprintf(buf, sizeof (buf), "%llu", maxswap);
5006 		bytes_to_units(buf, buf, sizeof (buf));
5007 		output_prop(fp, PT_SWAP, buf, B_TRUE);
5008 	}
5009 
5010 	if (showlocked == Z_OK) {
5011 		(void) snprintf(buf, sizeof (buf), "%llu", maxlocked);
5012 		bytes_to_units(buf, buf, sizeof (buf));
5013 		output_prop(fp, PT_LOCKED, buf, B_TRUE);
5014 	}
5015 }
5016 
5017 static void
5018 info_mcap(zone_dochandle_t handle, FILE *fp)
5019 {
5020 	int res1, res2, res3;
5021 	uint64_t swap_limit;
5022 	uint64_t locked_limit;
5023 	struct zone_mcaptab lookup;
5024 
5025 	bzero(&lookup, sizeof (lookup));
5026 	res1 = zonecfg_getmcapent(handle, &lookup);
5027 	res2 = zonecfg_get_aliased_rctl(handle, ALIAS_MAXSWAP, &swap_limit);
5028 	res3 = zonecfg_get_aliased_rctl(handle, ALIAS_MAXLOCKEDMEM,
5029 	    &locked_limit);
5030 
5031 	if (res1 == Z_OK || res2 == Z_OK || res3 == Z_OK)
5032 		output_mcap(fp, &lookup, res2, swap_limit, res3, locked_limit);
5033 }
5034 
5035 void
5036 info_func(cmd_t *cmd)
5037 {
5038 	FILE *fp = stdout;
5039 	bool need_to_close = FALSE;
5040 	char *pager;
5041 	int type;
5042 	int res1, res2;
5043 	uint64_t swap_limit;
5044 	uint64_t locked_limit;
5045 
5046 	assert(cmd != NULL);
5047 
5048 	if (initialize(TRUE) != Z_OK)
5049 		return;
5050 
5051 	/* don't page error output */
5052 	if (interactive_mode) {
5053 		if ((pager = getenv("PAGER")) == NULL)
5054 			pager = PAGER;
5055 		if ((fp = popen(pager, "w")) != NULL)
5056 			need_to_close = TRUE;
5057 		setbuf(fp, NULL);
5058 	}
5059 
5060 	if (!global_scope) {
5061 		switch (resource_scope) {
5062 		case RT_FS:
5063 			output_fs(fp, &in_progress_fstab);
5064 			break;
5065 		case RT_IPD:
5066 			output_ipd(fp, &in_progress_ipdtab);
5067 			break;
5068 		case RT_NET:
5069 			output_net(fp, &in_progress_nwiftab);
5070 			break;
5071 		case RT_DEVICE:
5072 			output_dev(fp, &in_progress_devtab);
5073 			break;
5074 		case RT_RCTL:
5075 			output_rctl(fp, &in_progress_rctltab);
5076 			break;
5077 		case RT_ATTR:
5078 			output_attr(fp, &in_progress_attrtab);
5079 			break;
5080 		case RT_DATASET:
5081 			output_ds(fp, &in_progress_dstab);
5082 			break;
5083 		case RT_DCPU:
5084 			output_pset(fp, &in_progress_psettab);
5085 			break;
5086 		case RT_PCAP:
5087 			output_pcap(fp);
5088 			break;
5089 		case RT_MCAP:
5090 			res1 = zonecfg_get_aliased_rctl(handle, ALIAS_MAXSWAP,
5091 			    &swap_limit);
5092 			res2 = zonecfg_get_aliased_rctl(handle,
5093 			    ALIAS_MAXLOCKEDMEM, &locked_limit);
5094 			output_mcap(fp, &in_progress_mcaptab, res1, swap_limit,
5095 			    res2, locked_limit);
5096 			break;
5097 		}
5098 		goto cleanup;
5099 	}
5100 
5101 	type = cmd->cmd_res_type;
5102 
5103 	if (gz_invalid_rt_property(type)) {
5104 		zerr(gettext("%s is not a valid property for the global zone."),
5105 		    rt_to_str(type));
5106 		goto cleanup;
5107 	}
5108 
5109 	if (gz_invalid_resource(type)) {
5110 		zerr(gettext("%s is not a valid resource for the global zone."),
5111 		    rt_to_str(type));
5112 		goto cleanup;
5113 	}
5114 
5115 	switch (cmd->cmd_res_type) {
5116 	case RT_UNKNOWN:
5117 		info_zonename(handle, fp);
5118 		if (!global_zone) {
5119 			info_zonepath(handle, fp);
5120 			info_brand(handle, fp);
5121 			info_autoboot(handle, fp);
5122 			info_bootargs(handle, fp);
5123 		}
5124 		info_pool(handle, fp);
5125 		if (!global_zone) {
5126 			info_limitpriv(handle, fp);
5127 			info_sched(handle, fp);
5128 			info_iptype(handle, fp);
5129 		}
5130 		info_aliased_rctl(handle, fp, ALIAS_MAXLWPS);
5131 		info_aliased_rctl(handle, fp, ALIAS_MAXSHMMEM);
5132 		info_aliased_rctl(handle, fp, ALIAS_MAXSHMIDS);
5133 		info_aliased_rctl(handle, fp, ALIAS_MAXMSGIDS);
5134 		info_aliased_rctl(handle, fp, ALIAS_MAXSEMIDS);
5135 		info_aliased_rctl(handle, fp, ALIAS_SHARES);
5136 		if (!global_zone) {
5137 			info_ipd(handle, fp, cmd);
5138 			info_fs(handle, fp, cmd);
5139 			info_net(handle, fp, cmd);
5140 			info_dev(handle, fp, cmd);
5141 		}
5142 		info_pset(handle, fp);
5143 		info_pcap(fp);
5144 		info_mcap(handle, fp);
5145 		if (!global_zone) {
5146 			info_attr(handle, fp, cmd);
5147 			info_ds(handle, fp, cmd);
5148 		}
5149 		info_rctl(handle, fp, cmd);
5150 		break;
5151 	case RT_ZONENAME:
5152 		info_zonename(handle, fp);
5153 		break;
5154 	case RT_ZONEPATH:
5155 		info_zonepath(handle, fp);
5156 		break;
5157 	case RT_BRAND:
5158 		info_brand(handle, fp);
5159 		break;
5160 	case RT_AUTOBOOT:
5161 		info_autoboot(handle, fp);
5162 		break;
5163 	case RT_POOL:
5164 		info_pool(handle, fp);
5165 		break;
5166 	case RT_LIMITPRIV:
5167 		info_limitpriv(handle, fp);
5168 		break;
5169 	case RT_BOOTARGS:
5170 		info_bootargs(handle, fp);
5171 		break;
5172 	case RT_SCHED:
5173 		info_sched(handle, fp);
5174 		break;
5175 	case RT_IPTYPE:
5176 		info_iptype(handle, fp);
5177 		break;
5178 	case RT_MAXLWPS:
5179 		info_aliased_rctl(handle, fp, ALIAS_MAXLWPS);
5180 		break;
5181 	case RT_MAXSHMMEM:
5182 		info_aliased_rctl(handle, fp, ALIAS_MAXSHMMEM);
5183 		break;
5184 	case RT_MAXSHMIDS:
5185 		info_aliased_rctl(handle, fp, ALIAS_MAXSHMIDS);
5186 		break;
5187 	case RT_MAXMSGIDS:
5188 		info_aliased_rctl(handle, fp, ALIAS_MAXMSGIDS);
5189 		break;
5190 	case RT_MAXSEMIDS:
5191 		info_aliased_rctl(handle, fp, ALIAS_MAXSEMIDS);
5192 		break;
5193 	case RT_SHARES:
5194 		info_aliased_rctl(handle, fp, ALIAS_SHARES);
5195 		break;
5196 	case RT_FS:
5197 		info_fs(handle, fp, cmd);
5198 		break;
5199 	case RT_IPD:
5200 		info_ipd(handle, fp, cmd);
5201 		break;
5202 	case RT_NET:
5203 		info_net(handle, fp, cmd);
5204 		break;
5205 	case RT_DEVICE:
5206 		info_dev(handle, fp, cmd);
5207 		break;
5208 	case RT_RCTL:
5209 		info_rctl(handle, fp, cmd);
5210 		break;
5211 	case RT_ATTR:
5212 		info_attr(handle, fp, cmd);
5213 		break;
5214 	case RT_DATASET:
5215 		info_ds(handle, fp, cmd);
5216 		break;
5217 	case RT_DCPU:
5218 		info_pset(handle, fp);
5219 		break;
5220 	case RT_PCAP:
5221 		info_pcap(fp);
5222 		break;
5223 	case RT_MCAP:
5224 		info_mcap(handle, fp);
5225 		break;
5226 	default:
5227 		zone_perror(rt_to_str(cmd->cmd_res_type), Z_NO_RESOURCE_TYPE,
5228 		    TRUE);
5229 	}
5230 
5231 cleanup:
5232 	if (need_to_close)
5233 		(void) pclose(fp);
5234 }
5235 
5236 /*
5237  * Helper function for verify-- checks that a required string property
5238  * exists.
5239  */
5240 static void
5241 check_reqd_prop(char *attr, int rt, int pt, int *ret_val)
5242 {
5243 	if (strlen(attr) == 0) {
5244 		zerr(gettext("%s: %s not specified"), rt_to_str(rt),
5245 		    pt_to_str(pt));
5246 		saw_error = TRUE;
5247 		if (*ret_val == Z_OK)
5248 			*ret_val = Z_REQD_PROPERTY_MISSING;
5249 	}
5250 }
5251 
5252 static int
5253 do_subproc(char *cmdbuf)
5254 {
5255 	char inbuf[MAX_CMD_LEN];
5256 	FILE *file;
5257 	int status;
5258 
5259 	file = popen(cmdbuf, "r");
5260 	if (file == NULL) {
5261 		zerr(gettext("Could not launch: %s"), cmdbuf);
5262 		return (-1);
5263 	}
5264 
5265 	while (fgets(inbuf, sizeof (inbuf), file) != NULL)
5266 		fprintf(stderr, "%s", inbuf);
5267 	status = pclose(file);
5268 
5269 	if (WIFSIGNALED(status)) {
5270 		zerr(gettext("%s unexpectedly terminated due to signal %d"),
5271 		    cmdbuf, WTERMSIG(status));
5272 		return (-1);
5273 	}
5274 	assert(WIFEXITED(status));
5275 	return (WEXITSTATUS(status));
5276 }
5277 
5278 static int
5279 brand_verify(zone_dochandle_t handle)
5280 {
5281 	char xml_file[32];
5282 	char cmdbuf[MAX_CMD_LEN];
5283 	brand_handle_t bh;
5284 	char brand[MAXNAMELEN];
5285 	int err;
5286 
5287 	if (zonecfg_get_brand(handle, brand, sizeof (brand)) != Z_OK) {
5288 		zerr("%s: %s\n", zone, gettext("could not get zone brand"));
5289 		return (Z_INVALID_DOCUMENT);
5290 	}
5291 	if ((bh = brand_open(brand)) == NULL) {
5292 		zerr("%s: %s\n", zone, gettext("unknown brand."));
5293 		return (Z_INVALID_DOCUMENT);
5294 	}
5295 
5296 	/*
5297 	 * Fetch the verify command, if any, from the brand configuration
5298 	 * and build the command line to execute it.
5299 	 */
5300 	strcpy(cmdbuf, EXEC_PREFIX);
5301 	err = brand_get_verify_cfg(bh, cmdbuf + EXEC_LEN,
5302 	    sizeof (cmdbuf) - (EXEC_LEN + (strlen(xml_file) + 1)));
5303 	brand_close(bh);
5304 	if (err != Z_OK) {
5305 		zerr("%s: %s\n", zone,
5306 		    gettext("could not get brand verification command"));
5307 		return (Z_INVALID_DOCUMENT);
5308 	}
5309 
5310 	/*
5311 	 * If the brand doesn't provide a verification routine, we just
5312 	 * return success.
5313 	 */
5314 	if (strlen(cmdbuf) == EXEC_LEN)
5315 		return (Z_OK);
5316 
5317 	/*
5318 	 * Dump the current config information for this zone to a file.
5319 	 */
5320 	strcpy(xml_file, "/tmp/zonecfg_verify.XXXXXX");
5321 	if (mkstemp(xml_file) == NULL)
5322 		return (Z_TEMP_FILE);
5323 	if ((err = zonecfg_verify_save(handle, xml_file)) != Z_OK) {
5324 		(void) unlink(xml_file);
5325 		return (err);
5326 	}
5327 
5328 	/*
5329 	 * Execute the verification command.
5330 	 */
5331 	if ((strlcat(cmdbuf, " ", MAX_CMD_LEN) >= MAX_CMD_LEN) ||
5332 	    (strlcat(cmdbuf, xml_file, MAX_CMD_LEN) >= MAX_CMD_LEN)) {
5333 		err = Z_BRAND_ERROR;
5334 	} else {
5335 		err = do_subproc(cmdbuf);
5336 	}
5337 
5338 	(void) unlink(xml_file);
5339 	return ((err == Z_OK) ? Z_OK : Z_BRAND_ERROR);
5340 }
5341 
5342 /*
5343  * See the DTD for which attributes are required for which resources.
5344  *
5345  * This function can be called by commit_func(), which needs to save things,
5346  * in addition to the general call from parse_and_run(), which doesn't need
5347  * things saved.  Since the parameters are standardized, we distinguish by
5348  * having commit_func() call here with cmd->cmd_arg set to "save" to indicate
5349  * that a save is needed.
5350  */
5351 void
5352 verify_func(cmd_t *cmd)
5353 {
5354 	struct zone_nwiftab nwiftab;
5355 	struct zone_fstab fstab;
5356 	struct zone_attrtab attrtab;
5357 	struct zone_rctltab rctltab;
5358 	struct zone_dstab dstab;
5359 	struct zone_psettab psettab;
5360 	char zonepath[MAXPATHLEN];
5361 	char sched[MAXNAMELEN];
5362 	char brand[MAXNAMELEN];
5363 	int err, ret_val = Z_OK, arg;
5364 	int pset_res;
5365 	bool save = FALSE;
5366 	bool arg_err = FALSE;
5367 	zone_iptype_t iptype;
5368 	boolean_t has_cpu_shares = B_FALSE;
5369 	boolean_t has_cpu_cap = B_FALSE;
5370 
5371 	optind = 0;
5372 	while ((arg = getopt(cmd->cmd_argc, cmd->cmd_argv, "?")) != EOF) {
5373 		switch (arg) {
5374 		case '?':
5375 			longer_usage(CMD_VERIFY);
5376 			arg_err = TRUE;
5377 			break;
5378 		default:
5379 			short_usage(CMD_VERIFY);
5380 			arg_err = TRUE;
5381 			break;
5382 		}
5383 	}
5384 	if (arg_err)
5385 		return;
5386 
5387 	if (optind > cmd->cmd_argc) {
5388 		short_usage(CMD_VERIFY);
5389 		return;
5390 	}
5391 
5392 	if (zone_is_read_only(CMD_VERIFY))
5393 		return;
5394 
5395 	assert(cmd != NULL);
5396 
5397 	if (cmd->cmd_argc > 0 && (strcmp(cmd->cmd_argv[0], "save") == 0))
5398 		save = TRUE;
5399 	if (initialize(TRUE) != Z_OK)
5400 		return;
5401 
5402 	if (zonecfg_get_zonepath(handle, zonepath, sizeof (zonepath)) != Z_OK &&
5403 	    !global_zone) {
5404 		zerr(gettext("%s not specified"), pt_to_str(PT_ZONEPATH));
5405 		ret_val = Z_REQD_RESOURCE_MISSING;
5406 		saw_error = TRUE;
5407 	}
5408 	if (strlen(zonepath) == 0 && !global_zone) {
5409 		zerr(gettext("%s cannot be empty."), pt_to_str(PT_ZONEPATH));
5410 		ret_val = Z_REQD_RESOURCE_MISSING;
5411 		saw_error = TRUE;
5412 	}
5413 
5414 	if ((err = zonecfg_get_brand(handle, brand, sizeof (brand))) != Z_OK) {
5415 		zone_perror(zone, err, TRUE);
5416 		return;
5417 	}
5418 	if (strcmp(brand, NATIVE_BRAND_NAME) != 0) {
5419 		if ((err = brand_verify(handle)) != Z_OK) {
5420 			zone_perror(zone, err, TRUE);
5421 			return;
5422 		}
5423 	}
5424 
5425 	if (zonecfg_get_iptype(handle, &iptype) != Z_OK) {
5426 		zerr("%s %s", gettext("cannot get"), pt_to_str(PT_IPTYPE));
5427 		ret_val = Z_REQD_RESOURCE_MISSING;
5428 		saw_error = TRUE;
5429 	}
5430 	if ((err = zonecfg_setipdent(handle)) != Z_OK) {
5431 		zone_perror(zone, err, TRUE);
5432 		return;
5433 	}
5434 	while (zonecfg_getipdent(handle, &fstab) == Z_OK) {
5435 		check_reqd_prop(fstab.zone_fs_dir, RT_IPD, PT_DIR, &ret_val);
5436 	}
5437 	(void) zonecfg_endipdent(handle);
5438 
5439 	if ((err = zonecfg_setfsent(handle)) != Z_OK) {
5440 		zone_perror(zone, err, TRUE);
5441 		return;
5442 	}
5443 	while (zonecfg_getfsent(handle, &fstab) == Z_OK) {
5444 		check_reqd_prop(fstab.zone_fs_dir, RT_FS, PT_DIR, &ret_val);
5445 		check_reqd_prop(fstab.zone_fs_special, RT_FS, PT_SPECIAL,
5446 		    &ret_val);
5447 		check_reqd_prop(fstab.zone_fs_type, RT_FS, PT_TYPE, &ret_val);
5448 
5449 		zonecfg_free_fs_option_list(fstab.zone_fs_options);
5450 	}
5451 	(void) zonecfg_endfsent(handle);
5452 
5453 	if ((err = zonecfg_setnwifent(handle)) != Z_OK) {
5454 		zone_perror(zone, err, TRUE);
5455 		return;
5456 	}
5457 	while (zonecfg_getnwifent(handle, &nwiftab) == Z_OK) {
5458 		/*
5459 		 * physical is required in all cases.
5460 		 * A shared IP requires an address, while
5461 		 * an exclusive IP must not have an address.
5462 		 */
5463 		check_reqd_prop(nwiftab.zone_nwif_physical, RT_NET,
5464 		    PT_PHYSICAL, &ret_val);
5465 
5466 		switch (iptype) {
5467 		case ZS_SHARED:
5468 			check_reqd_prop(nwiftab.zone_nwif_address, RT_NET,
5469 			    PT_ADDRESS, &ret_val);
5470 			break;
5471 		case ZS_EXCLUSIVE:
5472 			if (strlen(nwiftab.zone_nwif_address) > 0) {
5473 				zerr(gettext("%s: %s cannot be specified "
5474 				    "for an exclusive IP type"),
5475 				    rt_to_str(RT_NET), pt_to_str(PT_ADDRESS));
5476 				saw_error = TRUE;
5477 				if (ret_val == Z_OK)
5478 					ret_val = Z_INVAL;
5479 			}
5480 			break;
5481 		}
5482 	}
5483 	(void) zonecfg_endnwifent(handle);
5484 
5485 	if ((err = zonecfg_setrctlent(handle)) != Z_OK) {
5486 		zone_perror(zone, err, TRUE);
5487 		return;
5488 	}
5489 	while (zonecfg_getrctlent(handle, &rctltab) == Z_OK) {
5490 		check_reqd_prop(rctltab.zone_rctl_name, RT_RCTL, PT_NAME,
5491 		    &ret_val);
5492 
5493 		if (strcmp(rctltab.zone_rctl_name, "zone.cpu-shares") == 0)
5494 			has_cpu_shares = B_TRUE;
5495 
5496 		if (strcmp(rctltab.zone_rctl_name, "zone.cpu-cap") == 0)
5497 			has_cpu_cap = B_TRUE;
5498 
5499 		if (rctltab.zone_rctl_valptr == NULL) {
5500 			zerr(gettext("%s: no %s specified"),
5501 			    rt_to_str(RT_RCTL), pt_to_str(PT_VALUE));
5502 			saw_error = TRUE;
5503 			if (ret_val == Z_OK)
5504 				ret_val = Z_REQD_PROPERTY_MISSING;
5505 		} else {
5506 			zonecfg_free_rctl_value_list(rctltab.zone_rctl_valptr);
5507 		}
5508 	}
5509 	(void) zonecfg_endrctlent(handle);
5510 
5511 	if ((pset_res = zonecfg_lookup_pset(handle, &psettab)) == Z_OK &&
5512 	    has_cpu_shares) {
5513 		zerr(gettext("%s zone.cpu-shares and %s are incompatible."),
5514 		    rt_to_str(RT_RCTL), rt_to_str(RT_DCPU));
5515 		saw_error = TRUE;
5516 		if (ret_val == Z_OK)
5517 			ret_val = Z_INCOMPATIBLE;
5518 	}
5519 
5520 	if (has_cpu_shares && zonecfg_get_sched_class(handle, sched,
5521 	    sizeof (sched)) == Z_OK && strlen(sched) > 0 &&
5522 	    strcmp(sched, "FSS") != 0) {
5523 		zerr(gettext("WARNING: %s zone.cpu-shares and %s=%s are "
5524 		    "incompatible"),
5525 		    rt_to_str(RT_RCTL), rt_to_str(RT_SCHED), sched);
5526 		saw_error = TRUE;
5527 		if (ret_val == Z_OK)
5528 			ret_val = Z_INCOMPATIBLE;
5529 	}
5530 
5531 	if (pset_res == Z_OK && has_cpu_cap) {
5532 		zerr(gettext("%s zone.cpu-cap and the %s are incompatible."),
5533 		    rt_to_str(RT_RCTL), rt_to_str(RT_DCPU));
5534 		saw_error = TRUE;
5535 		if (ret_val == Z_OK)
5536 			ret_val = Z_INCOMPATIBLE;
5537 	}
5538 
5539 	if ((err = zonecfg_setattrent(handle)) != Z_OK) {
5540 		zone_perror(zone, err, TRUE);
5541 		return;
5542 	}
5543 	while (zonecfg_getattrent(handle, &attrtab) == Z_OK) {
5544 		check_reqd_prop(attrtab.zone_attr_name, RT_ATTR, PT_NAME,
5545 		    &ret_val);
5546 		check_reqd_prop(attrtab.zone_attr_type, RT_ATTR, PT_TYPE,
5547 		    &ret_val);
5548 		check_reqd_prop(attrtab.zone_attr_value, RT_ATTR, PT_VALUE,
5549 		    &ret_val);
5550 	}
5551 	(void) zonecfg_endattrent(handle);
5552 
5553 	if ((err = zonecfg_setdsent(handle)) != Z_OK) {
5554 		zone_perror(zone, err, TRUE);
5555 		return;
5556 	}
5557 	while (zonecfg_getdsent(handle, &dstab) == Z_OK) {
5558 		if (strlen(dstab.zone_dataset_name) == 0) {
5559 			zerr("%s: %s %s", rt_to_str(RT_DATASET),
5560 			    pt_to_str(PT_NAME), gettext("not specified"));
5561 			saw_error = TRUE;
5562 			if (ret_val == Z_OK)
5563 				ret_val = Z_REQD_PROPERTY_MISSING;
5564 		} else if (!zfs_name_valid(dstab.zone_dataset_name,
5565 		    ZFS_TYPE_FILESYSTEM)) {
5566 			zerr("%s: %s %s", rt_to_str(RT_DATASET),
5567 			    pt_to_str(PT_NAME), gettext("invalid"));
5568 			saw_error = TRUE;
5569 			if (ret_val == Z_OK)
5570 				ret_val = Z_BAD_PROPERTY;
5571 		}
5572 
5573 	}
5574 	(void) zonecfg_enddsent(handle);
5575 
5576 	if (!global_scope) {
5577 		zerr(gettext("resource specification incomplete"));
5578 		saw_error = TRUE;
5579 		if (ret_val == Z_OK)
5580 			ret_val = Z_INSUFFICIENT_SPEC;
5581 	}
5582 
5583 	if (save) {
5584 		if (ret_val == Z_OK) {
5585 			if ((ret_val = zonecfg_save(handle)) == Z_OK) {
5586 				need_to_commit = FALSE;
5587 				(void) strlcpy(revert_zone, zone,
5588 				    sizeof (revert_zone));
5589 			}
5590 		} else {
5591 			zerr(gettext("Zone %s failed to verify"), zone);
5592 		}
5593 	}
5594 	if (ret_val != Z_OK)
5595 		zone_perror(zone, ret_val, TRUE);
5596 }
5597 
5598 void
5599 cancel_func(cmd_t *cmd)
5600 {
5601 	int arg;
5602 	bool arg_err = FALSE;
5603 
5604 	assert(cmd != NULL);
5605 
5606 	optind = 0;
5607 	while ((arg = getopt(cmd->cmd_argc, cmd->cmd_argv, "?")) != EOF) {
5608 		switch (arg) {
5609 		case '?':
5610 			longer_usage(CMD_CANCEL);
5611 			arg_err = TRUE;
5612 			break;
5613 		default:
5614 			short_usage(CMD_CANCEL);
5615 			arg_err = TRUE;
5616 			break;
5617 		}
5618 	}
5619 	if (arg_err)
5620 		return;
5621 
5622 	if (optind != cmd->cmd_argc) {
5623 		short_usage(CMD_CANCEL);
5624 		return;
5625 	}
5626 
5627 	if (global_scope)
5628 		scope_usage(CMD_CANCEL);
5629 	global_scope = TRUE;
5630 	zonecfg_free_fs_option_list(in_progress_fstab.zone_fs_options);
5631 	bzero(&in_progress_fstab, sizeof (in_progress_fstab));
5632 	bzero(&in_progress_nwiftab, sizeof (in_progress_nwiftab));
5633 	bzero(&in_progress_ipdtab, sizeof (in_progress_ipdtab));
5634 	bzero(&in_progress_devtab, sizeof (in_progress_devtab));
5635 	zonecfg_free_rctl_value_list(in_progress_rctltab.zone_rctl_valptr);
5636 	bzero(&in_progress_rctltab, sizeof (in_progress_rctltab));
5637 	bzero(&in_progress_attrtab, sizeof (in_progress_attrtab));
5638 	bzero(&in_progress_dstab, sizeof (in_progress_dstab));
5639 }
5640 
5641 static int
5642 validate_attr_name(char *name)
5643 {
5644 	int i;
5645 
5646 	if (!isalnum(name[0])) {
5647 		zerr(gettext("Invalid %s %s %s: must start with an alpha-"
5648 		    "numeric character."), rt_to_str(RT_ATTR),
5649 		    pt_to_str(PT_NAME), name);
5650 		return (Z_INVAL);
5651 	}
5652 	for (i = 1; name[i]; i++)
5653 		if (!isalnum(name[i]) && name[i] != '-' && name[i] != '.') {
5654 			zerr(gettext("Invalid %s %s %s: can only contain "
5655 			    "alpha-numeric characters, plus '-' and '.'."),
5656 			    rt_to_str(RT_ATTR), pt_to_str(PT_NAME), name);
5657 			return (Z_INVAL);
5658 		}
5659 	return (Z_OK);
5660 }
5661 
5662 static int
5663 validate_attr_type_val(struct zone_attrtab *attrtab)
5664 {
5665 	boolean_t boolval;
5666 	int64_t intval;
5667 	char strval[MAXNAMELEN];
5668 	uint64_t uintval;
5669 
5670 	if (strcmp(attrtab->zone_attr_type, "boolean") == 0) {
5671 		if (zonecfg_get_attr_boolean(attrtab, &boolval) == Z_OK)
5672 			return (Z_OK);
5673 		zerr(gettext("invalid %s value for %s=%s"),
5674 		    rt_to_str(RT_ATTR), pt_to_str(PT_TYPE), "boolean");
5675 		return (Z_ERR);
5676 	}
5677 
5678 	if (strcmp(attrtab->zone_attr_type, "int") == 0) {
5679 		if (zonecfg_get_attr_int(attrtab, &intval) == Z_OK)
5680 			return (Z_OK);
5681 		zerr(gettext("invalid %s value for %s=%s"),
5682 		    rt_to_str(RT_ATTR), pt_to_str(PT_TYPE), "int");
5683 		return (Z_ERR);
5684 	}
5685 
5686 	if (strcmp(attrtab->zone_attr_type, "string") == 0) {
5687 		if (zonecfg_get_attr_string(attrtab, strval,
5688 		    sizeof (strval)) == Z_OK)
5689 			return (Z_OK);
5690 		zerr(gettext("invalid %s value for %s=%s"),
5691 		    rt_to_str(RT_ATTR), pt_to_str(PT_TYPE), "string");
5692 		return (Z_ERR);
5693 	}
5694 
5695 	if (strcmp(attrtab->zone_attr_type, "uint") == 0) {
5696 		if (zonecfg_get_attr_uint(attrtab, &uintval) == Z_OK)
5697 			return (Z_OK);
5698 		zerr(gettext("invalid %s value for %s=%s"),
5699 		    rt_to_str(RT_ATTR), pt_to_str(PT_TYPE), "uint");
5700 		return (Z_ERR);
5701 	}
5702 
5703 	zerr(gettext("invalid %s %s '%s'"), rt_to_str(RT_ATTR),
5704 	    pt_to_str(PT_TYPE), attrtab->zone_attr_type);
5705 	return (Z_ERR);
5706 }
5707 
5708 /*
5709  * Helper function for end_func-- checks the existence of a given property
5710  * and emits a message if not specified.
5711  */
5712 static int
5713 end_check_reqd(char *attr, int pt, bool *validation_failed)
5714 {
5715 	if (strlen(attr) == 0) {
5716 		*validation_failed = TRUE;
5717 		zerr(gettext("%s not specified"), pt_to_str(pt));
5718 		return (Z_ERR);
5719 	}
5720 	return (Z_OK);
5721 }
5722 
5723 void
5724 end_func(cmd_t *cmd)
5725 {
5726 	bool validation_failed = FALSE;
5727 	bool arg_err = FALSE;
5728 	struct zone_fstab tmp_fstab;
5729 	struct zone_nwiftab tmp_nwiftab;
5730 	struct zone_devtab tmp_devtab;
5731 	struct zone_rctltab tmp_rctltab;
5732 	struct zone_attrtab tmp_attrtab;
5733 	struct zone_dstab tmp_dstab;
5734 	int err, arg, res1, res2, res3;
5735 	uint64_t swap_limit;
5736 	uint64_t locked_limit;
5737 	uint64_t proc_cap;
5738 
5739 	assert(cmd != NULL);
5740 
5741 	optind = 0;
5742 	while ((arg = getopt(cmd->cmd_argc, cmd->cmd_argv, "?")) != EOF) {
5743 		switch (arg) {
5744 		case '?':
5745 			longer_usage(CMD_END);
5746 			arg_err = TRUE;
5747 			break;
5748 		default:
5749 			short_usage(CMD_END);
5750 			arg_err = TRUE;
5751 			break;
5752 		}
5753 	}
5754 	if (arg_err)
5755 		return;
5756 
5757 	if (optind != cmd->cmd_argc) {
5758 		short_usage(CMD_END);
5759 		return;
5760 	}
5761 
5762 	if (global_scope) {
5763 		scope_usage(CMD_END);
5764 		return;
5765 	}
5766 
5767 	assert(end_op == CMD_ADD || end_op == CMD_SELECT);
5768 
5769 	switch (resource_scope) {
5770 	case RT_FS:
5771 		/* First make sure everything was filled in. */
5772 		if (end_check_reqd(in_progress_fstab.zone_fs_dir,
5773 		    PT_DIR, &validation_failed) == Z_OK) {
5774 			if (in_progress_fstab.zone_fs_dir[0] != '/') {
5775 				zerr(gettext("%s %s is not an absolute path."),
5776 				    pt_to_str(PT_DIR),
5777 				    in_progress_fstab.zone_fs_dir);
5778 				validation_failed = TRUE;
5779 			}
5780 		}
5781 
5782 		(void) end_check_reqd(in_progress_fstab.zone_fs_special,
5783 		    PT_SPECIAL, &validation_failed);
5784 
5785 		if (in_progress_fstab.zone_fs_raw[0] != '\0' &&
5786 		    in_progress_fstab.zone_fs_raw[0] != '/') {
5787 			zerr(gettext("%s %s is not an absolute path."),
5788 			    pt_to_str(PT_RAW),
5789 			    in_progress_fstab.zone_fs_raw);
5790 			validation_failed = TRUE;
5791 		}
5792 
5793 		(void) end_check_reqd(in_progress_fstab.zone_fs_type, PT_TYPE,
5794 		    &validation_failed);
5795 
5796 		if (validation_failed) {
5797 			saw_error = TRUE;
5798 			return;
5799 		}
5800 
5801 		if (end_op == CMD_ADD) {
5802 			/* Make sure there isn't already one like this. */
5803 			bzero(&tmp_fstab, sizeof (tmp_fstab));
5804 			(void) strlcpy(tmp_fstab.zone_fs_dir,
5805 			    in_progress_fstab.zone_fs_dir,
5806 			    sizeof (tmp_fstab.zone_fs_dir));
5807 			err = zonecfg_lookup_filesystem(handle, &tmp_fstab);
5808 			zonecfg_free_fs_option_list(tmp_fstab.zone_fs_options);
5809 			if (err == Z_OK) {
5810 				zerr(gettext("A %s resource "
5811 				    "with the %s '%s' already exists."),
5812 				    rt_to_str(RT_FS), pt_to_str(PT_DIR),
5813 				    in_progress_fstab.zone_fs_dir);
5814 				saw_error = TRUE;
5815 				return;
5816 			}
5817 			err = zonecfg_add_filesystem(handle,
5818 			    &in_progress_fstab);
5819 		} else {
5820 			err = zonecfg_modify_filesystem(handle, &old_fstab,
5821 			    &in_progress_fstab);
5822 		}
5823 		zonecfg_free_fs_option_list(in_progress_fstab.zone_fs_options);
5824 		in_progress_fstab.zone_fs_options = NULL;
5825 		break;
5826 
5827 	case RT_IPD:
5828 		/* First make sure everything was filled in. */
5829 		if (end_check_reqd(in_progress_ipdtab.zone_fs_dir, PT_DIR,
5830 		    &validation_failed) == Z_OK) {
5831 			if (in_progress_ipdtab.zone_fs_dir[0] != '/') {
5832 				zerr(gettext("%s %s is not an absolute path."),
5833 				    pt_to_str(PT_DIR),
5834 				    in_progress_ipdtab.zone_fs_dir);
5835 				validation_failed = TRUE;
5836 			}
5837 		}
5838 		if (validation_failed) {
5839 			saw_error = TRUE;
5840 			return;
5841 		}
5842 
5843 		if (end_op == CMD_ADD) {
5844 			/* Make sure there isn't already one like this. */
5845 			bzero(&tmp_fstab, sizeof (tmp_fstab));
5846 			(void) strlcpy(tmp_fstab.zone_fs_dir,
5847 			    in_progress_ipdtab.zone_fs_dir,
5848 			    sizeof (tmp_fstab.zone_fs_dir));
5849 			err = zonecfg_lookup_ipd(handle, &tmp_fstab);
5850 			if (err == Z_OK) {
5851 				zerr(gettext("An %s resource "
5852 				    "with the %s '%s' already exists."),
5853 				    rt_to_str(RT_IPD), pt_to_str(PT_DIR),
5854 				    in_progress_ipdtab.zone_fs_dir);
5855 				saw_error = TRUE;
5856 				return;
5857 			}
5858 			err = zonecfg_add_ipd(handle, &in_progress_ipdtab);
5859 		} else {
5860 			err = zonecfg_modify_ipd(handle, &old_ipdtab,
5861 			    &in_progress_ipdtab);
5862 		}
5863 		break;
5864 	case RT_NET:
5865 		/*
5866 		 * First make sure everything was filled in.
5867 		 * Since we don't know whether IP will be shared
5868 		 * or exclusive here, some checks are deferred until
5869 		 * the verify command.
5870 		 */
5871 		(void) end_check_reqd(in_progress_nwiftab.zone_nwif_physical,
5872 		    PT_PHYSICAL, &validation_failed);
5873 
5874 		if (validation_failed) {
5875 			saw_error = TRUE;
5876 			return;
5877 		}
5878 		if (end_op == CMD_ADD) {
5879 			/* Make sure there isn't already one like this. */
5880 			bzero(&tmp_nwiftab, sizeof (tmp_nwiftab));
5881 			(void) strlcpy(tmp_nwiftab.zone_nwif_physical,
5882 			    in_progress_nwiftab.zone_nwif_physical,
5883 			    sizeof (tmp_nwiftab.zone_nwif_physical));
5884 			(void) strlcpy(tmp_nwiftab.zone_nwif_address,
5885 			    in_progress_nwiftab.zone_nwif_address,
5886 			    sizeof (tmp_nwiftab.zone_nwif_address));
5887 			if (zonecfg_lookup_nwif(handle, &tmp_nwiftab) == Z_OK) {
5888 				zerr(gettext("A %s resource with the %s '%s', "
5889 				    "and %s '%s' already exists."),
5890 				    rt_to_str(RT_NET),
5891 				    pt_to_str(PT_PHYSICAL),
5892 				    in_progress_nwiftab.zone_nwif_physical,
5893 				    pt_to_str(PT_ADDRESS),
5894 				    in_progress_nwiftab.zone_nwif_address);
5895 				saw_error = TRUE;
5896 				return;
5897 			}
5898 			err = zonecfg_add_nwif(handle, &in_progress_nwiftab);
5899 		} else {
5900 			err = zonecfg_modify_nwif(handle, &old_nwiftab,
5901 			    &in_progress_nwiftab);
5902 		}
5903 		break;
5904 
5905 	case RT_DEVICE:
5906 		/* First make sure everything was filled in. */
5907 		(void) end_check_reqd(in_progress_devtab.zone_dev_match,
5908 		    PT_MATCH, &validation_failed);
5909 
5910 		if (validation_failed) {
5911 			saw_error = TRUE;
5912 			return;
5913 		}
5914 
5915 		if (end_op == CMD_ADD) {
5916 			/* Make sure there isn't already one like this. */
5917 			(void) strlcpy(tmp_devtab.zone_dev_match,
5918 			    in_progress_devtab.zone_dev_match,
5919 			    sizeof (tmp_devtab.zone_dev_match));
5920 			if (zonecfg_lookup_dev(handle, &tmp_devtab) == Z_OK) {
5921 				zerr(gettext("A %s resource with the %s '%s' "
5922 				    "already exists."), rt_to_str(RT_DEVICE),
5923 				    pt_to_str(PT_MATCH),
5924 				    in_progress_devtab.zone_dev_match);
5925 				saw_error = TRUE;
5926 				return;
5927 			}
5928 			err = zonecfg_add_dev(handle, &in_progress_devtab);
5929 		} else {
5930 			err = zonecfg_modify_dev(handle, &old_devtab,
5931 			    &in_progress_devtab);
5932 		}
5933 		break;
5934 
5935 	case RT_RCTL:
5936 		/* First make sure everything was filled in. */
5937 		(void) end_check_reqd(in_progress_rctltab.zone_rctl_name,
5938 		    PT_NAME, &validation_failed);
5939 
5940 		if (in_progress_rctltab.zone_rctl_valptr == NULL) {
5941 			zerr(gettext("no %s specified"), pt_to_str(PT_VALUE));
5942 			validation_failed = TRUE;
5943 		}
5944 
5945 		if (validation_failed) {
5946 			saw_error = TRUE;
5947 			return;
5948 		}
5949 
5950 		if (end_op == CMD_ADD) {
5951 			/* Make sure there isn't already one like this. */
5952 			(void) strlcpy(tmp_rctltab.zone_rctl_name,
5953 			    in_progress_rctltab.zone_rctl_name,
5954 			    sizeof (tmp_rctltab.zone_rctl_name));
5955 			tmp_rctltab.zone_rctl_valptr = NULL;
5956 			err = zonecfg_lookup_rctl(handle, &tmp_rctltab);
5957 			zonecfg_free_rctl_value_list(
5958 			    tmp_rctltab.zone_rctl_valptr);
5959 			if (err == Z_OK) {
5960 				zerr(gettext("A %s resource "
5961 				    "with the %s '%s' already exists."),
5962 				    rt_to_str(RT_RCTL), pt_to_str(PT_NAME),
5963 				    in_progress_rctltab.zone_rctl_name);
5964 				saw_error = TRUE;
5965 				return;
5966 			}
5967 			err = zonecfg_add_rctl(handle, &in_progress_rctltab);
5968 		} else {
5969 			err = zonecfg_modify_rctl(handle, &old_rctltab,
5970 			    &in_progress_rctltab);
5971 		}
5972 		if (err == Z_OK) {
5973 			zonecfg_free_rctl_value_list(
5974 			    in_progress_rctltab.zone_rctl_valptr);
5975 			in_progress_rctltab.zone_rctl_valptr = NULL;
5976 		}
5977 		break;
5978 
5979 	case RT_ATTR:
5980 		/* First make sure everything was filled in. */
5981 		(void) end_check_reqd(in_progress_attrtab.zone_attr_name,
5982 		    PT_NAME, &validation_failed);
5983 		(void) end_check_reqd(in_progress_attrtab.zone_attr_type,
5984 		    PT_TYPE, &validation_failed);
5985 		(void) end_check_reqd(in_progress_attrtab.zone_attr_value,
5986 		    PT_VALUE, &validation_failed);
5987 
5988 		if (validate_attr_name(in_progress_attrtab.zone_attr_name) !=
5989 		    Z_OK)
5990 			validation_failed = TRUE;
5991 
5992 		if (validate_attr_type_val(&in_progress_attrtab) != Z_OK)
5993 			validation_failed = TRUE;
5994 
5995 		if (validation_failed) {
5996 			saw_error = TRUE;
5997 			return;
5998 		}
5999 		if (end_op == CMD_ADD) {
6000 			/* Make sure there isn't already one like this. */
6001 			bzero(&tmp_attrtab, sizeof (tmp_attrtab));
6002 			(void) strlcpy(tmp_attrtab.zone_attr_name,
6003 			    in_progress_attrtab.zone_attr_name,
6004 			    sizeof (tmp_attrtab.zone_attr_name));
6005 			if (zonecfg_lookup_attr(handle, &tmp_attrtab) == Z_OK) {
6006 				zerr(gettext("An %s resource "
6007 				    "with the %s '%s' already exists."),
6008 				    rt_to_str(RT_ATTR), pt_to_str(PT_NAME),
6009 				    in_progress_attrtab.zone_attr_name);
6010 				saw_error = TRUE;
6011 				return;
6012 			}
6013 			err = zonecfg_add_attr(handle, &in_progress_attrtab);
6014 		} else {
6015 			err = zonecfg_modify_attr(handle, &old_attrtab,
6016 			    &in_progress_attrtab);
6017 		}
6018 		break;
6019 	case RT_DATASET:
6020 		/* First make sure everything was filled in. */
6021 		if (strlen(in_progress_dstab.zone_dataset_name) == 0) {
6022 			zerr("%s %s", pt_to_str(PT_NAME),
6023 			    gettext("not specified"));
6024 			saw_error = TRUE;
6025 			validation_failed = TRUE;
6026 		}
6027 		if (validation_failed)
6028 			return;
6029 		if (end_op == CMD_ADD) {
6030 			/* Make sure there isn't already one like this. */
6031 			bzero(&tmp_dstab, sizeof (tmp_dstab));
6032 			(void) strlcpy(tmp_dstab.zone_dataset_name,
6033 			    in_progress_dstab.zone_dataset_name,
6034 			    sizeof (tmp_dstab.zone_dataset_name));
6035 			err = zonecfg_lookup_ds(handle, &tmp_dstab);
6036 			if (err == Z_OK) {
6037 				zerr(gettext("A %s resource "
6038 				    "with the %s '%s' already exists."),
6039 				    rt_to_str(RT_DATASET), pt_to_str(PT_NAME),
6040 				    in_progress_dstab.zone_dataset_name);
6041 				saw_error = TRUE;
6042 				return;
6043 			}
6044 			err = zonecfg_add_ds(handle, &in_progress_dstab);
6045 		} else {
6046 			err = zonecfg_modify_ds(handle, &old_dstab,
6047 			    &in_progress_dstab);
6048 		}
6049 		break;
6050 	case RT_DCPU:
6051 		/* Make sure everything was filled in. */
6052 		if (end_check_reqd(in_progress_psettab.zone_ncpu_min,
6053 		    PT_NCPUS, &validation_failed) != Z_OK) {
6054 			saw_error = TRUE;
6055 			return;
6056 		}
6057 
6058 		if (end_op == CMD_ADD) {
6059 			err = zonecfg_add_pset(handle, &in_progress_psettab);
6060 		} else {
6061 			err = zonecfg_modify_pset(handle, &in_progress_psettab);
6062 		}
6063 		break;
6064 	case RT_PCAP:
6065 		/* Make sure everything was filled in. */
6066 		if (zonecfg_get_aliased_rctl(handle, ALIAS_CPUCAP, &proc_cap)
6067 		    != Z_OK) {
6068 			zerr(gettext("%s not specified"), pt_to_str(PT_NCPUS));
6069 			saw_error = TRUE;
6070 			validation_failed = TRUE;
6071 			return;
6072 		}
6073 		err = Z_OK;
6074 		break;
6075 	case RT_MCAP:
6076 		/* Make sure everything was filled in. */
6077 		res1 = strlen(in_progress_mcaptab.zone_physmem_cap) == 0 ?
6078 		    Z_ERR : Z_OK;
6079 		res2 = zonecfg_get_aliased_rctl(handle, ALIAS_MAXSWAP,
6080 		    &swap_limit);
6081 		res3 = zonecfg_get_aliased_rctl(handle, ALIAS_MAXLOCKEDMEM,
6082 		    &locked_limit);
6083 
6084 		if (res1 != Z_OK && res2 != Z_OK && res3 != Z_OK) {
6085 			zerr(gettext("No property was specified.  One of %s, "
6086 			    "%s or %s is required."), pt_to_str(PT_PHYSICAL),
6087 			    pt_to_str(PT_SWAP), pt_to_str(PT_LOCKED));
6088 			saw_error = TRUE;
6089 			return;
6090 		}
6091 
6092 		/* if phys & locked are both set, verify locked <= phys */
6093 		if (res1 == Z_OK && res3 == Z_OK) {
6094 			uint64_t phys_limit;
6095 			char *endp;
6096 
6097 			phys_limit = strtoull(
6098 			    in_progress_mcaptab.zone_physmem_cap, &endp, 10);
6099 			if (phys_limit < locked_limit) {
6100 				zerr(gettext("The %s cap must be less than or "
6101 				    "equal to the %s cap."),
6102 				    pt_to_str(PT_LOCKED),
6103 				    pt_to_str(PT_PHYSICAL));
6104 				saw_error = TRUE;
6105 				return;
6106 			}
6107 		}
6108 
6109 		err = Z_OK;
6110 		if (res1 == Z_OK) {
6111 			/*
6112 			 * We could be ending from either an add operation
6113 			 * or a select operation.  Since all of the properties
6114 			 * within this resource are optional, we always use
6115 			 * modify on the mcap entry.  zonecfg_modify_mcap()
6116 			 * will handle both adding and modifying a memory cap.
6117 			 */
6118 			err = zonecfg_modify_mcap(handle, &in_progress_mcaptab);
6119 		} else if (end_op == CMD_SELECT) {
6120 			/*
6121 			 * If we're ending from a select and the physical
6122 			 * memory cap is empty then the user could have cleared
6123 			 * the physical cap value, so try to delete the entry.
6124 			 */
6125 			(void) zonecfg_delete_mcap(handle);
6126 		}
6127 		break;
6128 	default:
6129 		zone_perror(rt_to_str(resource_scope), Z_NO_RESOURCE_TYPE,
6130 		    TRUE);
6131 		saw_error = TRUE;
6132 		return;
6133 	}
6134 
6135 	if (err != Z_OK) {
6136 		zone_perror(zone, err, TRUE);
6137 	} else {
6138 		need_to_commit = TRUE;
6139 		global_scope = TRUE;
6140 		end_op = -1;
6141 	}
6142 }
6143 
6144 void
6145 commit_func(cmd_t *cmd)
6146 {
6147 	int arg;
6148 	bool arg_err = FALSE;
6149 
6150 	optind = 0;
6151 	while ((arg = getopt(cmd->cmd_argc, cmd->cmd_argv, "?")) != EOF) {
6152 		switch (arg) {
6153 		case '?':
6154 			longer_usage(CMD_COMMIT);
6155 			arg_err = TRUE;
6156 			break;
6157 		default:
6158 			short_usage(CMD_COMMIT);
6159 			arg_err = TRUE;
6160 			break;
6161 		}
6162 	}
6163 	if (arg_err)
6164 		return;
6165 
6166 	if (optind != cmd->cmd_argc) {
6167 		short_usage(CMD_COMMIT);
6168 		return;
6169 	}
6170 
6171 	if (zone_is_read_only(CMD_COMMIT))
6172 		return;
6173 
6174 	assert(cmd != NULL);
6175 
6176 	cmd->cmd_argc = 1;
6177 	/*
6178 	 * cmd_arg normally comes from a strdup() in the lexer, and the
6179 	 * whole cmd structure and its (char *) attributes are freed at
6180 	 * the completion of each command, so the strdup() below is needed
6181 	 * to match this and prevent a core dump from trying to free()
6182 	 * something that can't be.
6183 	 */
6184 	if ((cmd->cmd_argv[0] = strdup("save")) == NULL) {
6185 		zone_perror(zone, Z_NOMEM, TRUE);
6186 		exit(Z_ERR);
6187 	}
6188 	cmd->cmd_argv[1] = NULL;
6189 	verify_func(cmd);
6190 }
6191 
6192 void
6193 revert_func(cmd_t *cmd)
6194 {
6195 	char line[128];	/* enough to ask a question */
6196 	bool force = FALSE;
6197 	bool arg_err = FALSE;
6198 	int err, arg, answer;
6199 
6200 	optind = 0;
6201 	while ((arg = getopt(cmd->cmd_argc, cmd->cmd_argv, "?F")) != EOF) {
6202 		switch (arg) {
6203 		case '?':
6204 			longer_usage(CMD_REVERT);
6205 			arg_err = TRUE;
6206 			break;
6207 		case 'F':
6208 			force = TRUE;
6209 			break;
6210 		default:
6211 			short_usage(CMD_REVERT);
6212 			arg_err = TRUE;
6213 			break;
6214 		}
6215 	}
6216 	if (arg_err)
6217 		return;
6218 
6219 	if (optind != cmd->cmd_argc) {
6220 		short_usage(CMD_REVERT);
6221 		return;
6222 	}
6223 
6224 	if (zone_is_read_only(CMD_REVERT))
6225 		return;
6226 
6227 	if (zonecfg_check_handle(handle) != Z_OK) {
6228 		zerr(gettext("No changes to revert."));
6229 		saw_error = TRUE;
6230 		return;
6231 	}
6232 
6233 	if (!force) {
6234 		(void) snprintf(line, sizeof (line),
6235 		    gettext("Are you sure you want to revert"));
6236 		if ((answer = ask_yesno(FALSE, line)) == -1) {
6237 			zerr(gettext("Input not from terminal and -F not "
6238 			    "specified:\n%s command ignored, exiting."),
6239 			    cmd_to_str(CMD_REVERT));
6240 			exit(Z_ERR);
6241 		}
6242 		if (answer != 1)
6243 			return;
6244 	}
6245 
6246 	/*
6247 	 * Time for a new handle: finish the old one off first
6248 	 * then get a new one properly to avoid leaks.
6249 	 */
6250 	zonecfg_fini_handle(handle);
6251 	if ((handle = zonecfg_init_handle()) == NULL) {
6252 		zone_perror(execname, Z_NOMEM, TRUE);
6253 		exit(Z_ERR);
6254 	}
6255 	if ((err = zonecfg_get_handle(revert_zone, handle)) != Z_OK) {
6256 		saw_error = TRUE;
6257 		got_handle = FALSE;
6258 		if (err == Z_NO_ZONE)
6259 			zerr(gettext("%s: no such saved zone to revert to."),
6260 			    revert_zone);
6261 		else
6262 			zone_perror(zone, err, TRUE);
6263 	}
6264 	(void) strlcpy(zone, revert_zone, sizeof (zone));
6265 }
6266 
6267 void
6268 help_func(cmd_t *cmd)
6269 {
6270 	int i;
6271 
6272 	assert(cmd != NULL);
6273 
6274 	if (cmd->cmd_argc == 0) {
6275 		usage(TRUE, global_scope ? HELP_SUBCMDS : HELP_RES_SCOPE);
6276 		return;
6277 	}
6278 	if (strcmp(cmd->cmd_argv[0], "usage") == 0) {
6279 		usage(TRUE, HELP_USAGE);
6280 		return;
6281 	}
6282 	if (strcmp(cmd->cmd_argv[0], "commands") == 0) {
6283 		usage(TRUE, HELP_SUBCMDS);
6284 		return;
6285 	}
6286 	if (strcmp(cmd->cmd_argv[0], "syntax") == 0) {
6287 		usage(TRUE, HELP_SYNTAX | HELP_RES_PROPS);
6288 		return;
6289 	}
6290 	if (strcmp(cmd->cmd_argv[0], "-?") == 0) {
6291 		longer_usage(CMD_HELP);
6292 		return;
6293 	}
6294 
6295 	for (i = 0; i <= CMD_MAX; i++) {
6296 		if (strcmp(cmd->cmd_argv[0], cmd_to_str(i)) == 0) {
6297 			longer_usage(i);
6298 			return;
6299 		}
6300 	}
6301 	/* We do not use zerr() here because we do not want its extra \n. */
6302 	(void) fprintf(stderr, gettext("Unknown help subject %s.  "),
6303 	    cmd->cmd_argv[0]);
6304 	usage(FALSE, HELP_META);
6305 }
6306 
6307 static int
6308 string_to_yyin(char *string)
6309 {
6310 	if ((yyin = tmpfile()) == NULL) {
6311 		zone_perror(execname, Z_TEMP_FILE, TRUE);
6312 		return (Z_ERR);
6313 	}
6314 	if (fwrite(string, strlen(string), 1, yyin) != 1) {
6315 		zone_perror(execname, Z_TEMP_FILE, TRUE);
6316 		return (Z_ERR);
6317 	}
6318 	if (fseek(yyin, 0, SEEK_SET) != 0) {
6319 		zone_perror(execname, Z_TEMP_FILE, TRUE);
6320 		return (Z_ERR);
6321 	}
6322 	return (Z_OK);
6323 }
6324 
6325 /* This is the back-end helper function for read_input() below. */
6326 
6327 static int
6328 cleanup()
6329 {
6330 	int answer;
6331 	cmd_t *cmd;
6332 
6333 	if (!interactive_mode && !cmd_file_mode) {
6334 		/*
6335 		 * If we're not in interactive mode, and we're not in command
6336 		 * file mode, then we must be in commands-from-the-command-line
6337 		 * mode.  As such, we can't loop back and ask for more input.
6338 		 * It was OK to prompt for such things as whether or not to
6339 		 * really delete a zone in the command handler called from
6340 		 * yyparse() above, but "really quit?" makes no sense in this
6341 		 * context.  So disable prompting.
6342 		 */
6343 		ok_to_prompt = FALSE;
6344 	}
6345 	if (!global_scope) {
6346 		if (!time_to_exit) {
6347 			/*
6348 			 * Just print a simple error message in the -1 case,
6349 			 * since exit_func() already handles that case, and
6350 			 * EOF means we are finished anyway.
6351 			 */
6352 			answer = ask_yesno(FALSE,
6353 			    gettext("Resource incomplete; really quit"));
6354 			if (answer == -1) {
6355 				zerr(gettext("Resource incomplete."));
6356 				return (Z_ERR);
6357 			}
6358 			if (answer != 1) {
6359 				yyin = stdin;
6360 				return (Z_REPEAT);
6361 			}
6362 		} else {
6363 			saw_error = TRUE;
6364 		}
6365 	}
6366 	/*
6367 	 * Make sure we tried something and that the handle checks
6368 	 * out, or we would get a false error trying to commit.
6369 	 */
6370 	if (need_to_commit && zonecfg_check_handle(handle) == Z_OK) {
6371 		if ((cmd = alloc_cmd()) == NULL) {
6372 			zone_perror(zone, Z_NOMEM, TRUE);
6373 			return (Z_ERR);
6374 		}
6375 		cmd->cmd_argc = 0;
6376 		cmd->cmd_argv[0] = NULL;
6377 		commit_func(cmd);
6378 		free_cmd(cmd);
6379 		/*
6380 		 * need_to_commit will get set back to FALSE if the
6381 		 * configuration is saved successfully.
6382 		 */
6383 		if (need_to_commit) {
6384 			if (force_exit) {
6385 				zerr(gettext("Configuration not saved."));
6386 				return (Z_ERR);
6387 			}
6388 			answer = ask_yesno(FALSE,
6389 			    gettext("Configuration not saved; really quit"));
6390 			if (answer == -1) {
6391 				zerr(gettext("Configuration not saved."));
6392 				return (Z_ERR);
6393 			}
6394 			if (answer != 1) {
6395 				time_to_exit = FALSE;
6396 				yyin = stdin;
6397 				return (Z_REPEAT);
6398 			}
6399 		}
6400 	}
6401 	return ((need_to_commit || saw_error) ? Z_ERR : Z_OK);
6402 }
6403 
6404 /*
6405  * read_input() is the driver of this program.  It is a wrapper around
6406  * yyparse(), printing appropriate prompts when needed, checking for
6407  * exit conditions and reacting appropriately [the latter in its cleanup()
6408  * helper function].
6409  *
6410  * Like most zonecfg functions, it returns Z_OK or Z_ERR, *or* Z_REPEAT
6411  * so do_interactive() knows that we are not really done (i.e, we asked
6412  * the user if we should really quit and the user said no).
6413  */
6414 static int
6415 read_input()
6416 {
6417 	bool yyin_is_a_tty = isatty(fileno(yyin));
6418 	/*
6419 	 * The prompt is "e:z> " or "e:z:r> " where e is execname, z is zone
6420 	 * and r is resource_scope: 5 is for the two ":"s + "> " + terminator.
6421 	 */
6422 	char prompt[MAXPATHLEN + ZONENAME_MAX + MAX_RT_STRLEN + 5], *line;
6423 
6424 	/* yyin should have been set to the appropriate (FILE *) if not stdin */
6425 	newline_terminated = TRUE;
6426 	for (;;) {
6427 		if (yyin_is_a_tty) {
6428 			if (newline_terminated) {
6429 				if (global_scope)
6430 					(void) snprintf(prompt, sizeof (prompt),
6431 					    "%s:%s> ", execname, zone);
6432 				else
6433 					(void) snprintf(prompt, sizeof (prompt),
6434 					    "%s:%s:%s> ", execname, zone,
6435 					    rt_to_str(resource_scope));
6436 			}
6437 			/*
6438 			 * If the user hits ^C then we want to catch it and
6439 			 * start over.  If the user hits EOF then we want to
6440 			 * bail out.
6441 			 */
6442 			line = gl_get_line(gl, prompt, NULL, -1);
6443 			if (gl_return_status(gl) == GLR_SIGNAL) {
6444 				gl_abandon_line(gl);
6445 				continue;
6446 			}
6447 			if (line == NULL)
6448 				break;
6449 			(void) string_to_yyin(line);
6450 			while (!feof(yyin))
6451 				yyparse();
6452 		} else {
6453 			yyparse();
6454 		}
6455 		/* Bail out on an error in command file mode. */
6456 		if (saw_error && cmd_file_mode && !interactive_mode)
6457 			time_to_exit = TRUE;
6458 		if (time_to_exit || (!yyin_is_a_tty && feof(yyin)))
6459 			break;
6460 	}
6461 	return (cleanup());
6462 }
6463 
6464 /*
6465  * This function is used in the zonecfg-interactive-mode scenario: it just
6466  * calls read_input() until we are done.
6467  */
6468 
6469 static int
6470 do_interactive(void)
6471 {
6472 	int err;
6473 
6474 	interactive_mode = TRUE;
6475 	if (!read_only_mode) {
6476 		/*
6477 		 * Try to set things up proactively in interactive mode, so
6478 		 * that if the zone in question does not exist yet, we can
6479 		 * provide the user with a clue.
6480 		 */
6481 		(void) initialize(FALSE);
6482 	}
6483 	do {
6484 		err = read_input();
6485 	} while (err == Z_REPEAT);
6486 	return (err);
6487 }
6488 
6489 /*
6490  * cmd_file is slightly more complicated, as it has to open the command file
6491  * and set yyin appropriately.  Once that is done, though, it just calls
6492  * read_input(), and only once, since prompting is not possible.
6493  */
6494 
6495 static int
6496 cmd_file(char *file)
6497 {
6498 	FILE *infile;
6499 	int err;
6500 	struct stat statbuf;
6501 	bool using_real_file = (strcmp(file, "-") != 0);
6502 
6503 	if (using_real_file) {
6504 		/*
6505 		 * zerr() prints a line number in cmd_file_mode, which we do
6506 		 * not want here, so temporarily unset it.
6507 		 */
6508 		cmd_file_mode = FALSE;
6509 		if ((infile = fopen(file, "r")) == NULL) {
6510 			zerr(gettext("could not open file %s: %s"),
6511 			    file, strerror(errno));
6512 			return (Z_ERR);
6513 		}
6514 		if ((err = fstat(fileno(infile), &statbuf)) != 0) {
6515 			zerr(gettext("could not stat file %s: %s"),
6516 			    file, strerror(errno));
6517 			err = Z_ERR;
6518 			goto done;
6519 		}
6520 		if (!S_ISREG(statbuf.st_mode)) {
6521 			zerr(gettext("%s is not a regular file."), file);
6522 			err = Z_ERR;
6523 			goto done;
6524 		}
6525 		yyin = infile;
6526 		cmd_file_mode = TRUE;
6527 		ok_to_prompt = FALSE;
6528 	} else {
6529 		/*
6530 		 * "-f -" is essentially the same as interactive mode,
6531 		 * so treat it that way.
6532 		 */
6533 		interactive_mode = TRUE;
6534 	}
6535 	/* Z_REPEAT is for interactive mode; treat it like Z_ERR here. */
6536 	if ((err = read_input()) == Z_REPEAT)
6537 		err = Z_ERR;
6538 done:
6539 	if (using_real_file)
6540 		(void) fclose(infile);
6541 	return (err);
6542 }
6543 
6544 /*
6545  * Since yacc is based on reading from a (FILE *) whereas what we get from
6546  * the command line is in argv format, we need to convert when the user
6547  * gives us commands directly from the command line.  That is done here by
6548  * concatenating the argv list into a space-separated string, writing it
6549  * to a temp file, and rewinding the file so yyin can be set to it.  Then
6550  * we call read_input(), and only once, since prompting about whether to
6551  * continue or quit would make no sense in this context.
6552  */
6553 
6554 static int
6555 one_command_at_a_time(int argc, char *argv[])
6556 {
6557 	char *command;
6558 	size_t len = 2; /* terminal \n\0 */
6559 	int i, err;
6560 
6561 	for (i = 0; i < argc; i++)
6562 		len += strlen(argv[i]) + 1;
6563 	if ((command = malloc(len)) == NULL) {
6564 		zone_perror(execname, Z_NOMEM, TRUE);
6565 		return (Z_ERR);
6566 	}
6567 	(void) strlcpy(command, argv[0], len);
6568 	for (i = 1; i < argc; i++) {
6569 		(void) strlcat(command, " ", len);
6570 		(void) strlcat(command, argv[i], len);
6571 	}
6572 	(void) strlcat(command, "\n", len);
6573 	err = string_to_yyin(command);
6574 	free(command);
6575 	if (err != Z_OK)
6576 		return (err);
6577 	while (!feof(yyin))
6578 		yyparse();
6579 	return (cleanup());
6580 }
6581 
6582 static char *
6583 get_execbasename(char *execfullname)
6584 {
6585 	char *last_slash, *execbasename;
6586 
6587 	/* guard against '/' at end of command invocation */
6588 	for (;;) {
6589 		last_slash = strrchr(execfullname, '/');
6590 		if (last_slash == NULL) {
6591 			execbasename = execfullname;
6592 			break;
6593 		} else {
6594 			execbasename = last_slash + 1;
6595 			if (*execbasename == '\0') {
6596 				*last_slash = '\0';
6597 				continue;
6598 			}
6599 			break;
6600 		}
6601 	}
6602 	return (execbasename);
6603 }
6604 
6605 int
6606 main(int argc, char *argv[])
6607 {
6608 	int err, arg;
6609 	struct stat st;
6610 
6611 	/* This must be before anything goes to stdout. */
6612 	setbuf(stdout, NULL);
6613 
6614 	saw_error = FALSE;
6615 	cmd_file_mode = FALSE;
6616 	execname = get_execbasename(argv[0]);
6617 
6618 	(void) setlocale(LC_ALL, "");
6619 	(void) textdomain(TEXT_DOMAIN);
6620 
6621 	if (getzoneid() != GLOBAL_ZONEID) {
6622 		zerr(gettext("%s can only be run from the global zone."),
6623 		    execname);
6624 		exit(Z_ERR);
6625 	}
6626 
6627 	if (argc < 2) {
6628 		usage(FALSE, HELP_USAGE | HELP_SUBCMDS);
6629 		exit(Z_USAGE);
6630 	}
6631 	if (strcmp(argv[1], cmd_to_str(CMD_HELP)) == 0) {
6632 		(void) one_command_at_a_time(argc - 1, &(argv[1]));
6633 		exit(Z_OK);
6634 	}
6635 
6636 	while ((arg = getopt(argc, argv, "?f:R:z:")) != EOF) {
6637 		switch (arg) {
6638 		case '?':
6639 			if (optopt == '?')
6640 				usage(TRUE, HELP_USAGE | HELP_SUBCMDS);
6641 			else
6642 				usage(FALSE, HELP_USAGE);
6643 			exit(Z_USAGE);
6644 			/* NOTREACHED */
6645 		case 'f':
6646 			cmd_file_name = optarg;
6647 			cmd_file_mode = TRUE;
6648 			break;
6649 		case 'R':
6650 			if (*optarg != '/') {
6651 				zerr(gettext("root path must be absolute: %s"),
6652 				    optarg);
6653 				exit(Z_USAGE);
6654 			}
6655 			if (stat(optarg, &st) == -1 || !S_ISDIR(st.st_mode)) {
6656 				zerr(gettext(
6657 				    "root path must be a directory: %s"),
6658 				    optarg);
6659 				exit(Z_USAGE);
6660 			}
6661 			zonecfg_set_root(optarg);
6662 			break;
6663 		case 'z':
6664 			if (strcmp(optarg, GLOBAL_ZONENAME) == 0) {
6665 				global_zone = TRUE;
6666 			} else if (zonecfg_validate_zonename(optarg) != Z_OK) {
6667 				zone_perror(optarg, Z_BOGUS_ZONE_NAME, TRUE);
6668 				usage(FALSE, HELP_SYNTAX);
6669 				exit(Z_USAGE);
6670 			}
6671 			(void) strlcpy(zone, optarg, sizeof (zone));
6672 			(void) strlcpy(revert_zone, optarg, sizeof (zone));
6673 			break;
6674 		default:
6675 			usage(FALSE, HELP_USAGE);
6676 			exit(Z_USAGE);
6677 		}
6678 	}
6679 
6680 	if (optind > argc || strcmp(zone, "") == 0) {
6681 		usage(FALSE, HELP_USAGE);
6682 		exit(Z_USAGE);
6683 	}
6684 
6685 	if ((err = zonecfg_access(zone, W_OK)) == Z_OK) {
6686 		read_only_mode = FALSE;
6687 	} else if (err == Z_ACCES) {
6688 		read_only_mode = TRUE;
6689 		/* skip this message in one-off from command line mode */
6690 		if (optind == argc)
6691 			(void) fprintf(stderr, gettext("WARNING: you do not "
6692 			    "have write access to this zone's configuration "
6693 			    "file;\ngoing into read-only mode.\n"));
6694 	} else {
6695 		fprintf(stderr, "%s: Could not access zone configuration "
6696 		    "store: %s\n", execname, zonecfg_strerror(err));
6697 		exit(Z_ERR);
6698 	}
6699 
6700 	if ((handle = zonecfg_init_handle()) == NULL) {
6701 		zone_perror(execname, Z_NOMEM, TRUE);
6702 		exit(Z_ERR);
6703 	}
6704 
6705 	/*
6706 	 * This may get set back to FALSE again in cmd_file() if cmd_file_name
6707 	 * is a "real" file as opposed to "-" (i.e. meaning use stdin).
6708 	 */
6709 	if (isatty(STDIN_FILENO))
6710 		ok_to_prompt = TRUE;
6711 	if ((gl = new_GetLine(MAX_LINE_LEN, MAX_CMD_HIST)) == NULL)
6712 		exit(Z_ERR);
6713 	if (gl_customize_completion(gl, NULL, cmd_cpl_fn) != 0)
6714 		exit(Z_ERR);
6715 	(void) sigset(SIGINT, SIG_IGN);
6716 	if (optind == argc) {
6717 		if (!cmd_file_mode)
6718 			err = do_interactive();
6719 		else
6720 			err = cmd_file(cmd_file_name);
6721 	} else {
6722 		err = one_command_at_a_time(argc - optind, &(argv[optind]));
6723 	}
6724 	zonecfg_fini_handle(handle);
6725 	if (brand != NULL)
6726 		brand_close(brand);
6727 	(void) del_GetLine(gl);
6728 	return (err);
6729 }
6730