xref: /illumos-gate/usr/src/cmd/zonecfg/zonecfg.c (revision e2738c5e21a9e5d9a6525e48af4738deda3df455)
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 2006 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * zonecfg is a lex/yacc based command interpreter used to manage zone
31  * configurations.  The lexer (see zonecfg_lex.l) builds up tokens, which
32  * the grammar (see zonecfg_grammar.y) builds up into commands, some of
33  * which takes resources and/or properties as arguments.  See the block
34  * comments near the end of zonecfg_grammar.y for how the data structures
35  * which keep track of these resources and properties are built up.
36  *
37  * The resource/property data structures are inserted into a command
38  * structure (see zonecfg.h), which also keeps track of command names,
39  * miscellaneous arguments, and function handlers.  The grammar selects
40  * the appropriate function handler, each of which takes a pointer to a
41  * command structure as its sole argument, and invokes it.  The grammar
42  * itself is "entered" (a la the Matrix) by yyparse(), which is called
43  * from read_input(), our main driving function.  That in turn is called
44  * by one of do_interactive(), cmd_file() or one_command_at_a_time(), each
45  * of which is called from main() depending on how the program was invoked.
46  *
47  * The rest of this module consists of the various function handlers and
48  * their helper functions.  Some of these functions, particularly the
49  * X_to_str() functions, which maps command, resource and property numbers
50  * to strings, are used quite liberally, as doing so results in a better
51  * program w/rt I18N, reducing the need for translation notes.
52  */
53 
54 #include <sys/mntent.h>
55 #include <sys/varargs.h>
56 #include <sys/sysmacros.h>
57 
58 #include <errno.h>
59 #include <strings.h>
60 #include <unistd.h>
61 #include <ctype.h>
62 #include <stdlib.h>
63 #include <assert.h>
64 #include <sys/stat.h>
65 #include <zone.h>
66 #include <arpa/inet.h>
67 #include <netdb.h>
68 #include <locale.h>
69 #include <libintl.h>
70 #include <alloca.h>
71 #include <signal.h>
72 #include <libtecla.h>
73 #include <libzfs.h>
74 
75 #include <libzonecfg.h>
76 #include "zonecfg.h"
77 
78 #if !defined(TEXT_DOMAIN)		/* should be defined by cc -D */
79 #define	TEXT_DOMAIN	"SYS_TEST"	/* Use this only if it wasn't */
80 #endif
81 
82 #define	PAGER	"/usr/bin/more"
83 
84 struct help {
85 	uint_t	cmd_num;
86 	char	*cmd_name;
87 	uint_t	flags;
88 	char	*short_usage;
89 };
90 
91 extern int yyparse(void);
92 extern int lex_lineno;
93 
94 #define	MAX_LINE_LEN	1024
95 #define	MAX_CMD_HIST	1024
96 
97 /*
98  * Each SHELP_ should be a simple string.
99  */
100 
101 #define	SHELP_ADD	"add <resource-type>\n\t(global scope)\n" \
102 	"add <property-name> <property-value>\n\t(resource scope)"
103 #define	SHELP_CANCEL	"cancel"
104 #define	SHELP_COMMIT	"commit"
105 #define	SHELP_CREATE	"create [-F] [ -a <path> | -b | -t <template> ]"
106 #define	SHELP_DELETE	"delete [-F]"
107 #define	SHELP_END	"end"
108 #define	SHELP_EXIT	"exit [-F]"
109 #define	SHELP_EXPORT	"export [-f output-file]"
110 #define	SHELP_HELP	"help [commands] [syntax] [usage] [<command-name>]"
111 #define	SHELP_INFO	"info [<resource-type> [property-name=property-value]*]"
112 #define	SHELP_REMOVE	"remove <resource-type> { <property-name>=<property-" \
113 	"value> }\n\t(global scope)\nremove <property-name> <property-value>" \
114 	"\n\t(resource scope)"
115 #define	SHELP_REVERT	"revert [-F]"
116 #define	SHELP_SELECT	"select <resource-type> { <property-name>=" \
117 	"<property-value> }"
118 #define	SHELP_SET	"set <property-name>=<property-value>"
119 #define	SHELP_VERIFY	"verify"
120 
121 static struct help helptab[] = {
122 	{ CMD_ADD,	"add",		HELP_RES_PROPS,	SHELP_ADD, },
123 	{ CMD_CANCEL,	"cancel",	0,		SHELP_CANCEL, },
124 	{ CMD_COMMIT,	"commit",	0,		SHELP_COMMIT, },
125 	{ CMD_CREATE,	"create",	0,		SHELP_CREATE, },
126 	{ CMD_DELETE,	"delete",	0,		SHELP_DELETE, },
127 	{ CMD_END,	"end",		0,		SHELP_END, },
128 	{ CMD_EXIT,	"exit",		0,		SHELP_EXIT, },
129 	{ CMD_EXPORT,	"export",	0,		SHELP_EXPORT, },
130 	{ CMD_HELP,	"help",		0,		SHELP_HELP },
131 	{ CMD_INFO,	"info",		HELP_RES_PROPS,	SHELP_INFO, },
132 	{ CMD_REMOVE,	"remove",	HELP_RES_PROPS,	SHELP_REMOVE, },
133 	{ CMD_REVERT,	"revert",	0,		SHELP_REVERT, },
134 	{ CMD_SELECT,	"select",	HELP_RES_PROPS,	SHELP_SELECT, },
135 	{ CMD_SET,	"set",		HELP_PROPS,	SHELP_SET, },
136 	{ CMD_VERIFY,	"verify",	0,		SHELP_VERIFY, },
137 	{ 0 },
138 };
139 
140 #define	MAX_RT_STRLEN	16
141 
142 /* These *must* match the order of the RT_ define's from zonecfg.h */
143 static char *res_types[] = {
144 	"unknown",
145 	"zonename",
146 	"zonepath",
147 	"autoboot",
148 	"pool",
149 	"fs",
150 	"inherit-pkg-dir",
151 	"net",
152 	"device",
153 	"rctl",
154 	"attr",
155 	"dataset",
156 	"limitpriv",
157 	"bootargs",
158 	NULL
159 };
160 
161 /* These *must* match the order of the PT_ define's from zonecfg.h */
162 static char *prop_types[] = {
163 	"unknown",
164 	"zonename",
165 	"zonepath",
166 	"autoboot",
167 	"pool",
168 	"dir",
169 	"special",
170 	"type",
171 	"options",
172 	"address",
173 	"physical",
174 	"name",
175 	"value",
176 	"match",
177 	"priv",
178 	"limit",
179 	"action",
180 	"raw",
181 	"limitpriv",
182 	"bootargs",
183 	NULL
184 };
185 
186 /* These *must* match the order of the PROP_VAL_ define's from zonecfg.h */
187 static char *prop_val_types[] = {
188 	"simple",
189 	"complex",
190 	"list",
191 };
192 
193 /*
194  * The various _cmds[] lists below are for command tab-completion.
195  */
196 
197 /*
198  * remove has a space afterwards because it has qualifiers; the other commands
199  * that have qualifiers (add, select and set) don't need a space here because
200  * they have their own _cmds[] lists below.
201  */
202 static const char *global_scope_cmds[] = {
203 	"add",
204 	"commit",
205 	"create",
206 	"delete",
207 	"exit",
208 	"export",
209 	"help",
210 	"info",
211 	"remove ",
212 	"revert",
213 	"select",
214 	"set",
215 	"verify",
216 	NULL
217 };
218 
219 static const char *add_cmds[] = {
220 	"add fs",
221 	"add inherit-pkg-dir",
222 	"add net",
223 	"add device",
224 	"add rctl",
225 	"add attr",
226 	"add dataset",
227 	NULL
228 };
229 
230 static const char *remove_cmds[] = {
231 	"remove fs ",
232 	"remove inherit-pkg-dir ",
233 	"remove net ",
234 	"remove device ",
235 	"remove rctl ",
236 	"remove attr ",
237 	"remove dataset ",
238 	NULL
239 };
240 
241 static const char *select_cmds[] = {
242 	"select fs ",
243 	"select inherit-pkg-dir ",
244 	"select net ",
245 	"select device ",
246 	"select rctl ",
247 	"select attr ",
248 	"select dataset ",
249 	NULL
250 };
251 
252 static const char *set_cmds[] = {
253 	"set zonename=",
254 	"set zonepath=",
255 	"set autoboot=",
256 	"set pool=",
257 	"set limitpriv=",
258 	"set bootargs=",
259 	NULL
260 };
261 
262 static const char *info_cmds[] = {
263 	"info fs ",
264 	"info inherit-pkg-dir ",
265 	"info net ",
266 	"info device ",
267 	"info rctl ",
268 	"info attr ",
269 	"info dataset ",
270 	"info zonename",
271 	"info zonepath",
272 	"info autoboot",
273 	"info pool",
274 	"info limitpriv",
275 	"info bootargs",
276 	NULL
277 };
278 
279 static const char *fs_res_scope_cmds[] = {
280 	"add options ",
281 	"cancel",
282 	"end",
283 	"exit",
284 	"help",
285 	"info",
286 	"remove options ",
287 	"set dir=",
288 	"set raw=",
289 	"set special=",
290 	"set type=",
291 	NULL
292 };
293 
294 static const char *net_res_scope_cmds[] = {
295 	"cancel",
296 	"end",
297 	"exit",
298 	"help",
299 	"info",
300 	"set address=",
301 	"set physical=",
302 	NULL
303 };
304 
305 static const char *ipd_res_scope_cmds[] = {
306 	"cancel",
307 	"end",
308 	"exit",
309 	"help",
310 	"info",
311 	"set dir=",
312 	NULL
313 };
314 
315 static const char *device_res_scope_cmds[] = {
316 	"cancel",
317 	"end",
318 	"exit",
319 	"help",
320 	"info",
321 	"set match=",
322 	NULL
323 };
324 
325 static const char *attr_res_scope_cmds[] = {
326 	"cancel",
327 	"end",
328 	"exit",
329 	"help",
330 	"info",
331 	"set name=",
332 	"set type=",
333 	"set value=",
334 	NULL
335 };
336 
337 static const char *rctl_res_scope_cmds[] = {
338 	"add value ",
339 	"cancel",
340 	"end",
341 	"exit",
342 	"help",
343 	"info",
344 	"remove value ",
345 	"set name=",
346 	NULL
347 };
348 
349 static const char *dataset_res_scope_cmds[] = {
350 	"cancel",
351 	"end",
352 	"exit",
353 	"help",
354 	"info",
355 	"set name=",
356 	NULL
357 };
358 
359 /* Global variables */
360 
361 /* set early in main(), never modified thereafter, used all over the place */
362 static char *execname;
363 
364 /* set in main(), used all over the place */
365 static zone_dochandle_t handle;
366 
367 /* used all over the place */
368 static char zone[ZONENAME_MAX];
369 static char revert_zone[ZONENAME_MAX];
370 
371 /* set in modifying functions, checked in read_input() */
372 static bool need_to_commit = FALSE;
373 bool saw_error;
374 
375 /* set in yacc parser, checked in read_input() */
376 bool newline_terminated;
377 
378 /* set in main(), checked in lex error handler */
379 bool cmd_file_mode;
380 
381 /* set in exit_func(), checked in read_input() */
382 static bool time_to_exit = FALSE, force_exit = FALSE;
383 
384 /* used in short_usage() and zerr() */
385 static char *cmd_file_name = NULL;
386 
387 /* checked in read_input() and other places */
388 static bool ok_to_prompt = FALSE;
389 
390 /* set and checked in initialize() */
391 static bool got_handle = FALSE;
392 
393 /* initialized in do_interactive(), checked in initialize() */
394 static bool interactive_mode;
395 
396 /* set in main(), checked in multiple places */
397 static bool read_only_mode;
398 
399 static bool global_scope = TRUE; /* scope is outer/global or inner/resource */
400 static int resource_scope;	/* should be in the RT_ list from zonecfg.h */
401 static int end_op = -1;		/* operation on end is either add or modify */
402 
403 int num_prop_vals;		/* for grammar */
404 
405 /*
406  * These are for keeping track of resources as they are specified as part of
407  * the multi-step process.  They should be initialized by add_resource() or
408  * select_func() and filled in by add_property() or set_func().
409  */
410 static struct zone_fstab	old_fstab, in_progress_fstab;
411 static struct zone_fstab	old_ipdtab, in_progress_ipdtab;
412 static struct zone_nwiftab	old_nwiftab, in_progress_nwiftab;
413 static struct zone_devtab	old_devtab, in_progress_devtab;
414 static struct zone_rctltab	old_rctltab, in_progress_rctltab;
415 static struct zone_attrtab	old_attrtab, in_progress_attrtab;
416 static struct zone_dstab	old_dstab, in_progress_dstab;
417 
418 static GetLine *gl;	/* The gl_get_line() resource object */
419 
420 /* Functions begin here */
421 
422 static bool
423 initial_match(const char *line1, const char *line2, int word_end)
424 {
425 	if (word_end <= 0)
426 		return (TRUE);
427 	return (strncmp(line1, line2, word_end) == 0);
428 }
429 
430 static int
431 add_stuff(WordCompletion *cpl, const char *line1, const char **list,
432     int word_end)
433 {
434 	int i, err;
435 
436 	for (i = 0; list[i] != NULL; i++) {
437 		if (initial_match(line1, list[i], word_end)) {
438 			err = cpl_add_completion(cpl, line1, 0, word_end,
439 			    list[i] + word_end, "", "");
440 			if (err != 0)
441 				return (err);
442 		}
443 	}
444 	return (0);
445 }
446 
447 static
448 /* ARGSUSED */
449 CPL_MATCH_FN(cmd_cpl_fn)
450 {
451 	if (global_scope) {
452 		/*
453 		 * The MAX/MIN tests below are to make sure we have at least
454 		 * enough characters to distinguish from other prefixes (MAX)
455 		 * but only check MIN(what we have, what we're checking).
456 		 */
457 		if (strncmp(line, "add ", MAX(MIN(word_end, 4), 1)) == 0)
458 			return (add_stuff(cpl, line, add_cmds, word_end));
459 		if (strncmp(line, "select ", MAX(MIN(word_end, 7), 3)) == 0)
460 			return (add_stuff(cpl, line, select_cmds, word_end));
461 		if (strncmp(line, "set ", MAX(MIN(word_end, 4), 3)) == 0)
462 			return (add_stuff(cpl, line, set_cmds, word_end));
463 		if (strncmp(line, "remove ", MAX(MIN(word_end, 7), 1)) == 0)
464 			return (add_stuff(cpl, line, remove_cmds, word_end));
465 		if (strncmp(line, "info ", MAX(MIN(word_end, 5), 1)) == 0)
466 			return (add_stuff(cpl, line, info_cmds, word_end));
467 		return (add_stuff(cpl, line, global_scope_cmds, word_end));
468 	}
469 	switch (resource_scope) {
470 	case RT_FS:
471 		return (add_stuff(cpl, line, fs_res_scope_cmds, word_end));
472 	case RT_IPD:
473 		return (add_stuff(cpl, line, ipd_res_scope_cmds, word_end));
474 	case RT_NET:
475 		return (add_stuff(cpl, line, net_res_scope_cmds, word_end));
476 	case RT_DEVICE:
477 		return (add_stuff(cpl, line, device_res_scope_cmds, word_end));
478 	case RT_RCTL:
479 		return (add_stuff(cpl, line, rctl_res_scope_cmds, word_end));
480 	case RT_ATTR:
481 		return (add_stuff(cpl, line, attr_res_scope_cmds, word_end));
482 	case RT_DATASET:
483 		return (add_stuff(cpl, line, dataset_res_scope_cmds, word_end));
484 	}
485 	return (0);
486 }
487 
488 /*
489  * For the main CMD_func() functions below, several of them call getopt()
490  * then check optind against argc to make sure an extra parameter was not
491  * passed in.  The reason this is not caught in the grammar is that the
492  * grammar just checks for a miscellaneous TOKEN, which is *expected* to
493  * be "-F" (for example), but could be anything.  So (for example) this
494  * check will prevent "create bogus".
495  */
496 
497 cmd_t *
498 alloc_cmd(void)
499 {
500 	return (calloc(1, sizeof (cmd_t)));
501 }
502 
503 void
504 free_cmd(cmd_t *cmd)
505 {
506 	int i;
507 
508 	for (i = 0; i < MAX_EQ_PROP_PAIRS; i++)
509 		if (cmd->cmd_property_ptr[i] != NULL) {
510 			property_value_ptr_t pp = cmd->cmd_property_ptr[i];
511 
512 			switch (pp->pv_type) {
513 			case PROP_VAL_SIMPLE:
514 				free(pp->pv_simple);
515 				break;
516 			case PROP_VAL_COMPLEX:
517 				free_complex(pp->pv_complex);
518 				break;
519 			case PROP_VAL_LIST:
520 				free_list(pp->pv_list);
521 				break;
522 			}
523 		}
524 	for (i = 0; i < cmd->cmd_argc; i++)
525 		free(cmd->cmd_argv[i]);
526 	free(cmd);
527 }
528 
529 complex_property_ptr_t
530 alloc_complex(void)
531 {
532 	return (calloc(1, sizeof (complex_property_t)));
533 }
534 
535 void
536 free_complex(complex_property_ptr_t complex)
537 {
538 	if (complex == NULL)
539 		return;
540 	free_complex(complex->cp_next);
541 	if (complex->cp_value != NULL)
542 		free(complex->cp_value);
543 	free(complex);
544 }
545 
546 list_property_ptr_t
547 alloc_list(void)
548 {
549 	return (calloc(1, sizeof (list_property_t)));
550 }
551 
552 void
553 free_list(list_property_ptr_t list)
554 {
555 	if (list == NULL)
556 		return;
557 	if (list->lp_simple != NULL)
558 		free(list->lp_simple);
559 	free_complex(list->lp_complex);
560 	free_list(list->lp_next);
561 	free(list);
562 }
563 
564 void
565 free_outer_list(list_property_ptr_t list)
566 {
567 	if (list == NULL)
568 		return;
569 	free_outer_list(list->lp_next);
570 	free(list);
571 }
572 
573 static struct zone_rctlvaltab *
574 alloc_rctlvaltab(void)
575 {
576 	return (calloc(1, sizeof (struct zone_rctlvaltab)));
577 }
578 
579 static char *
580 rt_to_str(int res_type)
581 {
582 	assert(res_type >= RT_MIN && res_type <= RT_MAX);
583 	return (res_types[res_type]);
584 }
585 
586 static char *
587 pt_to_str(int prop_type)
588 {
589 	assert(prop_type >= PT_MIN && prop_type <= PT_MAX);
590 	return (prop_types[prop_type]);
591 }
592 
593 static char *
594 pvt_to_str(int pv_type)
595 {
596 	assert(pv_type >= PROP_VAL_MIN && pv_type <= PROP_VAL_MAX);
597 	return (prop_val_types[pv_type]);
598 }
599 
600 static char *
601 cmd_to_str(int cmd_num)
602 {
603 	assert(cmd_num >= CMD_MIN && cmd_num <= CMD_MAX);
604 	return (helptab[cmd_num].cmd_name);
605 }
606 
607 /*
608  * This is a separate function rather than a set of define's because of the
609  * gettext() wrapping.
610  */
611 
612 /*
613  * TRANSLATION_NOTE
614  * Each string below should have \t follow \n whenever needed; the
615  * initial \t and the terminal \n will be provided by the calling function.
616  */
617 
618 static char *
619 long_help(int cmd_num)
620 {
621 	static char line[1024];	/* arbitrary large amount */
622 
623 	assert(cmd_num >= CMD_MIN && cmd_num <= CMD_MAX);
624 	switch (cmd_num) {
625 		case CMD_HELP:
626 			return (gettext("Prints help message."));
627 		case CMD_CREATE:
628 			(void) snprintf(line, sizeof (line),
629 			    gettext("Creates a configuration for the "
630 			    "specified zone.  %s should be\n\tused to "
631 			    "begin configuring a new zone.  If overwriting an "
632 			    "existing\n\tconfiguration, the -F flag can be "
633 			    "used to force the action.  If\n\t-t template is "
634 			    "given, creates a configuration identical to the\n"
635 			    "\tspecified template, except that the zone name "
636 			    "is changed from\n\ttemplate to zonename.  '%s -a' "
637 			    "creates a configuration from a\n\tdetached "
638 			    "zonepath.  '%s -b' results in a blank "
639 			    "configuration.\n\t'%s' with no arguments applies "
640 			    "the Sun default settings."),
641 			    cmd_to_str(CMD_CREATE), cmd_to_str(CMD_CREATE),
642 			    cmd_to_str(CMD_CREATE), cmd_to_str(CMD_CREATE));
643 			return (line);
644 		case CMD_EXIT:
645 			return (gettext("Exits the program.  The -F flag can "
646 			    "be used to force the action."));
647 		case CMD_EXPORT:
648 			return (gettext("Prints configuration to standard "
649 			    "output, or to output-file if\n\tspecified, in "
650 			    "a form suitable for use in a command-file."));
651 		case CMD_ADD:
652 			return (gettext("Add specified resource to "
653 			    "configuration."));
654 		case CMD_DELETE:
655 			return (gettext("Deletes the specified zone.  The -F "
656 			    "flag can be used to force the\n\taction."));
657 		case CMD_REMOVE:
658 			return (gettext("Remove specified resource from "
659 			    "configuration.  Note that the curly\n\tbraces "
660 			    "('{', '}') mean one or more of whatever "
661 			    "is between them."));
662 		case CMD_SELECT:
663 			(void) snprintf(line, sizeof (line),
664 			    gettext("Selects a resource to modify.  "
665 			    "Resource modification is completed\n\twith the "
666 			    "command \"%s\".  The property name/value pairs "
667 			    "must uniquely\n\tidentify a resource.  Note that "
668 			    "the curly braces ('{', '}') mean one\n\tor more "
669 			    "of whatever is between them."),
670 			    cmd_to_str(CMD_END));
671 			return (line);
672 		case CMD_SET:
673 			return (gettext("Sets property values."));
674 		case CMD_INFO:
675 			return (gettext("Displays information about the "
676 			    "current configuration.  If resource\n\ttype is "
677 			    "specified, displays only information about "
678 			    "resources of\n\tthe relevant type.  If resource "
679 			    "id is specified, displays only\n\tinformation "
680 			    "about that resource."));
681 		case CMD_VERIFY:
682 			return (gettext("Verifies current configuration "
683 			    "for correctness (some resource types\n\thave "
684 			    "required properties)."));
685 		case CMD_COMMIT:
686 			(void) snprintf(line, sizeof (line),
687 			    gettext("Commits current configuration.  "
688 			    "Configuration must be committed to\n\tbe used by "
689 			    "%s.  Until the configuration is committed, "
690 			    "changes \n\tcan be removed with the %s "
691 			    "command.  This operation is\n\tattempted "
692 			    "automatically upon completion of a %s "
693 			    "session."), "zoneadm", cmd_to_str(CMD_REVERT),
694 			    "zonecfg");
695 			return (line);
696 		case CMD_REVERT:
697 			return (gettext("Reverts configuration back to the "
698 			    "last committed state.  The -F flag\n\tcan be "
699 			    "used to force the action."));
700 		case CMD_CANCEL:
701 			return (gettext("Cancels resource/property "
702 			    "specification."));
703 		case CMD_END:
704 			return (gettext("Ends resource/property "
705 			    "specification."));
706 	}
707 	/* NOTREACHED */
708 	return (NULL);
709 }
710 
711 /*
712  * Called with verbose TRUE when help is explicitly requested, FALSE for
713  * unexpected errors.
714  */
715 
716 void
717 usage(bool verbose, uint_t flags)
718 {
719 	FILE *fp = verbose ? stdout : stderr, *newfp;
720 	bool need_to_close = FALSE;
721 	char *pager;
722 	int i;
723 
724 	/* don't page error output */
725 	if (verbose && interactive_mode) {
726 		if ((pager = getenv("PAGER")) == NULL)
727 			pager = PAGER;
728 		if ((newfp = popen(pager, "w")) != NULL) {
729 			need_to_close = TRUE;
730 			fp = newfp;
731 		}
732 	}
733 	if (flags & HELP_META) {
734 		(void) fprintf(fp, gettext("More help is available for the "
735 		    "following:\n"));
736 		(void) fprintf(fp, "\n\tcommands ('%s commands')\n",
737 		    cmd_to_str(CMD_HELP));
738 		(void) fprintf(fp, "\tsyntax ('%s syntax')\n",
739 		    cmd_to_str(CMD_HELP));
740 		(void) fprintf(fp, "\tusage ('%s usage')\n\n",
741 		    cmd_to_str(CMD_HELP));
742 		(void) fprintf(fp, gettext("You may also obtain help on any "
743 		    "command by typing '%s <command-name>.'\n"),
744 		    cmd_to_str(CMD_HELP));
745 	}
746 	if (flags & HELP_RES_SCOPE) {
747 		switch (resource_scope) {
748 		case RT_FS:
749 			(void) fprintf(fp, gettext("The '%s' resource scope is "
750 			    "used to configure a file-system.\n"),
751 			    rt_to_str(resource_scope));
752 			(void) fprintf(fp, gettext("Valid commands:\n"));
753 			(void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
754 			    pt_to_str(PT_DIR), gettext("<path>"));
755 			(void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
756 			    pt_to_str(PT_SPECIAL), gettext("<path>"));
757 			(void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
758 			    pt_to_str(PT_RAW), gettext("<raw-device>"));
759 			(void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
760 			    pt_to_str(PT_TYPE), gettext("<file-system type>"));
761 			(void) fprintf(fp, "\t%s %s %s\n", cmd_to_str(CMD_ADD),
762 			    pt_to_str(PT_OPTIONS),
763 			    gettext("<file-system options>"));
764 			(void) fprintf(fp, "\t%s %s %s\n",
765 			    cmd_to_str(CMD_REMOVE), pt_to_str(PT_OPTIONS),
766 			    gettext("<file-system options>"));
767 			(void) fprintf(fp, gettext("Consult the file-system "
768 			    "specific manual page, such as mount_ufs(1M), "
769 			    "for\ndetails about file-system options.  Note "
770 			    "that any file-system options with an\nembedded "
771 			    "'=' character must be enclosed in double quotes, "
772 			    /*CSTYLED*/
773 			    "such as \"%s=5\".\n"), MNTOPT_RETRY);
774 			break;
775 		case RT_IPD:
776 			(void) fprintf(fp, gettext("The '%s' resource scope is "
777 			    "used to configure a directory\ninherited from the "
778 			    "global zone into a non-global zone in read-only "
779 			    "mode.\n"), rt_to_str(resource_scope));
780 			(void) fprintf(fp, gettext("Valid commands:\n"));
781 			(void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
782 			    pt_to_str(PT_DIR), gettext("<path>"));
783 			break;
784 		case RT_NET:
785 			(void) fprintf(fp, gettext("The '%s' resource scope is "
786 			    "used to configure a network interface.\n"),
787 			    rt_to_str(resource_scope));
788 			(void) fprintf(fp, gettext("Valid commands:\n"));
789 			(void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
790 			    pt_to_str(PT_ADDRESS), gettext("<IP-address>"));
791 			(void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
792 			    pt_to_str(PT_PHYSICAL), gettext("<interface>"));
793 			(void) fprintf(fp, gettext("See ifconfig(1M) for "
794 			    "details of the <interface> string.\n"));
795 			break;
796 		case RT_DEVICE:
797 			(void) fprintf(fp, gettext("The '%s' resource scope is "
798 			    "used to configure a device node.\n"),
799 			    rt_to_str(resource_scope));
800 			(void) fprintf(fp, gettext("Valid commands:\n"));
801 			(void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
802 			    pt_to_str(PT_MATCH), gettext("<device-path>"));
803 			break;
804 		case RT_RCTL:
805 			(void) fprintf(fp, gettext("The '%s' resource scope is "
806 			    "used to configure a resource control.\n"),
807 			    rt_to_str(resource_scope));
808 			(void) fprintf(fp, gettext("Valid commands:\n"));
809 			(void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
810 			    pt_to_str(PT_NAME), gettext("<string>"));
811 			(void) fprintf(fp, "\t%s %s (%s=%s,%s=%s,%s=%s)\n",
812 			    cmd_to_str(CMD_ADD), pt_to_str(PT_VALUE),
813 			    pt_to_str(PT_PRIV), gettext("<priv-value>"),
814 			    pt_to_str(PT_LIMIT), gettext("<number>"),
815 			    pt_to_str(PT_ACTION), gettext("<action-value>"));
816 			(void) fprintf(fp, "\t%s %s (%s=%s,%s=%s,%s=%s)\n",
817 			    cmd_to_str(CMD_REMOVE), pt_to_str(PT_VALUE),
818 			    pt_to_str(PT_PRIV), gettext("<priv-value>"),
819 			    pt_to_str(PT_LIMIT), gettext("<number>"),
820 			    pt_to_str(PT_ACTION), gettext("<action-value>"));
821 			(void) fprintf(fp, "%s\n\t%s := privileged\n"
822 			    "\t%s := none | deny\n", gettext("Where"),
823 			    gettext("<priv-value>"), gettext("<action-value>"));
824 			break;
825 		case RT_ATTR:
826 			(void) fprintf(fp, gettext("The '%s' resource scope is "
827 			    "used to configure a generic attribute.\n"),
828 			    rt_to_str(resource_scope));
829 			(void) fprintf(fp, gettext("Valid commands:\n"));
830 			(void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
831 			    pt_to_str(PT_NAME), gettext("<name>"));
832 			(void) fprintf(fp, "\t%s %s=boolean\n",
833 			    cmd_to_str(CMD_SET), pt_to_str(PT_TYPE));
834 			(void) fprintf(fp, "\t%s %s=true | false\n",
835 			    cmd_to_str(CMD_SET), pt_to_str(PT_VALUE));
836 			(void) fprintf(fp, gettext("or\n"));
837 			(void) fprintf(fp, "\t%s %s=int\n", cmd_to_str(CMD_SET),
838 			    pt_to_str(PT_TYPE));
839 			(void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
840 			    pt_to_str(PT_VALUE), gettext("<integer>"));
841 			(void) fprintf(fp, gettext("or\n"));
842 			(void) fprintf(fp, "\t%s %s=string\n",
843 			    cmd_to_str(CMD_SET), pt_to_str(PT_TYPE));
844 			(void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
845 			    pt_to_str(PT_VALUE), gettext("<string>"));
846 			(void) fprintf(fp, gettext("or\n"));
847 			(void) fprintf(fp, "\t%s %s=uint\n",
848 			    cmd_to_str(CMD_SET), pt_to_str(PT_TYPE));
849 			(void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
850 			    pt_to_str(PT_VALUE), gettext("<unsigned integer>"));
851 			break;
852 		case RT_DATASET:
853 			(void) fprintf(fp, gettext("The '%s' resource scope is "
854 			    "used to export ZFS datasets.\n"),
855 			    rt_to_str(resource_scope));
856 			(void) fprintf(fp, gettext("Valid commands:\n"));
857 			(void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
858 			    pt_to_str(PT_NAME), gettext("<name>"));
859 			break;
860 		}
861 		(void) fprintf(fp, gettext("And from any resource scope, you "
862 		    "can:\n"));
863 		(void) fprintf(fp, "\t%s\t%s\n", cmd_to_str(CMD_END),
864 		    gettext("(to conclude this operation)"));
865 		(void) fprintf(fp, "\t%s\t%s\n", cmd_to_str(CMD_CANCEL),
866 		    gettext("(to cancel this operation)"));
867 		(void) fprintf(fp, "\t%s\t%s\n", cmd_to_str(CMD_EXIT),
868 		    gettext("(to exit the zonecfg utility)"));
869 	}
870 	if (flags & HELP_USAGE) {
871 		(void) fprintf(fp, "%s:\t%s %s\n", gettext("usage"),
872 		    execname, cmd_to_str(CMD_HELP));
873 		(void) fprintf(fp, "\t%s -z <zone>\t\t\t(%s)\n",
874 		    execname, gettext("interactive"));
875 		(void) fprintf(fp, "\t%s -z <zone> <command>\n", execname);
876 		(void) fprintf(fp, "\t%s -z <zone> -f <command-file>\n",
877 		    execname);
878 	}
879 	if (flags & HELP_SUBCMDS) {
880 		(void) fprintf(fp, "%s:\n\n", gettext("Commands"));
881 		for (i = 0; i <= CMD_MAX; i++) {
882 			(void) fprintf(fp, "%s\n", helptab[i].short_usage);
883 			if (verbose)
884 				(void) fprintf(fp, "\t%s\n\n", long_help(i));
885 		}
886 	}
887 	if (flags & HELP_SYNTAX) {
888 		if (!verbose)
889 			(void) fprintf(fp, "\n");
890 		(void) fprintf(fp, "<zone> := [A-Za-z0-9][A-Za-z0-9_.-]*\n");
891 		(void) fprintf(fp, gettext("\t(except the reserved words "
892 		    "'%s' and anything starting with '%s')\n"), "global",
893 		    "SUNW");
894 		(void) fprintf(fp,
895 		    gettext("\tName must be less than %d characters.\n"),
896 		    ZONENAME_MAX);
897 		if (verbose)
898 			(void) fprintf(fp, "\n");
899 	}
900 	if (flags & HELP_NETADDR) {
901 		(void) fprintf(fp, gettext("\n<net-addr> :="));
902 		(void) fprintf(fp,
903 		    gettext("\t<IPv4-address>[/<IPv4-prefix-length>] |\n"));
904 		(void) fprintf(fp,
905 		    gettext("\t\t<IPv6-address>/<IPv6-prefix-length> |\n"));
906 		(void) fprintf(fp,
907 		    gettext("\t\t<hostname>[/<IPv4-prefix-length>]\n"));
908 		(void) fprintf(fp, gettext("See inet(3SOCKET) for IPv4 and "
909 		    "IPv6 address syntax.\n"));
910 		(void) fprintf(fp, gettext("<IPv4-prefix-length> := [0-32]\n"));
911 		(void) fprintf(fp,
912 		    gettext("<IPv6-prefix-length> := [0-128]\n"));
913 		(void) fprintf(fp,
914 		    gettext("<hostname> := [A-Za-z0-9][A-Za-z0-9-.]*\n"));
915 	}
916 	if (flags & HELP_RESOURCES) {
917 		(void) fprintf(fp, "<%s> := %s | %s | %s | %s | %s | %s |\n\t"
918 		    "%s\n\n",
919 		    gettext("resource type"), rt_to_str(RT_FS),
920 		    rt_to_str(RT_IPD), rt_to_str(RT_NET), rt_to_str(RT_DEVICE),
921 		    rt_to_str(RT_RCTL), rt_to_str(RT_ATTR),
922 		    rt_to_str(RT_DATASET));
923 	}
924 	if (flags & HELP_PROPS) {
925 		(void) fprintf(fp, gettext("For resource type ... there are "
926 		    "property types ...:\n"));
927 		(void) fprintf(fp, "\t%s\t%s\n", gettext("(global)"),
928 		    pt_to_str(PT_ZONENAME));
929 		(void) fprintf(fp, "\t%s\t%s\n", gettext("(global)"),
930 		    pt_to_str(PT_ZONEPATH));
931 		(void) fprintf(fp, "\t%s\t%s\n", gettext("(global)"),
932 		    pt_to_str(PT_AUTOBOOT));
933 		(void) fprintf(fp, "\t%s\t%s\n", gettext("(global)"),
934 		    pt_to_str(PT_BOOTARGS));
935 		(void) fprintf(fp, "\t%s\t%s\n", gettext("(global)"),
936 		    pt_to_str(PT_POOL));
937 		(void) fprintf(fp, "\t%s\t%s\n", gettext("(global)"),
938 		    pt_to_str(PT_LIMITPRIV));
939 		(void) fprintf(fp, "\t%s\t\t%s, %s, %s, %s\n", rt_to_str(RT_FS),
940 		    pt_to_str(PT_DIR), pt_to_str(PT_SPECIAL),
941 		    pt_to_str(PT_RAW), pt_to_str(PT_TYPE),
942 		    pt_to_str(PT_OPTIONS));
943 		(void) fprintf(fp, "\t%s\t%s\n", rt_to_str(RT_IPD),
944 		    pt_to_str(PT_DIR));
945 		(void) fprintf(fp, "\t%s\t\t%s, %s\n", rt_to_str(RT_NET),
946 		    pt_to_str(PT_ADDRESS), pt_to_str(PT_PHYSICAL));
947 		(void) fprintf(fp, "\t%s\t\t%s\n", rt_to_str(RT_DEVICE),
948 		    pt_to_str(PT_MATCH));
949 		(void) fprintf(fp, "\t%s\t\t%s, %s\n", rt_to_str(RT_RCTL),
950 		    pt_to_str(PT_NAME), pt_to_str(PT_VALUE));
951 		(void) fprintf(fp, "\t%s\t\t%s, %s, %s\n", rt_to_str(RT_ATTR),
952 		    pt_to_str(PT_NAME), pt_to_str(PT_TYPE),
953 		    pt_to_str(PT_VALUE));
954 		(void) fprintf(fp, "\t%s\t\t%s\n", rt_to_str(RT_DATASET),
955 		    pt_to_str(PT_NAME));
956 	}
957 	if (need_to_close)
958 		(void) pclose(fp);
959 }
960 
961 /* PRINTFLIKE1 */
962 static void
963 zerr(const char *fmt, ...)
964 {
965 	va_list alist;
966 	static int last_lineno;
967 
968 	/* lex_lineno has already been incremented in the lexer; compensate */
969 	if (cmd_file_mode && lex_lineno > last_lineno) {
970 		if (strcmp(cmd_file_name, "-") == 0)
971 			(void) fprintf(stderr, gettext("On line %d:\n"),
972 			    lex_lineno - 1);
973 		else
974 			(void) fprintf(stderr, gettext("On line %d of %s:\n"),
975 			    lex_lineno - 1, cmd_file_name);
976 		last_lineno = lex_lineno;
977 	}
978 	va_start(alist, fmt);
979 	(void) vfprintf(stderr, fmt, alist);
980 	(void) fprintf(stderr, "\n");
981 	va_end(alist);
982 }
983 
984 static void
985 zone_perror(char *prefix, int err, bool set_saw)
986 {
987 	zerr("%s: %s", prefix, zonecfg_strerror(err));
988 	if (set_saw)
989 		saw_error = TRUE;
990 }
991 
992 /*
993  * zone_perror() expects a single string, but for remove and select
994  * we have both the command and the resource type, so this wrapper
995  * function serves the same purpose in a slightly different way.
996  */
997 
998 static void
999 z_cmd_rt_perror(int cmd_num, int res_num, int err, bool set_saw)
1000 {
1001 	zerr("%s %s: %s", cmd_to_str(cmd_num), rt_to_str(res_num),
1002 	    zonecfg_strerror(err));
1003 	if (set_saw)
1004 		saw_error = TRUE;
1005 }
1006 
1007 /* returns Z_OK if successful, Z_foo from <libzonecfg.h> otherwise */
1008 static int
1009 initialize(bool handle_expected)
1010 {
1011 	int err;
1012 
1013 	if (zonecfg_check_handle(handle) != Z_OK) {
1014 		if ((err = zonecfg_get_handle(zone, handle)) == Z_OK) {
1015 			got_handle = TRUE;
1016 		} else {
1017 			zone_perror(zone, err, handle_expected || got_handle);
1018 			if (err == Z_NO_ZONE && !got_handle &&
1019 			    interactive_mode && !read_only_mode)
1020 				(void) printf(gettext("Use '%s' to begin "
1021 				    "configuring a new zone.\n"),
1022 				    cmd_to_str(CMD_CREATE));
1023 			return (err);
1024 		}
1025 	}
1026 	return (Z_OK);
1027 }
1028 
1029 static bool
1030 state_atleast(zone_state_t state)
1031 {
1032 	zone_state_t state_num;
1033 	int err;
1034 
1035 	if ((err = zone_get_state(zone, &state_num)) != Z_OK) {
1036 		/* all states are greater than "non-existent" */
1037 		if (err == Z_NO_ZONE)
1038 			return (B_FALSE);
1039 		zerr(gettext("Unexpectedly failed to determine state "
1040 		    "of zone %s: %s"), zone, zonecfg_strerror(err));
1041 		exit(Z_ERR);
1042 	}
1043 	return (state_num >= state);
1044 }
1045 
1046 /*
1047  * short_usage() is for bad syntax: getopt() issues, too many arguments, etc.
1048  */
1049 
1050 void
1051 short_usage(int command)
1052 {
1053 	/* lex_lineno has already been incremented in the lexer; compensate */
1054 	if (cmd_file_mode) {
1055 		if (strcmp(cmd_file_name, "-") == 0)
1056 			(void) fprintf(stderr,
1057 			    gettext("syntax error on line %d\n"),
1058 			    lex_lineno - 1);
1059 		else
1060 			(void) fprintf(stderr,
1061 			    gettext("syntax error on line %d of %s\n"),
1062 			    lex_lineno - 1, cmd_file_name);
1063 	}
1064 	(void) fprintf(stderr, "%s:\n%s\n", gettext("usage"),
1065 	    helptab[command].short_usage);
1066 	saw_error = TRUE;
1067 }
1068 
1069 /*
1070  * long_usage() is for bad semantics: e.g., wrong property type for a given
1071  * resource type.  It is also used by longer_usage() below.
1072  */
1073 
1074 void
1075 long_usage(uint_t cmd_num, bool set_saw)
1076 {
1077 	(void) fprintf(set_saw ? stderr : stdout, "%s:\n%s\n", gettext("usage"),
1078 	    helptab[cmd_num].short_usage);
1079 	(void) fprintf(set_saw ? stderr : stdout, "\t%s\n", long_help(cmd_num));
1080 	if (set_saw)
1081 		saw_error = TRUE;
1082 }
1083 
1084 /*
1085  * longer_usage() is for 'help foo' and 'foo -?': call long_usage() and also
1086  * any extra usage() flags as appropriate for whatever command.
1087  */
1088 
1089 void
1090 longer_usage(uint_t cmd_num)
1091 {
1092 	long_usage(cmd_num, FALSE);
1093 	if (helptab[cmd_num].flags != 0) {
1094 		(void) printf("\n");
1095 		usage(TRUE, helptab[cmd_num].flags);
1096 	}
1097 }
1098 
1099 /*
1100  * scope_usage() is simply used when a command is called from the wrong scope.
1101  */
1102 
1103 static void
1104 scope_usage(uint_t cmd_num)
1105 {
1106 	zerr(gettext("The %s command only makes sense in the %s scope."),
1107 	    cmd_to_str(cmd_num),
1108 	    global_scope ?  gettext("resource") : gettext("global"));
1109 	saw_error = TRUE;
1110 }
1111 
1112 /*
1113  * On input, TRUE => yes, FALSE => no.
1114  * On return, TRUE => 1, FALSE => no, could not ask => -1.
1115  */
1116 
1117 static int
1118 ask_yesno(bool default_answer, const char *question)
1119 {
1120 	char line[64];	/* should be enough to answer yes or no */
1121 
1122 	if (!ok_to_prompt) {
1123 		saw_error = TRUE;
1124 		return (-1);
1125 	}
1126 	for (;;) {
1127 		if (printf("%s (%s)? ", question,
1128 		    default_answer ? "[y]/n" : "y/[n]") < 0)
1129 			return (-1);
1130 		if (fgets(line, sizeof (line), stdin) == NULL)
1131 			return (-1);
1132 
1133 		if (line[0] == '\n')
1134 			return (default_answer ? 1 : 0);
1135 		if (tolower(line[0]) == 'y')
1136 			return (1);
1137 		if (tolower(line[0]) == 'n')
1138 			return (0);
1139 	}
1140 }
1141 
1142 /*
1143  * Prints warning if zone already exists.
1144  * In interactive mode, prompts if we should continue anyway and returns Z_OK
1145  * if so, Z_ERR if not.  In non-interactive mode, exits with Z_ERR.
1146  *
1147  * Note that if a zone exists and its state is >= INSTALLED, an error message
1148  * will be printed and this function will return Z_ERR regardless of mode.
1149  */
1150 
1151 static int
1152 check_if_zone_already_exists(bool force)
1153 {
1154 	char line[ZONENAME_MAX + 128];	/* enough to ask a question */
1155 	zone_dochandle_t tmphandle;
1156 	int res, answer;
1157 
1158 	if ((tmphandle = zonecfg_init_handle()) == NULL) {
1159 		zone_perror(execname, Z_NOMEM, TRUE);
1160 		exit(Z_ERR);
1161 	}
1162 	res = zonecfg_get_handle(zone, tmphandle);
1163 	zonecfg_fini_handle(tmphandle);
1164 	if (res != Z_OK)
1165 		return (Z_OK);
1166 
1167 	if (state_atleast(ZONE_STATE_INSTALLED)) {
1168 		zerr(gettext("Zone %s already installed; %s not allowed."),
1169 		    zone, cmd_to_str(CMD_CREATE));
1170 		return (Z_ERR);
1171 	}
1172 
1173 	if (force) {
1174 		(void) printf(gettext("Zone %s already exists; overwriting.\n"),
1175 		    zone);
1176 		return (Z_OK);
1177 	}
1178 	(void) snprintf(line, sizeof (line),
1179 	    gettext("Zone %s already exists; %s anyway"), zone,
1180 	    cmd_to_str(CMD_CREATE));
1181 	if ((answer = ask_yesno(FALSE, line)) == -1) {
1182 		zerr(gettext("Zone exists, input not from terminal and -F not "
1183 		    "specified:\n%s command ignored, exiting."),
1184 		    cmd_to_str(CMD_CREATE));
1185 		exit(Z_ERR);
1186 	}
1187 	return (answer == 1 ? Z_OK : Z_ERR);
1188 }
1189 
1190 static bool
1191 zone_is_read_only(int cmd_num)
1192 {
1193 	if (strncmp(zone, "SUNW", 4) == 0) {
1194 		zerr(gettext("%s: zones beginning with SUNW are read-only."),
1195 		    zone);
1196 		saw_error = TRUE;
1197 		return (TRUE);
1198 	}
1199 	if (read_only_mode) {
1200 		zerr(gettext("%s: cannot %s in read-only mode."), zone,
1201 		    cmd_to_str(cmd_num));
1202 		saw_error = TRUE;
1203 		return (TRUE);
1204 	}
1205 	return (FALSE);
1206 }
1207 
1208 /*
1209  * Create a new configuration.
1210  */
1211 void
1212 create_func(cmd_t *cmd)
1213 {
1214 	int err, arg;
1215 	char zone_template[ZONENAME_MAX];
1216 	char attach_path[MAXPATHLEN];
1217 	zone_dochandle_t tmphandle;
1218 	bool force = FALSE;
1219 	bool attach = FALSE;
1220 
1221 	assert(cmd != NULL);
1222 
1223 	/* This is the default if no arguments are given. */
1224 	(void) strlcpy(zone_template, "SUNWdefault", sizeof (zone_template));
1225 
1226 	optind = 0;
1227 	while ((arg = getopt(cmd->cmd_argc, cmd->cmd_argv, "?a:bFt:")) != EOF) {
1228 		switch (arg) {
1229 		case '?':
1230 			if (optopt == '?')
1231 				longer_usage(CMD_CREATE);
1232 			else
1233 				short_usage(CMD_CREATE);
1234 			return;
1235 		case 'a':
1236 			(void) strlcpy(attach_path, optarg,
1237 			    sizeof (attach_path));
1238 			attach = TRUE;
1239 			break;
1240 		case 'b':
1241 			(void) strlcpy(zone_template, "SUNWblank",
1242 			    sizeof (zone_template));
1243 			break;
1244 		case 'F':
1245 			force = TRUE;
1246 			break;
1247 		case 't':
1248 			(void) strlcpy(zone_template, optarg,
1249 			    sizeof (zone_template));
1250 			break;
1251 		default:
1252 			short_usage(CMD_CREATE);
1253 			return;
1254 		}
1255 	}
1256 	if (optind != cmd->cmd_argc) {
1257 		short_usage(CMD_CREATE);
1258 		return;
1259 	}
1260 
1261 	if (zone_is_read_only(CMD_CREATE))
1262 		return;
1263 
1264 	if (check_if_zone_already_exists(force) != Z_OK)
1265 		return;
1266 
1267 	/*
1268 	 * Get a temporary handle first.  If that fails, the old handle
1269 	 * will not be lost.  Then finish whichever one we don't need,
1270 	 * to avoid leaks.  Then get the handle for zone_template, and
1271 	 * set the name to zone: this "copy, rename" method is how
1272 	 * create -[b|t] works.
1273 	 */
1274 	if ((tmphandle = zonecfg_init_handle()) == NULL) {
1275 		zone_perror(execname, Z_NOMEM, TRUE);
1276 		exit(Z_ERR);
1277 	}
1278 
1279 	if (attach)
1280 		err = zonecfg_get_attach_handle(attach_path, zone, B_FALSE,
1281 		    tmphandle);
1282 	else
1283 		err = zonecfg_get_template_handle(zone_template, zone,
1284 		    tmphandle);
1285 
1286 	if (err != Z_OK) {
1287 		zonecfg_fini_handle(tmphandle);
1288 		if (attach && err == Z_NO_ZONE)
1289 			(void) fprintf(stderr, gettext("invalid path to "
1290 			    "detached zone\n"));
1291 		else if (attach && err == Z_INVALID_DOCUMENT)
1292 			(void) fprintf(stderr, gettext("Cannot attach to an "
1293 			    "earlier release of the operating system\n"));
1294 		else
1295 			zone_perror(zone_template, err, TRUE);
1296 		return;
1297 	}
1298 
1299 	need_to_commit = TRUE;
1300 	zonecfg_fini_handle(handle);
1301 	handle = tmphandle;
1302 	got_handle = TRUE;
1303 }
1304 
1305 /*
1306  * This malloc()'s memory, which must be freed by the caller.
1307  */
1308 static char *
1309 quoteit(char *instr)
1310 {
1311 	char *outstr;
1312 	size_t outstrsize = strlen(instr) + 3;	/* 2 quotes + '\0' */
1313 
1314 	if ((outstr = malloc(outstrsize)) == NULL) {
1315 		zone_perror(zone, Z_NOMEM, FALSE);
1316 		exit(Z_ERR);
1317 	}
1318 	if (strchr(instr, ' ') == NULL) {
1319 		(void) strlcpy(outstr, instr, outstrsize);
1320 		return (outstr);
1321 	}
1322 	(void) snprintf(outstr, outstrsize, "\"%s\"", instr);
1323 	return (outstr);
1324 }
1325 
1326 static void
1327 export_prop(FILE *of, int prop_num, char *prop_id)
1328 {
1329 	char *quote_str;
1330 
1331 	if (strlen(prop_id) == 0)
1332 		return;
1333 	quote_str = quoteit(prop_id);
1334 	(void) fprintf(of, "%s %s=%s\n", cmd_to_str(CMD_SET),
1335 	    pt_to_str(prop_num), quote_str);
1336 	free(quote_str);
1337 }
1338 
1339 void
1340 export_func(cmd_t *cmd)
1341 {
1342 	struct zone_nwiftab nwiftab;
1343 	struct zone_fstab fstab;
1344 	struct zone_devtab devtab;
1345 	struct zone_attrtab attrtab;
1346 	struct zone_rctltab rctltab;
1347 	struct zone_dstab dstab;
1348 	struct zone_rctlvaltab *valptr;
1349 	int err, arg;
1350 	char zonepath[MAXPATHLEN], outfile[MAXPATHLEN], pool[MAXNAMELEN];
1351 	char bootargs[BOOTARGS_MAX];
1352 	char *limitpriv;
1353 	FILE *of;
1354 	boolean_t autoboot;
1355 	bool need_to_close = FALSE;
1356 
1357 	assert(cmd != NULL);
1358 
1359 	outfile[0] = '\0';
1360 	optind = 0;
1361 	while ((arg = getopt(cmd->cmd_argc, cmd->cmd_argv, "?f:")) != EOF) {
1362 		switch (arg) {
1363 		case '?':
1364 			if (optopt == '?')
1365 				longer_usage(CMD_EXPORT);
1366 			else
1367 				short_usage(CMD_EXPORT);
1368 			return;
1369 		case 'f':
1370 			(void) strlcpy(outfile, optarg, sizeof (outfile));
1371 			break;
1372 		default:
1373 			short_usage(CMD_EXPORT);
1374 			return;
1375 		}
1376 	}
1377 	if (optind != cmd->cmd_argc) {
1378 		short_usage(CMD_EXPORT);
1379 		return;
1380 	}
1381 	if (strlen(outfile) == 0) {
1382 		of = stdout;
1383 	} else {
1384 		if ((of = fopen(outfile, "w")) == NULL) {
1385 			zerr(gettext("opening file %s: %s"),
1386 			    outfile, strerror(errno));
1387 			goto done;
1388 		}
1389 		setbuf(of, NULL);
1390 		need_to_close = TRUE;
1391 	}
1392 
1393 	if ((err = initialize(TRUE)) != Z_OK)
1394 		goto done;
1395 
1396 	(void) fprintf(of, "%s -b\n", cmd_to_str(CMD_CREATE));
1397 
1398 	if (zonecfg_get_zonepath(handle, zonepath, sizeof (zonepath)) == Z_OK &&
1399 	    strlen(zonepath) > 0)
1400 		(void) fprintf(of, "%s %s=%s\n", cmd_to_str(CMD_SET),
1401 		    pt_to_str(PT_ZONEPATH), zonepath);
1402 
1403 	if (zonecfg_get_autoboot(handle, &autoboot) == Z_OK)
1404 		(void) fprintf(of, "%s %s=%s\n", cmd_to_str(CMD_SET),
1405 		    pt_to_str(PT_AUTOBOOT), autoboot ? "true" : "false");
1406 
1407 	if (zonecfg_get_bootargs(handle, bootargs, sizeof (bootargs)) == Z_OK &&
1408 	    strlen(bootargs) > 0) {
1409 		(void) fprintf(of, "%s %s=%s\n", cmd_to_str(CMD_SET),
1410 		    pt_to_str(PT_BOOTARGS), bootargs);
1411 	}
1412 
1413 	if (zonecfg_get_pool(handle, pool, sizeof (pool)) == Z_OK &&
1414 	    strlen(pool) > 0)
1415 		(void) fprintf(of, "%s %s=%s\n", cmd_to_str(CMD_SET),
1416 		    pt_to_str(PT_POOL), pool);
1417 
1418 	if (zonecfg_get_limitpriv(handle, &limitpriv) == Z_OK &&
1419 	    strlen(limitpriv) > 0) {
1420 		(void) fprintf(of, "%s %s=%s\n", cmd_to_str(CMD_SET),
1421 		    pt_to_str(PT_LIMITPRIV), limitpriv);
1422 		free(limitpriv);
1423 	}
1424 
1425 
1426 	if ((err = zonecfg_setipdent(handle)) != Z_OK) {
1427 		zone_perror(zone, err, FALSE);
1428 		goto done;
1429 	}
1430 	while (zonecfg_getipdent(handle, &fstab) == Z_OK) {
1431 		(void) fprintf(of, "%s %s\n", cmd_to_str(CMD_ADD),
1432 		    rt_to_str(RT_IPD));
1433 		export_prop(of, PT_DIR, fstab.zone_fs_dir);
1434 		(void) fprintf(of, "%s\n", cmd_to_str(CMD_END));
1435 	}
1436 	(void) zonecfg_endipdent(handle);
1437 
1438 	if ((err = zonecfg_setfsent(handle)) != Z_OK) {
1439 		zone_perror(zone, err, FALSE);
1440 		goto done;
1441 	}
1442 	while (zonecfg_getfsent(handle, &fstab) == Z_OK) {
1443 		zone_fsopt_t *optptr;
1444 
1445 		(void) fprintf(of, "%s %s\n", cmd_to_str(CMD_ADD),
1446 		    rt_to_str(RT_FS));
1447 		export_prop(of, PT_DIR, fstab.zone_fs_dir);
1448 		export_prop(of, PT_SPECIAL, fstab.zone_fs_special);
1449 		export_prop(of, PT_RAW, fstab.zone_fs_raw);
1450 		export_prop(of, PT_TYPE, fstab.zone_fs_type);
1451 		for (optptr = fstab.zone_fs_options; optptr != NULL;
1452 		    optptr = optptr->zone_fsopt_next) {
1453 			/*
1454 			 * Simple property values with embedded equal signs
1455 			 * need to be quoted to prevent the lexer from
1456 			 * mis-parsing them as complex name=value pairs.
1457 			 */
1458 			if (strchr(optptr->zone_fsopt_opt, '='))
1459 				(void) fprintf(of, "%s %s \"%s\"\n",
1460 				    cmd_to_str(CMD_ADD),
1461 				    pt_to_str(PT_OPTIONS),
1462 				    optptr->zone_fsopt_opt);
1463 			else
1464 				(void) fprintf(of, "%s %s %s\n",
1465 				    cmd_to_str(CMD_ADD),
1466 				    pt_to_str(PT_OPTIONS),
1467 				    optptr->zone_fsopt_opt);
1468 		}
1469 		(void) fprintf(of, "%s\n", cmd_to_str(CMD_END));
1470 		zonecfg_free_fs_option_list(fstab.zone_fs_options);
1471 	}
1472 	(void) zonecfg_endfsent(handle);
1473 
1474 	if ((err = zonecfg_setnwifent(handle)) != Z_OK) {
1475 		zone_perror(zone, err, FALSE);
1476 		goto done;
1477 	}
1478 	while (zonecfg_getnwifent(handle, &nwiftab) == Z_OK) {
1479 		(void) fprintf(of, "%s %s\n", cmd_to_str(CMD_ADD),
1480 		    rt_to_str(RT_NET));
1481 		export_prop(of, PT_ADDRESS, nwiftab.zone_nwif_address);
1482 		export_prop(of, PT_PHYSICAL, nwiftab.zone_nwif_physical);
1483 		(void) fprintf(of, "%s\n", cmd_to_str(CMD_END));
1484 	}
1485 	(void) zonecfg_endnwifent(handle);
1486 
1487 	if ((err = zonecfg_setdevent(handle)) != Z_OK) {
1488 		zone_perror(zone, err, FALSE);
1489 		goto done;
1490 	}
1491 	while (zonecfg_getdevent(handle, &devtab) == Z_OK) {
1492 		(void) fprintf(of, "%s %s\n", cmd_to_str(CMD_ADD),
1493 		    rt_to_str(RT_DEVICE));
1494 		export_prop(of, PT_MATCH, devtab.zone_dev_match);
1495 		(void) fprintf(of, "%s\n", cmd_to_str(CMD_END));
1496 	}
1497 	(void) zonecfg_enddevent(handle);
1498 
1499 	if ((err = zonecfg_setrctlent(handle)) != Z_OK) {
1500 		zone_perror(zone, err, FALSE);
1501 		goto done;
1502 	}
1503 	while (zonecfg_getrctlent(handle, &rctltab) == Z_OK) {
1504 		(void) fprintf(of, "%s rctl\n", cmd_to_str(CMD_ADD));
1505 		export_prop(of, PT_NAME, rctltab.zone_rctl_name);
1506 		for (valptr = rctltab.zone_rctl_valptr; valptr != NULL;
1507 		    valptr = valptr->zone_rctlval_next) {
1508 			fprintf(of, "%s %s (%s=%s,%s=%s,%s=%s)\n",
1509 			    cmd_to_str(CMD_ADD), pt_to_str(PT_VALUE),
1510 			    pt_to_str(PT_PRIV), valptr->zone_rctlval_priv,
1511 			    pt_to_str(PT_LIMIT), valptr->zone_rctlval_limit,
1512 			    pt_to_str(PT_ACTION), valptr->zone_rctlval_action);
1513 		}
1514 		(void) fprintf(of, "%s\n", cmd_to_str(CMD_END));
1515 		zonecfg_free_rctl_value_list(rctltab.zone_rctl_valptr);
1516 	}
1517 	(void) zonecfg_endrctlent(handle);
1518 
1519 	if ((err = zonecfg_setattrent(handle)) != Z_OK) {
1520 		zone_perror(zone, err, FALSE);
1521 		goto done;
1522 	}
1523 	while (zonecfg_getattrent(handle, &attrtab) == Z_OK) {
1524 		(void) fprintf(of, "%s %s\n", cmd_to_str(CMD_ADD),
1525 		    rt_to_str(RT_ATTR));
1526 		export_prop(of, PT_NAME, attrtab.zone_attr_name);
1527 		export_prop(of, PT_TYPE, attrtab.zone_attr_type);
1528 		export_prop(of, PT_VALUE, attrtab.zone_attr_value);
1529 		(void) fprintf(of, "%s\n", cmd_to_str(CMD_END));
1530 	}
1531 	(void) zonecfg_endattrent(handle);
1532 
1533 	if ((err = zonecfg_setdsent(handle)) != Z_OK) {
1534 		zone_perror(zone, err, FALSE);
1535 		goto done;
1536 	}
1537 	while (zonecfg_getdsent(handle, &dstab) == Z_OK) {
1538 		(void) fprintf(of, "%s %s\n", cmd_to_str(CMD_ADD),
1539 		    rt_to_str(RT_DATASET));
1540 		export_prop(of, PT_NAME, dstab.zone_dataset_name);
1541 		(void) fprintf(of, "%s\n", cmd_to_str(CMD_END));
1542 	}
1543 	(void) zonecfg_enddsent(handle);
1544 
1545 done:
1546 	if (need_to_close)
1547 		(void) fclose(of);
1548 }
1549 
1550 void
1551 exit_func(cmd_t *cmd)
1552 {
1553 	int arg, answer;
1554 
1555 	optind = 0;
1556 	while ((arg = getopt(cmd->cmd_argc, cmd->cmd_argv, "?F")) != EOF) {
1557 		switch (arg) {
1558 		case '?':
1559 			longer_usage(CMD_EXIT);
1560 			return;
1561 		case 'F':
1562 			force_exit = TRUE;
1563 			break;
1564 		default:
1565 			short_usage(CMD_EXIT);
1566 			return;
1567 		}
1568 	}
1569 	if (optind < cmd->cmd_argc) {
1570 		short_usage(CMD_EXIT);
1571 		return;
1572 	}
1573 
1574 	if (global_scope || force_exit) {
1575 		time_to_exit = TRUE;
1576 		return;
1577 	}
1578 
1579 	answer = ask_yesno(FALSE, "Resource incomplete; really quit");
1580 	if (answer == -1) {
1581 		zerr(gettext("Resource incomplete, input "
1582 		    "not from terminal and -F not specified:\n%s command "
1583 		    "ignored, but exiting anyway."), cmd_to_str(CMD_EXIT));
1584 		exit(Z_ERR);
1585 	} else if (answer == 1) {
1586 		time_to_exit = TRUE;
1587 	}
1588 	/* (answer == 0) => just return */
1589 }
1590 
1591 static int
1592 validate_zonepath_syntax(char *path)
1593 {
1594 	if (path[0] != '/') {
1595 		zerr(gettext("%s is not an absolute path."), path);
1596 		return (Z_ERR);
1597 	}
1598 	if (strcmp(path, "/") == 0) {
1599 		zerr(gettext("/ is not allowed as a %s."),
1600 		    pt_to_str(PT_ZONEPATH));
1601 		return (Z_ERR);
1602 	}
1603 	return (Z_OK);
1604 }
1605 
1606 static void
1607 add_resource(cmd_t *cmd)
1608 {
1609 	int type;
1610 
1611 	if ((type = cmd->cmd_res_type) == RT_UNKNOWN) {
1612 		long_usage(CMD_ADD, TRUE);
1613 		goto bad;
1614 	}
1615 
1616 	switch (type) {
1617 	case RT_FS:
1618 		bzero(&in_progress_fstab, sizeof (in_progress_fstab));
1619 		return;
1620 	case RT_IPD:
1621 		if (state_atleast(ZONE_STATE_INSTALLED)) {
1622 			zerr(gettext("Zone %s already installed; %s %s not "
1623 			    "allowed."), zone, cmd_to_str(CMD_ADD),
1624 			    rt_to_str(RT_IPD));
1625 			goto bad;
1626 		}
1627 		bzero(&in_progress_ipdtab, sizeof (in_progress_ipdtab));
1628 		return;
1629 	case RT_NET:
1630 		bzero(&in_progress_nwiftab, sizeof (in_progress_nwiftab));
1631 		return;
1632 	case RT_DEVICE:
1633 		bzero(&in_progress_devtab, sizeof (in_progress_devtab));
1634 		return;
1635 	case RT_RCTL:
1636 		bzero(&in_progress_rctltab, sizeof (in_progress_rctltab));
1637 		return;
1638 	case RT_ATTR:
1639 		bzero(&in_progress_attrtab, sizeof (in_progress_attrtab));
1640 		return;
1641 	case RT_DATASET:
1642 		bzero(&in_progress_dstab, sizeof (in_progress_dstab));
1643 		return;
1644 	default:
1645 		zone_perror(rt_to_str(type), Z_NO_RESOURCE_TYPE, TRUE);
1646 		long_usage(CMD_ADD, TRUE);
1647 		usage(FALSE, HELP_RESOURCES);
1648 	}
1649 bad:
1650 	global_scope = TRUE;
1651 	end_op = -1;
1652 }
1653 
1654 static void
1655 do_complex_rctl_val(complex_property_ptr_t cp)
1656 {
1657 	struct zone_rctlvaltab *rctlvaltab;
1658 	complex_property_ptr_t cx;
1659 	bool seen_priv = FALSE, seen_limit = FALSE, seen_action = FALSE;
1660 	rctlblk_t *rctlblk;
1661 	int err;
1662 
1663 	if ((rctlvaltab = alloc_rctlvaltab()) == NULL) {
1664 		zone_perror(zone, Z_NOMEM, TRUE);
1665 		exit(Z_ERR);
1666 	}
1667 	for (cx = cp; cx != NULL; cx = cx->cp_next) {
1668 		switch (cx->cp_type) {
1669 		case PT_PRIV:
1670 			if (seen_priv) {
1671 				zerr(gettext("%s already specified"),
1672 				    pt_to_str(PT_PRIV));
1673 				goto bad;
1674 			}
1675 			(void) strlcpy(rctlvaltab->zone_rctlval_priv,
1676 			    cx->cp_value,
1677 			    sizeof (rctlvaltab->zone_rctlval_priv));
1678 			seen_priv = TRUE;
1679 			break;
1680 		case PT_LIMIT:
1681 			if (seen_limit) {
1682 				zerr(gettext("%s already specified"),
1683 				    pt_to_str(PT_LIMIT));
1684 				goto bad;
1685 			}
1686 			(void) strlcpy(rctlvaltab->zone_rctlval_limit,
1687 			    cx->cp_value,
1688 			    sizeof (rctlvaltab->zone_rctlval_limit));
1689 			seen_limit = TRUE;
1690 			break;
1691 		case PT_ACTION:
1692 			if (seen_action) {
1693 				zerr(gettext("%s already specified"),
1694 				    pt_to_str(PT_ACTION));
1695 				goto bad;
1696 			}
1697 			(void) strlcpy(rctlvaltab->zone_rctlval_action,
1698 			    cx->cp_value,
1699 			    sizeof (rctlvaltab->zone_rctlval_action));
1700 			seen_action = TRUE;
1701 			break;
1702 		default:
1703 			zone_perror(pt_to_str(PT_VALUE),
1704 			    Z_NO_PROPERTY_TYPE, TRUE);
1705 			long_usage(CMD_ADD, TRUE);
1706 			usage(FALSE, HELP_PROPS);
1707 			zonecfg_free_rctl_value_list(rctlvaltab);
1708 			return;
1709 		}
1710 	}
1711 	if (!seen_priv)
1712 		zerr(gettext("%s not specified"), pt_to_str(PT_PRIV));
1713 	if (!seen_limit)
1714 		zerr(gettext("%s not specified"), pt_to_str(PT_LIMIT));
1715 	if (!seen_action)
1716 		zerr(gettext("%s not specified"), pt_to_str(PT_ACTION));
1717 	if (!seen_priv || !seen_limit || !seen_action)
1718 		goto bad;
1719 	rctlvaltab->zone_rctlval_next = NULL;
1720 	rctlblk = alloca(rctlblk_size());
1721 	/*
1722 	 * Make sure the rctl value looks roughly correct; we won't know if
1723 	 * it's truly OK until we verify the configuration on the target
1724 	 * system.
1725 	 */
1726 	if (zonecfg_construct_rctlblk(rctlvaltab, rctlblk) != Z_OK ||
1727 	    !zonecfg_valid_rctlblk(rctlblk)) {
1728 		zerr(gettext("Invalid %s %s specification"), rt_to_str(RT_RCTL),
1729 		    pt_to_str(PT_VALUE));
1730 		goto bad;
1731 	}
1732 	err = zonecfg_add_rctl_value(&in_progress_rctltab, rctlvaltab);
1733 	if (err != Z_OK)
1734 		zone_perror(pt_to_str(PT_VALUE), err, TRUE);
1735 	return;
1736 
1737 bad:
1738 	zonecfg_free_rctl_value_list(rctlvaltab);
1739 }
1740 
1741 static void
1742 add_property(cmd_t *cmd)
1743 {
1744 	char *prop_id;
1745 	int err, res_type, prop_type;
1746 	property_value_ptr_t pp;
1747 	list_property_ptr_t l;
1748 
1749 	res_type = resource_scope;
1750 	prop_type = cmd->cmd_prop_name[0];
1751 	if (res_type == RT_UNKNOWN || prop_type == PT_UNKNOWN) {
1752 		long_usage(CMD_ADD, TRUE);
1753 		return;
1754 	}
1755 
1756 	if (cmd->cmd_prop_nv_pairs != 1) {
1757 		long_usage(CMD_ADD, TRUE);
1758 		return;
1759 	}
1760 
1761 	if (initialize(TRUE) != Z_OK)
1762 		return;
1763 
1764 	switch (res_type) {
1765 	case RT_FS:
1766 		if (prop_type != PT_OPTIONS) {
1767 			zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE,
1768 			    TRUE);
1769 			long_usage(CMD_ADD, TRUE);
1770 			usage(FALSE, HELP_PROPS);
1771 			return;
1772 		}
1773 		pp = cmd->cmd_property_ptr[0];
1774 		if (pp->pv_type != PROP_VAL_SIMPLE &&
1775 		    pp->pv_type != PROP_VAL_LIST) {
1776 			zerr(gettext("A %s or %s value was expected here."),
1777 			    pvt_to_str(PROP_VAL_SIMPLE),
1778 			    pvt_to_str(PROP_VAL_LIST));
1779 			saw_error = TRUE;
1780 			return;
1781 		}
1782 		if (pp->pv_type == PROP_VAL_SIMPLE) {
1783 			if (pp->pv_simple == NULL) {
1784 				long_usage(CMD_ADD, TRUE);
1785 				return;
1786 			}
1787 			prop_id = pp->pv_simple;
1788 			err = zonecfg_add_fs_option(&in_progress_fstab,
1789 			    prop_id);
1790 			if (err != Z_OK)
1791 				zone_perror(pt_to_str(prop_type), err, TRUE);
1792 		} else {
1793 			list_property_ptr_t list;
1794 
1795 			for (list = pp->pv_list; list != NULL;
1796 			    list = list->lp_next) {
1797 				prop_id = list->lp_simple;
1798 				if (prop_id == NULL)
1799 					break;
1800 				err = zonecfg_add_fs_option(
1801 				    &in_progress_fstab, prop_id);
1802 				if (err != Z_OK)
1803 					zone_perror(pt_to_str(prop_type), err,
1804 					    TRUE);
1805 			}
1806 		}
1807 		return;
1808 	case RT_RCTL:
1809 		if (prop_type != PT_VALUE) {
1810 			zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE,
1811 			    TRUE);
1812 			long_usage(CMD_ADD, TRUE);
1813 			usage(FALSE, HELP_PROPS);
1814 			return;
1815 		}
1816 		pp = cmd->cmd_property_ptr[0];
1817 		if (pp->pv_type != PROP_VAL_COMPLEX &&
1818 		    pp->pv_type != PROP_VAL_LIST) {
1819 			zerr(gettext("A %s or %s value was expected here."),
1820 			    pvt_to_str(PROP_VAL_COMPLEX),
1821 			    pvt_to_str(PROP_VAL_LIST));
1822 			saw_error = TRUE;
1823 			return;
1824 		}
1825 		if (pp->pv_type == PROP_VAL_COMPLEX) {
1826 			do_complex_rctl_val(pp->pv_complex);
1827 			return;
1828 		}
1829 		for (l = pp->pv_list; l != NULL; l = l->lp_next)
1830 			do_complex_rctl_val(l->lp_complex);
1831 		return;
1832 	default:
1833 		zone_perror(rt_to_str(res_type), Z_NO_RESOURCE_TYPE, TRUE);
1834 		long_usage(CMD_ADD, TRUE);
1835 		usage(FALSE, HELP_RESOURCES);
1836 		return;
1837 	}
1838 }
1839 
1840 void
1841 add_func(cmd_t *cmd)
1842 {
1843 	int arg;
1844 
1845 	assert(cmd != NULL);
1846 
1847 	optind = 0;
1848 	if ((arg = getopt(cmd->cmd_argc, cmd->cmd_argv, "?")) != EOF) {
1849 		switch (arg) {
1850 		case '?':
1851 			longer_usage(CMD_ADD);
1852 			return;
1853 		default:
1854 			short_usage(CMD_ADD);
1855 			return;
1856 		}
1857 	}
1858 	if (optind != cmd->cmd_argc) {
1859 		short_usage(CMD_ADD);
1860 		return;
1861 	}
1862 
1863 	if (zone_is_read_only(CMD_ADD))
1864 		return;
1865 
1866 	if (initialize(TRUE) != Z_OK)
1867 		return;
1868 	if (global_scope) {
1869 		global_scope = FALSE;
1870 		resource_scope = cmd->cmd_res_type;
1871 		end_op = CMD_ADD;
1872 		add_resource(cmd);
1873 	} else
1874 		add_property(cmd);
1875 }
1876 
1877 /*
1878  * This routine has an unusual implementation, because it tries very
1879  * hard to succeed in the face of a variety of failure modes.
1880  * The most common and most vexing occurs when the index file and
1881  * the /etc/zones/<zonename.xml> file are not both present.  In
1882  * this case, delete must eradicate as much of the zone state as is left
1883  * so that the user can later create a new zone with the same name.
1884  */
1885 void
1886 delete_func(cmd_t *cmd)
1887 {
1888 	int err, arg, answer;
1889 	char line[ZONENAME_MAX + 128];	/* enough to ask a question */
1890 	bool force = FALSE;
1891 
1892 	optind = 0;
1893 	while ((arg = getopt(cmd->cmd_argc, cmd->cmd_argv, "?F")) != EOF) {
1894 		switch (arg) {
1895 		case '?':
1896 			longer_usage(CMD_DELETE);
1897 			return;
1898 		case 'F':
1899 			force = TRUE;
1900 			break;
1901 		default:
1902 			short_usage(CMD_DELETE);
1903 			return;
1904 		}
1905 	}
1906 	if (optind != cmd->cmd_argc) {
1907 		short_usage(CMD_DELETE);
1908 		return;
1909 	}
1910 
1911 	if (zone_is_read_only(CMD_DELETE))
1912 		return;
1913 
1914 	if (!force) {
1915 		/*
1916 		 * Initialize sets up the global called "handle" and warns the
1917 		 * user if the zone is not configured.  In force mode, we don't
1918 		 * trust that evaluation, and hence skip it.  (We don't need the
1919 		 * handle to be loaded anyway, since zonecfg_destroy is done by
1920 		 * zonename).  However, we also have to take care to emulate the
1921 		 * messages spit out by initialize; see below.
1922 		 */
1923 		if (initialize(TRUE) != Z_OK)
1924 			return;
1925 
1926 		(void) snprintf(line, sizeof (line),
1927 		    gettext("Are you sure you want to delete zone %s"), zone);
1928 		if ((answer = ask_yesno(FALSE, line)) == -1) {
1929 			zerr(gettext("Input not from terminal and -F not "
1930 			    "specified:\n%s command ignored, exiting."),
1931 			    cmd_to_str(CMD_DELETE));
1932 			exit(Z_ERR);
1933 		}
1934 		if (answer != 1)
1935 			return;
1936 	}
1937 
1938 	if ((err = zonecfg_destroy(zone, force)) != Z_OK) {
1939 		if ((err == Z_BAD_ZONE_STATE) && !force) {
1940 			zerr(gettext("Zone %s not in %s state; %s not "
1941 			    "allowed.  Use -F to force %s."),
1942 			    zone, zone_state_str(ZONE_STATE_CONFIGURED),
1943 			    cmd_to_str(CMD_DELETE), cmd_to_str(CMD_DELETE));
1944 		} else {
1945 			zone_perror(zone, err, TRUE);
1946 		}
1947 	}
1948 	need_to_commit = FALSE;
1949 
1950 	/*
1951 	 * Emulate initialize's messaging; if there wasn't a valid handle to
1952 	 * begin with, then user had typed delete (or delete -F) multiple
1953 	 * times.  So we emit a message.
1954 	 *
1955 	 * We only do this in the 'force' case because normally, initialize()
1956 	 * takes care of this for us.
1957 	 */
1958 	if (force && zonecfg_check_handle(handle) != Z_OK && interactive_mode)
1959 		(void) printf(gettext("Use '%s' to begin "
1960 		    "configuring a new zone.\n"), cmd_to_str(CMD_CREATE));
1961 
1962 	/*
1963 	 * Time for a new handle: finish the old one off first
1964 	 * then get a new one properly to avoid leaks.
1965 	 */
1966 	if (got_handle) {
1967 		zonecfg_fini_handle(handle);
1968 		if ((handle = zonecfg_init_handle()) == NULL) {
1969 			zone_perror(execname, Z_NOMEM, TRUE);
1970 			exit(Z_ERR);
1971 		}
1972 		if ((err = zonecfg_get_handle(zone, handle)) != Z_OK) {
1973 			/* If there was no zone before, that's OK */
1974 			if (err != Z_NO_ZONE)
1975 				zone_perror(zone, err, TRUE);
1976 			got_handle = FALSE;
1977 		}
1978 	}
1979 }
1980 
1981 static int
1982 fill_in_fstab(cmd_t *cmd, struct zone_fstab *fstab, bool fill_in_only)
1983 {
1984 	int err, i;
1985 	property_value_ptr_t pp;
1986 
1987 	if ((err = initialize(TRUE)) != Z_OK)
1988 		return (err);
1989 
1990 	bzero(fstab, sizeof (*fstab));
1991 	for (i = 0; i < cmd->cmd_prop_nv_pairs; i++) {
1992 		pp = cmd->cmd_property_ptr[i];
1993 		if (pp->pv_type != PROP_VAL_SIMPLE || pp->pv_simple == NULL) {
1994 			zerr(gettext("A simple value was expected here."));
1995 			saw_error = TRUE;
1996 			return (Z_INSUFFICIENT_SPEC);
1997 		}
1998 		switch (cmd->cmd_prop_name[i]) {
1999 		case PT_DIR:
2000 			(void) strlcpy(fstab->zone_fs_dir, pp->pv_simple,
2001 			    sizeof (fstab->zone_fs_dir));
2002 			break;
2003 		case PT_SPECIAL:
2004 			(void) strlcpy(fstab->zone_fs_special, pp->pv_simple,
2005 			    sizeof (fstab->zone_fs_special));
2006 			break;
2007 		case PT_RAW:
2008 			(void) strlcpy(fstab->zone_fs_raw, pp->pv_simple,
2009 			    sizeof (fstab->zone_fs_raw));
2010 			break;
2011 		case PT_TYPE:
2012 			(void) strlcpy(fstab->zone_fs_type, pp->pv_simple,
2013 			    sizeof (fstab->zone_fs_type));
2014 			break;
2015 		default:
2016 			zone_perror(pt_to_str(cmd->cmd_prop_name[i]),
2017 			    Z_NO_PROPERTY_TYPE, TRUE);
2018 			return (Z_INSUFFICIENT_SPEC);
2019 		}
2020 	}
2021 	if (fill_in_only)
2022 		return (Z_OK);
2023 	return (zonecfg_lookup_filesystem(handle, fstab));
2024 }
2025 
2026 static int
2027 fill_in_ipdtab(cmd_t *cmd, struct zone_fstab *ipdtab, bool fill_in_only)
2028 {
2029 	int err, i;
2030 	property_value_ptr_t pp;
2031 
2032 	if ((err = initialize(TRUE)) != Z_OK)
2033 		return (err);
2034 
2035 	bzero(ipdtab, sizeof (*ipdtab));
2036 	for (i = 0; i < cmd->cmd_prop_nv_pairs; i++) {
2037 		pp = cmd->cmd_property_ptr[i];
2038 		if (pp->pv_type != PROP_VAL_SIMPLE || pp->pv_simple == NULL) {
2039 			zerr(gettext("A simple value was expected here."));
2040 			saw_error = TRUE;
2041 			return (Z_INSUFFICIENT_SPEC);
2042 		}
2043 		switch (cmd->cmd_prop_name[i]) {
2044 		case PT_DIR:
2045 			(void) strlcpy(ipdtab->zone_fs_dir, pp->pv_simple,
2046 			    sizeof (ipdtab->zone_fs_dir));
2047 			break;
2048 		default:
2049 			zone_perror(pt_to_str(cmd->cmd_prop_name[i]),
2050 			    Z_NO_PROPERTY_TYPE, TRUE);
2051 			return (Z_INSUFFICIENT_SPEC);
2052 		}
2053 	}
2054 	if (fill_in_only)
2055 		return (Z_OK);
2056 	return (zonecfg_lookup_ipd(handle, ipdtab));
2057 }
2058 
2059 static int
2060 fill_in_nwiftab(cmd_t *cmd, struct zone_nwiftab *nwiftab, bool fill_in_only)
2061 {
2062 	int err, i;
2063 	property_value_ptr_t pp;
2064 
2065 	if ((err = initialize(TRUE)) != Z_OK)
2066 		return (err);
2067 
2068 	bzero(nwiftab, sizeof (*nwiftab));
2069 	for (i = 0; i < cmd->cmd_prop_nv_pairs; i++) {
2070 		pp = cmd->cmd_property_ptr[i];
2071 		if (pp->pv_type != PROP_VAL_SIMPLE || pp->pv_simple == NULL) {
2072 			zerr(gettext("A simple value was expected here."));
2073 			saw_error = TRUE;
2074 			return (Z_INSUFFICIENT_SPEC);
2075 		}
2076 		switch (cmd->cmd_prop_name[i]) {
2077 		case PT_ADDRESS:
2078 			(void) strlcpy(nwiftab->zone_nwif_address,
2079 			    pp->pv_simple, sizeof (nwiftab->zone_nwif_address));
2080 			break;
2081 		case PT_PHYSICAL:
2082 			(void) strlcpy(nwiftab->zone_nwif_physical,
2083 			    pp->pv_simple,
2084 			    sizeof (nwiftab->zone_nwif_physical));
2085 			break;
2086 		default:
2087 			zone_perror(pt_to_str(cmd->cmd_prop_name[i]),
2088 			    Z_NO_PROPERTY_TYPE, TRUE);
2089 			return (Z_INSUFFICIENT_SPEC);
2090 		}
2091 	}
2092 	if (fill_in_only)
2093 		return (Z_OK);
2094 	err = zonecfg_lookup_nwif(handle, nwiftab);
2095 	return (err);
2096 }
2097 
2098 static int
2099 fill_in_devtab(cmd_t *cmd, struct zone_devtab *devtab, bool fill_in_only)
2100 {
2101 	int err, i;
2102 	property_value_ptr_t pp;
2103 
2104 	if ((err = initialize(TRUE)) != Z_OK)
2105 		return (err);
2106 
2107 	bzero(devtab, sizeof (*devtab));
2108 	for (i = 0; i < cmd->cmd_prop_nv_pairs; i++) {
2109 		pp = cmd->cmd_property_ptr[i];
2110 		if (pp->pv_type != PROP_VAL_SIMPLE || pp->pv_simple == NULL) {
2111 			zerr(gettext("A simple value was expected here."));
2112 			saw_error = TRUE;
2113 			return (Z_INSUFFICIENT_SPEC);
2114 		}
2115 		switch (cmd->cmd_prop_name[i]) {
2116 		case PT_MATCH:
2117 			(void) strlcpy(devtab->zone_dev_match, pp->pv_simple,
2118 			    sizeof (devtab->zone_dev_match));
2119 			break;
2120 		default:
2121 			zone_perror(pt_to_str(cmd->cmd_prop_name[i]),
2122 			    Z_NO_PROPERTY_TYPE, TRUE);
2123 			return (Z_INSUFFICIENT_SPEC);
2124 		}
2125 	}
2126 	if (fill_in_only)
2127 		return (Z_OK);
2128 	err = zonecfg_lookup_dev(handle, devtab);
2129 	return (err);
2130 }
2131 
2132 static int
2133 fill_in_rctltab(cmd_t *cmd, struct zone_rctltab *rctltab, bool fill_in_only)
2134 {
2135 	int err, i;
2136 	property_value_ptr_t pp;
2137 
2138 	if ((err = initialize(TRUE)) != Z_OK)
2139 		return (err);
2140 
2141 	bzero(rctltab, sizeof (*rctltab));
2142 	for (i = 0; i < cmd->cmd_prop_nv_pairs; i++) {
2143 		pp = cmd->cmd_property_ptr[i];
2144 		if (pp->pv_type != PROP_VAL_SIMPLE || pp->pv_simple == NULL) {
2145 			zerr(gettext("A simple value was expected here."));
2146 			saw_error = TRUE;
2147 			return (Z_INSUFFICIENT_SPEC);
2148 		}
2149 		switch (cmd->cmd_prop_name[i]) {
2150 		case PT_NAME:
2151 			(void) strlcpy(rctltab->zone_rctl_name, pp->pv_simple,
2152 			    sizeof (rctltab->zone_rctl_name));
2153 			break;
2154 		default:
2155 			zone_perror(pt_to_str(cmd->cmd_prop_name[i]),
2156 			    Z_NO_PROPERTY_TYPE, TRUE);
2157 			return (Z_INSUFFICIENT_SPEC);
2158 		}
2159 	}
2160 	if (fill_in_only)
2161 		return (Z_OK);
2162 	err = zonecfg_lookup_rctl(handle, rctltab);
2163 	return (err);
2164 }
2165 
2166 static int
2167 fill_in_attrtab(cmd_t *cmd, struct zone_attrtab *attrtab, bool fill_in_only)
2168 {
2169 	int err, i;
2170 	property_value_ptr_t pp;
2171 
2172 	if ((err = initialize(TRUE)) != Z_OK)
2173 		return (err);
2174 
2175 	bzero(attrtab, sizeof (*attrtab));
2176 	for (i = 0; i < cmd->cmd_prop_nv_pairs; i++) {
2177 		pp = cmd->cmd_property_ptr[i];
2178 		if (pp->pv_type != PROP_VAL_SIMPLE || pp->pv_simple == NULL) {
2179 			zerr(gettext("A simple value was expected here."));
2180 			saw_error = TRUE;
2181 			return (Z_INSUFFICIENT_SPEC);
2182 		}
2183 		switch (cmd->cmd_prop_name[i]) {
2184 		case PT_NAME:
2185 			(void) strlcpy(attrtab->zone_attr_name, pp->pv_simple,
2186 			    sizeof (attrtab->zone_attr_name));
2187 			break;
2188 		case PT_TYPE:
2189 			(void) strlcpy(attrtab->zone_attr_type, pp->pv_simple,
2190 			    sizeof (attrtab->zone_attr_type));
2191 			break;
2192 		case PT_VALUE:
2193 			(void) strlcpy(attrtab->zone_attr_value, pp->pv_simple,
2194 			    sizeof (attrtab->zone_attr_value));
2195 			break;
2196 		default:
2197 			zone_perror(pt_to_str(cmd->cmd_prop_name[i]),
2198 			    Z_NO_PROPERTY_TYPE, TRUE);
2199 			return (Z_INSUFFICIENT_SPEC);
2200 		}
2201 	}
2202 	if (fill_in_only)
2203 		return (Z_OK);
2204 	err = zonecfg_lookup_attr(handle, attrtab);
2205 	return (err);
2206 }
2207 
2208 static int
2209 fill_in_dstab(cmd_t *cmd, struct zone_dstab *dstab, bool fill_in_only)
2210 {
2211 	int err, i;
2212 	property_value_ptr_t pp;
2213 
2214 	if ((err = initialize(TRUE)) != Z_OK)
2215 		return (err);
2216 
2217 	dstab->zone_dataset_name[0] = '\0';
2218 	for (i = 0; i < cmd->cmd_prop_nv_pairs; i++) {
2219 		pp = cmd->cmd_property_ptr[i];
2220 		if (pp->pv_type != PROP_VAL_SIMPLE || pp->pv_simple == NULL) {
2221 			zerr(gettext("A simple value was expected here."));
2222 			saw_error = TRUE;
2223 			return (Z_INSUFFICIENT_SPEC);
2224 		}
2225 		switch (cmd->cmd_prop_name[i]) {
2226 		case PT_NAME:
2227 			(void) strlcpy(dstab->zone_dataset_name, pp->pv_simple,
2228 			    sizeof (dstab->zone_dataset_name));
2229 			break;
2230 		default:
2231 			zone_perror(pt_to_str(cmd->cmd_prop_name[i]),
2232 			    Z_NO_PROPERTY_TYPE, TRUE);
2233 			return (Z_INSUFFICIENT_SPEC);
2234 		}
2235 	}
2236 	if (fill_in_only)
2237 		return (Z_OK);
2238 	return (zonecfg_lookup_ds(handle, dstab));
2239 }
2240 
2241 static void
2242 remove_resource(cmd_t *cmd)
2243 {
2244 	int err, type;
2245 	struct zone_fstab fstab;
2246 	struct zone_nwiftab nwiftab;
2247 	struct zone_devtab devtab;
2248 	struct zone_attrtab attrtab;
2249 	struct zone_rctltab rctltab;
2250 	struct zone_dstab dstab;
2251 
2252 	if ((type = cmd->cmd_res_type) == RT_UNKNOWN) {
2253 		long_usage(CMD_REMOVE, TRUE);
2254 		return;
2255 	}
2256 
2257 	if (initialize(TRUE) != Z_OK)
2258 		return;
2259 
2260 	switch (type) {
2261 	case RT_FS:
2262 		if ((err = fill_in_fstab(cmd, &fstab, FALSE)) != Z_OK) {
2263 			z_cmd_rt_perror(CMD_REMOVE, RT_FS, err, TRUE);
2264 			return;
2265 		}
2266 		if ((err = zonecfg_delete_filesystem(handle, &fstab)) != Z_OK)
2267 			z_cmd_rt_perror(CMD_REMOVE, RT_FS, err, TRUE);
2268 		else
2269 			need_to_commit = TRUE;
2270 		zonecfg_free_fs_option_list(fstab.zone_fs_options);
2271 		return;
2272 	case RT_IPD:
2273 		if (state_atleast(ZONE_STATE_INSTALLED)) {
2274 			zerr(gettext("Zone %s already installed; %s %s not "
2275 			    "allowed."), zone, cmd_to_str(CMD_REMOVE),
2276 			    rt_to_str(RT_IPD));
2277 			return;
2278 		}
2279 		if ((err = fill_in_ipdtab(cmd, &fstab, FALSE)) != Z_OK) {
2280 			z_cmd_rt_perror(CMD_REMOVE, RT_IPD, err, TRUE);
2281 			return;
2282 		}
2283 		if ((err = zonecfg_delete_ipd(handle, &fstab)) != Z_OK)
2284 			z_cmd_rt_perror(CMD_REMOVE, RT_IPD, err, TRUE);
2285 		else
2286 			need_to_commit = TRUE;
2287 		return;
2288 	case RT_NET:
2289 		if ((err = fill_in_nwiftab(cmd, &nwiftab, FALSE)) != Z_OK) {
2290 			z_cmd_rt_perror(CMD_REMOVE, RT_NET, err, TRUE);
2291 			return;
2292 		}
2293 		if ((err = zonecfg_delete_nwif(handle, &nwiftab)) != Z_OK)
2294 			z_cmd_rt_perror(CMD_REMOVE, RT_NET, err, TRUE);
2295 		else
2296 			need_to_commit = TRUE;
2297 		return;
2298 	case RT_DEVICE:
2299 		if ((err = fill_in_devtab(cmd, &devtab, FALSE)) != Z_OK) {
2300 			z_cmd_rt_perror(CMD_REMOVE, RT_DEVICE, err, TRUE);
2301 			return;
2302 		}
2303 		if ((err = zonecfg_delete_dev(handle, &devtab)) != Z_OK)
2304 			z_cmd_rt_perror(CMD_REMOVE, RT_DEVICE, err, TRUE);
2305 		else
2306 			need_to_commit = TRUE;
2307 		return;
2308 	case RT_RCTL:
2309 		if ((err = fill_in_rctltab(cmd, &rctltab, FALSE)) != Z_OK) {
2310 			z_cmd_rt_perror(CMD_REMOVE, RT_RCTL, err, TRUE);
2311 			return;
2312 		}
2313 		if ((err = zonecfg_delete_rctl(handle, &rctltab)) != Z_OK)
2314 			z_cmd_rt_perror(CMD_REMOVE, RT_RCTL, err, TRUE);
2315 		else
2316 			need_to_commit = TRUE;
2317 		zonecfg_free_rctl_value_list(rctltab.zone_rctl_valptr);
2318 		return;
2319 	case RT_ATTR:
2320 		if ((err = fill_in_attrtab(cmd, &attrtab, FALSE)) != Z_OK) {
2321 			z_cmd_rt_perror(CMD_REMOVE, RT_ATTR, err, TRUE);
2322 			return;
2323 		}
2324 		if ((err = zonecfg_delete_attr(handle, &attrtab)) != Z_OK)
2325 			z_cmd_rt_perror(CMD_REMOVE, RT_ATTR, err, TRUE);
2326 		else
2327 			need_to_commit = TRUE;
2328 		return;
2329 	case RT_DATASET:
2330 		if ((err = fill_in_dstab(cmd, &dstab, FALSE)) != Z_OK) {
2331 			z_cmd_rt_perror(CMD_REMOVE, RT_DATASET, err, TRUE);
2332 			return;
2333 		}
2334 		if ((err = zonecfg_delete_ds(handle, &dstab)) != Z_OK)
2335 			z_cmd_rt_perror(CMD_REMOVE, RT_DATASET, err, TRUE);
2336 		else
2337 			need_to_commit = TRUE;
2338 		return;
2339 	default:
2340 		zone_perror(rt_to_str(type), Z_NO_RESOURCE_TYPE, TRUE);
2341 		long_usage(CMD_REMOVE, TRUE);
2342 		usage(FALSE, HELP_RESOURCES);
2343 		return;
2344 	}
2345 }
2346 
2347 static void
2348 remove_property(cmd_t *cmd)
2349 {
2350 	char *prop_id;
2351 	int err, res_type, prop_type;
2352 	property_value_ptr_t pp;
2353 	struct zone_rctlvaltab *rctlvaltab;
2354 	complex_property_ptr_t cx;
2355 
2356 	res_type = resource_scope;
2357 	prop_type = cmd->cmd_prop_name[0];
2358 	if (res_type == RT_UNKNOWN || prop_type == PT_UNKNOWN) {
2359 		long_usage(CMD_REMOVE, TRUE);
2360 		return;
2361 	}
2362 
2363 	if (cmd->cmd_prop_nv_pairs != 1) {
2364 		long_usage(CMD_ADD, TRUE);
2365 		return;
2366 	}
2367 
2368 	if (initialize(TRUE) != Z_OK)
2369 		return;
2370 
2371 	switch (res_type) {
2372 	case RT_FS:
2373 		if (prop_type != PT_OPTIONS) {
2374 			zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE,
2375 			    TRUE);
2376 			long_usage(CMD_REMOVE, TRUE);
2377 			usage(FALSE, HELP_PROPS);
2378 			return;
2379 		}
2380 		pp = cmd->cmd_property_ptr[0];
2381 		if (pp->pv_type == PROP_VAL_COMPLEX) {
2382 			zerr(gettext("A %s or %s value was expected here."),
2383 			    pvt_to_str(PROP_VAL_SIMPLE),
2384 			    pvt_to_str(PROP_VAL_LIST));
2385 			saw_error = TRUE;
2386 			return;
2387 		}
2388 		if (pp->pv_type == PROP_VAL_SIMPLE) {
2389 			if (pp->pv_simple == NULL) {
2390 				long_usage(CMD_ADD, TRUE);
2391 				return;
2392 			}
2393 			prop_id = pp->pv_simple;
2394 			err = zonecfg_remove_fs_option(&in_progress_fstab,
2395 			    prop_id);
2396 			if (err != Z_OK)
2397 				zone_perror(pt_to_str(prop_type), err, TRUE);
2398 		} else {
2399 			list_property_ptr_t list;
2400 
2401 			for (list = pp->pv_list; list != NULL;
2402 			    list = list->lp_next) {
2403 				prop_id = list->lp_simple;
2404 				if (prop_id == NULL)
2405 					break;
2406 				err = zonecfg_remove_fs_option(
2407 				    &in_progress_fstab, prop_id);
2408 				if (err != Z_OK)
2409 					zone_perror(pt_to_str(prop_type), err,
2410 					    TRUE);
2411 			}
2412 		}
2413 		return;
2414 	case RT_RCTL:
2415 		if (prop_type != PT_VALUE) {
2416 			zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE,
2417 			    TRUE);
2418 			long_usage(CMD_REMOVE, TRUE);
2419 			usage(FALSE, HELP_PROPS);
2420 			return;
2421 		}
2422 		pp = cmd->cmd_property_ptr[0];
2423 		if (pp->pv_type != PROP_VAL_COMPLEX) {
2424 			zerr(gettext("A %s value was expected here."),
2425 			    pvt_to_str(PROP_VAL_COMPLEX));
2426 			saw_error = TRUE;
2427 			return;
2428 		}
2429 		if ((rctlvaltab = alloc_rctlvaltab()) == NULL) {
2430 			zone_perror(zone, Z_NOMEM, TRUE);
2431 			exit(Z_ERR);
2432 		}
2433 		for (cx = pp->pv_complex; cx != NULL; cx = cx->cp_next) {
2434 			switch (cx->cp_type) {
2435 			case PT_PRIV:
2436 				(void) strlcpy(rctlvaltab->zone_rctlval_priv,
2437 				    cx->cp_value,
2438 				    sizeof (rctlvaltab->zone_rctlval_priv));
2439 				break;
2440 			case PT_LIMIT:
2441 				(void) strlcpy(rctlvaltab->zone_rctlval_limit,
2442 				    cx->cp_value,
2443 				    sizeof (rctlvaltab->zone_rctlval_limit));
2444 				break;
2445 			case PT_ACTION:
2446 				(void) strlcpy(rctlvaltab->zone_rctlval_action,
2447 				    cx->cp_value,
2448 				    sizeof (rctlvaltab->zone_rctlval_action));
2449 				break;
2450 			default:
2451 				zone_perror(pt_to_str(prop_type),
2452 				    Z_NO_PROPERTY_TYPE, TRUE);
2453 				long_usage(CMD_ADD, TRUE);
2454 				usage(FALSE, HELP_PROPS);
2455 				zonecfg_free_rctl_value_list(rctlvaltab);
2456 				return;
2457 			}
2458 		}
2459 		rctlvaltab->zone_rctlval_next = NULL;
2460 		err = zonecfg_remove_rctl_value(&in_progress_rctltab,
2461 		    rctlvaltab);
2462 		if (err != Z_OK)
2463 			zone_perror(pt_to_str(prop_type), err, TRUE);
2464 		zonecfg_free_rctl_value_list(rctlvaltab);
2465 		return;
2466 	default:
2467 		zone_perror(rt_to_str(res_type), Z_NO_RESOURCE_TYPE, TRUE);
2468 		long_usage(CMD_REMOVE, TRUE);
2469 		usage(FALSE, HELP_RESOURCES);
2470 		return;
2471 	}
2472 }
2473 
2474 void
2475 remove_func(cmd_t *cmd)
2476 {
2477 	if (zone_is_read_only(CMD_REMOVE))
2478 		return;
2479 
2480 	assert(cmd != NULL);
2481 
2482 	if (global_scope)
2483 		remove_resource(cmd);
2484 	else
2485 		remove_property(cmd);
2486 }
2487 
2488 void
2489 select_func(cmd_t *cmd)
2490 {
2491 	int type, err;
2492 
2493 	if (zone_is_read_only(CMD_SELECT))
2494 		return;
2495 
2496 	assert(cmd != NULL);
2497 
2498 	if (global_scope) {
2499 		global_scope = FALSE;
2500 		resource_scope = cmd->cmd_res_type;
2501 		end_op = CMD_SELECT;
2502 	} else {
2503 		scope_usage(CMD_SELECT);
2504 		return;
2505 	}
2506 
2507 	if ((type = cmd->cmd_res_type) == RT_UNKNOWN) {
2508 		long_usage(CMD_SELECT, TRUE);
2509 		return;
2510 	}
2511 
2512 	if (initialize(TRUE) != Z_OK)
2513 		return;
2514 
2515 	switch (type) {
2516 	case RT_FS:
2517 		if ((err = fill_in_fstab(cmd, &old_fstab, FALSE)) != Z_OK) {
2518 			z_cmd_rt_perror(CMD_SELECT, RT_FS, err, TRUE);
2519 			global_scope = TRUE;
2520 		}
2521 		bcopy(&old_fstab, &in_progress_fstab,
2522 		    sizeof (struct zone_fstab));
2523 		return;
2524 	case RT_IPD:
2525 		if (state_atleast(ZONE_STATE_INCOMPLETE)) {
2526 			zerr(gettext("Zone %s not in %s state; %s %s not "
2527 			    "allowed."), zone,
2528 			    zone_state_str(ZONE_STATE_CONFIGURED),
2529 			    cmd_to_str(CMD_SELECT), rt_to_str(RT_IPD));
2530 			global_scope = TRUE;
2531 			end_op = -1;
2532 			return;
2533 		}
2534 		if ((err = fill_in_ipdtab(cmd, &old_ipdtab, FALSE)) != Z_OK) {
2535 			z_cmd_rt_perror(CMD_SELECT, RT_IPD, err, TRUE);
2536 			global_scope = TRUE;
2537 		}
2538 		bcopy(&old_ipdtab, &in_progress_ipdtab,
2539 		    sizeof (struct zone_fstab));
2540 		return;
2541 	case RT_NET:
2542 		if ((err = fill_in_nwiftab(cmd, &old_nwiftab, FALSE)) != Z_OK) {
2543 			z_cmd_rt_perror(CMD_SELECT, RT_NET, err, TRUE);
2544 			global_scope = TRUE;
2545 		}
2546 		bcopy(&old_nwiftab, &in_progress_nwiftab,
2547 		    sizeof (struct zone_nwiftab));
2548 		return;
2549 	case RT_DEVICE:
2550 		if ((err = fill_in_devtab(cmd, &old_devtab, FALSE)) != Z_OK) {
2551 			z_cmd_rt_perror(CMD_SELECT, RT_DEVICE, err, TRUE);
2552 			global_scope = TRUE;
2553 		}
2554 		bcopy(&old_devtab, &in_progress_devtab,
2555 		    sizeof (struct zone_devtab));
2556 		return;
2557 	case RT_RCTL:
2558 		if ((err = fill_in_rctltab(cmd, &old_rctltab, FALSE)) != Z_OK) {
2559 			z_cmd_rt_perror(CMD_SELECT, RT_RCTL, err, TRUE);
2560 			global_scope = TRUE;
2561 		}
2562 		bcopy(&old_rctltab, &in_progress_rctltab,
2563 		    sizeof (struct zone_rctltab));
2564 		return;
2565 	case RT_ATTR:
2566 		if ((err = fill_in_attrtab(cmd, &old_attrtab, FALSE)) != Z_OK) {
2567 			z_cmd_rt_perror(CMD_SELECT, RT_ATTR, err, TRUE);
2568 			global_scope = TRUE;
2569 		}
2570 		bcopy(&old_attrtab, &in_progress_attrtab,
2571 		    sizeof (struct zone_attrtab));
2572 		return;
2573 	case RT_DATASET:
2574 		if ((err = fill_in_dstab(cmd, &old_dstab, FALSE)) != Z_OK) {
2575 			z_cmd_rt_perror(CMD_SELECT, RT_DATASET, err, TRUE);
2576 			global_scope = TRUE;
2577 		}
2578 		bcopy(&old_dstab, &in_progress_dstab,
2579 		    sizeof (struct zone_dstab));
2580 		return;
2581 	default:
2582 		zone_perror(rt_to_str(type), Z_NO_RESOURCE_TYPE, TRUE);
2583 		long_usage(CMD_SELECT, TRUE);
2584 		usage(FALSE, HELP_RESOURCES);
2585 		return;
2586 	}
2587 }
2588 
2589 /*
2590  * Network "addresses" can be one of the following forms:
2591  *	<IPv4 address>
2592  *	<IPv4 address>/<prefix length>
2593  *	<IPv6 address>/<prefix length>
2594  *	<host name>
2595  *	<host name>/<prefix length>
2596  * In other words, the "/" followed by a prefix length is allowed but not
2597  * required for IPv4 addresses and host names, and required for IPv6 addresses.
2598  * If a prefix length is given, it must be in the allowable range: 0 to 32 for
2599  * IPv4 addresses and host names, 0 to 128 for IPv6 addresses.
2600  * Host names must start with an alpha-numeric character, and all subsequent
2601  * characters must be either alpha-numeric or "-".
2602  */
2603 
2604 static int
2605 validate_net_address_syntax(char *address)
2606 {
2607 	char *slashp, part1[MAXHOSTNAMELEN];
2608 	struct in6_addr in6;
2609 	struct in_addr in4;
2610 	int prefixlen, i;
2611 
2612 	/*
2613 	 * Copy the part before any '/' into part1 or copy the whole
2614 	 * thing if there is no '/'.
2615 	 */
2616 	if ((slashp = strchr(address, '/')) != NULL) {
2617 		*slashp = '\0';
2618 		(void) strlcpy(part1, address, sizeof (part1));
2619 		*slashp = '/';
2620 		prefixlen = atoi(++slashp);
2621 	} else {
2622 		(void) strlcpy(part1, address, sizeof (part1));
2623 	}
2624 
2625 	if (inet_pton(AF_INET6, part1, &in6) == 1) {
2626 		if (slashp == NULL) {
2627 			zerr(gettext("%s: IPv6 addresses "
2628 			    "require /prefix-length suffix."), address);
2629 			return (Z_ERR);
2630 		}
2631 		if (prefixlen < 0 || prefixlen > 128) {
2632 			zerr(gettext("%s: IPv6 address "
2633 			    "prefix lengths must be 0 - 128."), address);
2634 			return (Z_ERR);
2635 		}
2636 		return (Z_OK);
2637 	}
2638 
2639 	/* At this point, any /prefix must be for IPv4. */
2640 	if (slashp != NULL) {
2641 		if (prefixlen < 0 || prefixlen > 32) {
2642 			zerr(gettext("%s: IPv4 address "
2643 			    "prefix lengths must be 0 - 32."), address);
2644 			return (Z_ERR);
2645 		}
2646 	}
2647 	if (inet_pton(AF_INET, part1, &in4) == 1)
2648 		return (Z_OK);
2649 
2650 	/* address may also be a host name */
2651 	if (!isalnum(part1[0])) {
2652 		zerr(gettext("%s: bogus host name or network address syntax"),
2653 		    part1);
2654 		saw_error = TRUE;
2655 		usage(FALSE, HELP_NETADDR);
2656 		return (Z_ERR);
2657 	}
2658 	for (i = 1; part1[i]; i++)
2659 		if (!isalnum(part1[i]) && part1[i] != '-' && part1[i] != '.') {
2660 			zerr(gettext("%s: bogus host name or "
2661 			    "network address syntax"), part1);
2662 			saw_error = TRUE;
2663 			usage(FALSE, HELP_NETADDR);
2664 			return (Z_ERR);
2665 		}
2666 	return (Z_OK);
2667 }
2668 
2669 static int
2670 validate_net_physical_syntax(char *ifname)
2671 {
2672 	if (strchr(ifname, ':') == NULL)
2673 		return (Z_OK);
2674 	zerr(gettext("%s: physical interface name required; "
2675 	    "logical interface name not allowed"), ifname);
2676 	return (Z_ERR);
2677 }
2678 
2679 static boolean_t
2680 valid_fs_type(const char *type)
2681 {
2682 	/*
2683 	 * Is this a valid path component?
2684 	 */
2685 	if (strlen(type) + 1 > MAXNAMELEN)
2686 		return (B_FALSE);
2687 	/*
2688 	 * Make sure a bad value for "type" doesn't make
2689 	 * /usr/lib/fs/<type>/mount turn into something else.
2690 	 */
2691 	if (strchr(type, '/') != NULL || type[0] == '\0' ||
2692 	    strcmp(type, ".") == 0 || strcmp(type, "..") == 0)
2693 		return (B_FALSE);
2694 	/*
2695 	 * More detailed verification happens later by zoneadm(1m).
2696 	 */
2697 	return (B_TRUE);
2698 }
2699 
2700 void
2701 set_func(cmd_t *cmd)
2702 {
2703 	char *prop_id;
2704 	int arg, err, res_type, prop_type;
2705 	property_value_ptr_t pp;
2706 	boolean_t autoboot;
2707 	boolean_t force_set = FALSE;
2708 
2709 	if (zone_is_read_only(CMD_SET))
2710 		return;
2711 
2712 	assert(cmd != NULL);
2713 
2714 	optind = opterr = 0;
2715 	while ((arg = getopt(cmd->cmd_argc, cmd->cmd_argv, "F")) != EOF) {
2716 		switch (arg) {
2717 		case 'F':
2718 			force_set = TRUE;
2719 			break;
2720 		default:
2721 			if (optopt == '?')
2722 				longer_usage(CMD_SET);
2723 			else
2724 				short_usage(CMD_SET);
2725 			return;
2726 		}
2727 	}
2728 
2729 	prop_type = cmd->cmd_prop_name[0];
2730 	if (global_scope) {
2731 		if (prop_type == PT_ZONENAME) {
2732 			res_type = RT_ZONENAME;
2733 		} else if (prop_type == PT_ZONEPATH) {
2734 			res_type = RT_ZONEPATH;
2735 		} else if (prop_type == PT_AUTOBOOT) {
2736 			res_type = RT_AUTOBOOT;
2737 		} else if (prop_type == PT_POOL) {
2738 			res_type = RT_POOL;
2739 		} else if (prop_type == PT_LIMITPRIV) {
2740 			res_type = RT_LIMITPRIV;
2741 		} else if (prop_type == PT_BOOTARGS) {
2742 			res_type = RT_BOOTARGS;
2743 		} else {
2744 			zerr(gettext("Cannot set a resource-specific property "
2745 			    "from the global scope."));
2746 			saw_error = TRUE;
2747 			return;
2748 		}
2749 	} else {
2750 		res_type = resource_scope;
2751 	}
2752 
2753 	if (force_set) {
2754 		if (res_type != RT_ZONEPATH) {
2755 			zerr(gettext("Only zonepath setting can be forced."));
2756 			saw_error = TRUE;
2757 			return;
2758 		}
2759 		if (!zonecfg_in_alt_root()) {
2760 			zerr(gettext("Zonepath is changeable only in an "
2761 			    "alternate root."));
2762 			saw_error = TRUE;
2763 			return;
2764 		}
2765 	}
2766 
2767 	pp = cmd->cmd_property_ptr[0];
2768 	/*
2769 	 * A nasty expression but not that complicated:
2770 	 * 1. fs options are simple or list (tested below)
2771 	 * 2. rctl value's are complex or list (tested below)
2772 	 * Anything else should be simple.
2773 	 */
2774 	if (!(res_type == RT_FS && prop_type == PT_OPTIONS) &&
2775 	    !(res_type == RT_RCTL && prop_type == PT_VALUE) &&
2776 	    (pp->pv_type != PROP_VAL_SIMPLE ||
2777 	    (prop_id = pp->pv_simple) == NULL)) {
2778 		zerr(gettext("A %s value was expected here."),
2779 		    pvt_to_str(PROP_VAL_SIMPLE));
2780 		saw_error = TRUE;
2781 		return;
2782 	}
2783 	if (prop_type == PT_UNKNOWN) {
2784 		long_usage(CMD_SET, TRUE);
2785 		return;
2786 	}
2787 
2788 	/*
2789 	 * Special case: the user can change the zone name prior to 'create';
2790 	 * if the zone already exists, we fall through letting initialize()
2791 	 * and the rest of the logic run.
2792 	 */
2793 	if (res_type == RT_ZONENAME && got_handle == FALSE &&
2794 	    !state_atleast(ZONE_STATE_CONFIGURED)) {
2795 		if ((err = zonecfg_validate_zonename(prop_id)) != Z_OK) {
2796 			zone_perror(prop_id, err, TRUE);
2797 			usage(FALSE, HELP_SYNTAX);
2798 			return;
2799 		}
2800 		(void) strlcpy(zone, prop_id, sizeof (zone));
2801 		return;
2802 	}
2803 
2804 	if (initialize(TRUE) != Z_OK)
2805 		return;
2806 
2807 	switch (res_type) {
2808 	case RT_ZONENAME:
2809 		if ((err = zonecfg_set_name(handle, prop_id)) != Z_OK) {
2810 			/*
2811 			 * Use prop_id instead of 'zone' here, since we're
2812 			 * reporting a problem about the *new* zonename.
2813 			 */
2814 			zone_perror(prop_id, err, TRUE);
2815 			usage(FALSE, HELP_SYNTAX);
2816 		} else {
2817 			need_to_commit = TRUE;
2818 			(void) strlcpy(zone, prop_id, sizeof (zone));
2819 		}
2820 		return;
2821 	case RT_ZONEPATH:
2822 		if (!force_set && state_atleast(ZONE_STATE_INSTALLED)) {
2823 			zerr(gettext("Zone %s already installed; %s %s not "
2824 			    "allowed."), zone, cmd_to_str(CMD_SET),
2825 			    rt_to_str(RT_ZONEPATH));
2826 			return;
2827 		}
2828 		if (validate_zonepath_syntax(prop_id) != Z_OK) {
2829 			saw_error = TRUE;
2830 			return;
2831 		}
2832 		if ((err = zonecfg_set_zonepath(handle, prop_id)) != Z_OK)
2833 			zone_perror(zone, err, TRUE);
2834 		else
2835 			need_to_commit = TRUE;
2836 		return;
2837 	case RT_AUTOBOOT:
2838 		if (strcmp(prop_id, "true") == 0) {
2839 			autoboot = B_TRUE;
2840 		} else if (strcmp(prop_id, "false") == 0) {
2841 			autoboot = B_FALSE;
2842 		} else {
2843 			zerr(gettext("%s value must be '%s' or '%s'."),
2844 			    pt_to_str(PT_AUTOBOOT), "true", "false");
2845 			saw_error = TRUE;
2846 			return;
2847 		}
2848 		if ((err = zonecfg_set_autoboot(handle, autoboot)) != Z_OK)
2849 			zone_perror(zone, err, TRUE);
2850 		else
2851 			need_to_commit = TRUE;
2852 		return;
2853 	case RT_POOL:
2854 		if ((err = zonecfg_set_pool(handle, prop_id)) != Z_OK)
2855 			zone_perror(zone, err, TRUE);
2856 		else
2857 			need_to_commit = TRUE;
2858 		return;
2859 	case RT_LIMITPRIV:
2860 		if ((err = zonecfg_set_limitpriv(handle, prop_id)) != Z_OK)
2861 			zone_perror(zone, err, TRUE);
2862 		else
2863 			need_to_commit = TRUE;
2864 		return;
2865 	case RT_BOOTARGS:
2866 		if ((err = zonecfg_set_bootargs(handle, prop_id)) != Z_OK)
2867 			zone_perror(zone, err, TRUE);
2868 		else
2869 			need_to_commit = TRUE;
2870 		return;
2871 	case RT_FS:
2872 		switch (prop_type) {
2873 		case PT_DIR:
2874 			(void) strlcpy(in_progress_fstab.zone_fs_dir, prop_id,
2875 			    sizeof (in_progress_fstab.zone_fs_dir));
2876 			return;
2877 		case PT_SPECIAL:
2878 			(void) strlcpy(in_progress_fstab.zone_fs_special,
2879 			    prop_id,
2880 			    sizeof (in_progress_fstab.zone_fs_special));
2881 			return;
2882 		case PT_RAW:
2883 			(void) strlcpy(in_progress_fstab.zone_fs_raw,
2884 			    prop_id, sizeof (in_progress_fstab.zone_fs_raw));
2885 			return;
2886 		case PT_TYPE:
2887 			if (!valid_fs_type(prop_id)) {
2888 				zerr(gettext("\"%s\" is not a valid %s."),
2889 				    prop_id, pt_to_str(PT_TYPE));
2890 				saw_error = TRUE;
2891 				return;
2892 			}
2893 			(void) strlcpy(in_progress_fstab.zone_fs_type, prop_id,
2894 			    sizeof (in_progress_fstab.zone_fs_type));
2895 			return;
2896 		case PT_OPTIONS:
2897 			if (pp->pv_type != PROP_VAL_SIMPLE &&
2898 			    pp->pv_type != PROP_VAL_LIST) {
2899 				zerr(gettext("A %s or %s value was expected "
2900 				    "here."), pvt_to_str(PROP_VAL_SIMPLE),
2901 				    pvt_to_str(PROP_VAL_LIST));
2902 				saw_error = TRUE;
2903 				return;
2904 			}
2905 			zonecfg_free_fs_option_list(
2906 			    in_progress_fstab.zone_fs_options);
2907 			in_progress_fstab.zone_fs_options = NULL;
2908 			if (!(pp->pv_type == PROP_VAL_LIST &&
2909 			    pp->pv_list == NULL))
2910 				add_property(cmd);
2911 			return;
2912 		default:
2913 			break;
2914 		}
2915 		zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE, TRUE);
2916 		long_usage(CMD_SET, TRUE);
2917 		usage(FALSE, HELP_PROPS);
2918 		return;
2919 	case RT_IPD:
2920 		switch (prop_type) {
2921 		case PT_DIR:
2922 			(void) strlcpy(in_progress_ipdtab.zone_fs_dir, prop_id,
2923 			    sizeof (in_progress_ipdtab.zone_fs_dir));
2924 			return;
2925 		default:
2926 			break;
2927 		}
2928 		zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE, TRUE);
2929 		long_usage(CMD_SET, TRUE);
2930 		usage(FALSE, HELP_PROPS);
2931 		return;
2932 	case RT_NET:
2933 		switch (prop_type) {
2934 		case PT_ADDRESS:
2935 			if (validate_net_address_syntax(prop_id) != Z_OK) {
2936 				saw_error = TRUE;
2937 				return;
2938 			}
2939 			(void) strlcpy(in_progress_nwiftab.zone_nwif_address,
2940 			    prop_id,
2941 			    sizeof (in_progress_nwiftab.zone_nwif_address));
2942 			break;
2943 		case PT_PHYSICAL:
2944 			if (validate_net_physical_syntax(prop_id) != Z_OK) {
2945 				saw_error = TRUE;
2946 				return;
2947 			}
2948 			(void) strlcpy(in_progress_nwiftab.zone_nwif_physical,
2949 			    prop_id,
2950 			    sizeof (in_progress_nwiftab.zone_nwif_physical));
2951 			break;
2952 		default:
2953 			zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE,
2954 			    TRUE);
2955 			long_usage(CMD_SET, TRUE);
2956 			usage(FALSE, HELP_PROPS);
2957 			return;
2958 		}
2959 		return;
2960 	case RT_DEVICE:
2961 		switch (prop_type) {
2962 		case PT_MATCH:
2963 			(void) strlcpy(in_progress_devtab.zone_dev_match,
2964 			    prop_id,
2965 			    sizeof (in_progress_devtab.zone_dev_match));
2966 			break;
2967 		default:
2968 			zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE,
2969 			    TRUE);
2970 			long_usage(CMD_SET, TRUE);
2971 			usage(FALSE, HELP_PROPS);
2972 			return;
2973 		}
2974 		return;
2975 	case RT_RCTL:
2976 		switch (prop_type) {
2977 		case PT_NAME:
2978 			if (!zonecfg_valid_rctlname(prop_id)) {
2979 				zerr(gettext("'%s' is not a valid zone %s "
2980 				    "name."), prop_id, rt_to_str(RT_RCTL));
2981 				return;
2982 			}
2983 			(void) strlcpy(in_progress_rctltab.zone_rctl_name,
2984 			    prop_id,
2985 			    sizeof (in_progress_rctltab.zone_rctl_name));
2986 			break;
2987 		case PT_VALUE:
2988 			if (pp->pv_type != PROP_VAL_COMPLEX &&
2989 			    pp->pv_type != PROP_VAL_LIST) {
2990 				zerr(gettext("A %s or %s value was expected "
2991 				    "here."), pvt_to_str(PROP_VAL_COMPLEX),
2992 				    pvt_to_str(PROP_VAL_LIST));
2993 				saw_error = TRUE;
2994 				return;
2995 			}
2996 			zonecfg_free_rctl_value_list(
2997 			    in_progress_rctltab.zone_rctl_valptr);
2998 			in_progress_rctltab.zone_rctl_valptr = NULL;
2999 			if (!(pp->pv_type == PROP_VAL_LIST &&
3000 			    pp->pv_list == NULL))
3001 				add_property(cmd);
3002 			break;
3003 		default:
3004 			zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE,
3005 			    TRUE);
3006 			long_usage(CMD_SET, TRUE);
3007 			usage(FALSE, HELP_PROPS);
3008 			return;
3009 		}
3010 		return;
3011 	case RT_ATTR:
3012 		switch (prop_type) {
3013 		case PT_NAME:
3014 			(void) strlcpy(in_progress_attrtab.zone_attr_name,
3015 			    prop_id,
3016 			    sizeof (in_progress_attrtab.zone_attr_name));
3017 			break;
3018 		case PT_TYPE:
3019 			(void) strlcpy(in_progress_attrtab.zone_attr_type,
3020 			    prop_id,
3021 			    sizeof (in_progress_attrtab.zone_attr_type));
3022 			break;
3023 		case PT_VALUE:
3024 			(void) strlcpy(in_progress_attrtab.zone_attr_value,
3025 			    prop_id,
3026 			    sizeof (in_progress_attrtab.zone_attr_value));
3027 			break;
3028 		default:
3029 			zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE,
3030 			    TRUE);
3031 			long_usage(CMD_SET, TRUE);
3032 			usage(FALSE, HELP_PROPS);
3033 			return;
3034 		}
3035 		return;
3036 	case RT_DATASET:
3037 		switch (prop_type) {
3038 		case PT_NAME:
3039 			(void) strlcpy(in_progress_dstab.zone_dataset_name,
3040 			    prop_id,
3041 			    sizeof (in_progress_dstab.zone_dataset_name));
3042 			return;
3043 		default:
3044 			break;
3045 		}
3046 		zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE, TRUE);
3047 		long_usage(CMD_SET, TRUE);
3048 		usage(FALSE, HELP_PROPS);
3049 		return;
3050 	default:
3051 		zone_perror(rt_to_str(res_type), Z_NO_RESOURCE_TYPE, TRUE);
3052 		long_usage(CMD_SET, TRUE);
3053 		usage(FALSE, HELP_RESOURCES);
3054 		return;
3055 	}
3056 }
3057 
3058 static void
3059 output_prop(FILE *fp, int pnum, char *pval, bool print_notspec)
3060 {
3061 	char *qstr;
3062 
3063 	if (*pval != '\0') {
3064 		qstr = quoteit(pval);
3065 		(void) fprintf(fp, "\t%s: %s\n", pt_to_str(pnum), qstr);
3066 		free(qstr);
3067 	} else if (print_notspec)
3068 		(void) fprintf(fp, gettext("\t%s not specified\n"),
3069 		    pt_to_str(pnum));
3070 }
3071 
3072 static void
3073 info_zonename(zone_dochandle_t handle, FILE *fp)
3074 {
3075 	char zonename[ZONENAME_MAX];
3076 
3077 	if (zonecfg_get_name(handle, zonename, sizeof (zonename)) == Z_OK)
3078 		(void) fprintf(fp, "%s: %s\n", pt_to_str(PT_ZONENAME),
3079 		    zonename);
3080 	else
3081 		(void) fprintf(fp, gettext("%s not specified\n"),
3082 		    pt_to_str(PT_ZONENAME));
3083 }
3084 
3085 static void
3086 info_zonepath(zone_dochandle_t handle, FILE *fp)
3087 {
3088 	char zonepath[MAXPATHLEN];
3089 
3090 	if (zonecfg_get_zonepath(handle, zonepath, sizeof (zonepath)) == Z_OK)
3091 		(void) fprintf(fp, "%s: %s\n", pt_to_str(PT_ZONEPATH),
3092 		    zonepath);
3093 	else {
3094 		(void) fprintf(fp, gettext("%s not specified\n"),
3095 		    pt_to_str(PT_ZONEPATH));
3096 	}
3097 }
3098 
3099 static void
3100 info_autoboot(zone_dochandle_t handle, FILE *fp)
3101 {
3102 	boolean_t autoboot;
3103 	int err;
3104 
3105 	if ((err = zonecfg_get_autoboot(handle, &autoboot)) == Z_OK)
3106 		(void) fprintf(fp, "%s: %s\n", pt_to_str(PT_AUTOBOOT),
3107 		    autoboot ? "true" : "false");
3108 	else
3109 		zone_perror(zone, err, TRUE);
3110 }
3111 
3112 static void
3113 info_pool(zone_dochandle_t handle, FILE *fp)
3114 {
3115 	char pool[MAXNAMELEN];
3116 	int err;
3117 
3118 	if ((err = zonecfg_get_pool(handle, pool, sizeof (pool))) == Z_OK)
3119 		(void) fprintf(fp, "%s: %s\n", pt_to_str(PT_POOL), pool);
3120 	else
3121 		zone_perror(zone, err, TRUE);
3122 }
3123 
3124 static void
3125 info_limitpriv(zone_dochandle_t handle, FILE *fp)
3126 {
3127 	char *limitpriv;
3128 	int err;
3129 
3130 	if ((err = zonecfg_get_limitpriv(handle, &limitpriv)) == Z_OK) {
3131 		(void) fprintf(fp, "%s: %s\n", pt_to_str(PT_LIMITPRIV),
3132 		    limitpriv);
3133 		free(limitpriv);
3134 	} else {
3135 		zone_perror(zone, err, TRUE);
3136 	}
3137 }
3138 
3139 static void
3140 info_bootargs(zone_dochandle_t handle, FILE *fp)
3141 {
3142 	char bootargs[BOOTARGS_MAX];
3143 	int err;
3144 
3145 	if ((err = zonecfg_get_bootargs(handle, bootargs,
3146 	    sizeof (bootargs))) == Z_OK) {
3147 		(void) fprintf(fp, "%s: %s\n", pt_to_str(PT_BOOTARGS),
3148 		    bootargs);
3149 	} else {
3150 		zone_perror(zone, err, TRUE);
3151 	}
3152 }
3153 
3154 static void
3155 output_fs(FILE *fp, struct zone_fstab *fstab)
3156 {
3157 	zone_fsopt_t *this;
3158 
3159 	(void) fprintf(fp, "%s:\n", rt_to_str(RT_FS));
3160 	output_prop(fp, PT_DIR, fstab->zone_fs_dir, B_TRUE);
3161 	output_prop(fp, PT_SPECIAL, fstab->zone_fs_special, B_TRUE);
3162 	output_prop(fp, PT_RAW, fstab->zone_fs_raw, B_TRUE);
3163 	output_prop(fp, PT_TYPE, fstab->zone_fs_type, B_TRUE);
3164 	(void) fprintf(fp, "\t%s: [", pt_to_str(PT_OPTIONS));
3165 	for (this = fstab->zone_fs_options; this != NULL;
3166 	    this = this->zone_fsopt_next) {
3167 		if (strchr(this->zone_fsopt_opt, '='))
3168 			(void) fprintf(fp, "\"%s\"", this->zone_fsopt_opt);
3169 		else
3170 			(void) fprintf(fp, "%s", this->zone_fsopt_opt);
3171 		if (this->zone_fsopt_next != NULL)
3172 			(void) fprintf(fp, ",");
3173 	}
3174 	(void) fprintf(fp, "]\n");
3175 }
3176 
3177 static void
3178 output_ipd(FILE *fp, struct zone_fstab *ipdtab)
3179 {
3180 	(void) fprintf(fp, "%s:\n", rt_to_str(RT_IPD));
3181 	output_prop(fp, PT_DIR, ipdtab->zone_fs_dir, B_TRUE);
3182 }
3183 
3184 static void
3185 info_fs(zone_dochandle_t handle, FILE *fp, cmd_t *cmd)
3186 {
3187 	struct zone_fstab lookup, user;
3188 	bool output = FALSE;
3189 
3190 	if (zonecfg_setfsent(handle) != Z_OK)
3191 		return;
3192 	while (zonecfg_getfsent(handle, &lookup) == Z_OK) {
3193 		if (cmd->cmd_prop_nv_pairs == 0) {
3194 			output_fs(fp, &lookup);
3195 			goto loopend;
3196 		}
3197 		if (fill_in_fstab(cmd, &user, TRUE) != Z_OK)
3198 			goto loopend;
3199 		if (strlen(user.zone_fs_dir) > 0 &&
3200 		    strcmp(user.zone_fs_dir, lookup.zone_fs_dir) != 0)
3201 			goto loopend;	/* no match */
3202 		if (strlen(user.zone_fs_special) > 0 &&
3203 		    strcmp(user.zone_fs_special, lookup.zone_fs_special) != 0)
3204 			goto loopend;	/* no match */
3205 		if (strlen(user.zone_fs_type) > 0 &&
3206 		    strcmp(user.zone_fs_type, lookup.zone_fs_type) != 0)
3207 			goto loopend;	/* no match */
3208 		output_fs(fp, &lookup);
3209 		output = TRUE;
3210 loopend:
3211 		zonecfg_free_fs_option_list(lookup.zone_fs_options);
3212 	}
3213 	(void) zonecfg_endfsent(handle);
3214 	/*
3215 	 * If a property n/v pair was specified, warn the user if there was
3216 	 * nothing to output.
3217 	 */
3218 	if (!output && cmd->cmd_prop_nv_pairs > 0)
3219 		(void) printf(gettext("No such %s resource.\n"),
3220 		    rt_to_str(RT_FS));
3221 }
3222 
3223 static void
3224 info_ipd(zone_dochandle_t handle, FILE *fp, cmd_t *cmd)
3225 {
3226 	struct zone_fstab lookup, user;
3227 	bool output = FALSE;
3228 
3229 	if (zonecfg_setipdent(handle) != Z_OK)
3230 		return;
3231 	while (zonecfg_getipdent(handle, &lookup) == Z_OK) {
3232 		if (cmd->cmd_prop_nv_pairs == 0) {
3233 			output_ipd(fp, &lookup);
3234 			continue;
3235 		}
3236 		if (fill_in_ipdtab(cmd, &user, TRUE) != Z_OK)
3237 			continue;
3238 		if (strlen(user.zone_fs_dir) > 0 &&
3239 		    strcmp(user.zone_fs_dir, lookup.zone_fs_dir) != 0)
3240 			continue;	/* no match */
3241 		output_ipd(fp, &lookup);
3242 		output = TRUE;
3243 	}
3244 	(void) zonecfg_endipdent(handle);
3245 	/*
3246 	 * If a property n/v pair was specified, warn the user if there was
3247 	 * nothing to output.
3248 	 */
3249 	if (!output && cmd->cmd_prop_nv_pairs > 0)
3250 		(void) printf(gettext("No such %s resource.\n"),
3251 		    rt_to_str(RT_IPD));
3252 }
3253 
3254 static void
3255 output_net(FILE *fp, struct zone_nwiftab *nwiftab)
3256 {
3257 	(void) fprintf(fp, "%s:\n", rt_to_str(RT_NET));
3258 	output_prop(fp, PT_ADDRESS, nwiftab->zone_nwif_address, B_TRUE);
3259 	output_prop(fp, PT_PHYSICAL, nwiftab->zone_nwif_physical, B_TRUE);
3260 }
3261 
3262 static void
3263 info_net(zone_dochandle_t handle, FILE *fp, cmd_t *cmd)
3264 {
3265 	struct zone_nwiftab lookup, user;
3266 	bool output = FALSE;
3267 
3268 	if (zonecfg_setnwifent(handle) != Z_OK)
3269 		return;
3270 	while (zonecfg_getnwifent(handle, &lookup) == Z_OK) {
3271 		if (cmd->cmd_prop_nv_pairs == 0) {
3272 			output_net(fp, &lookup);
3273 			continue;
3274 		}
3275 		if (fill_in_nwiftab(cmd, &user, TRUE) != Z_OK)
3276 			continue;
3277 		if (strlen(user.zone_nwif_physical) > 0 &&
3278 		    strcmp(user.zone_nwif_physical,
3279 		    lookup.zone_nwif_physical) != 0)
3280 			continue;	/* no match */
3281 		if (strlen(user.zone_nwif_address) > 0 &&
3282 		    !zonecfg_same_net_address(user.zone_nwif_address,
3283 		    lookup.zone_nwif_address))
3284 			continue;	/* no match */
3285 		output_net(fp, &lookup);
3286 		output = TRUE;
3287 	}
3288 	(void) zonecfg_endnwifent(handle);
3289 	/*
3290 	 * If a property n/v pair was specified, warn the user if there was
3291 	 * nothing to output.
3292 	 */
3293 	if (!output && cmd->cmd_prop_nv_pairs > 0)
3294 		(void) printf(gettext("No such %s resource.\n"),
3295 		    rt_to_str(RT_NET));
3296 }
3297 
3298 static void
3299 output_dev(FILE *fp, struct zone_devtab *devtab)
3300 {
3301 	(void) fprintf(fp, "%s\n", rt_to_str(RT_DEVICE));
3302 	output_prop(fp, PT_MATCH, devtab->zone_dev_match, B_TRUE);
3303 }
3304 
3305 static void
3306 info_dev(zone_dochandle_t handle, FILE *fp, cmd_t *cmd)
3307 {
3308 	struct zone_devtab lookup, user;
3309 	bool output = FALSE;
3310 
3311 	if (zonecfg_setdevent(handle) != Z_OK)
3312 		return;
3313 	while (zonecfg_getdevent(handle, &lookup) == Z_OK) {
3314 		if (cmd->cmd_prop_nv_pairs == 0) {
3315 			output_dev(fp, &lookup);
3316 			continue;
3317 		}
3318 		if (fill_in_devtab(cmd, &user, TRUE) != Z_OK)
3319 			continue;
3320 		if (strlen(user.zone_dev_match) > 0 &&
3321 		    strcmp(user.zone_dev_match, lookup.zone_dev_match) != 0)
3322 			continue;	/* no match */
3323 		output_dev(fp, &lookup);
3324 		output = TRUE;
3325 	}
3326 	(void) zonecfg_enddevent(handle);
3327 	/*
3328 	 * If a property n/v pair was specified, warn the user if there was
3329 	 * nothing to output.
3330 	 */
3331 	if (!output && cmd->cmd_prop_nv_pairs > 0)
3332 		(void) printf(gettext("No such %s resource.\n"),
3333 		    rt_to_str(RT_DEVICE));
3334 }
3335 
3336 static void
3337 output_rctl(FILE *fp, struct zone_rctltab *rctltab)
3338 {
3339 	struct zone_rctlvaltab *valptr;
3340 
3341 	(void) fprintf(fp, "%s:\n", rt_to_str(RT_RCTL));
3342 	output_prop(fp, PT_NAME, rctltab->zone_rctl_name, B_TRUE);
3343 	for (valptr = rctltab->zone_rctl_valptr; valptr != NULL;
3344 	    valptr = valptr->zone_rctlval_next) {
3345 		fprintf(fp, "\t%s: (%s=%s,%s=%s,%s=%s)\n",
3346 		    pt_to_str(PT_VALUE),
3347 		    pt_to_str(PT_PRIV), valptr->zone_rctlval_priv,
3348 		    pt_to_str(PT_LIMIT), valptr->zone_rctlval_limit,
3349 		    pt_to_str(PT_ACTION), valptr->zone_rctlval_action);
3350 	}
3351 }
3352 
3353 static void
3354 info_rctl(zone_dochandle_t handle, FILE *fp, cmd_t *cmd)
3355 {
3356 	struct zone_rctltab lookup, user;
3357 	bool output = FALSE;
3358 
3359 	if (zonecfg_setrctlent(handle) != Z_OK)
3360 		return;
3361 	while (zonecfg_getrctlent(handle, &lookup) == Z_OK) {
3362 		if (cmd->cmd_prop_nv_pairs == 0) {
3363 			output_rctl(fp, &lookup);
3364 		} else if (fill_in_rctltab(cmd, &user, TRUE) == Z_OK &&
3365 		    (strlen(user.zone_rctl_name) == 0 ||
3366 		    strcmp(user.zone_rctl_name, lookup.zone_rctl_name) == 0)) {
3367 			output_rctl(fp, &lookup);
3368 			output = TRUE;
3369 		}
3370 		zonecfg_free_rctl_value_list(lookup.zone_rctl_valptr);
3371 	}
3372 	(void) zonecfg_endrctlent(handle);
3373 	/*
3374 	 * If a property n/v pair was specified, warn the user if there was
3375 	 * nothing to output.
3376 	 */
3377 	if (!output && cmd->cmd_prop_nv_pairs > 0)
3378 		(void) printf(gettext("No such %s resource.\n"),
3379 		    rt_to_str(RT_RCTL));
3380 }
3381 
3382 static void
3383 output_attr(FILE *fp, struct zone_attrtab *attrtab)
3384 {
3385 	(void) fprintf(fp, "%s:\n", rt_to_str(RT_ATTR));
3386 	output_prop(fp, PT_NAME, attrtab->zone_attr_name, B_TRUE);
3387 	output_prop(fp, PT_TYPE, attrtab->zone_attr_type, B_TRUE);
3388 	output_prop(fp, PT_VALUE, attrtab->zone_attr_value, B_TRUE);
3389 }
3390 
3391 static void
3392 info_attr(zone_dochandle_t handle, FILE *fp, cmd_t *cmd)
3393 {
3394 	struct zone_attrtab lookup, user;
3395 	bool output = FALSE;
3396 
3397 	if (zonecfg_setattrent(handle) != Z_OK)
3398 		return;
3399 	while (zonecfg_getattrent(handle, &lookup) == Z_OK) {
3400 		if (cmd->cmd_prop_nv_pairs == 0) {
3401 			output_attr(fp, &lookup);
3402 			continue;
3403 		}
3404 		if (fill_in_attrtab(cmd, &user, TRUE) != Z_OK)
3405 			continue;
3406 		if (strlen(user.zone_attr_name) > 0 &&
3407 		    strcmp(user.zone_attr_name, lookup.zone_attr_name) != 0)
3408 			continue;	/* no match */
3409 		if (strlen(user.zone_attr_type) > 0 &&
3410 		    strcmp(user.zone_attr_type, lookup.zone_attr_type) != 0)
3411 			continue;	/* no match */
3412 		if (strlen(user.zone_attr_value) > 0 &&
3413 		    strcmp(user.zone_attr_value, lookup.zone_attr_value) != 0)
3414 			continue;	/* no match */
3415 		output_attr(fp, &lookup);
3416 		output = TRUE;
3417 	}
3418 	(void) zonecfg_endattrent(handle);
3419 	/*
3420 	 * If a property n/v pair was specified, warn the user if there was
3421 	 * nothing to output.
3422 	 */
3423 	if (!output && cmd->cmd_prop_nv_pairs > 0)
3424 		(void) printf(gettext("No such %s resource.\n"),
3425 		    rt_to_str(RT_ATTR));
3426 }
3427 
3428 static void
3429 output_ds(FILE *fp, struct zone_dstab *dstab)
3430 {
3431 	(void) fprintf(fp, "%s:\n", rt_to_str(RT_DATASET));
3432 	output_prop(fp, PT_NAME, dstab->zone_dataset_name, B_TRUE);
3433 }
3434 
3435 static void
3436 info_ds(zone_dochandle_t handle, FILE *fp, cmd_t *cmd)
3437 {
3438 	struct zone_dstab lookup, user;
3439 	bool output = FALSE;
3440 
3441 	if (zonecfg_setdevent(handle) != Z_OK)
3442 		return;
3443 	while (zonecfg_getdsent(handle, &lookup) == Z_OK) {
3444 		if (cmd->cmd_prop_nv_pairs == 0) {
3445 			output_ds(fp, &lookup);
3446 			continue;
3447 		}
3448 		if (fill_in_dstab(cmd, &user, TRUE) != Z_OK)
3449 			continue;
3450 		if (strlen(user.zone_dataset_name) > 0 &&
3451 		    strcmp(user.zone_dataset_name,
3452 		    lookup.zone_dataset_name) != 0)
3453 			continue;	/* no match */
3454 		output_ds(fp, &lookup);
3455 		output = TRUE;
3456 	}
3457 	(void) zonecfg_enddsent(handle);
3458 	/*
3459 	 * If a property n/v pair was specified, warn the user if there was
3460 	 * nothing to output.
3461 	 */
3462 	if (!output && cmd->cmd_prop_nv_pairs > 0)
3463 		(void) printf(gettext("No such %s resource.\n"),
3464 		    rt_to_str(RT_DATASET));
3465 }
3466 
3467 
3468 void
3469 info_func(cmd_t *cmd)
3470 {
3471 	FILE *fp = stdout;
3472 	bool need_to_close = FALSE;
3473 	char *pager;
3474 
3475 	assert(cmd != NULL);
3476 
3477 	if (initialize(TRUE) != Z_OK)
3478 		return;
3479 
3480 	/* don't page error output */
3481 	if (interactive_mode) {
3482 		if ((pager = getenv("PAGER")) == NULL)
3483 			pager = PAGER;
3484 		if ((fp = popen(pager, "w")) != NULL)
3485 			need_to_close = TRUE;
3486 		else
3487 			fp = stdout;
3488 		setbuf(fp, NULL);
3489 	}
3490 
3491 	if (!global_scope) {
3492 		switch (resource_scope) {
3493 		case RT_FS:
3494 			output_fs(fp, &in_progress_fstab);
3495 			break;
3496 		case RT_IPD:
3497 			output_ipd(fp, &in_progress_ipdtab);
3498 			break;
3499 		case RT_NET:
3500 			output_net(fp, &in_progress_nwiftab);
3501 			break;
3502 		case RT_DEVICE:
3503 			output_dev(fp, &in_progress_devtab);
3504 			break;
3505 		case RT_RCTL:
3506 			output_rctl(fp, &in_progress_rctltab);
3507 			break;
3508 		case RT_ATTR:
3509 			output_attr(fp, &in_progress_attrtab);
3510 			break;
3511 		case RT_DATASET:
3512 			output_ds(fp, &in_progress_dstab);
3513 			break;
3514 		}
3515 		goto cleanup;
3516 	}
3517 
3518 	switch (cmd->cmd_res_type) {
3519 	case RT_UNKNOWN:
3520 		info_zonename(handle, fp);
3521 		info_zonepath(handle, fp);
3522 		info_autoboot(handle, fp);
3523 		info_bootargs(handle, fp);
3524 		info_pool(handle, fp);
3525 		info_limitpriv(handle, fp);
3526 		info_ipd(handle, fp, cmd);
3527 		info_fs(handle, fp, cmd);
3528 		info_net(handle, fp, cmd);
3529 		info_dev(handle, fp, cmd);
3530 		info_rctl(handle, fp, cmd);
3531 		info_attr(handle, fp, cmd);
3532 		info_ds(handle, fp, cmd);
3533 		break;
3534 	case RT_ZONENAME:
3535 		info_zonename(handle, fp);
3536 		break;
3537 	case RT_ZONEPATH:
3538 		info_zonepath(handle, fp);
3539 		break;
3540 	case RT_AUTOBOOT:
3541 		info_autoboot(handle, fp);
3542 		break;
3543 	case RT_POOL:
3544 		info_pool(handle, fp);
3545 		break;
3546 	case RT_LIMITPRIV:
3547 		info_limitpriv(handle, fp);
3548 		break;
3549 	case RT_BOOTARGS:
3550 		info_bootargs(handle, fp);
3551 		break;
3552 	case RT_FS:
3553 		info_fs(handle, fp, cmd);
3554 		break;
3555 	case RT_IPD:
3556 		info_ipd(handle, fp, cmd);
3557 		break;
3558 	case RT_NET:
3559 		info_net(handle, fp, cmd);
3560 		break;
3561 	case RT_DEVICE:
3562 		info_dev(handle, fp, cmd);
3563 		break;
3564 	case RT_RCTL:
3565 		info_rctl(handle, fp, cmd);
3566 		break;
3567 	case RT_ATTR:
3568 		info_attr(handle, fp, cmd);
3569 		break;
3570 	case RT_DATASET:
3571 		info_ds(handle, fp, cmd);
3572 		break;
3573 	default:
3574 		zone_perror(rt_to_str(cmd->cmd_res_type), Z_NO_RESOURCE_TYPE,
3575 		    TRUE);
3576 	}
3577 
3578 cleanup:
3579 	if (need_to_close)
3580 		(void) pclose(fp);
3581 }
3582 
3583 /*
3584  * Helper function for verify-- checks that a required string property
3585  * exists.
3586  */
3587 static void
3588 check_reqd_prop(char *attr, int rt, int pt, int *ret_val)
3589 {
3590 	if (strlen(attr) == 0) {
3591 		zerr(gettext("%s: %s not specified"), rt_to_str(rt),
3592 		    pt_to_str(pt));
3593 		saw_error = TRUE;
3594 		if (*ret_val == Z_OK)
3595 			*ret_val = Z_REQD_PROPERTY_MISSING;
3596 	}
3597 }
3598 
3599 /*
3600  * See the DTD for which attributes are required for which resources.
3601  *
3602  * This function can be called by commit_func(), which needs to save things,
3603  * in addition to the general call from parse_and_run(), which doesn't need
3604  * things saved.  Since the parameters are standardized, we distinguish by
3605  * having commit_func() call here with cmd->cmd_arg set to "save" to indicate
3606  * that a save is needed.
3607  */
3608 void
3609 verify_func(cmd_t *cmd)
3610 {
3611 	struct zone_nwiftab nwiftab;
3612 	struct zone_fstab fstab;
3613 	struct zone_attrtab attrtab;
3614 	struct zone_rctltab rctltab;
3615 	struct zone_dstab dstab;
3616 	char zonepath[MAXPATHLEN];
3617 	int err, ret_val = Z_OK, arg;
3618 	bool save = FALSE;
3619 
3620 	optind = 0;
3621 	if ((arg = getopt(cmd->cmd_argc, cmd->cmd_argv, "?")) != EOF) {
3622 		switch (arg) {
3623 		case '?':
3624 			longer_usage(CMD_VERIFY);
3625 			return;
3626 		default:
3627 			short_usage(CMD_VERIFY);
3628 			return;
3629 		}
3630 	}
3631 	if (optind > cmd->cmd_argc) {
3632 		short_usage(CMD_VERIFY);
3633 		return;
3634 	}
3635 
3636 	if (zone_is_read_only(CMD_VERIFY))
3637 		return;
3638 
3639 	assert(cmd != NULL);
3640 
3641 	if (cmd->cmd_argc > 0 && (strcmp(cmd->cmd_argv[0], "save") == 0))
3642 		save = TRUE;
3643 	if (initialize(TRUE) != Z_OK)
3644 		return;
3645 
3646 	if (zonecfg_get_zonepath(handle, zonepath, sizeof (zonepath)) != Z_OK) {
3647 		zerr(gettext("%s not specified"), pt_to_str(PT_ZONEPATH));
3648 		ret_val = Z_REQD_RESOURCE_MISSING;
3649 		saw_error = TRUE;
3650 	}
3651 	if (strlen(zonepath) == 0) {
3652 		zerr(gettext("%s cannot be empty."), pt_to_str(PT_ZONEPATH));
3653 		ret_val = Z_REQD_RESOURCE_MISSING;
3654 		saw_error = TRUE;
3655 	}
3656 
3657 	if ((err = zonecfg_setipdent(handle)) != Z_OK) {
3658 		zone_perror(zone, err, TRUE);
3659 		return;
3660 	}
3661 	while (zonecfg_getipdent(handle, &fstab) == Z_OK) {
3662 		check_reqd_prop(fstab.zone_fs_dir, RT_IPD, PT_DIR, &ret_val);
3663 	}
3664 	(void) zonecfg_endipdent(handle);
3665 
3666 	if ((err = zonecfg_setfsent(handle)) != Z_OK) {
3667 		zone_perror(zone, err, TRUE);
3668 		return;
3669 	}
3670 	while (zonecfg_getfsent(handle, &fstab) == Z_OK) {
3671 		check_reqd_prop(fstab.zone_fs_dir, RT_FS, PT_DIR, &ret_val);
3672 		check_reqd_prop(fstab.zone_fs_special, RT_FS, PT_SPECIAL,
3673 		    &ret_val);
3674 		check_reqd_prop(fstab.zone_fs_type, RT_FS, PT_TYPE, &ret_val);
3675 
3676 		zonecfg_free_fs_option_list(fstab.zone_fs_options);
3677 	}
3678 	(void) zonecfg_endfsent(handle);
3679 
3680 	if ((err = zonecfg_setnwifent(handle)) != Z_OK) {
3681 		zone_perror(zone, err, TRUE);
3682 		return;
3683 	}
3684 	while (zonecfg_getnwifent(handle, &nwiftab) == Z_OK) {
3685 		check_reqd_prop(nwiftab.zone_nwif_address, RT_NET,
3686 		    PT_ADDRESS, &ret_val);
3687 		check_reqd_prop(nwiftab.zone_nwif_physical, RT_NET,
3688 		    PT_PHYSICAL, &ret_val);
3689 	}
3690 	(void) zonecfg_endnwifent(handle);
3691 
3692 	if ((err = zonecfg_setrctlent(handle)) != Z_OK) {
3693 		zone_perror(zone, err, TRUE);
3694 		return;
3695 	}
3696 	while (zonecfg_getrctlent(handle, &rctltab) == Z_OK) {
3697 		check_reqd_prop(rctltab.zone_rctl_name, RT_RCTL, PT_NAME,
3698 		    &ret_val);
3699 
3700 		if (rctltab.zone_rctl_valptr == NULL) {
3701 			zerr(gettext("%s: no %s specified"),
3702 			    rt_to_str(RT_RCTL), pt_to_str(PT_VALUE));
3703 			saw_error = TRUE;
3704 			if (ret_val == Z_OK)
3705 				ret_val = Z_REQD_PROPERTY_MISSING;
3706 		} else {
3707 			zonecfg_free_rctl_value_list(rctltab.zone_rctl_valptr);
3708 		}
3709 	}
3710 	(void) zonecfg_endrctlent(handle);
3711 
3712 	if ((err = zonecfg_setattrent(handle)) != Z_OK) {
3713 		zone_perror(zone, err, TRUE);
3714 		return;
3715 	}
3716 	while (zonecfg_getattrent(handle, &attrtab) == Z_OK) {
3717 		check_reqd_prop(attrtab.zone_attr_name, RT_ATTR, PT_NAME,
3718 		    &ret_val);
3719 		check_reqd_prop(attrtab.zone_attr_type, RT_ATTR, PT_TYPE,
3720 		    &ret_val);
3721 		check_reqd_prop(attrtab.zone_attr_value, RT_ATTR, PT_VALUE,
3722 		    &ret_val);
3723 	}
3724 	(void) zonecfg_endattrent(handle);
3725 
3726 	if ((err = zonecfg_setdsent(handle)) != Z_OK) {
3727 		zone_perror(zone, err, TRUE);
3728 		return;
3729 	}
3730 	while (zonecfg_getdsent(handle, &dstab) == Z_OK) {
3731 		if (strlen(dstab.zone_dataset_name) == 0) {
3732 			zerr("%s: %s %s", rt_to_str(RT_DATASET),
3733 			    pt_to_str(PT_NAME), gettext("not specified"));
3734 			saw_error = TRUE;
3735 			if (ret_val == Z_OK)
3736 				ret_val = Z_REQD_PROPERTY_MISSING;
3737 		} else if (!zfs_name_valid(dstab.zone_dataset_name,
3738 		    ZFS_TYPE_FILESYSTEM)) {
3739 			zerr("%s: %s %s", rt_to_str(RT_DATASET),
3740 			    pt_to_str(PT_NAME), gettext("invalid"));
3741 			saw_error = TRUE;
3742 			if (ret_val == Z_OK)
3743 				ret_val = Z_BAD_PROPERTY;
3744 		}
3745 
3746 	}
3747 	(void) zonecfg_enddsent(handle);
3748 
3749 	if (!global_scope) {
3750 		zerr(gettext("resource specification incomplete"));
3751 		saw_error = TRUE;
3752 		if (ret_val == Z_OK)
3753 			ret_val = Z_INSUFFICIENT_SPEC;
3754 	}
3755 
3756 	if (save) {
3757 		if (ret_val == Z_OK) {
3758 			if ((ret_val = zonecfg_save(handle)) == Z_OK) {
3759 				need_to_commit = FALSE;
3760 				(void) strlcpy(revert_zone, zone,
3761 				    sizeof (revert_zone));
3762 			}
3763 		} else {
3764 			zerr(gettext("Zone %s failed to verify"), zone);
3765 		}
3766 	}
3767 	if (ret_val != Z_OK)
3768 		zone_perror(zone, ret_val, TRUE);
3769 }
3770 
3771 void
3772 cancel_func(cmd_t *cmd)
3773 {
3774 	int arg;
3775 
3776 	assert(cmd != NULL);
3777 
3778 	optind = 0;
3779 	if ((arg = getopt(cmd->cmd_argc, cmd->cmd_argv, "?")) != EOF) {
3780 		switch (arg) {
3781 		case '?':
3782 			longer_usage(CMD_CANCEL);
3783 			return;
3784 		default:
3785 			short_usage(CMD_CANCEL);
3786 			return;
3787 		}
3788 	}
3789 	if (optind != cmd->cmd_argc) {
3790 		short_usage(CMD_CANCEL);
3791 		return;
3792 	}
3793 
3794 	if (global_scope)
3795 		scope_usage(CMD_CANCEL);
3796 	global_scope = TRUE;
3797 	zonecfg_free_fs_option_list(in_progress_fstab.zone_fs_options);
3798 	bzero(&in_progress_fstab, sizeof (in_progress_fstab));
3799 	bzero(&in_progress_nwiftab, sizeof (in_progress_nwiftab));
3800 	bzero(&in_progress_ipdtab, sizeof (in_progress_ipdtab));
3801 	bzero(&in_progress_devtab, sizeof (in_progress_devtab));
3802 	zonecfg_free_rctl_value_list(in_progress_rctltab.zone_rctl_valptr);
3803 	bzero(&in_progress_rctltab, sizeof (in_progress_rctltab));
3804 	bzero(&in_progress_attrtab, sizeof (in_progress_attrtab));
3805 	bzero(&in_progress_dstab, sizeof (in_progress_dstab));
3806 }
3807 
3808 static int
3809 validate_attr_name(char *name)
3810 {
3811 	int i;
3812 
3813 	if (!isalnum(name[0])) {
3814 		zerr(gettext("Invalid %s %s %s: must start with an alpha-"
3815 		    "numeric character."), rt_to_str(RT_ATTR),
3816 		    pt_to_str(PT_NAME), name);
3817 		return (Z_INVAL);
3818 	}
3819 	for (i = 1; name[i]; i++)
3820 		if (!isalnum(name[i]) && name[i] != '-' && name[i] != '.') {
3821 			zerr(gettext("Invalid %s %s %s: can only contain "
3822 			    "alpha-numeric characters, plus '-' and '.'."),
3823 			    rt_to_str(RT_ATTR), pt_to_str(PT_NAME), name);
3824 			return (Z_INVAL);
3825 		}
3826 	return (Z_OK);
3827 }
3828 
3829 static int
3830 validate_attr_type_val(struct zone_attrtab *attrtab)
3831 {
3832 	boolean_t boolval;
3833 	int64_t intval;
3834 	char strval[MAXNAMELEN];
3835 	uint64_t uintval;
3836 
3837 	if (strcmp(attrtab->zone_attr_type, "boolean") == 0) {
3838 		if (zonecfg_get_attr_boolean(attrtab, &boolval) == Z_OK)
3839 			return (Z_OK);
3840 		zerr(gettext("invalid %s value for %s=%s"),
3841 		    rt_to_str(RT_ATTR), pt_to_str(PT_TYPE), "boolean");
3842 		return (Z_ERR);
3843 	}
3844 
3845 	if (strcmp(attrtab->zone_attr_type, "int") == 0) {
3846 		if (zonecfg_get_attr_int(attrtab, &intval) == Z_OK)
3847 			return (Z_OK);
3848 		zerr(gettext("invalid %s value for %s=%s"),
3849 		    rt_to_str(RT_ATTR), pt_to_str(PT_TYPE), "int");
3850 		return (Z_ERR);
3851 	}
3852 
3853 	if (strcmp(attrtab->zone_attr_type, "string") == 0) {
3854 		if (zonecfg_get_attr_string(attrtab, strval,
3855 		    sizeof (strval)) == Z_OK)
3856 			return (Z_OK);
3857 		zerr(gettext("invalid %s value for %s=%s"),
3858 		    rt_to_str(RT_ATTR), pt_to_str(PT_TYPE), "string");
3859 		return (Z_ERR);
3860 	}
3861 
3862 	if (strcmp(attrtab->zone_attr_type, "uint") == 0) {
3863 		if (zonecfg_get_attr_uint(attrtab, &uintval) == Z_OK)
3864 			return (Z_OK);
3865 		zerr(gettext("invalid %s value for %s=%s"),
3866 		    rt_to_str(RT_ATTR), pt_to_str(PT_TYPE), "uint");
3867 		return (Z_ERR);
3868 	}
3869 
3870 	zerr(gettext("invalid %s %s '%s'"), rt_to_str(RT_ATTR),
3871 	    pt_to_str(PT_TYPE), attrtab->zone_attr_type);
3872 	return (Z_ERR);
3873 }
3874 
3875 /*
3876  * Helper function for end_func-- checks the existence of a given property
3877  * and emits a message if not specified.
3878  */
3879 static int
3880 end_check_reqd(char *attr, int pt, bool *validation_failed)
3881 {
3882 	if (strlen(attr) == 0) {
3883 		*validation_failed = TRUE;
3884 		zerr(gettext("%s not specified"), pt_to_str(pt));
3885 		return (Z_ERR);
3886 	}
3887 	return (Z_OK);
3888 }
3889 
3890 void
3891 end_func(cmd_t *cmd)
3892 {
3893 	bool validation_failed = FALSE;
3894 	struct zone_fstab tmp_fstab;
3895 	struct zone_nwiftab tmp_nwiftab;
3896 	struct zone_devtab tmp_devtab;
3897 	struct zone_rctltab tmp_rctltab;
3898 	struct zone_attrtab tmp_attrtab;
3899 	struct zone_dstab tmp_dstab;
3900 	int err, arg;
3901 
3902 	assert(cmd != NULL);
3903 
3904 	optind = 0;
3905 	if ((arg = getopt(cmd->cmd_argc, cmd->cmd_argv, "?")) != EOF) {
3906 		switch (arg) {
3907 		case '?':
3908 			longer_usage(CMD_END);
3909 			return;
3910 		default:
3911 			short_usage(CMD_END);
3912 			return;
3913 		}
3914 	}
3915 	if (optind != cmd->cmd_argc) {
3916 		short_usage(CMD_END);
3917 		return;
3918 	}
3919 
3920 	if (global_scope) {
3921 		scope_usage(CMD_END);
3922 		return;
3923 	}
3924 
3925 	assert(end_op == CMD_ADD || end_op == CMD_SELECT);
3926 
3927 	switch (resource_scope) {
3928 	case RT_FS:
3929 		/* First make sure everything was filled in. */
3930 		if (end_check_reqd(in_progress_fstab.zone_fs_dir,
3931 		    PT_DIR, &validation_failed) == Z_OK) {
3932 			if (in_progress_fstab.zone_fs_dir[0] != '/') {
3933 				zerr(gettext("%s %s is not an absolute path."),
3934 				    pt_to_str(PT_DIR),
3935 				    in_progress_fstab.zone_fs_dir);
3936 				validation_failed = TRUE;
3937 			}
3938 		}
3939 
3940 		(void) end_check_reqd(in_progress_fstab.zone_fs_special,
3941 		    PT_SPECIAL, &validation_failed);
3942 
3943 		if (in_progress_fstab.zone_fs_raw[0] != '\0' &&
3944 		    in_progress_fstab.zone_fs_raw[0] != '/') {
3945 			zerr(gettext("%s %s is not an absolute path."),
3946 			    pt_to_str(PT_RAW),
3947 			    in_progress_fstab.zone_fs_raw);
3948 			validation_failed = TRUE;
3949 		}
3950 
3951 		(void) end_check_reqd(in_progress_fstab.zone_fs_type, PT_TYPE,
3952 		    &validation_failed);
3953 
3954 		if (validation_failed) {
3955 			saw_error = TRUE;
3956 			return;
3957 		}
3958 
3959 		if (end_op == CMD_ADD) {
3960 			/* Make sure there isn't already one like this. */
3961 			bzero(&tmp_fstab, sizeof (tmp_fstab));
3962 			(void) strlcpy(tmp_fstab.zone_fs_dir,
3963 			    in_progress_fstab.zone_fs_dir,
3964 			    sizeof (tmp_fstab.zone_fs_dir));
3965 			err = zonecfg_lookup_filesystem(handle, &tmp_fstab);
3966 			zonecfg_free_fs_option_list(tmp_fstab.zone_fs_options);
3967 			if (err == Z_OK) {
3968 				zerr(gettext("A %s resource "
3969 				    "with the %s '%s' already exists."),
3970 				    rt_to_str(RT_FS), pt_to_str(PT_DIR),
3971 				    in_progress_fstab.zone_fs_dir);
3972 				saw_error = TRUE;
3973 				return;
3974 			}
3975 			err = zonecfg_add_filesystem(handle,
3976 			    &in_progress_fstab);
3977 		} else {
3978 			err = zonecfg_modify_filesystem(handle, &old_fstab,
3979 			    &in_progress_fstab);
3980 		}
3981 		zonecfg_free_fs_option_list(in_progress_fstab.zone_fs_options);
3982 		in_progress_fstab.zone_fs_options = NULL;
3983 		break;
3984 
3985 	case RT_IPD:
3986 		/* First make sure everything was filled in. */
3987 		if (end_check_reqd(in_progress_ipdtab.zone_fs_dir, PT_DIR,
3988 		    &validation_failed) == Z_OK) {
3989 			if (in_progress_ipdtab.zone_fs_dir[0] != '/') {
3990 				zerr(gettext("%s %s is not an absolute path."),
3991 				    pt_to_str(PT_DIR),
3992 				    in_progress_ipdtab.zone_fs_dir);
3993 				validation_failed = TRUE;
3994 			}
3995 		}
3996 		if (validation_failed) {
3997 			saw_error = TRUE;
3998 			return;
3999 		}
4000 
4001 		if (end_op == CMD_ADD) {
4002 			/* Make sure there isn't already one like this. */
4003 			bzero(&tmp_fstab, sizeof (tmp_fstab));
4004 			(void) strlcpy(tmp_fstab.zone_fs_dir,
4005 			    in_progress_ipdtab.zone_fs_dir,
4006 			    sizeof (tmp_fstab.zone_fs_dir));
4007 			err = zonecfg_lookup_ipd(handle, &tmp_fstab);
4008 			if (err == Z_OK) {
4009 				zerr(gettext("An %s resource "
4010 				    "with the %s '%s' already exists."),
4011 				    rt_to_str(RT_IPD), pt_to_str(PT_DIR),
4012 				    in_progress_ipdtab.zone_fs_dir);
4013 				saw_error = TRUE;
4014 				return;
4015 			}
4016 			err = zonecfg_add_ipd(handle, &in_progress_ipdtab);
4017 		} else {
4018 			err = zonecfg_modify_ipd(handle, &old_ipdtab,
4019 			    &in_progress_ipdtab);
4020 		}
4021 		break;
4022 	case RT_NET:
4023 		/* First make sure everything was filled in. */
4024 		(void) end_check_reqd(in_progress_nwiftab.zone_nwif_physical,
4025 		    PT_PHYSICAL, &validation_failed);
4026 		(void) end_check_reqd(in_progress_nwiftab.zone_nwif_address,
4027 		    PT_ADDRESS, &validation_failed);
4028 
4029 		if (validation_failed) {
4030 			saw_error = TRUE;
4031 			return;
4032 		}
4033 
4034 		if (end_op == CMD_ADD) {
4035 			/* Make sure there isn't already one like this. */
4036 			bzero(&tmp_nwiftab, sizeof (tmp_nwiftab));
4037 			(void) strlcpy(tmp_nwiftab.zone_nwif_address,
4038 			    in_progress_nwiftab.zone_nwif_address,
4039 			    sizeof (tmp_nwiftab.zone_nwif_address));
4040 			if (zonecfg_lookup_nwif(handle, &tmp_nwiftab) == Z_OK) {
4041 				zerr(gettext("A %s resource "
4042 				    "with the %s '%s' already exists."),
4043 				    rt_to_str(RT_NET), pt_to_str(PT_ADDRESS),
4044 				    in_progress_nwiftab.zone_nwif_address);
4045 				saw_error = TRUE;
4046 				return;
4047 			}
4048 			err = zonecfg_add_nwif(handle, &in_progress_nwiftab);
4049 		} else {
4050 			err = zonecfg_modify_nwif(handle, &old_nwiftab,
4051 			    &in_progress_nwiftab);
4052 		}
4053 		break;
4054 
4055 	case RT_DEVICE:
4056 		/* First make sure everything was filled in. */
4057 		(void) end_check_reqd(in_progress_devtab.zone_dev_match,
4058 		    PT_MATCH, &validation_failed);
4059 
4060 		if (validation_failed) {
4061 			saw_error = TRUE;
4062 			return;
4063 		}
4064 
4065 		if (end_op == CMD_ADD) {
4066 			/* Make sure there isn't already one like this. */
4067 			(void) strlcpy(tmp_devtab.zone_dev_match,
4068 			    in_progress_devtab.zone_dev_match,
4069 			    sizeof (tmp_devtab.zone_dev_match));
4070 			if (zonecfg_lookup_dev(handle, &tmp_devtab) == Z_OK) {
4071 				zerr(gettext("A %s resource with the %s '%s' "
4072 				    "already exists."), rt_to_str(RT_DEVICE),
4073 				    pt_to_str(PT_MATCH),
4074 				    in_progress_devtab.zone_dev_match);
4075 				saw_error = TRUE;
4076 				return;
4077 			}
4078 			err = zonecfg_add_dev(handle, &in_progress_devtab);
4079 		} else {
4080 			err = zonecfg_modify_dev(handle, &old_devtab,
4081 			    &in_progress_devtab);
4082 		}
4083 		break;
4084 
4085 	case RT_RCTL:
4086 		/* First make sure everything was filled in. */
4087 		(void) end_check_reqd(in_progress_rctltab.zone_rctl_name,
4088 		    PT_NAME, &validation_failed);
4089 
4090 		if (in_progress_rctltab.zone_rctl_valptr == NULL) {
4091 			zerr(gettext("no %s specified"), pt_to_str(PT_VALUE));
4092 			validation_failed = TRUE;
4093 		}
4094 
4095 		if (validation_failed) {
4096 			saw_error = TRUE;
4097 			return;
4098 		}
4099 
4100 		if (end_op == CMD_ADD) {
4101 			/* Make sure there isn't already one like this. */
4102 			(void) strlcpy(tmp_rctltab.zone_rctl_name,
4103 			    in_progress_rctltab.zone_rctl_name,
4104 			    sizeof (tmp_rctltab.zone_rctl_name));
4105 			tmp_rctltab.zone_rctl_valptr = NULL;
4106 			err = zonecfg_lookup_rctl(handle, &tmp_rctltab);
4107 			zonecfg_free_rctl_value_list(
4108 			    tmp_rctltab.zone_rctl_valptr);
4109 			if (err == Z_OK) {
4110 				zerr(gettext("A %s resource "
4111 				    "with the %s '%s' already exists."),
4112 				    rt_to_str(RT_RCTL), pt_to_str(PT_NAME),
4113 				    in_progress_rctltab.zone_rctl_name);
4114 				saw_error = TRUE;
4115 				return;
4116 			}
4117 			err = zonecfg_add_rctl(handle, &in_progress_rctltab);
4118 		} else {
4119 			err = zonecfg_modify_rctl(handle, &old_rctltab,
4120 			    &in_progress_rctltab);
4121 		}
4122 		if (err == Z_OK) {
4123 			zonecfg_free_rctl_value_list(
4124 			    in_progress_rctltab.zone_rctl_valptr);
4125 			in_progress_rctltab.zone_rctl_valptr = NULL;
4126 		}
4127 		break;
4128 
4129 	case RT_ATTR:
4130 		/* First make sure everything was filled in. */
4131 		(void) end_check_reqd(in_progress_attrtab.zone_attr_name,
4132 		    PT_NAME, &validation_failed);
4133 		(void) end_check_reqd(in_progress_attrtab.zone_attr_type,
4134 		    PT_TYPE, &validation_failed);
4135 		(void) end_check_reqd(in_progress_attrtab.zone_attr_value,
4136 		    PT_VALUE, &validation_failed);
4137 
4138 		if (validate_attr_name(in_progress_attrtab.zone_attr_name) !=
4139 		    Z_OK)
4140 			validation_failed = TRUE;
4141 
4142 		if (validate_attr_type_val(&in_progress_attrtab) != Z_OK)
4143 			validation_failed = TRUE;
4144 
4145 		if (validation_failed) {
4146 			saw_error = TRUE;
4147 			return;
4148 		}
4149 		if (end_op == CMD_ADD) {
4150 			/* Make sure there isn't already one like this. */
4151 			bzero(&tmp_attrtab, sizeof (tmp_attrtab));
4152 			(void) strlcpy(tmp_attrtab.zone_attr_name,
4153 			    in_progress_attrtab.zone_attr_name,
4154 			    sizeof (tmp_attrtab.zone_attr_name));
4155 			if (zonecfg_lookup_attr(handle, &tmp_attrtab) == Z_OK) {
4156 				zerr(gettext("An %s resource "
4157 				    "with the %s '%s' already exists."),
4158 				    rt_to_str(RT_ATTR), pt_to_str(PT_NAME),
4159 				    in_progress_attrtab.zone_attr_name);
4160 				saw_error = TRUE;
4161 				return;
4162 			}
4163 			err = zonecfg_add_attr(handle, &in_progress_attrtab);
4164 		} else {
4165 			err = zonecfg_modify_attr(handle, &old_attrtab,
4166 			    &in_progress_attrtab);
4167 		}
4168 		break;
4169 	case RT_DATASET:
4170 		/* First make sure everything was filled in. */
4171 		if (strlen(in_progress_dstab.zone_dataset_name) == 0) {
4172 			zerr("%s %s", pt_to_str(PT_NAME),
4173 			    gettext("not specified"));
4174 			saw_error = TRUE;
4175 			validation_failed = TRUE;
4176 		}
4177 		if (validation_failed)
4178 			return;
4179 		if (end_op == CMD_ADD) {
4180 			/* Make sure there isn't already one like this. */
4181 			bzero(&tmp_dstab, sizeof (tmp_dstab));
4182 			(void) strlcpy(tmp_dstab.zone_dataset_name,
4183 			    in_progress_dstab.zone_dataset_name,
4184 			    sizeof (tmp_dstab.zone_dataset_name));
4185 			err = zonecfg_lookup_ds(handle, &tmp_dstab);
4186 			if (err == Z_OK) {
4187 				zerr(gettext("A %s resource "
4188 				    "with the %s '%s' already exists."),
4189 				    rt_to_str(RT_DATASET), pt_to_str(PT_NAME),
4190 				    in_progress_dstab.zone_dataset_name);
4191 				saw_error = TRUE;
4192 				return;
4193 			}
4194 			err = zonecfg_add_ds(handle, &in_progress_dstab);
4195 		} else {
4196 			err = zonecfg_modify_ds(handle, &old_dstab,
4197 			    &in_progress_dstab);
4198 		}
4199 		break;
4200 	default:
4201 		zone_perror(rt_to_str(resource_scope), Z_NO_RESOURCE_TYPE,
4202 		    TRUE);
4203 		saw_error = TRUE;
4204 		return;
4205 	}
4206 
4207 	if (err != Z_OK) {
4208 		zone_perror(zone, err, TRUE);
4209 	} else {
4210 		need_to_commit = TRUE;
4211 		global_scope = TRUE;
4212 		end_op = -1;
4213 	}
4214 }
4215 
4216 void
4217 commit_func(cmd_t *cmd)
4218 {
4219 	int arg;
4220 
4221 	optind = 0;
4222 	if ((arg = getopt(cmd->cmd_argc, cmd->cmd_argv, "?")) != EOF) {
4223 		switch (arg) {
4224 		case '?':
4225 			longer_usage(CMD_COMMIT);
4226 			return;
4227 		default:
4228 			short_usage(CMD_COMMIT);
4229 			return;
4230 		}
4231 	}
4232 	if (optind != cmd->cmd_argc) {
4233 		short_usage(CMD_COMMIT);
4234 		return;
4235 	}
4236 
4237 	if (zone_is_read_only(CMD_COMMIT))
4238 		return;
4239 
4240 	assert(cmd != NULL);
4241 
4242 	cmd->cmd_argc = 1;
4243 	/*
4244 	 * cmd_arg normally comes from a strdup() in the lexer, and the
4245 	 * whole cmd structure and its (char *) attributes are freed at
4246 	 * the completion of each command, so the strdup() below is needed
4247 	 * to match this and prevent a core dump from trying to free()
4248 	 * something that can't be.
4249 	 */
4250 	if ((cmd->cmd_argv[0] = strdup("save")) == NULL) {
4251 		zone_perror(zone, Z_NOMEM, TRUE);
4252 		exit(Z_ERR);
4253 	}
4254 	cmd->cmd_argv[1] = NULL;
4255 	verify_func(cmd);
4256 }
4257 
4258 void
4259 revert_func(cmd_t *cmd)
4260 {
4261 	char line[128];	/* enough to ask a question */
4262 	bool force = FALSE;
4263 	int err, arg, answer;
4264 
4265 	optind = 0;
4266 	while ((arg = getopt(cmd->cmd_argc, cmd->cmd_argv, "?F")) != EOF) {
4267 		switch (arg) {
4268 		case '?':
4269 			longer_usage(CMD_REVERT);
4270 			return;
4271 		case 'F':
4272 			force = TRUE;
4273 			break;
4274 		default:
4275 			short_usage(CMD_REVERT);
4276 			return;
4277 		}
4278 	}
4279 	if (optind != cmd->cmd_argc) {
4280 		short_usage(CMD_REVERT);
4281 		return;
4282 	}
4283 
4284 	if (zone_is_read_only(CMD_REVERT))
4285 		return;
4286 
4287 	if (zonecfg_check_handle(handle) != Z_OK) {
4288 		zerr(gettext("No changes to revert."));
4289 		saw_error = TRUE;
4290 		return;
4291 	}
4292 
4293 	if (!force) {
4294 		(void) snprintf(line, sizeof (line),
4295 		    gettext("Are you sure you want to revert"));
4296 		if ((answer = ask_yesno(FALSE, line)) == -1) {
4297 			zerr(gettext("Input not from terminal and -F not "
4298 			    "specified:\n%s command ignored, exiting."),
4299 			    cmd_to_str(CMD_REVERT));
4300 			exit(Z_ERR);
4301 		}
4302 		if (answer != 1)
4303 			return;
4304 	}
4305 
4306 	/*
4307 	 * Time for a new handle: finish the old one off first
4308 	 * then get a new one properly to avoid leaks.
4309 	 */
4310 	zonecfg_fini_handle(handle);
4311 	if ((handle = zonecfg_init_handle()) == NULL) {
4312 		zone_perror(execname, Z_NOMEM, TRUE);
4313 		exit(Z_ERR);
4314 	}
4315 	if ((err = zonecfg_get_handle(revert_zone, handle)) != Z_OK) {
4316 		saw_error = TRUE;
4317 		got_handle = FALSE;
4318 		if (err == Z_NO_ZONE)
4319 			zerr(gettext("%s: no such saved zone to revert to."),
4320 			    revert_zone);
4321 		else
4322 			zone_perror(zone, err, TRUE);
4323 	}
4324 	(void) strlcpy(zone, revert_zone, sizeof (zone));
4325 }
4326 
4327 void
4328 help_func(cmd_t *cmd)
4329 {
4330 	int i;
4331 
4332 	assert(cmd != NULL);
4333 
4334 	if (cmd->cmd_argc == 0) {
4335 		usage(TRUE, global_scope ? HELP_SUBCMDS : HELP_RES_SCOPE);
4336 		return;
4337 	}
4338 	if (strcmp(cmd->cmd_argv[0], "usage") == 0) {
4339 		usage(TRUE, HELP_USAGE);
4340 		return;
4341 	}
4342 	if (strcmp(cmd->cmd_argv[0], "commands") == 0) {
4343 		usage(TRUE, HELP_SUBCMDS);
4344 		return;
4345 	}
4346 	if (strcmp(cmd->cmd_argv[0], "syntax") == 0) {
4347 		usage(TRUE, HELP_SYNTAX | HELP_RES_PROPS);
4348 		return;
4349 	}
4350 	if (strcmp(cmd->cmd_argv[0], "-?") == 0) {
4351 		longer_usage(CMD_HELP);
4352 		return;
4353 	}
4354 
4355 	for (i = 0; i <= CMD_MAX; i++) {
4356 		if (strcmp(cmd->cmd_argv[0], cmd_to_str(i)) == 0) {
4357 			longer_usage(i);
4358 			return;
4359 		}
4360 	}
4361 	/* We do not use zerr() here because we do not want its extra \n. */
4362 	(void) fprintf(stderr, gettext("Unknown help subject %s.  "),
4363 	    cmd->cmd_argv[0]);
4364 	usage(FALSE, HELP_META);
4365 }
4366 
4367 static int
4368 string_to_yyin(char *string)
4369 {
4370 	if ((yyin = tmpfile()) == NULL) {
4371 		zone_perror(execname, Z_TEMP_FILE, TRUE);
4372 		return (Z_ERR);
4373 	}
4374 	if (fwrite(string, strlen(string), 1, yyin) != 1) {
4375 		zone_perror(execname, Z_TEMP_FILE, TRUE);
4376 		return (Z_ERR);
4377 	}
4378 	if (fseek(yyin, 0, SEEK_SET) != 0) {
4379 		zone_perror(execname, Z_TEMP_FILE, TRUE);
4380 		return (Z_ERR);
4381 	}
4382 	return (Z_OK);
4383 }
4384 
4385 /* This is the back-end helper function for read_input() below. */
4386 
4387 static int
4388 cleanup()
4389 {
4390 	int answer;
4391 	cmd_t *cmd;
4392 
4393 	if (!interactive_mode && !cmd_file_mode) {
4394 		/*
4395 		 * If we're not in interactive mode, and we're not in command
4396 		 * file mode, then we must be in commands-from-the-command-line
4397 		 * mode.  As such, we can't loop back and ask for more input.
4398 		 * It was OK to prompt for such things as whether or not to
4399 		 * really delete a zone in the command handler called from
4400 		 * yyparse() above, but "really quit?" makes no sense in this
4401 		 * context.  So disable prompting.
4402 		 */
4403 		ok_to_prompt = FALSE;
4404 	}
4405 	if (!global_scope) {
4406 		if (!time_to_exit) {
4407 			/*
4408 			 * Just print a simple error message in the -1 case,
4409 			 * since exit_func() already handles that case, and
4410 			 * EOF means we are finished anyway.
4411 			 */
4412 			answer = ask_yesno(FALSE,
4413 			    gettext("Resource incomplete; really quit"));
4414 			if (answer == -1) {
4415 				zerr(gettext("Resource incomplete."));
4416 				return (Z_ERR);
4417 			}
4418 			if (answer != 1) {
4419 				yyin = stdin;
4420 				return (Z_REPEAT);
4421 			}
4422 		} else {
4423 			saw_error = TRUE;
4424 		}
4425 	}
4426 	/*
4427 	 * Make sure we tried something and that the handle checks
4428 	 * out, or we would get a false error trying to commit.
4429 	 */
4430 	if (need_to_commit && zonecfg_check_handle(handle) == Z_OK) {
4431 		if ((cmd = alloc_cmd()) == NULL) {
4432 			zone_perror(zone, Z_NOMEM, TRUE);
4433 			return (Z_ERR);
4434 		}
4435 		cmd->cmd_argc = 0;
4436 		cmd->cmd_argv[0] = NULL;
4437 		commit_func(cmd);
4438 		free_cmd(cmd);
4439 		/*
4440 		 * need_to_commit will get set back to FALSE if the
4441 		 * configuration is saved successfully.
4442 		 */
4443 		if (need_to_commit) {
4444 			if (force_exit) {
4445 				zerr(gettext("Configuration not saved."));
4446 				return (Z_ERR);
4447 			}
4448 			answer = ask_yesno(FALSE,
4449 			    gettext("Configuration not saved; really quit"));
4450 			if (answer == -1) {
4451 				zerr(gettext("Configuration not saved."));
4452 				return (Z_ERR);
4453 			}
4454 			if (answer != 1) {
4455 				time_to_exit = FALSE;
4456 				yyin = stdin;
4457 				return (Z_REPEAT);
4458 			}
4459 		}
4460 	}
4461 	return ((need_to_commit || saw_error) ? Z_ERR : Z_OK);
4462 }
4463 
4464 /*
4465  * read_input() is the driver of this program.  It is a wrapper around
4466  * yyparse(), printing appropriate prompts when needed, checking for
4467  * exit conditions and reacting appropriately [the latter in its cleanup()
4468  * helper function].
4469  *
4470  * Like most zonecfg functions, it returns Z_OK or Z_ERR, *or* Z_REPEAT
4471  * so do_interactive() knows that we are not really done (i.e, we asked
4472  * the user if we should really quit and the user said no).
4473  */
4474 static int
4475 read_input()
4476 {
4477 	bool yyin_is_a_tty = isatty(fileno(yyin));
4478 	/*
4479 	 * The prompt is "e:z> " or "e:z:r> " where e is execname, z is zone
4480 	 * and r is resource_scope: 5 is for the two ":"s + "> " + terminator.
4481 	 */
4482 	char prompt[MAXPATHLEN + ZONENAME_MAX + MAX_RT_STRLEN + 5], *line;
4483 
4484 	/* yyin should have been set to the appropriate (FILE *) if not stdin */
4485 	newline_terminated = TRUE;
4486 	for (;;) {
4487 		if (yyin_is_a_tty) {
4488 			if (newline_terminated) {
4489 				if (global_scope)
4490 					(void) snprintf(prompt, sizeof (prompt),
4491 					    "%s:%s> ", execname, zone);
4492 				else
4493 					(void) snprintf(prompt, sizeof (prompt),
4494 					    "%s:%s:%s> ", execname, zone,
4495 					    rt_to_str(resource_scope));
4496 			}
4497 			/*
4498 			 * If the user hits ^C then we want to catch it and
4499 			 * start over.  If the user hits EOF then we want to
4500 			 * bail out.
4501 			 */
4502 			line = gl_get_line(gl, prompt, NULL, -1);
4503 			if (gl_return_status(gl) == GLR_SIGNAL) {
4504 				gl_abandon_line(gl);
4505 				continue;
4506 			}
4507 			if (line == NULL)
4508 				break;
4509 			(void) string_to_yyin(line);
4510 			while (!feof(yyin))
4511 				yyparse();
4512 		} else {
4513 			yyparse();
4514 		}
4515 		/* Bail out on an error in command file mode. */
4516 		if (saw_error && cmd_file_mode && !interactive_mode)
4517 			time_to_exit = TRUE;
4518 		if (time_to_exit || (!yyin_is_a_tty && feof(yyin)))
4519 			break;
4520 	}
4521 	return (cleanup());
4522 }
4523 
4524 /*
4525  * This function is used in the zonecfg-interactive-mode scenario: it just
4526  * calls read_input() until we are done.
4527  */
4528 
4529 static int
4530 do_interactive(void)
4531 {
4532 	int err;
4533 
4534 	interactive_mode = TRUE;
4535 	if (!read_only_mode) {
4536 		/*
4537 		 * Try to set things up proactively in interactive mode, so
4538 		 * that if the zone in question does not exist yet, we can
4539 		 * provide the user with a clue.
4540 		 */
4541 		(void) initialize(FALSE);
4542 	}
4543 	do {
4544 		err = read_input();
4545 	} while (err == Z_REPEAT);
4546 	return (err);
4547 }
4548 
4549 /*
4550  * cmd_file is slightly more complicated, as it has to open the command file
4551  * and set yyin appropriately.  Once that is done, though, it just calls
4552  * read_input(), and only once, since prompting is not possible.
4553  */
4554 
4555 static int
4556 cmd_file(char *file)
4557 {
4558 	FILE *infile;
4559 	int err;
4560 	struct stat statbuf;
4561 	bool using_real_file = (strcmp(file, "-") != 0);
4562 
4563 	if (using_real_file) {
4564 		/*
4565 		 * zerr() prints a line number in cmd_file_mode, which we do
4566 		 * not want here, so temporarily unset it.
4567 		 */
4568 		cmd_file_mode = FALSE;
4569 		if ((infile = fopen(file, "r")) == NULL) {
4570 			zerr(gettext("could not open file %s: %s"),
4571 			    file, strerror(errno));
4572 			return (Z_ERR);
4573 		}
4574 		if ((err = fstat(fileno(infile), &statbuf)) != 0) {
4575 			zerr(gettext("could not stat file %s: %s"),
4576 			    file, strerror(errno));
4577 			err = Z_ERR;
4578 			goto done;
4579 		}
4580 		if (!S_ISREG(statbuf.st_mode)) {
4581 			zerr(gettext("%s is not a regular file."), file);
4582 			err = Z_ERR;
4583 			goto done;
4584 		}
4585 		yyin = infile;
4586 		cmd_file_mode = TRUE;
4587 		ok_to_prompt = FALSE;
4588 	} else {
4589 		/*
4590 		 * "-f -" is essentially the same as interactive mode,
4591 		 * so treat it that way.
4592 		 */
4593 		interactive_mode = TRUE;
4594 	}
4595 	/* Z_REPEAT is for interactive mode; treat it like Z_ERR here. */
4596 	if ((err = read_input()) == Z_REPEAT)
4597 		err = Z_ERR;
4598 done:
4599 	if (using_real_file)
4600 		(void) fclose(infile);
4601 	return (err);
4602 }
4603 
4604 /*
4605  * Since yacc is based on reading from a (FILE *) whereas what we get from
4606  * the command line is in argv format, we need to convert when the user
4607  * gives us commands directly from the command line.  That is done here by
4608  * concatenating the argv list into a space-separated string, writing it
4609  * to a temp file, and rewinding the file so yyin can be set to it.  Then
4610  * we call read_input(), and only once, since prompting about whether to
4611  * continue or quit would make no sense in this context.
4612  */
4613 
4614 static int
4615 one_command_at_a_time(int argc, char *argv[])
4616 {
4617 	char *command;
4618 	size_t len = 2; /* terminal \n\0 */
4619 	int i, err;
4620 
4621 	for (i = 0; i < argc; i++)
4622 		len += strlen(argv[i]) + 1;
4623 	if ((command = malloc(len)) == NULL) {
4624 		zone_perror(execname, Z_NOMEM, TRUE);
4625 		return (Z_ERR);
4626 	}
4627 	(void) strlcpy(command, argv[0], len);
4628 	for (i = 1; i < argc; i++) {
4629 		(void) strlcat(command, " ", len);
4630 		(void) strlcat(command, argv[i], len);
4631 	}
4632 	(void) strlcat(command, "\n", len);
4633 	err = string_to_yyin(command);
4634 	free(command);
4635 	if (err != Z_OK)
4636 		return (err);
4637 	while (!feof(yyin))
4638 		yyparse();
4639 	return (cleanup());
4640 }
4641 
4642 static char *
4643 get_execbasename(char *execfullname)
4644 {
4645 	char *last_slash, *execbasename;
4646 
4647 	/* guard against '/' at end of command invocation */
4648 	for (;;) {
4649 		last_slash = strrchr(execfullname, '/');
4650 		if (last_slash == NULL) {
4651 			execbasename = execfullname;
4652 			break;
4653 		} else {
4654 			execbasename = last_slash + 1;
4655 			if (*execbasename == '\0') {
4656 				*last_slash = '\0';
4657 				continue;
4658 			}
4659 			break;
4660 		}
4661 	}
4662 	return (execbasename);
4663 }
4664 
4665 int
4666 main(int argc, char *argv[])
4667 {
4668 	int err, arg;
4669 	struct stat st;
4670 
4671 	/* This must be before anything goes to stdout. */
4672 	setbuf(stdout, NULL);
4673 
4674 	saw_error = FALSE;
4675 	cmd_file_mode = FALSE;
4676 	execname = get_execbasename(argv[0]);
4677 
4678 	(void) setlocale(LC_ALL, "");
4679 	(void) textdomain(TEXT_DOMAIN);
4680 
4681 	if (getzoneid() != GLOBAL_ZONEID) {
4682 		zerr(gettext("%s can only be run from the global zone."),
4683 		    execname);
4684 		exit(Z_ERR);
4685 	}
4686 
4687 	if (argc < 2) {
4688 		usage(FALSE, HELP_USAGE | HELP_SUBCMDS);
4689 		exit(Z_USAGE);
4690 	}
4691 	if (strcmp(argv[1], cmd_to_str(CMD_HELP)) == 0) {
4692 		(void) one_command_at_a_time(argc - 1, &(argv[1]));
4693 		exit(Z_OK);
4694 	}
4695 
4696 	while ((arg = getopt(argc, argv, "?f:R:z:")) != EOF) {
4697 		switch (arg) {
4698 		case '?':
4699 			if (optopt == '?')
4700 				usage(TRUE, HELP_USAGE | HELP_SUBCMDS);
4701 			else
4702 				usage(FALSE, HELP_USAGE);
4703 			exit(Z_USAGE);
4704 			/* NOTREACHED */
4705 		case 'f':
4706 			cmd_file_name = optarg;
4707 			cmd_file_mode = TRUE;
4708 			break;
4709 		case 'R':
4710 			if (*optarg != '/') {
4711 				zerr(gettext("root path must be absolute: %s"),
4712 				    optarg);
4713 				exit(Z_USAGE);
4714 			}
4715 			if (stat(optarg, &st) == -1 || !S_ISDIR(st.st_mode)) {
4716 				zerr(gettext(
4717 				    "root path must be a directory: %s"),
4718 				    optarg);
4719 				exit(Z_USAGE);
4720 			}
4721 			zonecfg_set_root(optarg);
4722 			break;
4723 		case 'z':
4724 			if (zonecfg_validate_zonename(optarg) != Z_OK) {
4725 				zone_perror(optarg, Z_BOGUS_ZONE_NAME, TRUE);
4726 				usage(FALSE, HELP_SYNTAX);
4727 				exit(Z_USAGE);
4728 			}
4729 			(void) strlcpy(zone, optarg, sizeof (zone));
4730 			(void) strlcpy(revert_zone, optarg, sizeof (zone));
4731 			break;
4732 		default:
4733 			usage(FALSE, HELP_USAGE);
4734 			exit(Z_USAGE);
4735 		}
4736 	}
4737 
4738 	if (optind > argc || strcmp(zone, "") == 0) {
4739 		usage(FALSE, HELP_USAGE);
4740 		exit(Z_USAGE);
4741 	}
4742 
4743 	if ((err = zonecfg_access(zone, W_OK)) == Z_OK) {
4744 		read_only_mode = FALSE;
4745 	} else if (err == Z_ACCES) {
4746 		read_only_mode = TRUE;
4747 		/* skip this message in one-off from command line mode */
4748 		if (optind == argc)
4749 			(void) fprintf(stderr, gettext("WARNING: you do not "
4750 			    "have write access to this zone's configuration "
4751 			    "file;\ngoing into read-only mode.\n"));
4752 	} else {
4753 		fprintf(stderr, "%s: Could not access zone configuration "
4754 		    "store: %s\n", execname, zonecfg_strerror(err));
4755 		exit(Z_ERR);
4756 	}
4757 
4758 	if ((handle = zonecfg_init_handle()) == NULL) {
4759 		zone_perror(execname, Z_NOMEM, TRUE);
4760 		exit(Z_ERR);
4761 	}
4762 
4763 	/*
4764 	 * This may get set back to FALSE again in cmd_file() if cmd_file_name
4765 	 * is a "real" file as opposed to "-" (i.e. meaning use stdin).
4766 	 */
4767 	if (isatty(STDIN_FILENO))
4768 		ok_to_prompt = TRUE;
4769 	if ((gl = new_GetLine(MAX_LINE_LEN, MAX_CMD_HIST)) == NULL)
4770 		exit(Z_ERR);
4771 	if (gl_customize_completion(gl, NULL, cmd_cpl_fn) != 0)
4772 		exit(Z_ERR);
4773 	(void) sigset(SIGINT, SIG_IGN);
4774 	if (optind == argc) {
4775 		if (!cmd_file_mode)
4776 			err = do_interactive();
4777 		else
4778 			err = cmd_file(cmd_file_name);
4779 	} else {
4780 		err = one_command_at_a_time(argc - optind, &(argv[optind]));
4781 	}
4782 	zonecfg_fini_handle(handle);
4783 	(void) del_GetLine(gl);
4784 	return (err);
4785 }
4786