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