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