xref: /titanic_51/usr/src/cmd/zonecfg/zonecfg.c (revision d4188195113bc7f546b026033c66ea3e12de0e02)
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[32];
3691 	char cmdbuf[MAX_CMD_LEN];
3692 	brand_handle_t bh;
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 ((bh = 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(bh, cmdbuf + EXEC_LEN,
3711 	    sizeof (cmdbuf) - (EXEC_LEN + (strlen(xml_file) + 1)));
3712 	brand_close(bh);
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 	strcpy(xml_file, "/tmp/zonecfg_verify.XXXXXX");
3730 	if (mkstemp(xml_file) == NULL)
3731 		return (Z_TEMP_FILE);
3732 	if ((err = zonecfg_verify_save(handle, xml_file)) != Z_OK) {
3733 		(void) unlink(xml_file);
3734 		return (err);
3735 	}
3736 
3737 	/*
3738 	 * Execute the verification command.
3739 	 */
3740 	if ((strlcat(cmdbuf, " ", MAX_CMD_LEN) >= MAX_CMD_LEN) ||
3741 	    (strlcat(cmdbuf, xml_file, MAX_CMD_LEN) >= MAX_CMD_LEN)) {
3742 		err = Z_BRAND_ERROR;
3743 	} else {
3744 		err = do_subproc(cmdbuf);
3745 	}
3746 
3747 	(void) unlink(xml_file);
3748 	return ((err == Z_OK) ? Z_OK : Z_BRAND_ERROR);
3749 }
3750 
3751 /*
3752  * See the DTD for which attributes are required for which resources.
3753  *
3754  * This function can be called by commit_func(), which needs to save things,
3755  * in addition to the general call from parse_and_run(), which doesn't need
3756  * things saved.  Since the parameters are standardized, we distinguish by
3757  * having commit_func() call here with cmd->cmd_arg set to "save" to indicate
3758  * that a save is needed.
3759  */
3760 void
3761 verify_func(cmd_t *cmd)
3762 {
3763 	struct zone_nwiftab nwiftab;
3764 	struct zone_fstab fstab;
3765 	struct zone_attrtab attrtab;
3766 	struct zone_rctltab rctltab;
3767 	struct zone_dstab dstab;
3768 	char zonepath[MAXPATHLEN];
3769 	char brand[MAXNAMELEN];
3770 	int err, ret_val = Z_OK, arg;
3771 	bool save = FALSE;
3772 
3773 	optind = 0;
3774 	if ((arg = getopt(cmd->cmd_argc, cmd->cmd_argv, "?")) != EOF) {
3775 		switch (arg) {
3776 		case '?':
3777 			longer_usage(CMD_VERIFY);
3778 			return;
3779 		default:
3780 			short_usage(CMD_VERIFY);
3781 			return;
3782 		}
3783 	}
3784 	if (optind > cmd->cmd_argc) {
3785 		short_usage(CMD_VERIFY);
3786 		return;
3787 	}
3788 
3789 	if (zone_is_read_only(CMD_VERIFY))
3790 		return;
3791 
3792 	assert(cmd != NULL);
3793 
3794 	if (cmd->cmd_argc > 0 && (strcmp(cmd->cmd_argv[0], "save") == 0))
3795 		save = TRUE;
3796 	if (initialize(TRUE) != Z_OK)
3797 		return;
3798 
3799 	if (zonecfg_get_zonepath(handle, zonepath, sizeof (zonepath)) != Z_OK) {
3800 		zerr(gettext("%s not specified"), pt_to_str(PT_ZONEPATH));
3801 		ret_val = Z_REQD_RESOURCE_MISSING;
3802 		saw_error = TRUE;
3803 	}
3804 	if (strlen(zonepath) == 0) {
3805 		zerr(gettext("%s cannot be empty."), pt_to_str(PT_ZONEPATH));
3806 		ret_val = Z_REQD_RESOURCE_MISSING;
3807 		saw_error = TRUE;
3808 	}
3809 
3810 	if ((err = zonecfg_get_brand(handle, brand, sizeof (brand))) != Z_OK) {
3811 		zone_perror(zone, err, TRUE);
3812 		return;
3813 	}
3814 	if (strcmp(brand, NATIVE_BRAND_NAME) != 0) {
3815 		if ((err = brand_verify(handle)) != Z_OK) {
3816 			zone_perror(zone, err, TRUE);
3817 			return;
3818 		}
3819 	}
3820 
3821 	if ((err = zonecfg_setipdent(handle)) != Z_OK) {
3822 		zone_perror(zone, err, TRUE);
3823 		return;
3824 	}
3825 	while (zonecfg_getipdent(handle, &fstab) == Z_OK) {
3826 		check_reqd_prop(fstab.zone_fs_dir, RT_IPD, PT_DIR, &ret_val);
3827 	}
3828 	(void) zonecfg_endipdent(handle);
3829 
3830 	if ((err = zonecfg_setfsent(handle)) != Z_OK) {
3831 		zone_perror(zone, err, TRUE);
3832 		return;
3833 	}
3834 	while (zonecfg_getfsent(handle, &fstab) == Z_OK) {
3835 		check_reqd_prop(fstab.zone_fs_dir, RT_FS, PT_DIR, &ret_val);
3836 		check_reqd_prop(fstab.zone_fs_special, RT_FS, PT_SPECIAL,
3837 		    &ret_val);
3838 		check_reqd_prop(fstab.zone_fs_type, RT_FS, PT_TYPE, &ret_val);
3839 
3840 		zonecfg_free_fs_option_list(fstab.zone_fs_options);
3841 	}
3842 	(void) zonecfg_endfsent(handle);
3843 
3844 	if ((err = zonecfg_setnwifent(handle)) != Z_OK) {
3845 		zone_perror(zone, err, TRUE);
3846 		return;
3847 	}
3848 	while (zonecfg_getnwifent(handle, &nwiftab) == Z_OK) {
3849 		check_reqd_prop(nwiftab.zone_nwif_address, RT_NET,
3850 		    PT_ADDRESS, &ret_val);
3851 		check_reqd_prop(nwiftab.zone_nwif_physical, RT_NET,
3852 		    PT_PHYSICAL, &ret_val);
3853 	}
3854 	(void) zonecfg_endnwifent(handle);
3855 
3856 	if ((err = zonecfg_setrctlent(handle)) != Z_OK) {
3857 		zone_perror(zone, err, TRUE);
3858 		return;
3859 	}
3860 	while (zonecfg_getrctlent(handle, &rctltab) == Z_OK) {
3861 		check_reqd_prop(rctltab.zone_rctl_name, RT_RCTL, PT_NAME,
3862 		    &ret_val);
3863 
3864 		if (rctltab.zone_rctl_valptr == NULL) {
3865 			zerr(gettext("%s: no %s specified"),
3866 			    rt_to_str(RT_RCTL), pt_to_str(PT_VALUE));
3867 			saw_error = TRUE;
3868 			if (ret_val == Z_OK)
3869 				ret_val = Z_REQD_PROPERTY_MISSING;
3870 		} else {
3871 			zonecfg_free_rctl_value_list(rctltab.zone_rctl_valptr);
3872 		}
3873 	}
3874 	(void) zonecfg_endrctlent(handle);
3875 
3876 	if ((err = zonecfg_setattrent(handle)) != Z_OK) {
3877 		zone_perror(zone, err, TRUE);
3878 		return;
3879 	}
3880 	while (zonecfg_getattrent(handle, &attrtab) == Z_OK) {
3881 		check_reqd_prop(attrtab.zone_attr_name, RT_ATTR, PT_NAME,
3882 		    &ret_val);
3883 		check_reqd_prop(attrtab.zone_attr_type, RT_ATTR, PT_TYPE,
3884 		    &ret_val);
3885 		check_reqd_prop(attrtab.zone_attr_value, RT_ATTR, PT_VALUE,
3886 		    &ret_val);
3887 	}
3888 	(void) zonecfg_endattrent(handle);
3889 
3890 	if ((err = zonecfg_setdsent(handle)) != Z_OK) {
3891 		zone_perror(zone, err, TRUE);
3892 		return;
3893 	}
3894 	while (zonecfg_getdsent(handle, &dstab) == Z_OK) {
3895 		if (strlen(dstab.zone_dataset_name) == 0) {
3896 			zerr("%s: %s %s", rt_to_str(RT_DATASET),
3897 			    pt_to_str(PT_NAME), gettext("not specified"));
3898 			saw_error = TRUE;
3899 			if (ret_val == Z_OK)
3900 				ret_val = Z_REQD_PROPERTY_MISSING;
3901 		} else if (!zfs_name_valid(dstab.zone_dataset_name,
3902 		    ZFS_TYPE_FILESYSTEM)) {
3903 			zerr("%s: %s %s", rt_to_str(RT_DATASET),
3904 			    pt_to_str(PT_NAME), gettext("invalid"));
3905 			saw_error = TRUE;
3906 			if (ret_val == Z_OK)
3907 				ret_val = Z_BAD_PROPERTY;
3908 		}
3909 
3910 	}
3911 	(void) zonecfg_enddsent(handle);
3912 
3913 	if (!global_scope) {
3914 		zerr(gettext("resource specification incomplete"));
3915 		saw_error = TRUE;
3916 		if (ret_val == Z_OK)
3917 			ret_val = Z_INSUFFICIENT_SPEC;
3918 	}
3919 
3920 	if (save) {
3921 		if (ret_val == Z_OK) {
3922 			if ((ret_val = zonecfg_save(handle)) == Z_OK) {
3923 				need_to_commit = FALSE;
3924 				(void) strlcpy(revert_zone, zone,
3925 				    sizeof (revert_zone));
3926 			}
3927 		} else {
3928 			zerr(gettext("Zone %s failed to verify"), zone);
3929 		}
3930 	}
3931 	if (ret_val != Z_OK)
3932 		zone_perror(zone, ret_val, TRUE);
3933 }
3934 
3935 void
3936 cancel_func(cmd_t *cmd)
3937 {
3938 	int arg;
3939 
3940 	assert(cmd != NULL);
3941 
3942 	optind = 0;
3943 	if ((arg = getopt(cmd->cmd_argc, cmd->cmd_argv, "?")) != EOF) {
3944 		switch (arg) {
3945 		case '?':
3946 			longer_usage(CMD_CANCEL);
3947 			return;
3948 		default:
3949 			short_usage(CMD_CANCEL);
3950 			return;
3951 		}
3952 	}
3953 	if (optind != cmd->cmd_argc) {
3954 		short_usage(CMD_CANCEL);
3955 		return;
3956 	}
3957 
3958 	if (global_scope)
3959 		scope_usage(CMD_CANCEL);
3960 	global_scope = TRUE;
3961 	zonecfg_free_fs_option_list(in_progress_fstab.zone_fs_options);
3962 	bzero(&in_progress_fstab, sizeof (in_progress_fstab));
3963 	bzero(&in_progress_nwiftab, sizeof (in_progress_nwiftab));
3964 	bzero(&in_progress_ipdtab, sizeof (in_progress_ipdtab));
3965 	bzero(&in_progress_devtab, sizeof (in_progress_devtab));
3966 	zonecfg_free_rctl_value_list(in_progress_rctltab.zone_rctl_valptr);
3967 	bzero(&in_progress_rctltab, sizeof (in_progress_rctltab));
3968 	bzero(&in_progress_attrtab, sizeof (in_progress_attrtab));
3969 	bzero(&in_progress_dstab, sizeof (in_progress_dstab));
3970 }
3971 
3972 static int
3973 validate_attr_name(char *name)
3974 {
3975 	int i;
3976 
3977 	if (!isalnum(name[0])) {
3978 		zerr(gettext("Invalid %s %s %s: must start with an alpha-"
3979 		    "numeric character."), rt_to_str(RT_ATTR),
3980 		    pt_to_str(PT_NAME), name);
3981 		return (Z_INVAL);
3982 	}
3983 	for (i = 1; name[i]; i++)
3984 		if (!isalnum(name[i]) && name[i] != '-' && name[i] != '.') {
3985 			zerr(gettext("Invalid %s %s %s: can only contain "
3986 			    "alpha-numeric characters, plus '-' and '.'."),
3987 			    rt_to_str(RT_ATTR), pt_to_str(PT_NAME), name);
3988 			return (Z_INVAL);
3989 		}
3990 	return (Z_OK);
3991 }
3992 
3993 static int
3994 validate_attr_type_val(struct zone_attrtab *attrtab)
3995 {
3996 	boolean_t boolval;
3997 	int64_t intval;
3998 	char strval[MAXNAMELEN];
3999 	uint64_t uintval;
4000 
4001 	if (strcmp(attrtab->zone_attr_type, "boolean") == 0) {
4002 		if (zonecfg_get_attr_boolean(attrtab, &boolval) == Z_OK)
4003 			return (Z_OK);
4004 		zerr(gettext("invalid %s value for %s=%s"),
4005 		    rt_to_str(RT_ATTR), pt_to_str(PT_TYPE), "boolean");
4006 		return (Z_ERR);
4007 	}
4008 
4009 	if (strcmp(attrtab->zone_attr_type, "int") == 0) {
4010 		if (zonecfg_get_attr_int(attrtab, &intval) == Z_OK)
4011 			return (Z_OK);
4012 		zerr(gettext("invalid %s value for %s=%s"),
4013 		    rt_to_str(RT_ATTR), pt_to_str(PT_TYPE), "int");
4014 		return (Z_ERR);
4015 	}
4016 
4017 	if (strcmp(attrtab->zone_attr_type, "string") == 0) {
4018 		if (zonecfg_get_attr_string(attrtab, strval,
4019 		    sizeof (strval)) == Z_OK)
4020 			return (Z_OK);
4021 		zerr(gettext("invalid %s value for %s=%s"),
4022 		    rt_to_str(RT_ATTR), pt_to_str(PT_TYPE), "string");
4023 		return (Z_ERR);
4024 	}
4025 
4026 	if (strcmp(attrtab->zone_attr_type, "uint") == 0) {
4027 		if (zonecfg_get_attr_uint(attrtab, &uintval) == Z_OK)
4028 			return (Z_OK);
4029 		zerr(gettext("invalid %s value for %s=%s"),
4030 		    rt_to_str(RT_ATTR), pt_to_str(PT_TYPE), "uint");
4031 		return (Z_ERR);
4032 	}
4033 
4034 	zerr(gettext("invalid %s %s '%s'"), rt_to_str(RT_ATTR),
4035 	    pt_to_str(PT_TYPE), attrtab->zone_attr_type);
4036 	return (Z_ERR);
4037 }
4038 
4039 /*
4040  * Helper function for end_func-- checks the existence of a given property
4041  * and emits a message if not specified.
4042  */
4043 static int
4044 end_check_reqd(char *attr, int pt, bool *validation_failed)
4045 {
4046 	if (strlen(attr) == 0) {
4047 		*validation_failed = TRUE;
4048 		zerr(gettext("%s not specified"), pt_to_str(pt));
4049 		return (Z_ERR);
4050 	}
4051 	return (Z_OK);
4052 }
4053 
4054 void
4055 end_func(cmd_t *cmd)
4056 {
4057 	bool validation_failed = FALSE;
4058 	struct zone_fstab tmp_fstab;
4059 	struct zone_nwiftab tmp_nwiftab;
4060 	struct zone_devtab tmp_devtab;
4061 	struct zone_rctltab tmp_rctltab;
4062 	struct zone_attrtab tmp_attrtab;
4063 	struct zone_dstab tmp_dstab;
4064 	int err, arg;
4065 
4066 	assert(cmd != NULL);
4067 
4068 	optind = 0;
4069 	if ((arg = getopt(cmd->cmd_argc, cmd->cmd_argv, "?")) != EOF) {
4070 		switch (arg) {
4071 		case '?':
4072 			longer_usage(CMD_END);
4073 			return;
4074 		default:
4075 			short_usage(CMD_END);
4076 			return;
4077 		}
4078 	}
4079 	if (optind != cmd->cmd_argc) {
4080 		short_usage(CMD_END);
4081 		return;
4082 	}
4083 
4084 	if (global_scope) {
4085 		scope_usage(CMD_END);
4086 		return;
4087 	}
4088 
4089 	assert(end_op == CMD_ADD || end_op == CMD_SELECT);
4090 
4091 	switch (resource_scope) {
4092 	case RT_FS:
4093 		/* First make sure everything was filled in. */
4094 		if (end_check_reqd(in_progress_fstab.zone_fs_dir,
4095 		    PT_DIR, &validation_failed) == Z_OK) {
4096 			if (in_progress_fstab.zone_fs_dir[0] != '/') {
4097 				zerr(gettext("%s %s is not an absolute path."),
4098 				    pt_to_str(PT_DIR),
4099 				    in_progress_fstab.zone_fs_dir);
4100 				validation_failed = TRUE;
4101 			}
4102 		}
4103 
4104 		(void) end_check_reqd(in_progress_fstab.zone_fs_special,
4105 		    PT_SPECIAL, &validation_failed);
4106 
4107 		if (in_progress_fstab.zone_fs_raw[0] != '\0' &&
4108 		    in_progress_fstab.zone_fs_raw[0] != '/') {
4109 			zerr(gettext("%s %s is not an absolute path."),
4110 			    pt_to_str(PT_RAW),
4111 			    in_progress_fstab.zone_fs_raw);
4112 			validation_failed = TRUE;
4113 		}
4114 
4115 		(void) end_check_reqd(in_progress_fstab.zone_fs_type, PT_TYPE,
4116 		    &validation_failed);
4117 
4118 		if (validation_failed) {
4119 			saw_error = TRUE;
4120 			return;
4121 		}
4122 
4123 		if (end_op == CMD_ADD) {
4124 			/* Make sure there isn't already one like this. */
4125 			bzero(&tmp_fstab, sizeof (tmp_fstab));
4126 			(void) strlcpy(tmp_fstab.zone_fs_dir,
4127 			    in_progress_fstab.zone_fs_dir,
4128 			    sizeof (tmp_fstab.zone_fs_dir));
4129 			err = zonecfg_lookup_filesystem(handle, &tmp_fstab);
4130 			zonecfg_free_fs_option_list(tmp_fstab.zone_fs_options);
4131 			if (err == Z_OK) {
4132 				zerr(gettext("A %s resource "
4133 				    "with the %s '%s' already exists."),
4134 				    rt_to_str(RT_FS), pt_to_str(PT_DIR),
4135 				    in_progress_fstab.zone_fs_dir);
4136 				saw_error = TRUE;
4137 				return;
4138 			}
4139 			err = zonecfg_add_filesystem(handle,
4140 			    &in_progress_fstab);
4141 		} else {
4142 			err = zonecfg_modify_filesystem(handle, &old_fstab,
4143 			    &in_progress_fstab);
4144 		}
4145 		zonecfg_free_fs_option_list(in_progress_fstab.zone_fs_options);
4146 		in_progress_fstab.zone_fs_options = NULL;
4147 		break;
4148 
4149 	case RT_IPD:
4150 		/* First make sure everything was filled in. */
4151 		if (end_check_reqd(in_progress_ipdtab.zone_fs_dir, PT_DIR,
4152 		    &validation_failed) == Z_OK) {
4153 			if (in_progress_ipdtab.zone_fs_dir[0] != '/') {
4154 				zerr(gettext("%s %s is not an absolute path."),
4155 				    pt_to_str(PT_DIR),
4156 				    in_progress_ipdtab.zone_fs_dir);
4157 				validation_failed = TRUE;
4158 			}
4159 		}
4160 		if (validation_failed) {
4161 			saw_error = TRUE;
4162 			return;
4163 		}
4164 
4165 		if (end_op == CMD_ADD) {
4166 			/* Make sure there isn't already one like this. */
4167 			bzero(&tmp_fstab, sizeof (tmp_fstab));
4168 			(void) strlcpy(tmp_fstab.zone_fs_dir,
4169 			    in_progress_ipdtab.zone_fs_dir,
4170 			    sizeof (tmp_fstab.zone_fs_dir));
4171 			err = zonecfg_lookup_ipd(handle, &tmp_fstab);
4172 			if (err == Z_OK) {
4173 				zerr(gettext("An %s resource "
4174 				    "with the %s '%s' already exists."),
4175 				    rt_to_str(RT_IPD), pt_to_str(PT_DIR),
4176 				    in_progress_ipdtab.zone_fs_dir);
4177 				saw_error = TRUE;
4178 				return;
4179 			}
4180 			err = zonecfg_add_ipd(handle, &in_progress_ipdtab);
4181 		} else {
4182 			err = zonecfg_modify_ipd(handle, &old_ipdtab,
4183 			    &in_progress_ipdtab);
4184 		}
4185 		break;
4186 	case RT_NET:
4187 		/* First make sure everything was filled in. */
4188 		(void) end_check_reqd(in_progress_nwiftab.zone_nwif_physical,
4189 		    PT_PHYSICAL, &validation_failed);
4190 		(void) end_check_reqd(in_progress_nwiftab.zone_nwif_address,
4191 		    PT_ADDRESS, &validation_failed);
4192 
4193 		if (validation_failed) {
4194 			saw_error = TRUE;
4195 			return;
4196 		}
4197 
4198 		if (end_op == CMD_ADD) {
4199 			/* Make sure there isn't already one like this. */
4200 			bzero(&tmp_nwiftab, sizeof (tmp_nwiftab));
4201 			(void) strlcpy(tmp_nwiftab.zone_nwif_address,
4202 			    in_progress_nwiftab.zone_nwif_address,
4203 			    sizeof (tmp_nwiftab.zone_nwif_address));
4204 			if (zonecfg_lookup_nwif(handle, &tmp_nwiftab) == Z_OK) {
4205 				zerr(gettext("A %s resource "
4206 				    "with the %s '%s' already exists."),
4207 				    rt_to_str(RT_NET), pt_to_str(PT_ADDRESS),
4208 				    in_progress_nwiftab.zone_nwif_address);
4209 				saw_error = TRUE;
4210 				return;
4211 			}
4212 			err = zonecfg_add_nwif(handle, &in_progress_nwiftab);
4213 		} else {
4214 			err = zonecfg_modify_nwif(handle, &old_nwiftab,
4215 			    &in_progress_nwiftab);
4216 		}
4217 		break;
4218 
4219 	case RT_DEVICE:
4220 		/* First make sure everything was filled in. */
4221 		(void) end_check_reqd(in_progress_devtab.zone_dev_match,
4222 		    PT_MATCH, &validation_failed);
4223 
4224 		if (validation_failed) {
4225 			saw_error = TRUE;
4226 			return;
4227 		}
4228 
4229 		if (end_op == CMD_ADD) {
4230 			/* Make sure there isn't already one like this. */
4231 			(void) strlcpy(tmp_devtab.zone_dev_match,
4232 			    in_progress_devtab.zone_dev_match,
4233 			    sizeof (tmp_devtab.zone_dev_match));
4234 			if (zonecfg_lookup_dev(handle, &tmp_devtab) == Z_OK) {
4235 				zerr(gettext("A %s resource with the %s '%s' "
4236 				    "already exists."), rt_to_str(RT_DEVICE),
4237 				    pt_to_str(PT_MATCH),
4238 				    in_progress_devtab.zone_dev_match);
4239 				saw_error = TRUE;
4240 				return;
4241 			}
4242 			err = zonecfg_add_dev(handle, &in_progress_devtab);
4243 		} else {
4244 			err = zonecfg_modify_dev(handle, &old_devtab,
4245 			    &in_progress_devtab);
4246 		}
4247 		break;
4248 
4249 	case RT_RCTL:
4250 		/* First make sure everything was filled in. */
4251 		(void) end_check_reqd(in_progress_rctltab.zone_rctl_name,
4252 		    PT_NAME, &validation_failed);
4253 
4254 		if (in_progress_rctltab.zone_rctl_valptr == NULL) {
4255 			zerr(gettext("no %s specified"), pt_to_str(PT_VALUE));
4256 			validation_failed = TRUE;
4257 		}
4258 
4259 		if (validation_failed) {
4260 			saw_error = TRUE;
4261 			return;
4262 		}
4263 
4264 		if (end_op == CMD_ADD) {
4265 			/* Make sure there isn't already one like this. */
4266 			(void) strlcpy(tmp_rctltab.zone_rctl_name,
4267 			    in_progress_rctltab.zone_rctl_name,
4268 			    sizeof (tmp_rctltab.zone_rctl_name));
4269 			tmp_rctltab.zone_rctl_valptr = NULL;
4270 			err = zonecfg_lookup_rctl(handle, &tmp_rctltab);
4271 			zonecfg_free_rctl_value_list(
4272 			    tmp_rctltab.zone_rctl_valptr);
4273 			if (err == Z_OK) {
4274 				zerr(gettext("A %s resource "
4275 				    "with the %s '%s' already exists."),
4276 				    rt_to_str(RT_RCTL), pt_to_str(PT_NAME),
4277 				    in_progress_rctltab.zone_rctl_name);
4278 				saw_error = TRUE;
4279 				return;
4280 			}
4281 			err = zonecfg_add_rctl(handle, &in_progress_rctltab);
4282 		} else {
4283 			err = zonecfg_modify_rctl(handle, &old_rctltab,
4284 			    &in_progress_rctltab);
4285 		}
4286 		if (err == Z_OK) {
4287 			zonecfg_free_rctl_value_list(
4288 			    in_progress_rctltab.zone_rctl_valptr);
4289 			in_progress_rctltab.zone_rctl_valptr = NULL;
4290 		}
4291 		break;
4292 
4293 	case RT_ATTR:
4294 		/* First make sure everything was filled in. */
4295 		(void) end_check_reqd(in_progress_attrtab.zone_attr_name,
4296 		    PT_NAME, &validation_failed);
4297 		(void) end_check_reqd(in_progress_attrtab.zone_attr_type,
4298 		    PT_TYPE, &validation_failed);
4299 		(void) end_check_reqd(in_progress_attrtab.zone_attr_value,
4300 		    PT_VALUE, &validation_failed);
4301 
4302 		if (validate_attr_name(in_progress_attrtab.zone_attr_name) !=
4303 		    Z_OK)
4304 			validation_failed = TRUE;
4305 
4306 		if (validate_attr_type_val(&in_progress_attrtab) != Z_OK)
4307 			validation_failed = TRUE;
4308 
4309 		if (validation_failed) {
4310 			saw_error = TRUE;
4311 			return;
4312 		}
4313 		if (end_op == CMD_ADD) {
4314 			/* Make sure there isn't already one like this. */
4315 			bzero(&tmp_attrtab, sizeof (tmp_attrtab));
4316 			(void) strlcpy(tmp_attrtab.zone_attr_name,
4317 			    in_progress_attrtab.zone_attr_name,
4318 			    sizeof (tmp_attrtab.zone_attr_name));
4319 			if (zonecfg_lookup_attr(handle, &tmp_attrtab) == Z_OK) {
4320 				zerr(gettext("An %s resource "
4321 				    "with the %s '%s' already exists."),
4322 				    rt_to_str(RT_ATTR), pt_to_str(PT_NAME),
4323 				    in_progress_attrtab.zone_attr_name);
4324 				saw_error = TRUE;
4325 				return;
4326 			}
4327 			err = zonecfg_add_attr(handle, &in_progress_attrtab);
4328 		} else {
4329 			err = zonecfg_modify_attr(handle, &old_attrtab,
4330 			    &in_progress_attrtab);
4331 		}
4332 		break;
4333 	case RT_DATASET:
4334 		/* First make sure everything was filled in. */
4335 		if (strlen(in_progress_dstab.zone_dataset_name) == 0) {
4336 			zerr("%s %s", pt_to_str(PT_NAME),
4337 			    gettext("not specified"));
4338 			saw_error = TRUE;
4339 			validation_failed = TRUE;
4340 		}
4341 		if (validation_failed)
4342 			return;
4343 		if (end_op == CMD_ADD) {
4344 			/* Make sure there isn't already one like this. */
4345 			bzero(&tmp_dstab, sizeof (tmp_dstab));
4346 			(void) strlcpy(tmp_dstab.zone_dataset_name,
4347 			    in_progress_dstab.zone_dataset_name,
4348 			    sizeof (tmp_dstab.zone_dataset_name));
4349 			err = zonecfg_lookup_ds(handle, &tmp_dstab);
4350 			if (err == Z_OK) {
4351 				zerr(gettext("A %s resource "
4352 				    "with the %s '%s' already exists."),
4353 				    rt_to_str(RT_DATASET), pt_to_str(PT_NAME),
4354 				    in_progress_dstab.zone_dataset_name);
4355 				saw_error = TRUE;
4356 				return;
4357 			}
4358 			err = zonecfg_add_ds(handle, &in_progress_dstab);
4359 		} else {
4360 			err = zonecfg_modify_ds(handle, &old_dstab,
4361 			    &in_progress_dstab);
4362 		}
4363 		break;
4364 	default:
4365 		zone_perror(rt_to_str(resource_scope), Z_NO_RESOURCE_TYPE,
4366 		    TRUE);
4367 		saw_error = TRUE;
4368 		return;
4369 	}
4370 
4371 	if (err != Z_OK) {
4372 		zone_perror(zone, err, TRUE);
4373 	} else {
4374 		need_to_commit = TRUE;
4375 		global_scope = TRUE;
4376 		end_op = -1;
4377 	}
4378 }
4379 
4380 void
4381 commit_func(cmd_t *cmd)
4382 {
4383 	int arg;
4384 
4385 	optind = 0;
4386 	if ((arg = getopt(cmd->cmd_argc, cmd->cmd_argv, "?")) != EOF) {
4387 		switch (arg) {
4388 		case '?':
4389 			longer_usage(CMD_COMMIT);
4390 			return;
4391 		default:
4392 			short_usage(CMD_COMMIT);
4393 			return;
4394 		}
4395 	}
4396 	if (optind != cmd->cmd_argc) {
4397 		short_usage(CMD_COMMIT);
4398 		return;
4399 	}
4400 
4401 	if (zone_is_read_only(CMD_COMMIT))
4402 		return;
4403 
4404 	assert(cmd != NULL);
4405 
4406 	cmd->cmd_argc = 1;
4407 	/*
4408 	 * cmd_arg normally comes from a strdup() in the lexer, and the
4409 	 * whole cmd structure and its (char *) attributes are freed at
4410 	 * the completion of each command, so the strdup() below is needed
4411 	 * to match this and prevent a core dump from trying to free()
4412 	 * something that can't be.
4413 	 */
4414 	if ((cmd->cmd_argv[0] = strdup("save")) == NULL) {
4415 		zone_perror(zone, Z_NOMEM, TRUE);
4416 		exit(Z_ERR);
4417 	}
4418 	cmd->cmd_argv[1] = NULL;
4419 	verify_func(cmd);
4420 }
4421 
4422 void
4423 revert_func(cmd_t *cmd)
4424 {
4425 	char line[128];	/* enough to ask a question */
4426 	bool force = FALSE;
4427 	int err, arg, answer;
4428 
4429 	optind = 0;
4430 	while ((arg = getopt(cmd->cmd_argc, cmd->cmd_argv, "?F")) != EOF) {
4431 		switch (arg) {
4432 		case '?':
4433 			longer_usage(CMD_REVERT);
4434 			return;
4435 		case 'F':
4436 			force = TRUE;
4437 			break;
4438 		default:
4439 			short_usage(CMD_REVERT);
4440 			return;
4441 		}
4442 	}
4443 	if (optind != cmd->cmd_argc) {
4444 		short_usage(CMD_REVERT);
4445 		return;
4446 	}
4447 
4448 	if (zone_is_read_only(CMD_REVERT))
4449 		return;
4450 
4451 	if (zonecfg_check_handle(handle) != Z_OK) {
4452 		zerr(gettext("No changes to revert."));
4453 		saw_error = TRUE;
4454 		return;
4455 	}
4456 
4457 	if (!force) {
4458 		(void) snprintf(line, sizeof (line),
4459 		    gettext("Are you sure you want to revert"));
4460 		if ((answer = ask_yesno(FALSE, line)) == -1) {
4461 			zerr(gettext("Input not from terminal and -F not "
4462 			    "specified:\n%s command ignored, exiting."),
4463 			    cmd_to_str(CMD_REVERT));
4464 			exit(Z_ERR);
4465 		}
4466 		if (answer != 1)
4467 			return;
4468 	}
4469 
4470 	/*
4471 	 * Time for a new handle: finish the old one off first
4472 	 * then get a new one properly to avoid leaks.
4473 	 */
4474 	zonecfg_fini_handle(handle);
4475 	if ((handle = zonecfg_init_handle()) == NULL) {
4476 		zone_perror(execname, Z_NOMEM, TRUE);
4477 		exit(Z_ERR);
4478 	}
4479 	if ((err = zonecfg_get_handle(revert_zone, handle)) != Z_OK) {
4480 		saw_error = TRUE;
4481 		got_handle = FALSE;
4482 		if (err == Z_NO_ZONE)
4483 			zerr(gettext("%s: no such saved zone to revert to."),
4484 			    revert_zone);
4485 		else
4486 			zone_perror(zone, err, TRUE);
4487 	}
4488 	(void) strlcpy(zone, revert_zone, sizeof (zone));
4489 }
4490 
4491 void
4492 help_func(cmd_t *cmd)
4493 {
4494 	int i;
4495 
4496 	assert(cmd != NULL);
4497 
4498 	if (cmd->cmd_argc == 0) {
4499 		usage(TRUE, global_scope ? HELP_SUBCMDS : HELP_RES_SCOPE);
4500 		return;
4501 	}
4502 	if (strcmp(cmd->cmd_argv[0], "usage") == 0) {
4503 		usage(TRUE, HELP_USAGE);
4504 		return;
4505 	}
4506 	if (strcmp(cmd->cmd_argv[0], "commands") == 0) {
4507 		usage(TRUE, HELP_SUBCMDS);
4508 		return;
4509 	}
4510 	if (strcmp(cmd->cmd_argv[0], "syntax") == 0) {
4511 		usage(TRUE, HELP_SYNTAX | HELP_RES_PROPS);
4512 		return;
4513 	}
4514 	if (strcmp(cmd->cmd_argv[0], "-?") == 0) {
4515 		longer_usage(CMD_HELP);
4516 		return;
4517 	}
4518 
4519 	for (i = 0; i <= CMD_MAX; i++) {
4520 		if (strcmp(cmd->cmd_argv[0], cmd_to_str(i)) == 0) {
4521 			longer_usage(i);
4522 			return;
4523 		}
4524 	}
4525 	/* We do not use zerr() here because we do not want its extra \n. */
4526 	(void) fprintf(stderr, gettext("Unknown help subject %s.  "),
4527 	    cmd->cmd_argv[0]);
4528 	usage(FALSE, HELP_META);
4529 }
4530 
4531 static int
4532 string_to_yyin(char *string)
4533 {
4534 	if ((yyin = tmpfile()) == NULL) {
4535 		zone_perror(execname, Z_TEMP_FILE, TRUE);
4536 		return (Z_ERR);
4537 	}
4538 	if (fwrite(string, strlen(string), 1, yyin) != 1) {
4539 		zone_perror(execname, Z_TEMP_FILE, TRUE);
4540 		return (Z_ERR);
4541 	}
4542 	if (fseek(yyin, 0, SEEK_SET) != 0) {
4543 		zone_perror(execname, Z_TEMP_FILE, TRUE);
4544 		return (Z_ERR);
4545 	}
4546 	return (Z_OK);
4547 }
4548 
4549 /* This is the back-end helper function for read_input() below. */
4550 
4551 static int
4552 cleanup()
4553 {
4554 	int answer;
4555 	cmd_t *cmd;
4556 
4557 	if (!interactive_mode && !cmd_file_mode) {
4558 		/*
4559 		 * If we're not in interactive mode, and we're not in command
4560 		 * file mode, then we must be in commands-from-the-command-line
4561 		 * mode.  As such, we can't loop back and ask for more input.
4562 		 * It was OK to prompt for such things as whether or not to
4563 		 * really delete a zone in the command handler called from
4564 		 * yyparse() above, but "really quit?" makes no sense in this
4565 		 * context.  So disable prompting.
4566 		 */
4567 		ok_to_prompt = FALSE;
4568 	}
4569 	if (!global_scope) {
4570 		if (!time_to_exit) {
4571 			/*
4572 			 * Just print a simple error message in the -1 case,
4573 			 * since exit_func() already handles that case, and
4574 			 * EOF means we are finished anyway.
4575 			 */
4576 			answer = ask_yesno(FALSE,
4577 			    gettext("Resource incomplete; really quit"));
4578 			if (answer == -1) {
4579 				zerr(gettext("Resource incomplete."));
4580 				return (Z_ERR);
4581 			}
4582 			if (answer != 1) {
4583 				yyin = stdin;
4584 				return (Z_REPEAT);
4585 			}
4586 		} else {
4587 			saw_error = TRUE;
4588 		}
4589 	}
4590 	/*
4591 	 * Make sure we tried something and that the handle checks
4592 	 * out, or we would get a false error trying to commit.
4593 	 */
4594 	if (need_to_commit && zonecfg_check_handle(handle) == Z_OK) {
4595 		if ((cmd = alloc_cmd()) == NULL) {
4596 			zone_perror(zone, Z_NOMEM, TRUE);
4597 			return (Z_ERR);
4598 		}
4599 		cmd->cmd_argc = 0;
4600 		cmd->cmd_argv[0] = NULL;
4601 		commit_func(cmd);
4602 		free_cmd(cmd);
4603 		/*
4604 		 * need_to_commit will get set back to FALSE if the
4605 		 * configuration is saved successfully.
4606 		 */
4607 		if (need_to_commit) {
4608 			if (force_exit) {
4609 				zerr(gettext("Configuration not saved."));
4610 				return (Z_ERR);
4611 			}
4612 			answer = ask_yesno(FALSE,
4613 			    gettext("Configuration not saved; really quit"));
4614 			if (answer == -1) {
4615 				zerr(gettext("Configuration not saved."));
4616 				return (Z_ERR);
4617 			}
4618 			if (answer != 1) {
4619 				time_to_exit = FALSE;
4620 				yyin = stdin;
4621 				return (Z_REPEAT);
4622 			}
4623 		}
4624 	}
4625 	return ((need_to_commit || saw_error) ? Z_ERR : Z_OK);
4626 }
4627 
4628 /*
4629  * read_input() is the driver of this program.  It is a wrapper around
4630  * yyparse(), printing appropriate prompts when needed, checking for
4631  * exit conditions and reacting appropriately [the latter in its cleanup()
4632  * helper function].
4633  *
4634  * Like most zonecfg functions, it returns Z_OK or Z_ERR, *or* Z_REPEAT
4635  * so do_interactive() knows that we are not really done (i.e, we asked
4636  * the user if we should really quit and the user said no).
4637  */
4638 static int
4639 read_input()
4640 {
4641 	bool yyin_is_a_tty = isatty(fileno(yyin));
4642 	/*
4643 	 * The prompt is "e:z> " or "e:z:r> " where e is execname, z is zone
4644 	 * and r is resource_scope: 5 is for the two ":"s + "> " + terminator.
4645 	 */
4646 	char prompt[MAXPATHLEN + ZONENAME_MAX + MAX_RT_STRLEN + 5], *line;
4647 
4648 	/* yyin should have been set to the appropriate (FILE *) if not stdin */
4649 	newline_terminated = TRUE;
4650 	for (;;) {
4651 		if (yyin_is_a_tty) {
4652 			if (newline_terminated) {
4653 				if (global_scope)
4654 					(void) snprintf(prompt, sizeof (prompt),
4655 					    "%s:%s> ", execname, zone);
4656 				else
4657 					(void) snprintf(prompt, sizeof (prompt),
4658 					    "%s:%s:%s> ", execname, zone,
4659 					    rt_to_str(resource_scope));
4660 			}
4661 			/*
4662 			 * If the user hits ^C then we want to catch it and
4663 			 * start over.  If the user hits EOF then we want to
4664 			 * bail out.
4665 			 */
4666 			line = gl_get_line(gl, prompt, NULL, -1);
4667 			if (gl_return_status(gl) == GLR_SIGNAL) {
4668 				gl_abandon_line(gl);
4669 				continue;
4670 			}
4671 			if (line == NULL)
4672 				break;
4673 			(void) string_to_yyin(line);
4674 			while (!feof(yyin))
4675 				yyparse();
4676 		} else {
4677 			yyparse();
4678 		}
4679 		/* Bail out on an error in command file mode. */
4680 		if (saw_error && cmd_file_mode && !interactive_mode)
4681 			time_to_exit = TRUE;
4682 		if (time_to_exit || (!yyin_is_a_tty && feof(yyin)))
4683 			break;
4684 	}
4685 	return (cleanup());
4686 }
4687 
4688 /*
4689  * This function is used in the zonecfg-interactive-mode scenario: it just
4690  * calls read_input() until we are done.
4691  */
4692 
4693 static int
4694 do_interactive(void)
4695 {
4696 	int err;
4697 
4698 	interactive_mode = TRUE;
4699 	if (!read_only_mode) {
4700 		/*
4701 		 * Try to set things up proactively in interactive mode, so
4702 		 * that if the zone in question does not exist yet, we can
4703 		 * provide the user with a clue.
4704 		 */
4705 		(void) initialize(FALSE);
4706 	}
4707 	do {
4708 		err = read_input();
4709 	} while (err == Z_REPEAT);
4710 	return (err);
4711 }
4712 
4713 /*
4714  * cmd_file is slightly more complicated, as it has to open the command file
4715  * and set yyin appropriately.  Once that is done, though, it just calls
4716  * read_input(), and only once, since prompting is not possible.
4717  */
4718 
4719 static int
4720 cmd_file(char *file)
4721 {
4722 	FILE *infile;
4723 	int err;
4724 	struct stat statbuf;
4725 	bool using_real_file = (strcmp(file, "-") != 0);
4726 
4727 	if (using_real_file) {
4728 		/*
4729 		 * zerr() prints a line number in cmd_file_mode, which we do
4730 		 * not want here, so temporarily unset it.
4731 		 */
4732 		cmd_file_mode = FALSE;
4733 		if ((infile = fopen(file, "r")) == NULL) {
4734 			zerr(gettext("could not open file %s: %s"),
4735 			    file, strerror(errno));
4736 			return (Z_ERR);
4737 		}
4738 		if ((err = fstat(fileno(infile), &statbuf)) != 0) {
4739 			zerr(gettext("could not stat file %s: %s"),
4740 			    file, strerror(errno));
4741 			err = Z_ERR;
4742 			goto done;
4743 		}
4744 		if (!S_ISREG(statbuf.st_mode)) {
4745 			zerr(gettext("%s is not a regular file."), file);
4746 			err = Z_ERR;
4747 			goto done;
4748 		}
4749 		yyin = infile;
4750 		cmd_file_mode = TRUE;
4751 		ok_to_prompt = FALSE;
4752 	} else {
4753 		/*
4754 		 * "-f -" is essentially the same as interactive mode,
4755 		 * so treat it that way.
4756 		 */
4757 		interactive_mode = TRUE;
4758 	}
4759 	/* Z_REPEAT is for interactive mode; treat it like Z_ERR here. */
4760 	if ((err = read_input()) == Z_REPEAT)
4761 		err = Z_ERR;
4762 done:
4763 	if (using_real_file)
4764 		(void) fclose(infile);
4765 	return (err);
4766 }
4767 
4768 /*
4769  * Since yacc is based on reading from a (FILE *) whereas what we get from
4770  * the command line is in argv format, we need to convert when the user
4771  * gives us commands directly from the command line.  That is done here by
4772  * concatenating the argv list into a space-separated string, writing it
4773  * to a temp file, and rewinding the file so yyin can be set to it.  Then
4774  * we call read_input(), and only once, since prompting about whether to
4775  * continue or quit would make no sense in this context.
4776  */
4777 
4778 static int
4779 one_command_at_a_time(int argc, char *argv[])
4780 {
4781 	char *command;
4782 	size_t len = 2; /* terminal \n\0 */
4783 	int i, err;
4784 
4785 	for (i = 0; i < argc; i++)
4786 		len += strlen(argv[i]) + 1;
4787 	if ((command = malloc(len)) == NULL) {
4788 		zone_perror(execname, Z_NOMEM, TRUE);
4789 		return (Z_ERR);
4790 	}
4791 	(void) strlcpy(command, argv[0], len);
4792 	for (i = 1; i < argc; i++) {
4793 		(void) strlcat(command, " ", len);
4794 		(void) strlcat(command, argv[i], len);
4795 	}
4796 	(void) strlcat(command, "\n", len);
4797 	err = string_to_yyin(command);
4798 	free(command);
4799 	if (err != Z_OK)
4800 		return (err);
4801 	while (!feof(yyin))
4802 		yyparse();
4803 	return (cleanup());
4804 }
4805 
4806 static char *
4807 get_execbasename(char *execfullname)
4808 {
4809 	char *last_slash, *execbasename;
4810 
4811 	/* guard against '/' at end of command invocation */
4812 	for (;;) {
4813 		last_slash = strrchr(execfullname, '/');
4814 		if (last_slash == NULL) {
4815 			execbasename = execfullname;
4816 			break;
4817 		} else {
4818 			execbasename = last_slash + 1;
4819 			if (*execbasename == '\0') {
4820 				*last_slash = '\0';
4821 				continue;
4822 			}
4823 			break;
4824 		}
4825 	}
4826 	return (execbasename);
4827 }
4828 
4829 int
4830 main(int argc, char *argv[])
4831 {
4832 	int err, arg;
4833 	struct stat st;
4834 
4835 	/* This must be before anything goes to stdout. */
4836 	setbuf(stdout, NULL);
4837 
4838 	saw_error = FALSE;
4839 	cmd_file_mode = FALSE;
4840 	execname = get_execbasename(argv[0]);
4841 
4842 	(void) setlocale(LC_ALL, "");
4843 	(void) textdomain(TEXT_DOMAIN);
4844 
4845 	if (getzoneid() != GLOBAL_ZONEID) {
4846 		zerr(gettext("%s can only be run from the global zone."),
4847 		    execname);
4848 		exit(Z_ERR);
4849 	}
4850 
4851 	if (argc < 2) {
4852 		usage(FALSE, HELP_USAGE | HELP_SUBCMDS);
4853 		exit(Z_USAGE);
4854 	}
4855 	if (strcmp(argv[1], cmd_to_str(CMD_HELP)) == 0) {
4856 		(void) one_command_at_a_time(argc - 1, &(argv[1]));
4857 		exit(Z_OK);
4858 	}
4859 
4860 	while ((arg = getopt(argc, argv, "?f:R:z:")) != EOF) {
4861 		switch (arg) {
4862 		case '?':
4863 			if (optopt == '?')
4864 				usage(TRUE, HELP_USAGE | HELP_SUBCMDS);
4865 			else
4866 				usage(FALSE, HELP_USAGE);
4867 			exit(Z_USAGE);
4868 			/* NOTREACHED */
4869 		case 'f':
4870 			cmd_file_name = optarg;
4871 			cmd_file_mode = TRUE;
4872 			break;
4873 		case 'R':
4874 			if (*optarg != '/') {
4875 				zerr(gettext("root path must be absolute: %s"),
4876 				    optarg);
4877 				exit(Z_USAGE);
4878 			}
4879 			if (stat(optarg, &st) == -1 || !S_ISDIR(st.st_mode)) {
4880 				zerr(gettext(
4881 				    "root path must be a directory: %s"),
4882 				    optarg);
4883 				exit(Z_USAGE);
4884 			}
4885 			zonecfg_set_root(optarg);
4886 			break;
4887 		case 'z':
4888 			if (zonecfg_validate_zonename(optarg) != Z_OK) {
4889 				zone_perror(optarg, Z_BOGUS_ZONE_NAME, TRUE);
4890 				usage(FALSE, HELP_SYNTAX);
4891 				exit(Z_USAGE);
4892 			}
4893 			(void) strlcpy(zone, optarg, sizeof (zone));
4894 			(void) strlcpy(revert_zone, optarg, sizeof (zone));
4895 			break;
4896 		default:
4897 			usage(FALSE, HELP_USAGE);
4898 			exit(Z_USAGE);
4899 		}
4900 	}
4901 
4902 	if (optind > argc || strcmp(zone, "") == 0) {
4903 		usage(FALSE, HELP_USAGE);
4904 		exit(Z_USAGE);
4905 	}
4906 
4907 	if ((err = zonecfg_access(zone, W_OK)) == Z_OK) {
4908 		read_only_mode = FALSE;
4909 	} else if (err == Z_ACCES) {
4910 		read_only_mode = TRUE;
4911 		/* skip this message in one-off from command line mode */
4912 		if (optind == argc)
4913 			(void) fprintf(stderr, gettext("WARNING: you do not "
4914 			    "have write access to this zone's configuration "
4915 			    "file;\ngoing into read-only mode.\n"));
4916 	} else {
4917 		fprintf(stderr, "%s: Could not access zone configuration "
4918 		    "store: %s\n", execname, zonecfg_strerror(err));
4919 		exit(Z_ERR);
4920 	}
4921 
4922 	if ((handle = zonecfg_init_handle()) == NULL) {
4923 		zone_perror(execname, Z_NOMEM, TRUE);
4924 		exit(Z_ERR);
4925 	}
4926 
4927 	/*
4928 	 * This may get set back to FALSE again in cmd_file() if cmd_file_name
4929 	 * is a "real" file as opposed to "-" (i.e. meaning use stdin).
4930 	 */
4931 	if (isatty(STDIN_FILENO))
4932 		ok_to_prompt = TRUE;
4933 	if ((gl = new_GetLine(MAX_LINE_LEN, MAX_CMD_HIST)) == NULL)
4934 		exit(Z_ERR);
4935 	if (gl_customize_completion(gl, NULL, cmd_cpl_fn) != 0)
4936 		exit(Z_ERR);
4937 	(void) sigset(SIGINT, SIG_IGN);
4938 	if (optind == argc) {
4939 		if (!cmd_file_mode)
4940 			err = do_interactive();
4941 		else
4942 			err = cmd_file(cmd_file_name);
4943 	} else {
4944 		err = one_command_at_a_time(argc - optind, &(argv[optind]));
4945 	}
4946 	zonecfg_fini_handle(handle);
4947 	if (brand != NULL)
4948 		brand_close(brand);
4949 	(void) del_GetLine(gl);
4950 	return (err);
4951 }
4952