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