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