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