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