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