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