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