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