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