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