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