xref: /titanic_41/usr/src/cmd/zonecfg/zonecfg.c (revision fa9e4066f08beec538e775443c5be79dd423fcab)
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 	fstab->zone_fs_dir[0] = '\0';
1900 	fstab->zone_fs_special[0] = '\0';
1901 	fstab->zone_fs_type[0] = '\0';
1902 	fstab->zone_fs_options = NULL;
1903 	for (i = 0; i < cmd->cmd_prop_nv_pairs; i++) {
1904 		pp = cmd->cmd_property_ptr[i];
1905 		if (pp->pv_type != PROP_VAL_SIMPLE || pp->pv_simple == NULL) {
1906 			zerr(gettext("A simple value was expected here."));
1907 			saw_error = TRUE;
1908 			return (Z_INSUFFICIENT_SPEC);
1909 		}
1910 		switch (cmd->cmd_prop_name[i]) {
1911 		case PT_DIR:
1912 			(void) strlcpy(fstab->zone_fs_dir, pp->pv_simple,
1913 			    sizeof (fstab->zone_fs_dir));
1914 			break;
1915 		case PT_SPECIAL:
1916 			(void) strlcpy(fstab->zone_fs_special, pp->pv_simple,
1917 			    sizeof (fstab->zone_fs_special));
1918 			break;
1919 		case PT_RAW:
1920 			(void) strlcpy(fstab->zone_fs_raw, pp->pv_simple,
1921 			    sizeof (fstab->zone_fs_raw));
1922 			break;
1923 		case PT_TYPE:
1924 			(void) strlcpy(fstab->zone_fs_type, pp->pv_simple,
1925 			    sizeof (fstab->zone_fs_type));
1926 			break;
1927 		default:
1928 			zone_perror(pt_to_str(cmd->cmd_prop_name[i]),
1929 			    Z_NO_PROPERTY_TYPE, TRUE);
1930 			return (Z_INSUFFICIENT_SPEC);
1931 		}
1932 	}
1933 	if (fill_in_only)
1934 		return (Z_OK);
1935 	return (zonecfg_lookup_filesystem(handle, fstab));
1936 }
1937 
1938 static int
1939 fill_in_ipdtab(cmd_t *cmd, struct zone_fstab *ipdtab, bool fill_in_only)
1940 {
1941 	int err, i;
1942 	property_value_ptr_t pp;
1943 
1944 	if ((err = initialize(TRUE)) != Z_OK)
1945 		return (err);
1946 
1947 	ipdtab->zone_fs_dir[0] = '\0';
1948 	for (i = 0; i < cmd->cmd_prop_nv_pairs; i++) {
1949 		pp = cmd->cmd_property_ptr[i];
1950 		if (pp->pv_type != PROP_VAL_SIMPLE || pp->pv_simple == NULL) {
1951 			zerr(gettext("A simple value was expected here."));
1952 			saw_error = TRUE;
1953 			return (Z_INSUFFICIENT_SPEC);
1954 		}
1955 		switch (cmd->cmd_prop_name[i]) {
1956 		case PT_DIR:
1957 			(void) strlcpy(ipdtab->zone_fs_dir, pp->pv_simple,
1958 			    sizeof (ipdtab->zone_fs_dir));
1959 			break;
1960 		default:
1961 			zone_perror(pt_to_str(cmd->cmd_prop_name[i]),
1962 			    Z_NO_PROPERTY_TYPE, TRUE);
1963 			return (Z_INSUFFICIENT_SPEC);
1964 		}
1965 	}
1966 	if (fill_in_only)
1967 		return (Z_OK);
1968 	return (zonecfg_lookup_ipd(handle, ipdtab));
1969 }
1970 
1971 static int
1972 fill_in_nwiftab(cmd_t *cmd, struct zone_nwiftab *nwiftab, bool fill_in_only)
1973 {
1974 	int err, i;
1975 	property_value_ptr_t pp;
1976 
1977 	if ((err = initialize(TRUE)) != Z_OK)
1978 		return (err);
1979 
1980 	nwiftab->zone_nwif_address[0] = '\0';
1981 	nwiftab->zone_nwif_physical[0] = '\0';
1982 	for (i = 0; i < cmd->cmd_prop_nv_pairs; i++) {
1983 		pp = cmd->cmd_property_ptr[i];
1984 		if (pp->pv_type != PROP_VAL_SIMPLE || pp->pv_simple == NULL) {
1985 			zerr(gettext("A simple value was expected here."));
1986 			saw_error = TRUE;
1987 			return (Z_INSUFFICIENT_SPEC);
1988 		}
1989 		switch (cmd->cmd_prop_name[i]) {
1990 		case PT_ADDRESS:
1991 			(void) strlcpy(nwiftab->zone_nwif_address,
1992 			    pp->pv_simple, sizeof (nwiftab->zone_nwif_address));
1993 			break;
1994 		case PT_PHYSICAL:
1995 			(void) strlcpy(nwiftab->zone_nwif_physical,
1996 			    pp->pv_simple,
1997 			    sizeof (nwiftab->zone_nwif_physical));
1998 			break;
1999 		default:
2000 			zone_perror(pt_to_str(cmd->cmd_prop_name[i]),
2001 			    Z_NO_PROPERTY_TYPE, TRUE);
2002 			return (Z_INSUFFICIENT_SPEC);
2003 		}
2004 	}
2005 	if (fill_in_only)
2006 		return (Z_OK);
2007 	err = zonecfg_lookup_nwif(handle, nwiftab);
2008 	return (err);
2009 }
2010 
2011 static int
2012 fill_in_devtab(cmd_t *cmd, struct zone_devtab *devtab, bool fill_in_only)
2013 {
2014 	int err, i;
2015 	property_value_ptr_t pp;
2016 
2017 	if ((err = initialize(TRUE)) != Z_OK)
2018 		return (err);
2019 
2020 	devtab->zone_dev_match[0] = '\0';
2021 	for (i = 0; i < cmd->cmd_prop_nv_pairs; i++) {
2022 		pp = cmd->cmd_property_ptr[i];
2023 		if (pp->pv_type != PROP_VAL_SIMPLE || pp->pv_simple == NULL) {
2024 			zerr(gettext("A simple value was expected here."));
2025 			saw_error = TRUE;
2026 			return (Z_INSUFFICIENT_SPEC);
2027 		}
2028 		switch (cmd->cmd_prop_name[i]) {
2029 		case PT_MATCH:
2030 			(void) strlcpy(devtab->zone_dev_match, pp->pv_simple,
2031 			    sizeof (devtab->zone_dev_match));
2032 			break;
2033 		default:
2034 			zone_perror(pt_to_str(cmd->cmd_prop_name[i]),
2035 			    Z_NO_PROPERTY_TYPE, TRUE);
2036 			return (Z_INSUFFICIENT_SPEC);
2037 		}
2038 	}
2039 	if (fill_in_only)
2040 		return (Z_OK);
2041 	err = zonecfg_lookup_dev(handle, devtab);
2042 	return (err);
2043 }
2044 
2045 static int
2046 fill_in_rctltab(cmd_t *cmd, struct zone_rctltab *rctltab, bool fill_in_only)
2047 {
2048 	int err, i;
2049 	property_value_ptr_t pp;
2050 
2051 	if ((err = initialize(TRUE)) != Z_OK)
2052 		return (err);
2053 
2054 	rctltab->zone_rctl_name[0] = '\0';
2055 	for (i = 0; i < cmd->cmd_prop_nv_pairs; i++) {
2056 		pp = cmd->cmd_property_ptr[i];
2057 		if (pp->pv_type != PROP_VAL_SIMPLE || pp->pv_simple == NULL) {
2058 			zerr(gettext("A simple value was expected here."));
2059 			saw_error = TRUE;
2060 			return (Z_INSUFFICIENT_SPEC);
2061 		}
2062 		switch (cmd->cmd_prop_name[i]) {
2063 		case PT_NAME:
2064 			(void) strlcpy(rctltab->zone_rctl_name, pp->pv_simple,
2065 			    sizeof (rctltab->zone_rctl_name));
2066 			break;
2067 		default:
2068 			zone_perror(pt_to_str(cmd->cmd_prop_name[i]),
2069 			    Z_NO_PROPERTY_TYPE, TRUE);
2070 			return (Z_INSUFFICIENT_SPEC);
2071 		}
2072 	}
2073 	if (fill_in_only)
2074 		return (Z_OK);
2075 	err = zonecfg_lookup_rctl(handle, rctltab);
2076 	return (err);
2077 }
2078 
2079 static int
2080 fill_in_attrtab(cmd_t *cmd, struct zone_attrtab *attrtab, bool fill_in_only)
2081 {
2082 	int err, i;
2083 	property_value_ptr_t pp;
2084 
2085 	if ((err = initialize(TRUE)) != Z_OK)
2086 		return (err);
2087 
2088 	attrtab->zone_attr_name[0] = '\0';
2089 	attrtab->zone_attr_type[0] = '\0';
2090 	attrtab->zone_attr_value[0] = '\0';
2091 	for (i = 0; i < cmd->cmd_prop_nv_pairs; i++) {
2092 		pp = cmd->cmd_property_ptr[i];
2093 		if (pp->pv_type != PROP_VAL_SIMPLE || pp->pv_simple == NULL) {
2094 			zerr(gettext("A simple value was expected here."));
2095 			saw_error = TRUE;
2096 			return (Z_INSUFFICIENT_SPEC);
2097 		}
2098 		switch (cmd->cmd_prop_name[i]) {
2099 		case PT_NAME:
2100 			(void) strlcpy(attrtab->zone_attr_name, pp->pv_simple,
2101 			    sizeof (attrtab->zone_attr_name));
2102 			break;
2103 		case PT_TYPE:
2104 			(void) strlcpy(attrtab->zone_attr_type, pp->pv_simple,
2105 			    sizeof (attrtab->zone_attr_type));
2106 			break;
2107 		case PT_VALUE:
2108 			(void) strlcpy(attrtab->zone_attr_value, pp->pv_simple,
2109 			    sizeof (attrtab->zone_attr_value));
2110 			break;
2111 		default:
2112 			zone_perror(pt_to_str(cmd->cmd_prop_name[i]),
2113 			    Z_NO_PROPERTY_TYPE, TRUE);
2114 			return (Z_INSUFFICIENT_SPEC);
2115 		}
2116 	}
2117 	if (fill_in_only)
2118 		return (Z_OK);
2119 	err = zonecfg_lookup_attr(handle, attrtab);
2120 	return (err);
2121 }
2122 
2123 static int
2124 fill_in_dstab(cmd_t *cmd, struct zone_dstab *dstab, bool fill_in_only)
2125 {
2126 	int err, i;
2127 	property_value_ptr_t pp;
2128 
2129 	if ((err = initialize(TRUE)) != Z_OK)
2130 		return (err);
2131 
2132 	dstab->zone_dataset_name[0] = '\0';
2133 	for (i = 0; i < cmd->cmd_prop_nv_pairs; i++) {
2134 		pp = cmd->cmd_property_ptr[i];
2135 		if (pp->pv_type != PROP_VAL_SIMPLE || pp->pv_simple == NULL) {
2136 			zerr(gettext("A simple value was expected here."));
2137 			saw_error = TRUE;
2138 			return (Z_INSUFFICIENT_SPEC);
2139 		}
2140 		switch (cmd->cmd_prop_name[i]) {
2141 		case PT_NAME:
2142 			(void) strlcpy(dstab->zone_dataset_name, pp->pv_simple,
2143 			    sizeof (dstab->zone_dataset_name));
2144 			break;
2145 		default:
2146 			zone_perror(pt_to_str(cmd->cmd_prop_name[i]),
2147 			    Z_NO_PROPERTY_TYPE, TRUE);
2148 			return (Z_INSUFFICIENT_SPEC);
2149 		}
2150 	}
2151 	if (fill_in_only)
2152 		return (Z_OK);
2153 	return (zonecfg_lookup_ds(handle, dstab));
2154 }
2155 
2156 static void
2157 remove_resource(cmd_t *cmd)
2158 {
2159 	int err, type;
2160 	struct zone_fstab fstab;
2161 	struct zone_nwiftab nwiftab;
2162 	struct zone_devtab devtab;
2163 	struct zone_attrtab attrtab;
2164 	struct zone_rctltab rctltab;
2165 	struct zone_dstab dstab;
2166 
2167 	if ((type = cmd->cmd_res_type) == RT_UNKNOWN) {
2168 		long_usage(CMD_REMOVE, TRUE);
2169 		return;
2170 	}
2171 
2172 	if (initialize(TRUE) != Z_OK)
2173 		return;
2174 
2175 	switch (type) {
2176 	case RT_FS:
2177 		if ((err = fill_in_fstab(cmd, &fstab, FALSE)) != Z_OK) {
2178 			z_cmd_rt_perror(CMD_REMOVE, RT_FS, err, TRUE);
2179 			return;
2180 		}
2181 		if ((err = zonecfg_delete_filesystem(handle, &fstab)) != Z_OK)
2182 			z_cmd_rt_perror(CMD_REMOVE, RT_FS, err, TRUE);
2183 		else
2184 			need_to_commit = TRUE;
2185 		zonecfg_free_fs_option_list(fstab.zone_fs_options);
2186 		return;
2187 	case RT_IPD:
2188 		if (state_atleast(ZONE_STATE_INSTALLED)) {
2189 			zerr(gettext("Zone %s already installed; %s %s not "
2190 			    "allowed."), zone, cmd_to_str(CMD_REMOVE),
2191 			    rt_to_str(RT_IPD));
2192 			return;
2193 		}
2194 		if ((err = fill_in_ipdtab(cmd, &fstab, FALSE)) != Z_OK) {
2195 			z_cmd_rt_perror(CMD_REMOVE, RT_IPD, err, TRUE);
2196 			return;
2197 		}
2198 		if ((err = zonecfg_delete_ipd(handle, &fstab)) != Z_OK)
2199 			z_cmd_rt_perror(CMD_REMOVE, RT_IPD, err, TRUE);
2200 		else
2201 			need_to_commit = TRUE;
2202 		return;
2203 	case RT_NET:
2204 		if ((err = fill_in_nwiftab(cmd, &nwiftab, FALSE)) != Z_OK) {
2205 			z_cmd_rt_perror(CMD_REMOVE, RT_NET, err, TRUE);
2206 			return;
2207 		}
2208 		if ((err = zonecfg_delete_nwif(handle, &nwiftab)) != Z_OK)
2209 			z_cmd_rt_perror(CMD_REMOVE, RT_NET, err, TRUE);
2210 		else
2211 			need_to_commit = TRUE;
2212 		return;
2213 	case RT_DEVICE:
2214 		if ((err = fill_in_devtab(cmd, &devtab, FALSE)) != Z_OK) {
2215 			z_cmd_rt_perror(CMD_REMOVE, RT_DEVICE, err, TRUE);
2216 			return;
2217 		}
2218 		if ((err = zonecfg_delete_dev(handle, &devtab)) != Z_OK)
2219 			z_cmd_rt_perror(CMD_REMOVE, RT_DEVICE, err, TRUE);
2220 		else
2221 			need_to_commit = TRUE;
2222 		return;
2223 	case RT_RCTL:
2224 		if ((err = fill_in_rctltab(cmd, &rctltab, FALSE)) != Z_OK) {
2225 			z_cmd_rt_perror(CMD_REMOVE, RT_RCTL, err, TRUE);
2226 			return;
2227 		}
2228 		if ((err = zonecfg_delete_rctl(handle, &rctltab)) != Z_OK)
2229 			z_cmd_rt_perror(CMD_REMOVE, RT_RCTL, err, TRUE);
2230 		else
2231 			need_to_commit = TRUE;
2232 		zonecfg_free_rctl_value_list(rctltab.zone_rctl_valptr);
2233 		return;
2234 	case RT_ATTR:
2235 		if ((err = fill_in_attrtab(cmd, &attrtab, FALSE)) != Z_OK) {
2236 			z_cmd_rt_perror(CMD_REMOVE, RT_ATTR, err, TRUE);
2237 			return;
2238 		}
2239 		if ((err = zonecfg_delete_attr(handle, &attrtab)) != Z_OK)
2240 			z_cmd_rt_perror(CMD_REMOVE, RT_ATTR, err, TRUE);
2241 		else
2242 			need_to_commit = TRUE;
2243 		return;
2244 	case RT_DATASET:
2245 		if ((err = fill_in_dstab(cmd, &dstab, FALSE)) != Z_OK) {
2246 			z_cmd_rt_perror(CMD_REMOVE, RT_DATASET, err, TRUE);
2247 			return;
2248 		}
2249 		if ((err = zonecfg_delete_ds(handle, &dstab)) != Z_OK)
2250 			z_cmd_rt_perror(CMD_REMOVE, RT_DATASET, err, TRUE);
2251 		else
2252 			need_to_commit = TRUE;
2253 		return;
2254 	default:
2255 		zone_perror(rt_to_str(type), Z_NO_RESOURCE_TYPE, TRUE);
2256 		long_usage(CMD_REMOVE, TRUE);
2257 		usage(FALSE, HELP_RESOURCES);
2258 		return;
2259 	}
2260 }
2261 
2262 static void
2263 remove_property(cmd_t *cmd)
2264 {
2265 	char *prop_id;
2266 	int err, res_type, prop_type;
2267 	property_value_ptr_t pp;
2268 	struct zone_rctlvaltab *rctlvaltab;
2269 	complex_property_ptr_t cx;
2270 
2271 	res_type = resource_scope;
2272 	prop_type = cmd->cmd_prop_name[0];
2273 	if (res_type == RT_UNKNOWN || prop_type == PT_UNKNOWN) {
2274 		long_usage(CMD_REMOVE, TRUE);
2275 		return;
2276 	}
2277 
2278 	if (cmd->cmd_prop_nv_pairs != 1) {
2279 		long_usage(CMD_ADD, TRUE);
2280 		return;
2281 	}
2282 
2283 	if (initialize(TRUE) != Z_OK)
2284 		return;
2285 
2286 	switch (res_type) {
2287 	case RT_FS:
2288 		if (prop_type != PT_OPTIONS) {
2289 			zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE,
2290 			    TRUE);
2291 			long_usage(CMD_REMOVE, TRUE);
2292 			usage(FALSE, HELP_PROPS);
2293 			return;
2294 		}
2295 		pp = cmd->cmd_property_ptr[0];
2296 		if (pp->pv_type == PROP_VAL_COMPLEX) {
2297 			zerr(gettext("A %s or %s value was expected here."),
2298 			    pvt_to_str(PROP_VAL_SIMPLE),
2299 			    pvt_to_str(PROP_VAL_LIST));
2300 			saw_error = TRUE;
2301 			return;
2302 		}
2303 		if (pp->pv_type == PROP_VAL_SIMPLE) {
2304 			if (pp->pv_simple == NULL) {
2305 				long_usage(CMD_ADD, TRUE);
2306 				return;
2307 			}
2308 			prop_id = pp->pv_simple;
2309 			err = zonecfg_remove_fs_option(&in_progress_fstab,
2310 			    prop_id);
2311 			if (err != Z_OK)
2312 				zone_perror(pt_to_str(prop_type), err, TRUE);
2313 		} else {
2314 			list_property_ptr_t list;
2315 
2316 			for (list = pp->pv_list; list != NULL;
2317 			    list = list->lp_next) {
2318 				prop_id = list->lp_simple;
2319 				if (prop_id == NULL)
2320 					break;
2321 				err = zonecfg_remove_fs_option(
2322 				    &in_progress_fstab, prop_id);
2323 				if (err != Z_OK)
2324 					zone_perror(pt_to_str(prop_type), err,
2325 					    TRUE);
2326 			}
2327 		}
2328 		return;
2329 	case RT_RCTL:
2330 		if (prop_type != PT_VALUE) {
2331 			zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE,
2332 			    TRUE);
2333 			long_usage(CMD_REMOVE, TRUE);
2334 			usage(FALSE, HELP_PROPS);
2335 			return;
2336 		}
2337 		pp = cmd->cmd_property_ptr[0];
2338 		if (pp->pv_type != PROP_VAL_COMPLEX) {
2339 			zerr(gettext("A %s value was expected here."),
2340 			    pvt_to_str(PROP_VAL_COMPLEX));
2341 			saw_error = TRUE;
2342 			return;
2343 		}
2344 		if ((rctlvaltab = alloc_rctlvaltab()) == NULL) {
2345 			zone_perror(zone, Z_NOMEM, TRUE);
2346 			exit(Z_ERR);
2347 		}
2348 		for (cx = pp->pv_complex; cx != NULL; cx = cx->cp_next) {
2349 			switch (cx->cp_type) {
2350 			case PT_PRIV:
2351 				(void) strlcpy(rctlvaltab->zone_rctlval_priv,
2352 				    cx->cp_value,
2353 				    sizeof (rctlvaltab->zone_rctlval_priv));
2354 				break;
2355 			case PT_LIMIT:
2356 				(void) strlcpy(rctlvaltab->zone_rctlval_limit,
2357 				    cx->cp_value,
2358 				    sizeof (rctlvaltab->zone_rctlval_limit));
2359 				break;
2360 			case PT_ACTION:
2361 				(void) strlcpy(rctlvaltab->zone_rctlval_action,
2362 				    cx->cp_value,
2363 				    sizeof (rctlvaltab->zone_rctlval_action));
2364 				break;
2365 			default:
2366 				zone_perror(pt_to_str(prop_type),
2367 				    Z_NO_PROPERTY_TYPE, TRUE);
2368 				long_usage(CMD_ADD, TRUE);
2369 				usage(FALSE, HELP_PROPS);
2370 				zonecfg_free_rctl_value_list(rctlvaltab);
2371 				return;
2372 			}
2373 		}
2374 		rctlvaltab->zone_rctlval_next = NULL;
2375 		err = zonecfg_remove_rctl_value(&in_progress_rctltab,
2376 		    rctlvaltab);
2377 		if (err != Z_OK)
2378 			zone_perror(pt_to_str(prop_type), err, TRUE);
2379 		zonecfg_free_rctl_value_list(rctlvaltab);
2380 		return;
2381 	default:
2382 		zone_perror(rt_to_str(res_type), Z_NO_RESOURCE_TYPE, TRUE);
2383 		long_usage(CMD_REMOVE, TRUE);
2384 		usage(FALSE, HELP_RESOURCES);
2385 		return;
2386 	}
2387 }
2388 
2389 void
2390 remove_func(cmd_t *cmd)
2391 {
2392 	if (zone_is_read_only(CMD_REMOVE))
2393 		return;
2394 
2395 	assert(cmd != NULL);
2396 
2397 	if (global_scope)
2398 		remove_resource(cmd);
2399 	else
2400 		remove_property(cmd);
2401 }
2402 
2403 void
2404 select_func(cmd_t *cmd)
2405 {
2406 	int type, err;
2407 
2408 	if (zone_is_read_only(CMD_SELECT))
2409 		return;
2410 
2411 	assert(cmd != NULL);
2412 
2413 	if (global_scope) {
2414 		global_scope = FALSE;
2415 		resource_scope = cmd->cmd_res_type;
2416 		end_op = CMD_SELECT;
2417 	} else {
2418 		scope_usage(CMD_SELECT);
2419 		return;
2420 	}
2421 
2422 	if ((type = cmd->cmd_res_type) == RT_UNKNOWN) {
2423 		long_usage(CMD_SELECT, TRUE);
2424 		return;
2425 	}
2426 
2427 	if (initialize(TRUE) != Z_OK)
2428 		return;
2429 
2430 	switch (type) {
2431 	case RT_FS:
2432 		if ((err = fill_in_fstab(cmd, &old_fstab, FALSE)) != Z_OK) {
2433 			z_cmd_rt_perror(CMD_SELECT, RT_FS, err, TRUE);
2434 			global_scope = TRUE;
2435 		}
2436 		bcopy(&old_fstab, &in_progress_fstab,
2437 		    sizeof (struct zone_fstab));
2438 		return;
2439 	case RT_IPD:
2440 		if (state_atleast(ZONE_STATE_INCOMPLETE)) {
2441 			zerr(gettext("Zone %s not in %s state; %s %s not "
2442 			    "allowed."), zone,
2443 			    zone_state_str(ZONE_STATE_CONFIGURED),
2444 			    cmd_to_str(CMD_SELECT), rt_to_str(RT_IPD));
2445 			global_scope = TRUE;
2446 			end_op = -1;
2447 			return;
2448 		}
2449 		if ((err = fill_in_ipdtab(cmd, &old_ipdtab, FALSE)) != Z_OK) {
2450 			z_cmd_rt_perror(CMD_SELECT, RT_IPD, err, TRUE);
2451 			global_scope = TRUE;
2452 		}
2453 		bcopy(&old_ipdtab, &in_progress_ipdtab,
2454 		    sizeof (struct zone_fstab));
2455 		return;
2456 	case RT_NET:
2457 		if ((err = fill_in_nwiftab(cmd, &old_nwiftab, FALSE)) != Z_OK) {
2458 			z_cmd_rt_perror(CMD_SELECT, RT_NET, err, TRUE);
2459 			global_scope = TRUE;
2460 		}
2461 		bcopy(&old_nwiftab, &in_progress_nwiftab,
2462 		    sizeof (struct zone_nwiftab));
2463 		return;
2464 	case RT_DEVICE:
2465 		if ((err = fill_in_devtab(cmd, &old_devtab, FALSE)) != Z_OK) {
2466 			z_cmd_rt_perror(CMD_SELECT, RT_DEVICE, err, TRUE);
2467 			global_scope = TRUE;
2468 		}
2469 		bcopy(&old_devtab, &in_progress_devtab,
2470 		    sizeof (struct zone_devtab));
2471 		return;
2472 	case RT_RCTL:
2473 		if ((err = fill_in_rctltab(cmd, &old_rctltab, FALSE)) != Z_OK) {
2474 			z_cmd_rt_perror(CMD_SELECT, RT_RCTL, err, TRUE);
2475 			global_scope = TRUE;
2476 		}
2477 		bcopy(&old_rctltab, &in_progress_rctltab,
2478 		    sizeof (struct zone_rctltab));
2479 		return;
2480 	case RT_ATTR:
2481 		if ((err = fill_in_attrtab(cmd, &old_attrtab, FALSE)) != Z_OK) {
2482 			z_cmd_rt_perror(CMD_SELECT, RT_ATTR, err, TRUE);
2483 			global_scope = TRUE;
2484 		}
2485 		bcopy(&old_attrtab, &in_progress_attrtab,
2486 		    sizeof (struct zone_attrtab));
2487 		return;
2488 	case RT_DATASET:
2489 		if ((err = fill_in_dstab(cmd, &old_dstab, FALSE)) != Z_OK) {
2490 			z_cmd_rt_perror(CMD_SELECT, RT_DATASET, err, TRUE);
2491 			global_scope = TRUE;
2492 		}
2493 		bcopy(&old_dstab, &in_progress_dstab,
2494 		    sizeof (struct zone_dstab));
2495 		return;
2496 	default:
2497 		zone_perror(rt_to_str(type), Z_NO_RESOURCE_TYPE, TRUE);
2498 		long_usage(CMD_SELECT, TRUE);
2499 		usage(FALSE, HELP_RESOURCES);
2500 		return;
2501 	}
2502 }
2503 
2504 /*
2505  * Network "addresses" can be one of the following forms:
2506  *	<IPv4 address>
2507  *	<IPv4 address>/<prefix length>
2508  *	<IPv6 address>/<prefix length>
2509  *	<host name>
2510  *	<host name>/<prefix length>
2511  * In other words, the "/" followed by a prefix length is allowed but not
2512  * required for IPv4 addresses and host names, and required for IPv6 addresses.
2513  * If a prefix length is given, it must be in the allowable range: 0 to 32 for
2514  * IPv4 addresses and host names, 0 to 128 for IPv6 addresses.
2515  * Host names must start with an alpha-numeric character, and all subsequent
2516  * characters must be either alpha-numeric or "-".
2517  */
2518 
2519 static int
2520 validate_net_address_syntax(char *address)
2521 {
2522 	char *slashp, part1[MAXHOSTNAMELEN];
2523 	struct in6_addr in6;
2524 	struct in_addr in4;
2525 	int prefixlen, i;
2526 
2527 	/*
2528 	 * Copy the part before any '/' into part1 or copy the whole
2529 	 * thing if there is no '/'.
2530 	 */
2531 	if ((slashp = strchr(address, '/')) != NULL) {
2532 		*slashp = '\0';
2533 		(void) strlcpy(part1, address, sizeof (part1));
2534 		*slashp = '/';
2535 		prefixlen = atoi(++slashp);
2536 	} else {
2537 		(void) strlcpy(part1, address, sizeof (part1));
2538 	}
2539 
2540 	if (inet_pton(AF_INET6, part1, &in6) == 1) {
2541 		if (slashp == NULL) {
2542 			zerr(gettext("%s: IPv6 addresses "
2543 			    "require /prefix-length suffix."), address);
2544 			return (Z_ERR);
2545 		}
2546 		if (prefixlen < 0 || prefixlen > 128) {
2547 			zerr(gettext("%s: IPv6 address "
2548 			    "prefix lengths must be 0 - 128."), address);
2549 			return (Z_ERR);
2550 		}
2551 		return (Z_OK);
2552 	}
2553 
2554 	/* At this point, any /prefix must be for IPv4. */
2555 	if (slashp != NULL) {
2556 		if (prefixlen < 0 || prefixlen > 32) {
2557 			zerr(gettext("%s: IPv4 address "
2558 			    "prefix lengths must be 0 - 32."), address);
2559 			return (Z_ERR);
2560 		}
2561 	}
2562 	if (inet_pton(AF_INET, part1, &in4) == 1)
2563 		return (Z_OK);
2564 
2565 	/* address may also be a host name */
2566 	if (!isalnum(part1[0])) {
2567 		zerr(gettext("%s: bogus host name or network address syntax"),
2568 		    part1);
2569 		saw_error = TRUE;
2570 		usage(FALSE, HELP_NETADDR);
2571 		return (Z_ERR);
2572 	}
2573 	for (i = 1; part1[i]; i++)
2574 		if (!isalnum(part1[i]) && part1[i] != '-' && part1[i] != '.') {
2575 			zerr(gettext("%s: bogus host name or "
2576 			    "network address syntax"), part1);
2577 			saw_error = TRUE;
2578 			usage(FALSE, HELP_NETADDR);
2579 			return (Z_ERR);
2580 		}
2581 	return (Z_OK);
2582 }
2583 
2584 static int
2585 validate_net_physical_syntax(char *ifname)
2586 {
2587 	if (strchr(ifname, ':') == NULL)
2588 		return (Z_OK);
2589 	zerr(gettext("%s: physical interface name required; "
2590 	    "logical interface name not allowed"), ifname);
2591 	return (Z_ERR);
2592 }
2593 
2594 static boolean_t
2595 valid_fs_type(const char *type)
2596 {
2597 	/*
2598 	 * Is this a valid path component?
2599 	 */
2600 	if (strlen(type) + 1 > MAXNAMELEN)
2601 		return (B_FALSE);
2602 	/*
2603 	 * Make sure a bad value for "type" doesn't make
2604 	 * /usr/lib/fs/<type>/mount turn into something else.
2605 	 */
2606 	if (strchr(type, '/') != NULL || type[0] == '\0' ||
2607 	    strcmp(type, ".") == 0 || strcmp(type, "..") == 0)
2608 		return (B_FALSE);
2609 	/*
2610 	 * More detailed verification happens later by zoneadm(1m).
2611 	 */
2612 	return (B_TRUE);
2613 }
2614 
2615 void
2616 set_func(cmd_t *cmd)
2617 {
2618 	char *prop_id;
2619 	int err, res_type, prop_type;
2620 	property_value_ptr_t pp;
2621 	boolean_t autoboot;
2622 
2623 	if (zone_is_read_only(CMD_SET))
2624 		return;
2625 
2626 	assert(cmd != NULL);
2627 
2628 	prop_type = cmd->cmd_prop_name[0];
2629 	if (global_scope) {
2630 		if (prop_type == PT_ZONENAME) {
2631 			res_type = RT_ZONENAME;
2632 		} else if (prop_type == PT_ZONEPATH) {
2633 			res_type = RT_ZONEPATH;
2634 		} else if (prop_type == PT_AUTOBOOT) {
2635 			res_type = RT_AUTOBOOT;
2636 		} else if (prop_type == PT_POOL) {
2637 			res_type = RT_POOL;
2638 		} else {
2639 			zerr(gettext("Cannot set a resource-specific property "
2640 			    "from the global scope."));
2641 			saw_error = TRUE;
2642 			return;
2643 		}
2644 	} else {
2645 		res_type = resource_scope;
2646 	}
2647 
2648 	pp = cmd->cmd_property_ptr[0];
2649 	/*
2650 	 * A nasty expression but not that complicated:
2651 	 * 1. fs options are simple or list (tested below)
2652 	 * 2. rctl value's are complex or list (tested below)
2653 	 * Anything else should be simple.
2654 	 */
2655 	if (!(res_type == RT_FS && prop_type == PT_OPTIONS) &&
2656 	    !(res_type == RT_RCTL && prop_type == PT_VALUE) &&
2657 	    (pp->pv_type != PROP_VAL_SIMPLE ||
2658 	    (prop_id = pp->pv_simple) == NULL)) {
2659 		zerr(gettext("A %s value was expected here."),
2660 		    pvt_to_str(PROP_VAL_SIMPLE));
2661 		saw_error = TRUE;
2662 		return;
2663 	}
2664 	if (prop_type == PT_UNKNOWN) {
2665 		long_usage(CMD_SET, TRUE);
2666 		return;
2667 	}
2668 
2669 	/*
2670 	 * Special case: the user can change the zone name prior to 'create';
2671 	 * if the zone already exists, we fall through letting initialize()
2672 	 * and the rest of the logic run.
2673 	 */
2674 	if (res_type == RT_ZONENAME && got_handle == FALSE &&
2675 	    !state_atleast(ZONE_STATE_CONFIGURED)) {
2676 		(void) strlcpy(zone, prop_id, sizeof (zone));
2677 		return;
2678 	}
2679 
2680 	if (initialize(TRUE) != Z_OK)
2681 		return;
2682 
2683 	switch (res_type) {
2684 	case RT_ZONENAME:
2685 		if ((err = zonecfg_set_name(handle, prop_id)) != Z_OK) {
2686 			/*
2687 			 * Use prop_id instead of 'zone' here, since we're
2688 			 * reporting a problem about the *new* zonename.
2689 			 */
2690 			zone_perror(prop_id, err, TRUE);
2691 		} else {
2692 			need_to_commit = TRUE;
2693 			(void) strlcpy(zone, prop_id, sizeof (zone));
2694 		}
2695 		return;
2696 	case RT_ZONEPATH:
2697 		if (state_atleast(ZONE_STATE_INSTALLED)) {
2698 			zerr(gettext("Zone %s already installed; %s %s not "
2699 			    "allowed."), zone, cmd_to_str(CMD_SET),
2700 			    rt_to_str(RT_ZONEPATH));
2701 			return;
2702 		}
2703 		if (validate_zonepath_syntax(prop_id) != Z_OK) {
2704 			saw_error = TRUE;
2705 			return;
2706 		}
2707 		if ((err = zonecfg_set_zonepath(handle, prop_id)) != Z_OK)
2708 			zone_perror(zone, err, TRUE);
2709 		else
2710 			need_to_commit = TRUE;
2711 		return;
2712 	case RT_AUTOBOOT:
2713 		if (strcmp(prop_id, "true") == 0) {
2714 			autoboot = B_TRUE;
2715 		} else if (strcmp(prop_id, "false") == 0) {
2716 			autoboot = B_FALSE;
2717 		} else {
2718 			zerr(gettext("%s value must be '%s' or '%s'."),
2719 			    pt_to_str(PT_AUTOBOOT), "true", "false");
2720 			saw_error = TRUE;
2721 			return;
2722 		}
2723 		if ((err = zonecfg_set_autoboot(handle, autoboot)) != Z_OK)
2724 			zone_perror(zone, err, TRUE);
2725 		else
2726 			need_to_commit = TRUE;
2727 		return;
2728 	case RT_POOL:
2729 		if ((err = zonecfg_set_pool(handle, prop_id)) != Z_OK)
2730 			zone_perror(zone, err, TRUE);
2731 		else
2732 			need_to_commit = TRUE;
2733 		return;
2734 	case RT_FS:
2735 		switch (prop_type) {
2736 		case PT_DIR:
2737 			(void) strlcpy(in_progress_fstab.zone_fs_dir, prop_id,
2738 			    sizeof (in_progress_fstab.zone_fs_dir));
2739 			return;
2740 		case PT_SPECIAL:
2741 			(void) strlcpy(in_progress_fstab.zone_fs_special,
2742 			    prop_id,
2743 			    sizeof (in_progress_fstab.zone_fs_special));
2744 			return;
2745 		case PT_RAW:
2746 			(void) strlcpy(in_progress_fstab.zone_fs_raw,
2747 			    prop_id, sizeof (in_progress_fstab.zone_fs_raw));
2748 			return;
2749 		case PT_TYPE:
2750 			if (!valid_fs_type(prop_id)) {
2751 				zerr(gettext("\"%s\" is not a valid %s."),
2752 				    prop_id, pt_to_str(PT_TYPE));
2753 				saw_error = TRUE;
2754 				return;
2755 			}
2756 			(void) strlcpy(in_progress_fstab.zone_fs_type, prop_id,
2757 			    sizeof (in_progress_fstab.zone_fs_type));
2758 			return;
2759 		case PT_OPTIONS:
2760 			if (pp->pv_type != PROP_VAL_SIMPLE &&
2761 			    pp->pv_type != PROP_VAL_LIST) {
2762 				zerr(gettext("A %s or %s value was expected "
2763 				    "here."), pvt_to_str(PROP_VAL_SIMPLE),
2764 				    pvt_to_str(PROP_VAL_LIST));
2765 				saw_error = TRUE;
2766 				return;
2767 			}
2768 			zonecfg_free_fs_option_list(
2769 			    in_progress_fstab.zone_fs_options);
2770 			in_progress_fstab.zone_fs_options = NULL;
2771 			if (!(pp->pv_type == PROP_VAL_LIST &&
2772 			    pp->pv_list == NULL))
2773 				add_property(cmd);
2774 			return;
2775 		default:
2776 			break;
2777 		}
2778 		zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE, TRUE);
2779 		long_usage(CMD_SET, TRUE);
2780 		usage(FALSE, HELP_PROPS);
2781 		return;
2782 	case RT_IPD:
2783 		switch (prop_type) {
2784 		case PT_DIR:
2785 			(void) strlcpy(in_progress_ipdtab.zone_fs_dir, prop_id,
2786 			    sizeof (in_progress_ipdtab.zone_fs_dir));
2787 			return;
2788 		default:
2789 			break;
2790 		}
2791 		zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE, TRUE);
2792 		long_usage(CMD_SET, TRUE);
2793 		usage(FALSE, HELP_PROPS);
2794 		return;
2795 	case RT_NET:
2796 		switch (prop_type) {
2797 		case PT_ADDRESS:
2798 			if (validate_net_address_syntax(prop_id) != Z_OK) {
2799 				saw_error = TRUE;
2800 				return;
2801 			}
2802 			(void) strlcpy(in_progress_nwiftab.zone_nwif_address,
2803 			    prop_id,
2804 			    sizeof (in_progress_nwiftab.zone_nwif_address));
2805 			break;
2806 		case PT_PHYSICAL:
2807 			if (validate_net_physical_syntax(prop_id) != Z_OK) {
2808 				saw_error = TRUE;
2809 				return;
2810 			}
2811 			(void) strlcpy(in_progress_nwiftab.zone_nwif_physical,
2812 			    prop_id,
2813 			    sizeof (in_progress_nwiftab.zone_nwif_physical));
2814 			break;
2815 		default:
2816 			zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE,
2817 			    TRUE);
2818 			long_usage(CMD_SET, TRUE);
2819 			usage(FALSE, HELP_PROPS);
2820 			return;
2821 		}
2822 		return;
2823 	case RT_DEVICE:
2824 		switch (prop_type) {
2825 		case PT_MATCH:
2826 			(void) strlcpy(in_progress_devtab.zone_dev_match,
2827 			    prop_id,
2828 			    sizeof (in_progress_devtab.zone_dev_match));
2829 			break;
2830 		default:
2831 			zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE,
2832 			    TRUE);
2833 			long_usage(CMD_SET, TRUE);
2834 			usage(FALSE, HELP_PROPS);
2835 			return;
2836 		}
2837 		return;
2838 	case RT_RCTL:
2839 		switch (prop_type) {
2840 		case PT_NAME:
2841 			if (!zonecfg_valid_rctlname(prop_id)) {
2842 				zerr(gettext("'%s' is not a valid zone %s "
2843 				    "name."), prop_id, rt_to_str(RT_RCTL));
2844 				return;
2845 			}
2846 			(void) strlcpy(in_progress_rctltab.zone_rctl_name,
2847 			    prop_id,
2848 			    sizeof (in_progress_rctltab.zone_rctl_name));
2849 			break;
2850 		case PT_VALUE:
2851 			if (pp->pv_type != PROP_VAL_COMPLEX &&
2852 			    pp->pv_type != PROP_VAL_LIST) {
2853 				zerr(gettext("A %s or %s value was expected "
2854 				    "here."), pvt_to_str(PROP_VAL_COMPLEX),
2855 				    pvt_to_str(PROP_VAL_LIST));
2856 				saw_error = TRUE;
2857 				return;
2858 			}
2859 			zonecfg_free_rctl_value_list(
2860 			    in_progress_rctltab.zone_rctl_valptr);
2861 			in_progress_rctltab.zone_rctl_valptr = NULL;
2862 			if (!(pp->pv_type == PROP_VAL_LIST &&
2863 			    pp->pv_list == NULL))
2864 				add_property(cmd);
2865 			break;
2866 		default:
2867 			zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE,
2868 			    TRUE);
2869 			long_usage(CMD_SET, TRUE);
2870 			usage(FALSE, HELP_PROPS);
2871 			return;
2872 		}
2873 		return;
2874 	case RT_ATTR:
2875 		switch (prop_type) {
2876 		case PT_NAME:
2877 			(void) strlcpy(in_progress_attrtab.zone_attr_name,
2878 			    prop_id,
2879 			    sizeof (in_progress_attrtab.zone_attr_name));
2880 			break;
2881 		case PT_TYPE:
2882 			(void) strlcpy(in_progress_attrtab.zone_attr_type,
2883 			    prop_id,
2884 			    sizeof (in_progress_attrtab.zone_attr_type));
2885 			break;
2886 		case PT_VALUE:
2887 			(void) strlcpy(in_progress_attrtab.zone_attr_value,
2888 			    prop_id,
2889 			    sizeof (in_progress_attrtab.zone_attr_value));
2890 			break;
2891 		default:
2892 			zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE,
2893 			    TRUE);
2894 			long_usage(CMD_SET, TRUE);
2895 			usage(FALSE, HELP_PROPS);
2896 			return;
2897 		}
2898 		return;
2899 	case RT_DATASET:
2900 		switch (prop_type) {
2901 		case PT_NAME:
2902 			(void) strlcpy(in_progress_dstab.zone_dataset_name,
2903 			    prop_id,
2904 			    sizeof (in_progress_dstab.zone_dataset_name));
2905 			return;
2906 		default:
2907 			break;
2908 		}
2909 		zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE, TRUE);
2910 		long_usage(CMD_SET, TRUE);
2911 		usage(FALSE, HELP_PROPS);
2912 		return;
2913 	default:
2914 		zone_perror(rt_to_str(res_type), Z_NO_RESOURCE_TYPE, TRUE);
2915 		long_usage(CMD_SET, TRUE);
2916 		usage(FALSE, HELP_RESOURCES);
2917 		return;
2918 	}
2919 }
2920 
2921 static void
2922 output_prop(FILE *fp, int pnum, char *pval, bool print_notspec)
2923 {
2924 	char *qstr;
2925 
2926 	if (*pval != '\0') {
2927 		qstr = quoteit(pval);
2928 		(void) fprintf(fp, "\t%s: %s\n", pt_to_str(pnum), qstr);
2929 		free(qstr);
2930 	} else if (print_notspec)
2931 		(void) fprintf(fp, gettext("\t%s not specified\n"),
2932 		    pt_to_str(pnum));
2933 }
2934 
2935 static void
2936 info_zonename(zone_dochandle_t handle, FILE *fp)
2937 {
2938 	char zonename[ZONENAME_MAX];
2939 
2940 	if (zonecfg_get_name(handle, zonename, sizeof (zonename)) == Z_OK)
2941 		(void) fprintf(fp, "%s: %s\n", pt_to_str(PT_ZONENAME),
2942 		    zonename);
2943 	else
2944 		(void) fprintf(fp, gettext("%s not specified\n"),
2945 		    pt_to_str(PT_ZONENAME));
2946 }
2947 
2948 static void
2949 info_zonepath(zone_dochandle_t handle, FILE *fp)
2950 {
2951 	char zonepath[MAXPATHLEN];
2952 
2953 	if (zonecfg_get_zonepath(handle, zonepath, sizeof (zonepath)) == Z_OK)
2954 		(void) fprintf(fp, "%s: %s\n", pt_to_str(PT_ZONEPATH),
2955 		    zonepath);
2956 	else {
2957 		(void) fprintf(fp, gettext("%s not specified\n"),
2958 		    pt_to_str(PT_ZONEPATH));
2959 	}
2960 }
2961 
2962 static void
2963 info_autoboot(zone_dochandle_t handle, FILE *fp)
2964 {
2965 	boolean_t autoboot;
2966 	int err;
2967 
2968 	if ((err = zonecfg_get_autoboot(handle, &autoboot)) == Z_OK)
2969 		(void) fprintf(fp, "%s: %s\n", pt_to_str(PT_AUTOBOOT),
2970 		    autoboot ? "true" : "false");
2971 	else
2972 		zone_perror(zone, err, TRUE);
2973 }
2974 
2975 static void
2976 info_pool(zone_dochandle_t handle, FILE *fp)
2977 {
2978 	char pool[MAXNAMELEN];
2979 	int err;
2980 
2981 	if ((err = zonecfg_get_pool(handle, pool, sizeof (pool))) == Z_OK)
2982 		(void) fprintf(fp, "%s: %s\n", pt_to_str(PT_POOL), pool);
2983 	else
2984 		zone_perror(zone, err, TRUE);
2985 }
2986 
2987 static void
2988 output_fs(FILE *fp, struct zone_fstab *fstab)
2989 {
2990 	zone_fsopt_t *this;
2991 
2992 	(void) fprintf(fp, "%s:\n", rt_to_str(RT_FS));
2993 	output_prop(fp, PT_DIR, fstab->zone_fs_dir, B_TRUE);
2994 	output_prop(fp, PT_SPECIAL, fstab->zone_fs_special, B_TRUE);
2995 	output_prop(fp, PT_RAW, fstab->zone_fs_raw, B_TRUE);
2996 	output_prop(fp, PT_TYPE, fstab->zone_fs_type, B_TRUE);
2997 	(void) fprintf(fp, "\t%s: [", pt_to_str(PT_OPTIONS));
2998 	for (this = fstab->zone_fs_options; this != NULL;
2999 	    this = this->zone_fsopt_next) {
3000 		if (strchr(this->zone_fsopt_opt, '='))
3001 			(void) fprintf(fp, "\"%s\"", this->zone_fsopt_opt);
3002 		else
3003 			(void) fprintf(fp, "%s", this->zone_fsopt_opt);
3004 		if (this->zone_fsopt_next != NULL)
3005 			(void) fprintf(fp, ",");
3006 	}
3007 	(void) fprintf(fp, "]\n");
3008 }
3009 
3010 static void
3011 output_ipd(FILE *fp, struct zone_fstab *ipdtab)
3012 {
3013 	(void) fprintf(fp, "%s:\n", rt_to_str(RT_IPD));
3014 	output_prop(fp, PT_DIR, ipdtab->zone_fs_dir, B_TRUE);
3015 }
3016 
3017 static void
3018 info_fs(zone_dochandle_t handle, FILE *fp, cmd_t *cmd)
3019 {
3020 	struct zone_fstab lookup, user;
3021 	bool output = FALSE;
3022 
3023 	if (zonecfg_setfsent(handle) != Z_OK)
3024 		return;
3025 	while (zonecfg_getfsent(handle, &lookup) == Z_OK) {
3026 		if (cmd->cmd_prop_nv_pairs == 0) {
3027 			output_fs(fp, &lookup);
3028 			goto loopend;
3029 		}
3030 		if (fill_in_fstab(cmd, &user, TRUE) != Z_OK)
3031 			goto loopend;
3032 		if (strlen(user.zone_fs_dir) > 0 &&
3033 		    strcmp(user.zone_fs_dir, lookup.zone_fs_dir) != 0)
3034 			goto loopend;	/* no match */
3035 		if (strlen(user.zone_fs_special) > 0 &&
3036 		    strcmp(user.zone_fs_special, lookup.zone_fs_special) != 0)
3037 			goto loopend;	/* no match */
3038 		if (strlen(user.zone_fs_type) > 0 &&
3039 		    strcmp(user.zone_fs_type, lookup.zone_fs_type) != 0)
3040 			goto loopend;	/* no match */
3041 		output_fs(fp, &lookup);
3042 		output = TRUE;
3043 loopend:
3044 		zonecfg_free_fs_option_list(lookup.zone_fs_options);
3045 	}
3046 	(void) zonecfg_endfsent(handle);
3047 	/*
3048 	 * If a property n/v pair was specified, warn the user if there was
3049 	 * nothing to output.
3050 	 */
3051 	if (!output && cmd->cmd_prop_nv_pairs > 0)
3052 		(void) printf(gettext("No such %s resource.\n"),
3053 		    rt_to_str(RT_FS));
3054 }
3055 
3056 static void
3057 info_ipd(zone_dochandle_t handle, FILE *fp, cmd_t *cmd)
3058 {
3059 	struct zone_fstab lookup, user;
3060 	bool output = FALSE;
3061 
3062 	if (zonecfg_setipdent(handle) != Z_OK)
3063 		return;
3064 	while (zonecfg_getipdent(handle, &lookup) == Z_OK) {
3065 		if (cmd->cmd_prop_nv_pairs == 0) {
3066 			output_ipd(fp, &lookup);
3067 			continue;
3068 		}
3069 		if (fill_in_ipdtab(cmd, &user, TRUE) != Z_OK)
3070 			continue;
3071 		if (strlen(user.zone_fs_dir) > 0 &&
3072 		    strcmp(user.zone_fs_dir, lookup.zone_fs_dir) != 0)
3073 			continue;	/* no match */
3074 		output_ipd(fp, &lookup);
3075 		output = TRUE;
3076 	}
3077 	(void) zonecfg_endipdent(handle);
3078 	/*
3079 	 * If a property n/v pair was specified, warn the user if there was
3080 	 * nothing to output.
3081 	 */
3082 	if (!output && cmd->cmd_prop_nv_pairs > 0)
3083 		(void) printf(gettext("No such %s resource.\n"),
3084 		    rt_to_str(RT_IPD));
3085 }
3086 
3087 static void
3088 output_net(FILE *fp, struct zone_nwiftab *nwiftab)
3089 {
3090 	(void) fprintf(fp, "%s:\n", rt_to_str(RT_NET));
3091 	output_prop(fp, PT_ADDRESS, nwiftab->zone_nwif_address, B_TRUE);
3092 	output_prop(fp, PT_PHYSICAL, nwiftab->zone_nwif_physical, B_TRUE);
3093 }
3094 
3095 static void
3096 info_net(zone_dochandle_t handle, FILE *fp, cmd_t *cmd)
3097 {
3098 	struct zone_nwiftab lookup, user;
3099 	bool output = FALSE;
3100 
3101 	if (zonecfg_setnwifent(handle) != Z_OK)
3102 		return;
3103 	while (zonecfg_getnwifent(handle, &lookup) == Z_OK) {
3104 		if (cmd->cmd_prop_nv_pairs == 0) {
3105 			output_net(fp, &lookup);
3106 			continue;
3107 		}
3108 		if (fill_in_nwiftab(cmd, &user, TRUE) != Z_OK)
3109 			continue;
3110 		if (strlen(user.zone_nwif_physical) > 0 &&
3111 		    strcmp(user.zone_nwif_physical,
3112 		    lookup.zone_nwif_physical) != 0)
3113 			continue;	/* no match */
3114 		if (strlen(user.zone_nwif_address) > 0 &&
3115 		    !zonecfg_same_net_address(user.zone_nwif_address,
3116 		    lookup.zone_nwif_address))
3117 			continue;	/* no match */
3118 		output_net(fp, &lookup);
3119 		output = TRUE;
3120 	}
3121 	(void) zonecfg_endnwifent(handle);
3122 	/*
3123 	 * If a property n/v pair was specified, warn the user if there was
3124 	 * nothing to output.
3125 	 */
3126 	if (!output && cmd->cmd_prop_nv_pairs > 0)
3127 		(void) printf(gettext("No such %s resource.\n"),
3128 		    rt_to_str(RT_NET));
3129 }
3130 
3131 static void
3132 output_dev(FILE *fp, struct zone_devtab *devtab)
3133 {
3134 	(void) fprintf(fp, "%s\n", rt_to_str(RT_DEVICE));
3135 	output_prop(fp, PT_MATCH, devtab->zone_dev_match, B_TRUE);
3136 }
3137 
3138 static void
3139 info_dev(zone_dochandle_t handle, FILE *fp, cmd_t *cmd)
3140 {
3141 	struct zone_devtab lookup, user;
3142 	bool output = FALSE;
3143 
3144 	if (zonecfg_setdevent(handle) != Z_OK)
3145 		return;
3146 	while (zonecfg_getdevent(handle, &lookup) == Z_OK) {
3147 		if (cmd->cmd_prop_nv_pairs == 0) {
3148 			output_dev(fp, &lookup);
3149 			continue;
3150 		}
3151 		if (fill_in_devtab(cmd, &user, TRUE) != Z_OK)
3152 			continue;
3153 		if (strlen(user.zone_dev_match) > 0 &&
3154 		    strcmp(user.zone_dev_match, lookup.zone_dev_match) != 0)
3155 			continue;	/* no match */
3156 		output_dev(fp, &lookup);
3157 		output = TRUE;
3158 	}
3159 	(void) zonecfg_enddevent(handle);
3160 	/*
3161 	 * If a property n/v pair was specified, warn the user if there was
3162 	 * nothing to output.
3163 	 */
3164 	if (!output && cmd->cmd_prop_nv_pairs > 0)
3165 		(void) printf(gettext("No such %s resource.\n"),
3166 		    rt_to_str(RT_DEVICE));
3167 }
3168 
3169 static void
3170 output_rctl(FILE *fp, struct zone_rctltab *rctltab)
3171 {
3172 	struct zone_rctlvaltab *valptr;
3173 
3174 	(void) fprintf(fp, "%s:\n", rt_to_str(RT_RCTL));
3175 	output_prop(fp, PT_NAME, rctltab->zone_rctl_name, B_TRUE);
3176 	for (valptr = rctltab->zone_rctl_valptr; valptr != NULL;
3177 	    valptr = valptr->zone_rctlval_next) {
3178 		fprintf(fp, "\t%s: (%s=%s,%s=%s,%s=%s)\n",
3179 		    pt_to_str(PT_VALUE),
3180 		    pt_to_str(PT_PRIV), valptr->zone_rctlval_priv,
3181 		    pt_to_str(PT_LIMIT), valptr->zone_rctlval_limit,
3182 		    pt_to_str(PT_ACTION), valptr->zone_rctlval_action);
3183 	}
3184 }
3185 
3186 static void
3187 info_rctl(zone_dochandle_t handle, FILE *fp, cmd_t *cmd)
3188 {
3189 	struct zone_rctltab lookup, user;
3190 	bool output = FALSE;
3191 
3192 	if (zonecfg_setrctlent(handle) != Z_OK)
3193 		return;
3194 	while (zonecfg_getrctlent(handle, &lookup) == Z_OK) {
3195 		if (cmd->cmd_prop_nv_pairs == 0) {
3196 			output_rctl(fp, &lookup);
3197 		} else if (fill_in_rctltab(cmd, &user, TRUE) == Z_OK &&
3198 		    (strlen(user.zone_rctl_name) == 0 ||
3199 		    strcmp(user.zone_rctl_name, lookup.zone_rctl_name) == 0)) {
3200 			output_rctl(fp, &lookup);
3201 			output = TRUE;
3202 		}
3203 		zonecfg_free_rctl_value_list(lookup.zone_rctl_valptr);
3204 	}
3205 	(void) zonecfg_endrctlent(handle);
3206 	/*
3207 	 * If a property n/v pair was specified, warn the user if there was
3208 	 * nothing to output.
3209 	 */
3210 	if (!output && cmd->cmd_prop_nv_pairs > 0)
3211 		(void) printf(gettext("No such %s resource.\n"),
3212 		    rt_to_str(RT_RCTL));
3213 }
3214 
3215 static void
3216 output_attr(FILE *fp, struct zone_attrtab *attrtab)
3217 {
3218 	(void) fprintf(fp, "%s:\n", rt_to_str(RT_ATTR));
3219 	output_prop(fp, PT_NAME, attrtab->zone_attr_name, B_TRUE);
3220 	output_prop(fp, PT_TYPE, attrtab->zone_attr_type, B_TRUE);
3221 	output_prop(fp, PT_VALUE, attrtab->zone_attr_value, B_TRUE);
3222 }
3223 
3224 static void
3225 info_attr(zone_dochandle_t handle, FILE *fp, cmd_t *cmd)
3226 {
3227 	struct zone_attrtab lookup, user;
3228 	bool output = FALSE;
3229 
3230 	if (zonecfg_setattrent(handle) != Z_OK)
3231 		return;
3232 	while (zonecfg_getattrent(handle, &lookup) == Z_OK) {
3233 		if (cmd->cmd_prop_nv_pairs == 0) {
3234 			output_attr(fp, &lookup);
3235 			continue;
3236 		}
3237 		if (fill_in_attrtab(cmd, &user, TRUE) != Z_OK)
3238 			continue;
3239 		if (strlen(user.zone_attr_name) > 0 &&
3240 		    strcmp(user.zone_attr_name, lookup.zone_attr_name) != 0)
3241 			continue;	/* no match */
3242 		if (strlen(user.zone_attr_type) > 0 &&
3243 		    strcmp(user.zone_attr_type, lookup.zone_attr_type) != 0)
3244 			continue;	/* no match */
3245 		if (strlen(user.zone_attr_value) > 0 &&
3246 		    strcmp(user.zone_attr_value, lookup.zone_attr_value) != 0)
3247 			continue;	/* no match */
3248 		output_attr(fp, &lookup);
3249 		output = TRUE;
3250 	}
3251 	(void) zonecfg_endattrent(handle);
3252 	/*
3253 	 * If a property n/v pair was specified, warn the user if there was
3254 	 * nothing to output.
3255 	 */
3256 	if (!output && cmd->cmd_prop_nv_pairs > 0)
3257 		(void) printf(gettext("No such %s resource.\n"),
3258 		    rt_to_str(RT_ATTR));
3259 }
3260 
3261 static void
3262 output_ds(FILE *fp, struct zone_dstab *dstab)
3263 {
3264 	(void) fprintf(fp, "%s:\n", rt_to_str(RT_DATASET));
3265 	output_prop(fp, PT_NAME, dstab->zone_dataset_name, B_TRUE);
3266 }
3267 
3268 static void
3269 info_ds(zone_dochandle_t handle, FILE *fp, cmd_t *cmd)
3270 {
3271 	struct zone_dstab lookup, user;
3272 	bool output = FALSE;
3273 
3274 	if (zonecfg_setdevent(handle) != Z_OK)
3275 		return;
3276 	while (zonecfg_getdsent(handle, &lookup) == Z_OK) {
3277 		if (cmd->cmd_prop_nv_pairs == 0) {
3278 			output_ds(fp, &lookup);
3279 			continue;
3280 		}
3281 		if (fill_in_dstab(cmd, &user, TRUE) != Z_OK)
3282 			continue;
3283 		if (strlen(user.zone_dataset_name) > 0 &&
3284 		    strcmp(user.zone_dataset_name,
3285 		    lookup.zone_dataset_name) != 0)
3286 			continue;	/* no match */
3287 		output_ds(fp, &lookup);
3288 		output = TRUE;
3289 	}
3290 	(void) zonecfg_enddsent(handle);
3291 	/*
3292 	 * If a property n/v pair was specified, warn the user if there was
3293 	 * nothing to output.
3294 	 */
3295 	if (!output && cmd->cmd_prop_nv_pairs > 0)
3296 		(void) printf(gettext("No such %s resource.\n"),
3297 		    rt_to_str(RT_DATASET));
3298 }
3299 
3300 
3301 void
3302 info_func(cmd_t *cmd)
3303 {
3304 	FILE *fp = stdout;
3305 	bool need_to_close = FALSE;
3306 	char *pager;
3307 
3308 	assert(cmd != NULL);
3309 
3310 	if (initialize(TRUE) != Z_OK)
3311 		return;
3312 
3313 	/* don't page error output */
3314 	if (interactive_mode) {
3315 		if ((pager = getenv("PAGER")) == NULL)
3316 			pager = PAGER;
3317 		if ((fp = popen(pager, "w")) != NULL)
3318 			need_to_close = TRUE;
3319 		else
3320 			fp = stdout;
3321 		setbuf(fp, NULL);
3322 	}
3323 
3324 	if (!global_scope) {
3325 		switch (resource_scope) {
3326 		case RT_FS:
3327 			output_fs(fp, &in_progress_fstab);
3328 			break;
3329 		case RT_IPD:
3330 			output_ipd(fp, &in_progress_ipdtab);
3331 			break;
3332 		case RT_NET:
3333 			output_net(fp, &in_progress_nwiftab);
3334 			break;
3335 		case RT_DEVICE:
3336 			output_dev(fp, &in_progress_devtab);
3337 			break;
3338 		case RT_RCTL:
3339 			output_rctl(fp, &in_progress_rctltab);
3340 			break;
3341 		case RT_ATTR:
3342 			output_attr(fp, &in_progress_attrtab);
3343 			break;
3344 		case RT_DATASET:
3345 			output_ds(fp, &in_progress_dstab);
3346 			break;
3347 		}
3348 		goto cleanup;
3349 	}
3350 
3351 	switch (cmd->cmd_res_type) {
3352 	case RT_UNKNOWN:
3353 		info_zonename(handle, fp);
3354 		info_zonepath(handle, fp);
3355 		info_autoboot(handle, fp);
3356 		info_pool(handle, fp);
3357 		info_ipd(handle, fp, cmd);
3358 		info_fs(handle, fp, cmd);
3359 		info_net(handle, fp, cmd);
3360 		info_dev(handle, fp, cmd);
3361 		info_rctl(handle, fp, cmd);
3362 		info_attr(handle, fp, cmd);
3363 		info_ds(handle, fp, cmd);
3364 		break;
3365 	case RT_ZONENAME:
3366 		info_zonename(handle, fp);
3367 		break;
3368 	case RT_ZONEPATH:
3369 		info_zonepath(handle, fp);
3370 		break;
3371 	case RT_AUTOBOOT:
3372 		info_autoboot(handle, fp);
3373 		break;
3374 	case RT_POOL:
3375 		info_pool(handle, fp);
3376 		break;
3377 	case RT_FS:
3378 		info_fs(handle, fp, cmd);
3379 		break;
3380 	case RT_IPD:
3381 		info_ipd(handle, fp, cmd);
3382 		break;
3383 	case RT_NET:
3384 		info_net(handle, fp, cmd);
3385 		break;
3386 	case RT_DEVICE:
3387 		info_dev(handle, fp, cmd);
3388 		break;
3389 	case RT_RCTL:
3390 		info_rctl(handle, fp, cmd);
3391 		break;
3392 	case RT_ATTR:
3393 		info_attr(handle, fp, cmd);
3394 		break;
3395 	case RT_DATASET:
3396 		info_ds(handle, fp, cmd);
3397 		break;
3398 	default:
3399 		zone_perror(rt_to_str(cmd->cmd_res_type), Z_NO_RESOURCE_TYPE,
3400 		    TRUE);
3401 	}
3402 
3403 cleanup:
3404 	if (need_to_close)
3405 		(void) pclose(fp);
3406 }
3407 
3408 /*
3409  * Helper function for verify-- checks that a required string property
3410  * exists.
3411  */
3412 static void
3413 check_reqd_prop(char *attr, int rt, int pt, int *ret_val)
3414 {
3415 	if (strlen(attr) == 0) {
3416 		zerr(gettext("%s: %s not specified"), rt_to_str(rt),
3417 		    pt_to_str(pt));
3418 		saw_error = TRUE;
3419 		if (*ret_val == Z_OK)
3420 			*ret_val = Z_REQD_PROPERTY_MISSING;
3421 	}
3422 }
3423 
3424 /*
3425  * See the DTD for which attributes are required for which resources.
3426  *
3427  * This function can be called by commit_func(), which needs to save things,
3428  * in addition to the general call from parse_and_run(), which doesn't need
3429  * things saved.  Since the parameters are standardized, we distinguish by
3430  * having commit_func() call here with cmd->cmd_arg set to "save" to indicate
3431  * that a save is needed.
3432  */
3433 void
3434 verify_func(cmd_t *cmd)
3435 {
3436 	struct zone_nwiftab nwiftab;
3437 	struct zone_fstab fstab;
3438 	struct zone_attrtab attrtab;
3439 	struct zone_rctltab rctltab;
3440 	struct zone_dstab dstab;
3441 	char zonepath[MAXPATHLEN];
3442 	int err, ret_val = Z_OK, arg;
3443 	bool save = FALSE;
3444 
3445 	optind = 0;
3446 	if ((arg = getopt(cmd->cmd_argc, cmd->cmd_argv, "?")) != EOF) {
3447 		switch (arg) {
3448 		case '?':
3449 			longer_usage(CMD_VERIFY);
3450 			return;
3451 		default:
3452 			short_usage(CMD_VERIFY);
3453 			return;
3454 		}
3455 	}
3456 	if (optind > cmd->cmd_argc) {
3457 		short_usage(CMD_VERIFY);
3458 		return;
3459 	}
3460 
3461 	if (zone_is_read_only(CMD_VERIFY))
3462 		return;
3463 
3464 	assert(cmd != NULL);
3465 
3466 	if (cmd->cmd_argc > 0 && (strcmp(cmd->cmd_argv[0], "save") == 0))
3467 		save = TRUE;
3468 	if (initialize(TRUE) != Z_OK)
3469 		return;
3470 
3471 	if (zonecfg_get_zonepath(handle, zonepath, sizeof (zonepath)) != Z_OK) {
3472 		zerr(gettext("%s not specified"), pt_to_str(PT_ZONEPATH));
3473 		ret_val = Z_REQD_RESOURCE_MISSING;
3474 		saw_error = TRUE;
3475 	}
3476 	if (strlen(zonepath) == 0) {
3477 		zerr(gettext("%s cannot be empty."), pt_to_str(PT_ZONEPATH));
3478 		ret_val = Z_REQD_RESOURCE_MISSING;
3479 		saw_error = TRUE;
3480 	}
3481 
3482 	if ((err = zonecfg_setipdent(handle)) != Z_OK) {
3483 		zone_perror(zone, err, TRUE);
3484 		return;
3485 	}
3486 	while (zonecfg_getipdent(handle, &fstab) == Z_OK) {
3487 		check_reqd_prop(fstab.zone_fs_dir, RT_IPD, PT_DIR, &ret_val);
3488 	}
3489 	(void) zonecfg_endipdent(handle);
3490 
3491 	if ((err = zonecfg_setfsent(handle)) != Z_OK) {
3492 		zone_perror(zone, err, TRUE);
3493 		return;
3494 	}
3495 	while (zonecfg_getfsent(handle, &fstab) == Z_OK) {
3496 		check_reqd_prop(fstab.zone_fs_dir, RT_FS, PT_DIR, &ret_val);
3497 		check_reqd_prop(fstab.zone_fs_special, RT_FS, PT_SPECIAL,
3498 		    &ret_val);
3499 		check_reqd_prop(fstab.zone_fs_type, RT_FS, PT_TYPE, &ret_val);
3500 
3501 		zonecfg_free_fs_option_list(fstab.zone_fs_options);
3502 	}
3503 	(void) zonecfg_endfsent(handle);
3504 
3505 	if ((err = zonecfg_setnwifent(handle)) != Z_OK) {
3506 		zone_perror(zone, err, TRUE);
3507 		return;
3508 	}
3509 	while (zonecfg_getnwifent(handle, &nwiftab) == Z_OK) {
3510 		check_reqd_prop(nwiftab.zone_nwif_address, RT_NET,
3511 		    PT_ADDRESS, &ret_val);
3512 		check_reqd_prop(nwiftab.zone_nwif_physical, RT_NET,
3513 		    PT_PHYSICAL, &ret_val);
3514 	}
3515 	(void) zonecfg_endnwifent(handle);
3516 
3517 	if ((err = zonecfg_setrctlent(handle)) != Z_OK) {
3518 		zone_perror(zone, err, TRUE);
3519 		return;
3520 	}
3521 	while (zonecfg_getrctlent(handle, &rctltab) == Z_OK) {
3522 		check_reqd_prop(rctltab.zone_rctl_name, RT_RCTL, PT_NAME,
3523 		    &ret_val);
3524 
3525 		if (rctltab.zone_rctl_valptr == NULL) {
3526 			zerr(gettext("%s: no %s specified"),
3527 			    rt_to_str(RT_RCTL), pt_to_str(PT_VALUE));
3528 			saw_error = TRUE;
3529 			if (ret_val == Z_OK)
3530 				ret_val = Z_REQD_PROPERTY_MISSING;
3531 		} else {
3532 			zonecfg_free_rctl_value_list(rctltab.zone_rctl_valptr);
3533 		}
3534 	}
3535 	(void) zonecfg_endrctlent(handle);
3536 
3537 	if ((err = zonecfg_setattrent(handle)) != Z_OK) {
3538 		zone_perror(zone, err, TRUE);
3539 		return;
3540 	}
3541 	while (zonecfg_getattrent(handle, &attrtab) == Z_OK) {
3542 		check_reqd_prop(attrtab.zone_attr_name, RT_ATTR, PT_NAME,
3543 		    &ret_val);
3544 		check_reqd_prop(attrtab.zone_attr_type, RT_ATTR, PT_TYPE,
3545 		    &ret_val);
3546 		check_reqd_prop(attrtab.zone_attr_value, RT_ATTR, PT_VALUE,
3547 		    &ret_val);
3548 	}
3549 	(void) zonecfg_endattrent(handle);
3550 
3551 	if ((err = zonecfg_setdsent(handle)) != Z_OK) {
3552 		zone_perror(zone, err, TRUE);
3553 		return;
3554 	}
3555 	while (zonecfg_getdsent(handle, &dstab) == Z_OK) {
3556 		if (strlen(dstab.zone_dataset_name) == 0) {
3557 			zerr("%s: %s %s", rt_to_str(RT_DATASET),
3558 			    pt_to_str(PT_NAME), gettext("not specified"));
3559 			saw_error = TRUE;
3560 			if (ret_val == Z_OK)
3561 				ret_val = Z_REQD_PROPERTY_MISSING;
3562 		} else if (!zfs_name_valid(dstab.zone_dataset_name,
3563 		    ZFS_TYPE_FILESYSTEM)) {
3564 			zerr("%s: %s %s", rt_to_str(RT_DATASET),
3565 			    pt_to_str(PT_NAME), gettext("invalid"));
3566 			saw_error = TRUE;
3567 			if (ret_val == Z_OK)
3568 				ret_val = Z_BAD_PROPERTY;
3569 		}
3570 
3571 	}
3572 	(void) zonecfg_enddsent(handle);
3573 
3574 	if (!global_scope) {
3575 		zerr(gettext("resource specification incomplete"));
3576 		saw_error = TRUE;
3577 		if (ret_val == Z_OK)
3578 			ret_val = Z_INSUFFICIENT_SPEC;
3579 	}
3580 
3581 	if (save) {
3582 		if (ret_val == Z_OK) {
3583 			if ((ret_val = zonecfg_save(handle)) == Z_OK) {
3584 				need_to_commit = FALSE;
3585 				(void) strlcpy(revert_zone, zone,
3586 				    sizeof (revert_zone));
3587 			}
3588 		} else {
3589 			zerr(gettext("Zone %s failed to verify"), zone);
3590 		}
3591 	}
3592 	if (ret_val != Z_OK)
3593 		zone_perror(zone, ret_val, TRUE);
3594 }
3595 
3596 void
3597 cancel_func(cmd_t *cmd)
3598 {
3599 	int arg;
3600 
3601 	assert(cmd != NULL);
3602 
3603 	optind = 0;
3604 	if ((arg = getopt(cmd->cmd_argc, cmd->cmd_argv, "?")) != EOF) {
3605 		switch (arg) {
3606 		case '?':
3607 			longer_usage(CMD_CANCEL);
3608 			return;
3609 		default:
3610 			short_usage(CMD_CANCEL);
3611 			return;
3612 		}
3613 	}
3614 	if (optind != cmd->cmd_argc) {
3615 		short_usage(CMD_CANCEL);
3616 		return;
3617 	}
3618 
3619 	if (global_scope)
3620 		scope_usage(CMD_CANCEL);
3621 	global_scope = TRUE;
3622 	zonecfg_free_fs_option_list(in_progress_fstab.zone_fs_options);
3623 	bzero(&in_progress_fstab, sizeof (in_progress_fstab));
3624 	bzero(&in_progress_nwiftab, sizeof (in_progress_nwiftab));
3625 	bzero(&in_progress_ipdtab, sizeof (in_progress_ipdtab));
3626 	bzero(&in_progress_devtab, sizeof (in_progress_devtab));
3627 	zonecfg_free_rctl_value_list(in_progress_rctltab.zone_rctl_valptr);
3628 	bzero(&in_progress_rctltab, sizeof (in_progress_rctltab));
3629 	bzero(&in_progress_attrtab, sizeof (in_progress_attrtab));
3630 	bzero(&in_progress_dstab, sizeof (in_progress_dstab));
3631 }
3632 
3633 static int
3634 validate_attr_name(char *name)
3635 {
3636 	int i;
3637 
3638 	if (!isalnum(name[0])) {
3639 		zerr(gettext("Invalid %s %s %s: must start with an alpha-"
3640 		    "numeric character."), rt_to_str(RT_ATTR),
3641 		    pt_to_str(PT_NAME), name);
3642 		return (Z_INVAL);
3643 	}
3644 	for (i = 1; name[i]; i++)
3645 		if (!isalnum(name[i]) && name[i] != '-' && name[i] != '.') {
3646 			zerr(gettext("Invalid %s %s %s: can only contain "
3647 			    "alpha-numeric characters, plus '-' and '.'."),
3648 			    rt_to_str(RT_ATTR), pt_to_str(PT_NAME), name);
3649 			return (Z_INVAL);
3650 		}
3651 	return (Z_OK);
3652 }
3653 
3654 static int
3655 validate_attr_type_val(struct zone_attrtab *attrtab)
3656 {
3657 	boolean_t boolval;
3658 	int64_t intval;
3659 	char strval[MAXNAMELEN];
3660 	uint64_t uintval;
3661 
3662 	if (strcmp(attrtab->zone_attr_type, "boolean") == 0) {
3663 		if (zonecfg_get_attr_boolean(attrtab, &boolval) == Z_OK)
3664 			return (Z_OK);
3665 		zerr(gettext("invalid %s value for %s=%s"),
3666 		    rt_to_str(RT_ATTR), pt_to_str(PT_TYPE), "boolean");
3667 		return (Z_ERR);
3668 	}
3669 
3670 	if (strcmp(attrtab->zone_attr_type, "int") == 0) {
3671 		if (zonecfg_get_attr_int(attrtab, &intval) == Z_OK)
3672 			return (Z_OK);
3673 		zerr(gettext("invalid %s value for %s=%s"),
3674 		    rt_to_str(RT_ATTR), pt_to_str(PT_TYPE), "int");
3675 		return (Z_ERR);
3676 	}
3677 
3678 	if (strcmp(attrtab->zone_attr_type, "string") == 0) {
3679 		if (zonecfg_get_attr_string(attrtab, strval,
3680 		    sizeof (strval)) == Z_OK)
3681 			return (Z_OK);
3682 		zerr(gettext("invalid %s value for %s=%s"),
3683 		    rt_to_str(RT_ATTR), pt_to_str(PT_TYPE), "string");
3684 		return (Z_ERR);
3685 	}
3686 
3687 	if (strcmp(attrtab->zone_attr_type, "uint") == 0) {
3688 		if (zonecfg_get_attr_uint(attrtab, &uintval) == Z_OK)
3689 			return (Z_OK);
3690 		zerr(gettext("invalid %s value for %s=%s"),
3691 		    rt_to_str(RT_ATTR), pt_to_str(PT_TYPE), "uint");
3692 		return (Z_ERR);
3693 	}
3694 
3695 	zerr(gettext("invalid %s %s '%s'"), rt_to_str(RT_ATTR),
3696 	    pt_to_str(PT_TYPE), attrtab->zone_attr_type);
3697 	return (Z_ERR);
3698 }
3699 
3700 /*
3701  * Helper function for end_func-- checks the existence of a given property
3702  * and emits a message if not specified.
3703  */
3704 static int
3705 end_check_reqd(char *attr, int pt, bool *validation_failed)
3706 {
3707 	if (strlen(attr) == 0) {
3708 		*validation_failed = TRUE;
3709 		zerr(gettext("%s not specified"), pt_to_str(pt));
3710 		return (Z_ERR);
3711 	}
3712 	return (Z_OK);
3713 }
3714 
3715 void
3716 end_func(cmd_t *cmd)
3717 {
3718 	bool validation_failed = FALSE;
3719 	struct zone_fstab tmp_fstab;
3720 	struct zone_nwiftab tmp_nwiftab;
3721 	struct zone_devtab tmp_devtab;
3722 	struct zone_rctltab tmp_rctltab;
3723 	struct zone_attrtab tmp_attrtab;
3724 	struct zone_dstab tmp_dstab;
3725 	int err, arg;
3726 
3727 	assert(cmd != NULL);
3728 
3729 	optind = 0;
3730 	if ((arg = getopt(cmd->cmd_argc, cmd->cmd_argv, "?")) != EOF) {
3731 		switch (arg) {
3732 		case '?':
3733 			longer_usage(CMD_END);
3734 			return;
3735 		default:
3736 			short_usage(CMD_END);
3737 			return;
3738 		}
3739 	}
3740 	if (optind != cmd->cmd_argc) {
3741 		short_usage(CMD_END);
3742 		return;
3743 	}
3744 
3745 	if (global_scope) {
3746 		scope_usage(CMD_END);
3747 		return;
3748 	}
3749 
3750 	assert(end_op == CMD_ADD || end_op == CMD_SELECT);
3751 
3752 	switch (resource_scope) {
3753 	case RT_FS:
3754 		/* First make sure everything was filled in. */
3755 		if (end_check_reqd(in_progress_fstab.zone_fs_dir,
3756 		    PT_DIR, &validation_failed) == Z_OK) {
3757 			if (in_progress_fstab.zone_fs_dir[0] != '/') {
3758 				zerr(gettext("%s %s is not an absolute path."),
3759 				    pt_to_str(PT_DIR),
3760 				    in_progress_fstab.zone_fs_dir);
3761 				validation_failed = TRUE;
3762 			}
3763 		}
3764 
3765 		(void) end_check_reqd(in_progress_fstab.zone_fs_special,
3766 		    PT_SPECIAL, &validation_failed);
3767 
3768 		if (in_progress_fstab.zone_fs_raw[0] != '\0' &&
3769 		    in_progress_fstab.zone_fs_raw[0] != '/') {
3770 			zerr(gettext("%s %s is not an absolute path."),
3771 			    pt_to_str(PT_RAW),
3772 			    in_progress_fstab.zone_fs_raw);
3773 			validation_failed = TRUE;
3774 		}
3775 
3776 		(void) end_check_reqd(in_progress_fstab.zone_fs_type, PT_TYPE,
3777 		    &validation_failed);
3778 
3779 		if (validation_failed) {
3780 			saw_error = TRUE;
3781 			return;
3782 		}
3783 
3784 		if (end_op == CMD_ADD) {
3785 			/* Make sure there isn't already one like this. */
3786 			bzero(&tmp_fstab, sizeof (tmp_fstab));
3787 			(void) strlcpy(tmp_fstab.zone_fs_dir,
3788 			    in_progress_fstab.zone_fs_dir,
3789 			    sizeof (tmp_fstab.zone_fs_dir));
3790 			err = zonecfg_lookup_filesystem(handle, &tmp_fstab);
3791 			zonecfg_free_fs_option_list(tmp_fstab.zone_fs_options);
3792 			if (err == Z_OK) {
3793 				zerr(gettext("A %s resource "
3794 				    "with the %s '%s' already exists."),
3795 				    rt_to_str(RT_FS), pt_to_str(PT_DIR),
3796 				    in_progress_fstab.zone_fs_dir);
3797 				saw_error = TRUE;
3798 				return;
3799 			}
3800 			err = zonecfg_add_filesystem(handle,
3801 			    &in_progress_fstab);
3802 		} else {
3803 			err = zonecfg_modify_filesystem(handle, &old_fstab,
3804 			    &in_progress_fstab);
3805 		}
3806 		zonecfg_free_fs_option_list(in_progress_fstab.zone_fs_options);
3807 		in_progress_fstab.zone_fs_options = NULL;
3808 		break;
3809 
3810 	case RT_IPD:
3811 		/* First make sure everything was filled in. */
3812 		if (end_check_reqd(in_progress_ipdtab.zone_fs_dir, PT_DIR,
3813 		    &validation_failed) == Z_OK) {
3814 			if (in_progress_ipdtab.zone_fs_dir[0] != '/') {
3815 				zerr(gettext("%s %s is not an absolute path."),
3816 				    pt_to_str(PT_DIR),
3817 				    in_progress_ipdtab.zone_fs_dir);
3818 				validation_failed = TRUE;
3819 			}
3820 		}
3821 		if (validation_failed) {
3822 			saw_error = TRUE;
3823 			return;
3824 		}
3825 
3826 		if (end_op == CMD_ADD) {
3827 			/* Make sure there isn't already one like this. */
3828 			bzero(&tmp_fstab, sizeof (tmp_fstab));
3829 			(void) strlcpy(tmp_fstab.zone_fs_dir,
3830 			    in_progress_ipdtab.zone_fs_dir,
3831 			    sizeof (tmp_fstab.zone_fs_dir));
3832 			err = zonecfg_lookup_ipd(handle, &tmp_fstab);
3833 			if (err == Z_OK) {
3834 				zerr(gettext("An %s resource "
3835 				    "with the %s '%s' already exists."),
3836 				    rt_to_str(RT_IPD), pt_to_str(PT_DIR),
3837 				    in_progress_ipdtab.zone_fs_dir);
3838 				saw_error = TRUE;
3839 				return;
3840 			}
3841 			err = zonecfg_add_ipd(handle, &in_progress_ipdtab);
3842 		} else {
3843 			err = zonecfg_modify_ipd(handle, &old_ipdtab,
3844 			    &in_progress_ipdtab);
3845 		}
3846 		break;
3847 	case RT_NET:
3848 		/* First make sure everything was filled in. */
3849 		(void) end_check_reqd(in_progress_nwiftab.zone_nwif_physical,
3850 		    PT_PHYSICAL, &validation_failed);
3851 		(void) end_check_reqd(in_progress_nwiftab.zone_nwif_address,
3852 		    PT_ADDRESS, &validation_failed);
3853 
3854 		if (validation_failed) {
3855 			saw_error = TRUE;
3856 			return;
3857 		}
3858 
3859 		if (end_op == CMD_ADD) {
3860 			/* Make sure there isn't already one like this. */
3861 			bzero(&tmp_nwiftab, sizeof (tmp_nwiftab));
3862 			(void) strlcpy(tmp_nwiftab.zone_nwif_address,
3863 			    in_progress_nwiftab.zone_nwif_address,
3864 			    sizeof (tmp_nwiftab.zone_nwif_address));
3865 			if (zonecfg_lookup_nwif(handle, &tmp_nwiftab) == Z_OK) {
3866 				zerr(gettext("A %s resource "
3867 				    "with the %s '%s' already exists."),
3868 				    rt_to_str(RT_NET), pt_to_str(PT_ADDRESS),
3869 				    in_progress_nwiftab.zone_nwif_address);
3870 				saw_error = TRUE;
3871 				return;
3872 			}
3873 			err = zonecfg_add_nwif(handle, &in_progress_nwiftab);
3874 		} else {
3875 			err = zonecfg_modify_nwif(handle, &old_nwiftab,
3876 			    &in_progress_nwiftab);
3877 		}
3878 		break;
3879 
3880 	case RT_DEVICE:
3881 		/* First make sure everything was filled in. */
3882 		(void) end_check_reqd(in_progress_devtab.zone_dev_match,
3883 		    PT_MATCH, &validation_failed);
3884 
3885 		if (validation_failed) {
3886 			saw_error = TRUE;
3887 			return;
3888 		}
3889 
3890 		if (end_op == CMD_ADD) {
3891 			/* Make sure there isn't already one like this. */
3892 			(void) strlcpy(tmp_devtab.zone_dev_match,
3893 			    in_progress_devtab.zone_dev_match,
3894 			    sizeof (tmp_devtab.zone_dev_match));
3895 			if (zonecfg_lookup_dev(handle, &tmp_devtab) == Z_OK) {
3896 				zerr(gettext("A %s resource with the %s '%s' "
3897 				    "already exists."), rt_to_str(RT_DEVICE),
3898 				    pt_to_str(PT_MATCH),
3899 				    in_progress_devtab.zone_dev_match);
3900 				saw_error = TRUE;
3901 				return;
3902 			}
3903 			err = zonecfg_add_dev(handle, &in_progress_devtab);
3904 		} else {
3905 			err = zonecfg_modify_dev(handle, &old_devtab,
3906 			    &in_progress_devtab);
3907 		}
3908 		break;
3909 
3910 	case RT_RCTL:
3911 		/* First make sure everything was filled in. */
3912 		(void) end_check_reqd(in_progress_rctltab.zone_rctl_name,
3913 		    PT_NAME, &validation_failed);
3914 
3915 		if (in_progress_rctltab.zone_rctl_valptr == NULL) {
3916 			zerr(gettext("no %s specified"), pt_to_str(PT_VALUE));
3917 			validation_failed = TRUE;
3918 		}
3919 
3920 		if (validation_failed) {
3921 			saw_error = TRUE;
3922 			return;
3923 		}
3924 
3925 		if (end_op == CMD_ADD) {
3926 			/* Make sure there isn't already one like this. */
3927 			(void) strlcpy(tmp_rctltab.zone_rctl_name,
3928 			    in_progress_rctltab.zone_rctl_name,
3929 			    sizeof (tmp_rctltab.zone_rctl_name));
3930 			tmp_rctltab.zone_rctl_valptr = NULL;
3931 			err = zonecfg_lookup_rctl(handle, &tmp_rctltab);
3932 			zonecfg_free_rctl_value_list(
3933 			    tmp_rctltab.zone_rctl_valptr);
3934 			if (err == Z_OK) {
3935 				zerr(gettext("A %s resource "
3936 				    "with the %s '%s' already exists."),
3937 				    rt_to_str(RT_RCTL), pt_to_str(PT_NAME),
3938 				    in_progress_rctltab.zone_rctl_name);
3939 				saw_error = TRUE;
3940 				return;
3941 			}
3942 			err = zonecfg_add_rctl(handle, &in_progress_rctltab);
3943 		} else {
3944 			err = zonecfg_modify_rctl(handle, &old_rctltab,
3945 			    &in_progress_rctltab);
3946 		}
3947 		if (err == Z_OK) {
3948 			zonecfg_free_rctl_value_list(
3949 			    in_progress_rctltab.zone_rctl_valptr);
3950 			in_progress_rctltab.zone_rctl_valptr = NULL;
3951 		}
3952 		break;
3953 
3954 	case RT_ATTR:
3955 		/* First make sure everything was filled in. */
3956 		(void) end_check_reqd(in_progress_attrtab.zone_attr_name,
3957 		    PT_NAME, &validation_failed);
3958 		(void) end_check_reqd(in_progress_attrtab.zone_attr_type,
3959 		    PT_TYPE, &validation_failed);
3960 		(void) end_check_reqd(in_progress_attrtab.zone_attr_value,
3961 		    PT_VALUE, &validation_failed);
3962 
3963 		if (validate_attr_name(in_progress_attrtab.zone_attr_name) !=
3964 		    Z_OK)
3965 			validation_failed = TRUE;
3966 
3967 		if (validate_attr_type_val(&in_progress_attrtab) != Z_OK)
3968 			validation_failed = TRUE;
3969 
3970 		if (validation_failed) {
3971 			saw_error = TRUE;
3972 			return;
3973 		}
3974 		if (end_op == CMD_ADD) {
3975 			/* Make sure there isn't already one like this. */
3976 			bzero(&tmp_attrtab, sizeof (tmp_attrtab));
3977 			(void) strlcpy(tmp_attrtab.zone_attr_name,
3978 			    in_progress_attrtab.zone_attr_name,
3979 			    sizeof (tmp_attrtab.zone_attr_name));
3980 			if (zonecfg_lookup_attr(handle, &tmp_attrtab) == Z_OK) {
3981 				zerr(gettext("An %s resource "
3982 				    "with the %s '%s' already exists."),
3983 				    rt_to_str(RT_ATTR), pt_to_str(PT_NAME),
3984 				    in_progress_attrtab.zone_attr_name);
3985 				saw_error = TRUE;
3986 				return;
3987 			}
3988 			err = zonecfg_add_attr(handle, &in_progress_attrtab);
3989 		} else {
3990 			err = zonecfg_modify_attr(handle, &old_attrtab,
3991 			    &in_progress_attrtab);
3992 		}
3993 		break;
3994 	case RT_DATASET:
3995 		/* First make sure everything was filled in. */
3996 		if (strlen(in_progress_dstab.zone_dataset_name) == 0) {
3997 			zerr("%s %s", pt_to_str(PT_NAME),
3998 			    gettext("not specified"));
3999 			saw_error = TRUE;
4000 			validation_failed = TRUE;
4001 		}
4002 		if (validation_failed)
4003 			return;
4004 		if (end_op == CMD_ADD) {
4005 			/* Make sure there isn't already one like this. */
4006 			bzero(&tmp_dstab, sizeof (tmp_dstab));
4007 			(void) strlcpy(tmp_dstab.zone_dataset_name,
4008 			    in_progress_dstab.zone_dataset_name,
4009 			    sizeof (tmp_dstab.zone_dataset_name));
4010 			err = zonecfg_lookup_ds(handle, &tmp_dstab);
4011 			if (err == Z_OK) {
4012 				zerr(gettext("A %s resource "
4013 				    "with the %s '%s' already exists."),
4014 				    rt_to_str(RT_DATASET), pt_to_str(PT_NAME),
4015 				    in_progress_dstab.zone_dataset_name);
4016 				saw_error = TRUE;
4017 				return;
4018 			}
4019 			err = zonecfg_add_ds(handle, &in_progress_dstab);
4020 		} else {
4021 			err = zonecfg_modify_ds(handle, &old_dstab,
4022 			    &in_progress_dstab);
4023 		}
4024 		break;
4025 	default:
4026 		zone_perror(rt_to_str(resource_scope), Z_NO_RESOURCE_TYPE,
4027 		    TRUE);
4028 		saw_error = TRUE;
4029 		return;
4030 	}
4031 
4032 	if (err != Z_OK) {
4033 		zone_perror(zone, err, TRUE);
4034 	} else {
4035 		need_to_commit = TRUE;
4036 		global_scope = TRUE;
4037 		end_op = -1;
4038 	}
4039 }
4040 
4041 void
4042 commit_func(cmd_t *cmd)
4043 {
4044 	int arg;
4045 
4046 	optind = 0;
4047 	if ((arg = getopt(cmd->cmd_argc, cmd->cmd_argv, "?")) != EOF) {
4048 		switch (arg) {
4049 		case '?':
4050 			longer_usage(CMD_COMMIT);
4051 			return;
4052 		default:
4053 			short_usage(CMD_COMMIT);
4054 			return;
4055 		}
4056 	}
4057 	if (optind != cmd->cmd_argc) {
4058 		short_usage(CMD_COMMIT);
4059 		return;
4060 	}
4061 
4062 	if (zone_is_read_only(CMD_COMMIT))
4063 		return;
4064 
4065 	assert(cmd != NULL);
4066 
4067 	cmd->cmd_argc = 1;
4068 	/*
4069 	 * cmd_arg normally comes from a strdup() in the lexer, and the
4070 	 * whole cmd structure and its (char *) attributes are freed at
4071 	 * the completion of each command, so the strdup() below is needed
4072 	 * to match this and prevent a core dump from trying to free()
4073 	 * something that can't be.
4074 	 */
4075 	if ((cmd->cmd_argv[0] = strdup("save")) == NULL) {
4076 		zone_perror(zone, Z_NOMEM, TRUE);
4077 		exit(Z_ERR);
4078 	}
4079 	cmd->cmd_argv[1] = NULL;
4080 	verify_func(cmd);
4081 }
4082 
4083 void
4084 revert_func(cmd_t *cmd)
4085 {
4086 	char line[128];	/* enough to ask a question */
4087 	bool force = FALSE;
4088 	int err, arg, answer;
4089 
4090 	optind = 0;
4091 	while ((arg = getopt(cmd->cmd_argc, cmd->cmd_argv, "?F")) != EOF) {
4092 		switch (arg) {
4093 		case '?':
4094 			longer_usage(CMD_REVERT);
4095 			return;
4096 		case 'F':
4097 			force = TRUE;
4098 			break;
4099 		default:
4100 			short_usage(CMD_REVERT);
4101 			return;
4102 		}
4103 	}
4104 	if (optind != cmd->cmd_argc) {
4105 		short_usage(CMD_REVERT);
4106 		return;
4107 	}
4108 
4109 	if (zone_is_read_only(CMD_REVERT))
4110 		return;
4111 
4112 	if (zonecfg_check_handle(handle) != Z_OK) {
4113 		zerr(gettext("No changes to revert."));
4114 		saw_error = TRUE;
4115 		return;
4116 	}
4117 
4118 	if (!force) {
4119 		(void) snprintf(line, sizeof (line),
4120 		    gettext("Are you sure you want to revert"));
4121 		if ((answer = ask_yesno(FALSE, line)) == -1) {
4122 			zerr(gettext("Input not from terminal and -F not "
4123 			    "specified:\n%s command ignored, exiting."),
4124 			    cmd_to_str(CMD_REVERT));
4125 			exit(Z_ERR);
4126 		}
4127 		if (answer != 1)
4128 			return;
4129 	}
4130 
4131 	/*
4132 	 * Time for a new handle: finish the old one off first
4133 	 * then get a new one properly to avoid leaks.
4134 	 */
4135 	zonecfg_fini_handle(handle);
4136 	if ((handle = zonecfg_init_handle()) == NULL) {
4137 		zone_perror(execname, Z_NOMEM, TRUE);
4138 		exit(Z_ERR);
4139 	}
4140 	if ((err = zonecfg_get_handle(revert_zone, handle)) != Z_OK) {
4141 		saw_error = TRUE;
4142 		got_handle = FALSE;
4143 		if (err == Z_NO_ZONE)
4144 			zerr(gettext("%s: no such saved zone to revert to."),
4145 			    revert_zone);
4146 		else
4147 			zone_perror(zone, err, TRUE);
4148 	}
4149 	(void) strlcpy(zone, revert_zone, sizeof (zone));
4150 }
4151 
4152 void
4153 help_func(cmd_t *cmd)
4154 {
4155 	int i;
4156 
4157 	assert(cmd != NULL);
4158 
4159 	if (cmd->cmd_argc == 0) {
4160 		usage(TRUE, global_scope ? HELP_SUBCMDS : HELP_RES_SCOPE);
4161 		return;
4162 	}
4163 	if (strcmp(cmd->cmd_argv[0], "usage") == 0) {
4164 		usage(TRUE, HELP_USAGE);
4165 		return;
4166 	}
4167 	if (strcmp(cmd->cmd_argv[0], "commands") == 0) {
4168 		usage(TRUE, HELP_SUBCMDS);
4169 		return;
4170 	}
4171 	if (strcmp(cmd->cmd_argv[0], "syntax") == 0) {
4172 		usage(TRUE, HELP_SYNTAX | HELP_RES_PROPS);
4173 		return;
4174 	}
4175 	if (strcmp(cmd->cmd_argv[0], "-?") == 0) {
4176 		longer_usage(CMD_HELP);
4177 		return;
4178 	}
4179 
4180 	for (i = 0; i <= CMD_MAX; i++) {
4181 		if (strcmp(cmd->cmd_argv[0], cmd_to_str(i)) == 0) {
4182 			longer_usage(i);
4183 			return;
4184 		}
4185 	}
4186 	/* We do not use zerr() here because we do not want its extra \n. */
4187 	(void) fprintf(stderr, gettext("Unknown help subject %s.  "),
4188 	    cmd->cmd_argv[0]);
4189 	usage(FALSE, HELP_META);
4190 }
4191 
4192 static int
4193 string_to_yyin(char *string)
4194 {
4195 	if ((yyin = tmpfile()) == NULL) {
4196 		zone_perror(execname, Z_TEMP_FILE, TRUE);
4197 		return (Z_ERR);
4198 	}
4199 	if (fwrite(string, strlen(string), 1, yyin) != 1) {
4200 		zone_perror(execname, Z_TEMP_FILE, TRUE);
4201 		return (Z_ERR);
4202 	}
4203 	if (fseek(yyin, 0, SEEK_SET) != 0) {
4204 		zone_perror(execname, Z_TEMP_FILE, TRUE);
4205 		return (Z_ERR);
4206 	}
4207 	return (Z_OK);
4208 }
4209 
4210 /* This is the back-end helper function for read_input() below. */
4211 
4212 static int
4213 cleanup()
4214 {
4215 	int answer;
4216 	cmd_t *cmd;
4217 
4218 	if (!interactive_mode && !cmd_file_mode) {
4219 		/*
4220 		 * If we're not in interactive mode, and we're not in command
4221 		 * file mode, then we must be in commands-from-the-command-line
4222 		 * mode.  As such, we can't loop back and ask for more input.
4223 		 * It was OK to prompt for such things as whether or not to
4224 		 * really delete a zone in the command handler called from
4225 		 * yyparse() above, but "really quit?" makes no sense in this
4226 		 * context.  So disable prompting.
4227 		 */
4228 		ok_to_prompt = FALSE;
4229 	}
4230 	if (!global_scope) {
4231 		if (!time_to_exit) {
4232 			/*
4233 			 * Just print a simple error message in the -1 case,
4234 			 * since exit_func() already handles that case, and
4235 			 * EOF means we are finished anyway.
4236 			 */
4237 			answer = ask_yesno(FALSE,
4238 			    gettext("Resource incomplete; really quit"));
4239 			if (answer == -1) {
4240 				zerr(gettext("Resource incomplete."));
4241 				return (Z_ERR);
4242 			}
4243 			if (answer != 1) {
4244 				yyin = stdin;
4245 				return (Z_REPEAT);
4246 			}
4247 		} else {
4248 			saw_error = TRUE;
4249 		}
4250 	}
4251 	/*
4252 	 * Make sure we tried something and that the handle checks
4253 	 * out, or we would get a false error trying to commit.
4254 	 */
4255 	if (need_to_commit && zonecfg_check_handle(handle) == Z_OK) {
4256 		if ((cmd = alloc_cmd()) == NULL) {
4257 			zone_perror(zone, Z_NOMEM, TRUE);
4258 			return (Z_ERR);
4259 		}
4260 		cmd->cmd_argc = 0;
4261 		cmd->cmd_argv[0] = NULL;
4262 		commit_func(cmd);
4263 		free_cmd(cmd);
4264 		/*
4265 		 * need_to_commit will get set back to FALSE if the
4266 		 * configuration is saved successfully.
4267 		 */
4268 		if (need_to_commit) {
4269 			if (force_exit) {
4270 				zerr(gettext("Configuration not saved."));
4271 				return (Z_ERR);
4272 			}
4273 			answer = ask_yesno(FALSE,
4274 			    gettext("Configuration not saved; really quit"));
4275 			if (answer == -1) {
4276 				zerr(gettext("Configuration not saved."));
4277 				return (Z_ERR);
4278 			}
4279 			if (answer != 1) {
4280 				time_to_exit = FALSE;
4281 				yyin = stdin;
4282 				return (Z_REPEAT);
4283 			}
4284 		}
4285 	}
4286 	return ((need_to_commit || saw_error) ? Z_ERR : Z_OK);
4287 }
4288 
4289 /*
4290  * read_input() is the driver of this program.  It is a wrapper around
4291  * yyparse(), printing appropriate prompts when needed, checking for
4292  * exit conditions and reacting appropriately [the latter in its cleanup()
4293  * helper function].
4294  *
4295  * Like most zonecfg functions, it returns Z_OK or Z_ERR, *or* Z_REPEAT
4296  * so do_interactive() knows that we are not really done (i.e, we asked
4297  * the user if we should really quit and the user said no).
4298  */
4299 static int
4300 read_input()
4301 {
4302 	bool yyin_is_a_tty = isatty(fileno(yyin));
4303 	/*
4304 	 * The prompt is "e:z> " or "e:z:r> " where e is execname, z is zone
4305 	 * and r is resource_scope: 5 is for the two ":"s + "> " + terminator.
4306 	 */
4307 	char prompt[MAXPATHLEN + ZONENAME_MAX + MAX_RT_STRLEN + 5], *line;
4308 
4309 	/* yyin should have been set to the appropriate (FILE *) if not stdin */
4310 	newline_terminated = TRUE;
4311 	for (;;) {
4312 		if (yyin_is_a_tty) {
4313 			if (newline_terminated) {
4314 				if (global_scope)
4315 					(void) snprintf(prompt, sizeof (prompt),
4316 					    "%s:%s> ", execname, zone);
4317 				else
4318 					(void) snprintf(prompt, sizeof (prompt),
4319 					    "%s:%s:%s> ", execname, zone,
4320 					    rt_to_str(resource_scope));
4321 			}
4322 			/*
4323 			 * If the user hits ^C then we want to catch it and
4324 			 * start over.  If the user hits EOF then we want to
4325 			 * bail out.
4326 			 */
4327 			line = gl_get_line(gl, prompt, NULL, -1);
4328 			if (gl_return_status(gl) == GLR_SIGNAL) {
4329 				gl_abandon_line(gl);
4330 				continue;
4331 			}
4332 			if (line == NULL)
4333 				break;
4334 			(void) string_to_yyin(line);
4335 			while (!feof(yyin))
4336 				yyparse();
4337 		} else {
4338 			yyparse();
4339 		}
4340 		/* Bail out on an error in command file mode. */
4341 		if (saw_error && cmd_file_mode && !interactive_mode)
4342 			time_to_exit = TRUE;
4343 		if (time_to_exit || (!yyin_is_a_tty && feof(yyin)))
4344 			break;
4345 	}
4346 	return (cleanup());
4347 }
4348 
4349 /*
4350  * This function is used in the zonecfg-interactive-mode scenario: it just
4351  * calls read_input() until we are done.
4352  */
4353 
4354 static int
4355 do_interactive(void)
4356 {
4357 	int err;
4358 
4359 	interactive_mode = TRUE;
4360 	if (!read_only_mode) {
4361 		/*
4362 		 * Try to set things up proactively in interactive mode, so
4363 		 * that if the zone in question does not exist yet, we can
4364 		 * provide the user with a clue.
4365 		 */
4366 		(void) initialize(FALSE);
4367 	}
4368 	do {
4369 		err = read_input();
4370 	} while (err == Z_REPEAT);
4371 	return (err);
4372 }
4373 
4374 /*
4375  * cmd_file is slightly more complicated, as it has to open the command file
4376  * and set yyin appropriately.  Once that is done, though, it just calls
4377  * read_input(), and only once, since prompting is not possible.
4378  */
4379 
4380 static int
4381 cmd_file(char *file)
4382 {
4383 	FILE *infile;
4384 	int err;
4385 	struct stat statbuf;
4386 	bool using_real_file = (strcmp(file, "-") != 0);
4387 
4388 	if (using_real_file) {
4389 		/*
4390 		 * zerr() prints a line number in cmd_file_mode, which we do
4391 		 * not want here, so temporarily unset it.
4392 		 */
4393 		cmd_file_mode = FALSE;
4394 		if ((infile = fopen(file, "r")) == NULL) {
4395 			zerr(gettext("could not open file %s: %s"),
4396 			    file, strerror(errno));
4397 			return (Z_ERR);
4398 		}
4399 		if ((err = fstat(fileno(infile), &statbuf)) != 0) {
4400 			zerr(gettext("could not stat file %s: %s"),
4401 			    file, strerror(errno));
4402 			err = Z_ERR;
4403 			goto done;
4404 		}
4405 		if (!S_ISREG(statbuf.st_mode)) {
4406 			zerr(gettext("%s is not a regular file."), file);
4407 			err = Z_ERR;
4408 			goto done;
4409 		}
4410 		yyin = infile;
4411 		cmd_file_mode = TRUE;
4412 		ok_to_prompt = FALSE;
4413 	} else {
4414 		/*
4415 		 * "-f -" is essentially the same as interactive mode,
4416 		 * so treat it that way.
4417 		 */
4418 		interactive_mode = TRUE;
4419 	}
4420 	/* Z_REPEAT is for interactive mode; treat it like Z_ERR here. */
4421 	if ((err = read_input()) == Z_REPEAT)
4422 		err = Z_ERR;
4423 done:
4424 	if (using_real_file)
4425 		(void) fclose(infile);
4426 	return (err);
4427 }
4428 
4429 /*
4430  * Since yacc is based on reading from a (FILE *) whereas what we get from
4431  * the command line is in argv format, we need to convert when the user
4432  * gives us commands directly from the command line.  That is done here by
4433  * concatenating the argv list into a space-separated string, writing it
4434  * to a temp file, and rewinding the file so yyin can be set to it.  Then
4435  * we call read_input(), and only once, since prompting about whether to
4436  * continue or quit would make no sense in this context.
4437  */
4438 
4439 static int
4440 one_command_at_a_time(int argc, char *argv[])
4441 {
4442 	char *command;
4443 	size_t len = 2; /* terminal \n\0 */
4444 	int i, err;
4445 
4446 	for (i = 0; i < argc; i++)
4447 		len += strlen(argv[i]) + 1;
4448 	if ((command = malloc(len)) == NULL) {
4449 		zone_perror(execname, Z_NOMEM, TRUE);
4450 		return (Z_ERR);
4451 	}
4452 	(void) strlcpy(command, argv[0], len);
4453 	for (i = 1; i < argc; i++) {
4454 		(void) strlcat(command, " ", len);
4455 		(void) strlcat(command, argv[i], len);
4456 	}
4457 	(void) strlcat(command, "\n", len);
4458 	err = string_to_yyin(command);
4459 	free(command);
4460 	if (err != Z_OK)
4461 		return (err);
4462 	while (!feof(yyin))
4463 		yyparse();
4464 	return (cleanup());
4465 }
4466 
4467 static char *
4468 get_execbasename(char *execfullname)
4469 {
4470 	char *last_slash, *execbasename;
4471 
4472 	/* guard against '/' at end of command invocation */
4473 	for (;;) {
4474 		last_slash = strrchr(execfullname, '/');
4475 		if (last_slash == NULL) {
4476 			execbasename = execfullname;
4477 			break;
4478 		} else {
4479 			execbasename = last_slash + 1;
4480 			if (*execbasename == '\0') {
4481 				*last_slash = '\0';
4482 				continue;
4483 			}
4484 			break;
4485 		}
4486 	}
4487 	return (execbasename);
4488 }
4489 
4490 int
4491 main(int argc, char *argv[])
4492 {
4493 	int err, arg;
4494 
4495 	/* This must be before anything goes to stdout. */
4496 	setbuf(stdout, NULL);
4497 
4498 	saw_error = FALSE;
4499 	cmd_file_mode = FALSE;
4500 	execname = get_execbasename(argv[0]);
4501 
4502 	(void) setlocale(LC_ALL, "");
4503 	(void) textdomain(TEXT_DOMAIN);
4504 
4505 	if (getzoneid() != GLOBAL_ZONEID) {
4506 		zerr(gettext("%s can only be run from the global zone."),
4507 		    execname);
4508 		exit(Z_ERR);
4509 	}
4510 
4511 	if (argc < 2) {
4512 		usage(FALSE, HELP_USAGE | HELP_SUBCMDS);
4513 		exit(Z_USAGE);
4514 	}
4515 	if (strcmp(argv[1], cmd_to_str(CMD_HELP)) == 0) {
4516 		(void) one_command_at_a_time(argc - 1, &(argv[1]));
4517 		exit(Z_OK);
4518 	}
4519 
4520 	while ((arg = getopt(argc, argv, "?f:z:")) != EOF) {
4521 		switch (arg) {
4522 		case '?':
4523 			if (optopt == '?')
4524 				usage(TRUE, HELP_USAGE | HELP_SUBCMDS);
4525 			else
4526 				usage(FALSE, HELP_USAGE);
4527 			exit(Z_USAGE);
4528 			/* NOTREACHED */
4529 		case 'f':
4530 			cmd_file_name = optarg;
4531 			cmd_file_mode = TRUE;
4532 			break;
4533 		case 'z':
4534 			if (zonecfg_validate_zonename(optarg) != Z_OK) {
4535 				zone_perror(optarg, Z_BOGUS_ZONE_NAME, TRUE);
4536 				usage(FALSE, HELP_SYNTAX);
4537 				exit(Z_USAGE);
4538 			}
4539 			(void) strlcpy(zone, optarg, sizeof (zone));
4540 			(void) strlcpy(revert_zone, optarg, sizeof (zone));
4541 			break;
4542 		default:
4543 			usage(FALSE, HELP_USAGE);
4544 			exit(Z_USAGE);
4545 		}
4546 	}
4547 
4548 	if (optind > argc || strcmp(zone, "") == 0) {
4549 		usage(FALSE, HELP_USAGE);
4550 		exit(Z_USAGE);
4551 	}
4552 
4553 	if ((err = zonecfg_access(zone, W_OK)) == Z_OK) {
4554 		read_only_mode = FALSE;
4555 	} else if (err == Z_ACCES) {
4556 		read_only_mode = TRUE;
4557 		/* skip this message in one-off from command line mode */
4558 		if (optind == argc)
4559 			(void) fprintf(stderr, gettext("WARNING: you do not "
4560 			    "have write access to this zone's configuration "
4561 			    "file;\ngoing into read-only mode.\n"));
4562 	} else {
4563 		fprintf(stderr, "%s: Could not access zone configuration "
4564 		    "store: %s\n", execname, zonecfg_strerror(err));
4565 		exit(Z_ERR);
4566 	}
4567 
4568 	if ((handle = zonecfg_init_handle()) == NULL) {
4569 		zone_perror(execname, Z_NOMEM, TRUE);
4570 		exit(Z_ERR);
4571 	}
4572 
4573 	/*
4574 	 * This may get set back to FALSE again in cmd_file() if cmd_file_name
4575 	 * is a "real" file as opposed to "-" (i.e. meaning use stdin).
4576 	 */
4577 	if (isatty(STDIN_FILENO))
4578 		ok_to_prompt = TRUE;
4579 	if ((gl = new_GetLine(MAX_LINE_LEN, MAX_CMD_HIST)) == NULL)
4580 		exit(Z_ERR);
4581 	if (gl_customize_completion(gl, NULL, cmd_cpl_fn) != 0)
4582 		exit(Z_ERR);
4583 	(void) sigset(SIGINT, SIG_IGN);
4584 	if (optind == argc) {
4585 		if (!cmd_file_mode)
4586 			err = do_interactive();
4587 		else
4588 			err = cmd_file(cmd_file_name);
4589 	} else {
4590 		err = one_command_at_a_time(argc - optind, &(argv[optind]));
4591 	}
4592 	zonecfg_fini_handle(handle);
4593 	(void) del_GetLine(gl);
4594 	return (err);
4595 }
4596