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