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