1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
24  */
25 
26 #include <libsysevent.h>
27 #include <pthread.h>
28 #include <stdlib.h>
29 #include <errno.h>
30 #include <fnmatch.h>
31 #include <strings.h>
32 #include <unistd.h>
33 #include <assert.h>
34 #include <libgen.h>
35 #include <libintl.h>
36 #include <alloca.h>
37 #include <ctype.h>
38 #include <sys/acl.h>
39 #include <sys/stat.h>
40 #include <sys/brand.h>
41 #include <sys/mntio.h>
42 #include <sys/mnttab.h>
43 #include <sys/nvpair.h>
44 #include <sys/types.h>
45 #include <sys/sockio.h>
46 #include <sys/systeminfo.h>
47 #include <ftw.h>
48 #include <pool.h>
49 #include <libscf.h>
50 #include <libproc.h>
51 #include <sys/priocntl.h>
52 #include <libuutil.h>
53 #include <wait.h>
54 #include <bsm/adt.h>
55 #include <auth_attr.h>
56 #include <auth_list.h>
57 #include <secdb.h>
58 #include <user_attr.h>
59 #include <prof_attr.h>
60 
61 #include <arpa/inet.h>
62 #include <netdb.h>
63 
64 #include <libxml/xmlmemory.h>
65 #include <libxml/parser.h>
66 
67 #include <libdevinfo.h>
68 #include <uuid/uuid.h>
69 #include <dirent.h>
70 #include <libbrand.h>
71 
72 #include <libzonecfg.h>
73 #include "zonecfg_impl.h"
74 
75 #define	_PATH_TMPFILE	"/zonecfg.XXXXXX"
76 #define	ZONE_CB_RETRY_COUNT		10
77 #define	ZONE_EVENT_PING_SUBCLASS	"ping"
78 #define	ZONE_EVENT_PING_PUBLISHER	"solaris"
79 
80 /* Hard-code the DTD element/attribute/entity names just once, here. */
81 #define	DTD_ELEM_ATTR		(const xmlChar *) "attr"
82 #define	DTD_ELEM_COMMENT	(const xmlChar *) "comment"
83 #define	DTD_ELEM_DEVICE		(const xmlChar *) "device"
84 #define	DTD_ELEM_FS		(const xmlChar *) "filesystem"
85 #define	DTD_ELEM_FSOPTION	(const xmlChar *) "fsoption"
86 #define	DTD_ELEM_IPD		(const xmlChar *) "inherited-pkg-dir"
87 #define	DTD_ELEM_NET		(const xmlChar *) "network"
88 #define	DTD_ELEM_RCTL		(const xmlChar *) "rctl"
89 #define	DTD_ELEM_RCTLVALUE	(const xmlChar *) "rctl-value"
90 #define	DTD_ELEM_ZONE		(const xmlChar *) "zone"
91 #define	DTD_ELEM_DATASET	(const xmlChar *) "dataset"
92 #define	DTD_ELEM_TMPPOOL	(const xmlChar *) "tmp_pool"
93 #define	DTD_ELEM_PSET		(const xmlChar *) "pset"
94 #define	DTD_ELEM_MCAP		(const xmlChar *) "mcap"
95 #define	DTD_ELEM_PACKAGE	(const xmlChar *) "package"
96 #define	DTD_ELEM_PATCH		(const xmlChar *) "patch"
97 #define	DTD_ELEM_OBSOLETES	(const xmlChar *) "obsoletes"
98 #define	DTD_ELEM_DEV_PERM	(const xmlChar *) "dev-perm"
99 #define	DTD_ELEM_ADMIN		(const xmlChar *) "admin"
100 
101 #define	DTD_ATTR_ACTION		(const xmlChar *) "action"
102 #define	DTD_ATTR_ADDRESS	(const xmlChar *) "address"
103 #define	DTD_ATTR_AUTOBOOT	(const xmlChar *) "autoboot"
104 #define	DTD_ATTR_IPTYPE		(const xmlChar *) "ip-type"
105 #define	DTD_ATTR_DEFROUTER	(const xmlChar *) "defrouter"
106 #define	DTD_ATTR_DIR		(const xmlChar *) "directory"
107 #define	DTD_ATTR_LIMIT		(const xmlChar *) "limit"
108 #define	DTD_ATTR_LIMITPRIV	(const xmlChar *) "limitpriv"
109 #define	DTD_ATTR_BOOTARGS	(const xmlChar *) "bootargs"
110 #define	DTD_ATTR_SCHED		(const xmlChar *) "scheduling-class"
111 #define	DTD_ATTR_MATCH		(const xmlChar *) "match"
112 #define	DTD_ATTR_NAME		(const xmlChar *) "name"
113 #define	DTD_ATTR_PHYSICAL	(const xmlChar *) "physical"
114 #define	DTD_ATTR_POOL		(const xmlChar *) "pool"
115 #define	DTD_ATTR_PRIV		(const xmlChar *) "priv"
116 #define	DTD_ATTR_RAW		(const xmlChar *) "raw"
117 #define	DTD_ATTR_SPECIAL	(const xmlChar *) "special"
118 #define	DTD_ATTR_TYPE		(const xmlChar *) "type"
119 #define	DTD_ATTR_VALUE		(const xmlChar *) "value"
120 #define	DTD_ATTR_ZONEPATH	(const xmlChar *) "zonepath"
121 #define	DTD_ATTR_NCPU_MIN	(const xmlChar *) "ncpu_min"
122 #define	DTD_ATTR_NCPU_MAX	(const xmlChar *) "ncpu_max"
123 #define	DTD_ATTR_IMPORTANCE	(const xmlChar *) "importance"
124 #define	DTD_ATTR_PHYSCAP	(const xmlChar *) "physcap"
125 #define	DTD_ATTR_VERSION	(const xmlChar *) "version"
126 #define	DTD_ATTR_ID		(const xmlChar *) "id"
127 #define	DTD_ATTR_UID		(const xmlChar *) "uid"
128 #define	DTD_ATTR_GID		(const xmlChar *) "gid"
129 #define	DTD_ATTR_MODE		(const xmlChar *) "mode"
130 #define	DTD_ATTR_ACL		(const xmlChar *) "acl"
131 #define	DTD_ATTR_BRAND		(const xmlChar *) "brand"
132 #define	DTD_ATTR_HOSTID		(const xmlChar *) "hostid"
133 #define	DTD_ATTR_USER		(const xmlChar *) "user"
134 #define	DTD_ATTR_AUTHS		(const xmlChar *) "auths"
135 #define	DTD_ATTR_FS_ALLOWED	(const xmlChar *) "fs-allowed"
136 
137 #define	DTD_ENTITY_BOOLEAN	"boolean"
138 #define	DTD_ENTITY_DEVPATH	"devpath"
139 #define	DTD_ENTITY_DRIVER	"driver"
140 #define	DTD_ENTITY_DRVMIN	"drv_min"
141 #define	DTD_ENTITY_FALSE	"false"
142 #define	DTD_ENTITY_INT		"int"
143 #define	DTD_ENTITY_STRING	"string"
144 #define	DTD_ENTITY_TRUE		"true"
145 #define	DTD_ENTITY_UINT		"uint"
146 
147 #define	DTD_ENTITY_BOOL_LEN	6	/* "false" */
148 
149 #define	ATTACH_FORCED	"SUNWattached.xml"
150 
151 #define	TMP_POOL_NAME	"SUNWtmp_%s"
152 #define	MAX_TMP_POOL_NAME	(ZONENAME_MAX + 9)
153 #define	RCAP_SERVICE	"system/rcap:default"
154 #define	POOLD_SERVICE	"system/pools/dynamic:default"
155 
156 /*
157  * rctl alias definitions
158  *
159  * This holds the alias, the full rctl name, the default priv value, action
160  * and lower limit.  The functions that handle rctl aliases step through
161  * this table, matching on the alias, and using the full values for setting
162  * the rctl entry as well the limit for validation.
163  */
164 static struct alias {
165 	char *shortname;
166 	char *realname;
167 	char *priv;
168 	char *action;
169 	uint64_t low_limit;
170 } aliases[] = {
171 	{ALIAS_MAXLWPS, "zone.max-lwps", "privileged", "deny", 100},
172 	{ALIAS_MAXSHMMEM, "zone.max-shm-memory", "privileged", "deny", 0},
173 	{ALIAS_MAXSHMIDS, "zone.max-shm-ids", "privileged", "deny", 0},
174 	{ALIAS_MAXMSGIDS, "zone.max-msg-ids", "privileged", "deny", 0},
175 	{ALIAS_MAXSEMIDS, "zone.max-sem-ids", "privileged", "deny", 0},
176 	{ALIAS_MAXLOCKEDMEM, "zone.max-locked-memory", "privileged", "deny", 0},
177 	{ALIAS_MAXSWAP, "zone.max-swap", "privileged", "deny", 0},
178 	{ALIAS_SHARES, "zone.cpu-shares", "privileged", "none", 0},
179 	{ALIAS_CPUCAP, "zone.cpu-cap", "privileged", "deny", 0},
180 	{ALIAS_MAXPROCS, "zone.max-processes", "privileged", "deny", 100},
181 	{NULL, NULL, NULL, NULL, 0}
182 };
183 
184 /*
185  * Structure for applying rctls to a running zone.  It allows important
186  * process values to be passed together easily.
187  */
188 typedef struct pr_info_handle {
189 	struct ps_prochandle *pr;
190 	pid_t pid;
191 } pr_info_handle_t;
192 
193 struct zone_dochandle {
194 	char		*zone_dh_rootdir;
195 	xmlDocPtr	zone_dh_doc;
196 	xmlNodePtr	zone_dh_cur;
197 	xmlNodePtr	zone_dh_top;
198 	boolean_t	zone_dh_newzone;
199 	boolean_t	zone_dh_snapshot;
200 	boolean_t	zone_dh_sw_inv;
201 	zone_userauths_t	*zone_dh_userauths;
202 	char		zone_dh_delete_name[ZONENAME_MAX];
203 };
204 
205 struct znotify {
206 	void * zn_private;
207 	evchan_t *zn_eventchan;
208 	int (*zn_callback)(const  char *zonename, zoneid_t zid,
209 	    const char *newstate, const char *oldstate, hrtime_t when, void *p);
210 	pthread_mutex_t zn_mutex;
211 	pthread_cond_t zn_cond;
212 	pthread_mutex_t zn_bigmutex;
213 	volatile enum {ZN_UNLOCKED, ZN_LOCKED, ZN_PING_INFLIGHT,
214 	    ZN_PING_RECEIVED} zn_state;
215 	char zn_subscriber_id[MAX_SUBID_LEN];
216 	volatile boolean_t zn_failed;
217 	int zn_failure_count;
218 };
219 
220 /* used to track nested zone-lock operations */
221 static int zone_lock_cnt = 0;
222 
223 /* used to communicate lock status to children */
224 #define	LOCK_ENV_VAR	"_ZONEADM_LOCK_HELD"
225 static char zoneadm_lock_held[] = LOCK_ENV_VAR"=1";
226 static char zoneadm_lock_not_held[] = LOCK_ENV_VAR"=0";
227 
228 char *zonecfg_root = "";
229 
230 /*
231  * For functions which return int, which is most of the functions herein,
232  * the return values should be from the Z_foo set defined in <libzonecfg.h>.
233  * In some instances, we take pains mapping some libc errno values to Z_foo
234  * values from this set.
235  */
236 
237 /*
238  * Set the root (/) path for all zonecfg configuration files.  This is a
239  * private interface used by Live Upgrade extensions to access zone
240  * configuration inside mounted alternate boot environments.
241  */
242 void
243 zonecfg_set_root(const char *rootpath)
244 {
245 	if (*zonecfg_root != '\0')
246 		free(zonecfg_root);
247 	if (rootpath == NULL || rootpath[0] == '\0' || rootpath[1] == '\0' ||
248 	    (zonecfg_root = strdup(rootpath)) == NULL)
249 		zonecfg_root = "";
250 }
251 
252 const char *
253 zonecfg_get_root(void)
254 {
255 	return (zonecfg_root);
256 }
257 
258 boolean_t
259 zonecfg_in_alt_root(void)
260 {
261 	return (*zonecfg_root != '\0');
262 }
263 
264 /*
265  * Callers of the _file_path() functions are expected to have the second
266  * parameter be a (char foo[MAXPATHLEN]).
267  */
268 
269 static boolean_t
270 config_file_path(const char *zonename, char *answer)
271 {
272 	return (snprintf(answer, MAXPATHLEN, "%s%s/%s.xml", zonecfg_root,
273 	    ZONE_CONFIG_ROOT, zonename) < MAXPATHLEN);
274 }
275 
276 static boolean_t
277 snap_file_path(const char *zonename, char *answer)
278 {
279 	return (snprintf(answer, MAXPATHLEN, "%s%s/%s.snapshot.xml",
280 	    zonecfg_root, ZONE_SNAPSHOT_ROOT, zonename) < MAXPATHLEN);
281 }
282 
283 /*ARGSUSED*/
284 static void
285 zonecfg_error_func(void *ctx, const char *msg, ...)
286 {
287 	/*
288 	 * This function does nothing by design.  Its purpose is to prevent
289 	 * libxml from dumping unwanted messages to stdout/stderr.
290 	 */
291 }
292 
293 zone_dochandle_t
294 zonecfg_init_handle(void)
295 {
296 	zone_dochandle_t handle = calloc(1, sizeof (struct zone_dochandle));
297 	if (handle == NULL) {
298 		errno = Z_NOMEM;
299 		return (NULL);
300 	}
301 
302 	/* generic libxml initialization */
303 	(void) xmlLineNumbersDefault(1);
304 	xmlLoadExtDtdDefaultValue |= XML_DETECT_IDS;
305 	xmlDoValidityCheckingDefaultValue = 1;
306 	(void) xmlKeepBlanksDefault(0);
307 	xmlGetWarningsDefaultValue = 0;
308 	xmlSetGenericErrorFunc(NULL, zonecfg_error_func);
309 
310 	return (handle);
311 }
312 
313 int
314 zonecfg_check_handle(zone_dochandle_t handle)
315 {
316 	if (handle == NULL || handle->zone_dh_doc == NULL)
317 		return (Z_BAD_HANDLE);
318 	return (Z_OK);
319 }
320 
321 void
322 zonecfg_fini_handle(zone_dochandle_t handle)
323 {
324 	if (zonecfg_check_handle(handle) == Z_OK)
325 		xmlFreeDoc(handle->zone_dh_doc);
326 	if (handle != NULL)
327 		free(handle);
328 }
329 
330 static int
331 zonecfg_destroy_impl(char *filename)
332 {
333 	if (unlink(filename) == -1) {
334 		if (errno == EACCES)
335 			return (Z_ACCES);
336 		if (errno == ENOENT)
337 			return (Z_NO_ZONE);
338 		return (Z_MISC_FS);
339 	}
340 	return (Z_OK);
341 }
342 
343 int
344 zonecfg_destroy(const char *zonename, boolean_t force)
345 {
346 	char path[MAXPATHLEN];
347 	struct zoneent ze;
348 	int err, state_err;
349 	zone_state_t state;
350 
351 	if (!config_file_path(zonename, path))
352 		return (Z_MISC_FS);
353 
354 	state_err = zone_get_state((char *)zonename, &state);
355 	err = access(path, W_OK);
356 
357 	/*
358 	 * If there is no file, and no index entry, reliably indicate that no
359 	 * such zone exists.
360 	 */
361 	if ((state_err == Z_NO_ZONE) && (err == -1) && (errno == ENOENT))
362 		return (Z_NO_ZONE);
363 
364 	/*
365 	 * Handle any other filesystem related errors (except if the XML
366 	 * file is missing, which we treat silently), unless we're forcing,
367 	 * in which case we plow on.
368 	 */
369 	if (err == -1 && errno != ENOENT) {
370 		if (errno == EACCES)
371 			return (Z_ACCES);
372 		else if (!force)
373 			return (Z_MISC_FS);
374 	}
375 
376 	if (state > ZONE_STATE_INSTALLED)
377 		return (Z_BAD_ZONE_STATE);
378 
379 	if (!force && state > ZONE_STATE_CONFIGURED)
380 		return (Z_BAD_ZONE_STATE);
381 
382 	/*
383 	 * Index deletion succeeds even if the entry doesn't exist.  So this
384 	 * will fail only if we've had some more severe problem.
385 	 */
386 	bzero(&ze, sizeof (ze));
387 	(void) strlcpy(ze.zone_name, zonename, sizeof (ze.zone_name));
388 	if ((err = putzoneent(&ze, PZE_REMOVE)) != Z_OK)
389 		if (!force)
390 			return (err);
391 
392 	err = zonecfg_destroy_impl(path);
393 
394 	/*
395 	 * Treat failure to find the XML file silently, since, well, it's
396 	 * gone, and with the index file cleaned up, we're done.
397 	 */
398 	if (err == Z_OK || err == Z_NO_ZONE)
399 		return (Z_OK);
400 	return (err);
401 }
402 
403 int
404 zonecfg_destroy_snapshot(const char *zonename)
405 {
406 	char path[MAXPATHLEN];
407 
408 	if (!snap_file_path(zonename, path))
409 		return (Z_MISC_FS);
410 	return (zonecfg_destroy_impl(path));
411 }
412 
413 static int
414 getroot(zone_dochandle_t handle, xmlNodePtr *root)
415 {
416 	if (zonecfg_check_handle(handle) == Z_BAD_HANDLE)
417 		return (Z_BAD_HANDLE);
418 
419 	*root = xmlDocGetRootElement(handle->zone_dh_doc);
420 
421 	if (*root == NULL)
422 		return (Z_EMPTY_DOCUMENT);
423 
424 	if (xmlStrcmp((*root)->name, DTD_ELEM_ZONE))
425 		return (Z_WRONG_DOC_TYPE);
426 
427 	return (Z_OK);
428 }
429 
430 static int
431 operation_prep(zone_dochandle_t handle)
432 {
433 	xmlNodePtr root;
434 	int err;
435 
436 	if ((err = getroot(handle, &root)) != 0)
437 		return (err);
438 
439 	handle->zone_dh_cur = root;
440 	handle->zone_dh_top = root;
441 	return (Z_OK);
442 }
443 
444 static int
445 fetchprop(xmlNodePtr cur, const xmlChar *propname, char *dst, size_t dstsize)
446 {
447 	xmlChar *property;
448 	size_t srcsize;
449 
450 	if ((property = xmlGetProp(cur, propname)) == NULL)
451 		return (Z_BAD_PROPERTY);
452 	srcsize = strlcpy(dst, (char *)property, dstsize);
453 	xmlFree(property);
454 	if (srcsize >= dstsize)
455 		return (Z_TOO_BIG);
456 	return (Z_OK);
457 }
458 
459 static int
460 fetch_alloc_prop(xmlNodePtr cur, const xmlChar *propname, char **dst)
461 {
462 	xmlChar *property;
463 
464 	if ((property = xmlGetProp(cur, propname)) == NULL)
465 		return (Z_BAD_PROPERTY);
466 	if ((*dst = strdup((char *)property)) == NULL) {
467 		xmlFree(property);
468 		return (Z_NOMEM);
469 	}
470 	xmlFree(property);
471 	return (Z_OK);
472 }
473 
474 static int
475 getrootattr(zone_dochandle_t handle, const xmlChar *propname,
476     char *propval, size_t propsize)
477 {
478 	xmlNodePtr root;
479 	int err;
480 
481 	if ((err = getroot(handle, &root)) != 0)
482 		return (err);
483 
484 	return (fetchprop(root, propname, propval, propsize));
485 }
486 
487 static int
488 get_alloc_rootattr(zone_dochandle_t handle, const xmlChar *propname,
489     char **propval)
490 {
491 	xmlNodePtr root;
492 	int err;
493 
494 	if ((err = getroot(handle, &root)) != 0)
495 		return (err);
496 
497 	return (fetch_alloc_prop(root, propname, propval));
498 }
499 
500 static int
501 setrootattr(zone_dochandle_t handle, const xmlChar *propname,
502     const char *propval)
503 {
504 	int err;
505 	xmlNodePtr root;
506 
507 	if ((err = getroot(handle, &root)) != Z_OK)
508 		return (err);
509 
510 	/*
511 	 * If we get a null propval remove the property (ignore return since it
512 	 * may not be set to begin with).
513 	 */
514 	if (propval == NULL) {
515 		(void) xmlUnsetProp(root, propname);
516 	} else {
517 		if (xmlSetProp(root, propname, (const xmlChar *) propval)
518 		    == NULL)
519 			return (Z_INVAL);
520 	}
521 	return (Z_OK);
522 }
523 
524 static void
525 addcomment(zone_dochandle_t handle, const char *comment)
526 {
527 	xmlNodePtr node;
528 	node = xmlNewComment((xmlChar *) comment);
529 
530 	if (node != NULL)
531 		(void) xmlAddPrevSibling(handle->zone_dh_top, node);
532 }
533 
534 static void
535 stripcomments(zone_dochandle_t handle)
536 {
537 	xmlDocPtr top;
538 	xmlNodePtr child, next;
539 
540 	top = handle->zone_dh_doc;
541 	for (child = top->xmlChildrenNode; child != NULL; child = next) {
542 		next = child->next;
543 		if (child->name == NULL)
544 			continue;
545 		if (xmlStrcmp(child->name, DTD_ELEM_COMMENT) == 0) {
546 			next = child->next;
547 			xmlUnlinkNode(child);
548 			xmlFreeNode(child);
549 		}
550 	}
551 }
552 
553 static void
554 strip_sw_inv(zone_dochandle_t handle)
555 {
556 	xmlNodePtr root, child, next;
557 
558 	root = xmlDocGetRootElement(handle->zone_dh_doc);
559 	for (child = root->xmlChildrenNode; child != NULL; child = next) {
560 		next = child->next;
561 		if (child->name == NULL)
562 			continue;
563 		if (xmlStrcmp(child->name, DTD_ELEM_PACKAGE) == 0 ||
564 		    xmlStrcmp(child->name, DTD_ELEM_PATCH) == 0) {
565 			next = child->next;
566 			xmlUnlinkNode(child);
567 			xmlFreeNode(child);
568 		}
569 	}
570 }
571 
572 static int
573 zonecfg_get_handle_impl(const char *zonename, const char *filename,
574     zone_dochandle_t handle)
575 {
576 	xmlValidCtxtPtr cvp;
577 	struct stat statbuf;
578 	int valid;
579 
580 	if (zonename == NULL)
581 		return (Z_NO_ZONE);
582 
583 	if ((handle->zone_dh_doc = xmlParseFile(filename)) == NULL) {
584 		/* distinguish file not found vs. found but not parsed */
585 		if (stat(filename, &statbuf) == 0)
586 			return (Z_INVALID_DOCUMENT);
587 		return (Z_NO_ZONE);
588 	}
589 	if ((cvp = xmlNewValidCtxt()) == NULL)
590 		return (Z_NOMEM);
591 	cvp->error = zonecfg_error_func;
592 	cvp->warning = zonecfg_error_func;
593 	valid = xmlValidateDocument(cvp, handle->zone_dh_doc);
594 	xmlFreeValidCtxt(cvp);
595 	if (valid == 0)
596 		return (Z_INVALID_DOCUMENT);
597 
598 	/* delete any comments such as inherited Sun copyright / ident str */
599 	stripcomments(handle);
600 	return (Z_OK);
601 }
602 
603 int
604 zonecfg_get_handle(const char *zonename, zone_dochandle_t handle)
605 {
606 	char path[MAXPATHLEN];
607 
608 	if (!config_file_path(zonename, path))
609 		return (Z_MISC_FS);
610 	handle->zone_dh_newzone = B_FALSE;
611 
612 	return (zonecfg_get_handle_impl(zonename, path, handle));
613 }
614 
615 int
616 zonecfg_get_attach_handle(const char *path, const char *fname,
617     const char *zonename, boolean_t preserve_sw, zone_dochandle_t handle)
618 {
619 	char		migpath[MAXPATHLEN];
620 	int		err;
621 	struct stat	buf;
622 
623 	if (snprintf(migpath, sizeof (migpath), "%s/root", path) >=
624 	    sizeof (migpath))
625 		return (Z_NOMEM);
626 
627 	if (stat(migpath, &buf) == -1 || !S_ISDIR(buf.st_mode))
628 		return (Z_NO_ZONE);
629 
630 	if (snprintf(migpath, sizeof (migpath), "%s/%s", path, fname) >=
631 	    sizeof (migpath))
632 		return (Z_NOMEM);
633 
634 	if ((err = zonecfg_get_handle_impl(zonename, migpath, handle)) != Z_OK)
635 		return (err);
636 
637 	if (!preserve_sw)
638 		strip_sw_inv(handle);
639 
640 	handle->zone_dh_newzone = B_TRUE;
641 	if ((err = setrootattr(handle, DTD_ATTR_ZONEPATH, path)) != Z_OK)
642 		return (err);
643 
644 	return (setrootattr(handle, DTD_ATTR_NAME, zonename));
645 }
646 
647 int
648 zonecfg_get_snapshot_handle(const char *zonename, zone_dochandle_t handle)
649 {
650 	char path[MAXPATHLEN];
651 
652 	if (!snap_file_path(zonename, path))
653 		return (Z_MISC_FS);
654 	handle->zone_dh_newzone = B_FALSE;
655 	return (zonecfg_get_handle_impl(zonename, path, handle));
656 }
657 
658 int
659 zonecfg_get_template_handle(const char *template, const char *zonename,
660     zone_dochandle_t handle)
661 {
662 	char path[MAXPATHLEN];
663 	int err;
664 
665 	if (!config_file_path(template, path))
666 		return (Z_MISC_FS);
667 
668 	if ((err = zonecfg_get_handle_impl(template, path, handle)) != Z_OK)
669 		return (err);
670 	handle->zone_dh_newzone = B_TRUE;
671 	return (setrootattr(handle, DTD_ATTR_NAME, zonename));
672 }
673 
674 int
675 zonecfg_get_xml_handle(const char *path, zone_dochandle_t handle)
676 {
677 	struct stat buf;
678 	int err;
679 
680 	if (stat(path, &buf) == -1)
681 		return (Z_MISC_FS);
682 
683 	if ((err = zonecfg_get_handle_impl("xml", path, handle)) != Z_OK)
684 		return (err);
685 	handle->zone_dh_newzone = B_TRUE;
686 	return (Z_OK);
687 }
688 
689 /*
690  * Initialize two handles from the manifest read on fd.  The rem_handle
691  * is initialized from the input file, including the sw inventory.  The
692  * local_handle is initialized with the same zone configuration but with
693  * no sw inventory.
694  */
695 int
696 zonecfg_attach_manifest(int fd, zone_dochandle_t local_handle,
697     zone_dochandle_t rem_handle)
698 {
699 	xmlValidCtxtPtr cvp;
700 	int valid;
701 
702 	/* load the manifest into the handle for the remote system */
703 	if ((rem_handle->zone_dh_doc = xmlReadFd(fd, NULL, NULL, 0)) == NULL) {
704 		return (Z_INVALID_DOCUMENT);
705 	}
706 	if ((cvp = xmlNewValidCtxt()) == NULL)
707 		return (Z_NOMEM);
708 	cvp->error = zonecfg_error_func;
709 	cvp->warning = zonecfg_error_func;
710 	valid = xmlValidateDocument(cvp, rem_handle->zone_dh_doc);
711 	xmlFreeValidCtxt(cvp);
712 	if (valid == 0)
713 		return (Z_INVALID_DOCUMENT);
714 
715 	/* delete any comments such as inherited Sun copyright / ident str */
716 	stripcomments(rem_handle);
717 
718 	rem_handle->zone_dh_newzone = B_TRUE;
719 	rem_handle->zone_dh_sw_inv = B_TRUE;
720 
721 	/*
722 	 * Now use the remote system handle to generate a local system handle
723 	 * with an identical zones configuration but no sw inventory.
724 	 */
725 	if ((local_handle->zone_dh_doc = xmlCopyDoc(rem_handle->zone_dh_doc,
726 	    1)) == NULL) {
727 		return (Z_INVALID_DOCUMENT);
728 	}
729 
730 	/*
731 	 * We need to re-run xmlValidateDocument on local_handle to properly
732 	 * update the in-core representation of the configuration.
733 	 */
734 	if ((cvp = xmlNewValidCtxt()) == NULL)
735 		return (Z_NOMEM);
736 	cvp->error = zonecfg_error_func;
737 	cvp->warning = zonecfg_error_func;
738 	valid = xmlValidateDocument(cvp, local_handle->zone_dh_doc);
739 	xmlFreeValidCtxt(cvp);
740 	if (valid == 0)
741 		return (Z_INVALID_DOCUMENT);
742 
743 	strip_sw_inv(local_handle);
744 
745 	local_handle->zone_dh_newzone = B_TRUE;
746 	local_handle->zone_dh_sw_inv = B_FALSE;
747 
748 	return (Z_OK);
749 }
750 
751 static boolean_t
752 is_renaming(zone_dochandle_t handle)
753 {
754 	if (handle->zone_dh_newzone)
755 		return (B_FALSE);
756 	if (strlen(handle->zone_dh_delete_name) > 0)
757 		return (B_TRUE);
758 	return (B_FALSE);
759 }
760 
761 static boolean_t
762 is_new(zone_dochandle_t handle)
763 {
764 	return (handle->zone_dh_newzone || handle->zone_dh_snapshot);
765 }
766 
767 static boolean_t
768 is_snapshot(zone_dochandle_t handle)
769 {
770 	return (handle->zone_dh_snapshot);
771 }
772 
773 /*
774  * It would be great to be able to use libc's ctype(3c) macros, but we
775  * can't, as they are locale sensitive, and it would break our limited thread
776  * safety if this routine had to change the app locale on the fly.
777  */
778 int
779 zonecfg_validate_zonename(const char *zone)
780 {
781 	int i;
782 
783 	if (strcmp(zone, GLOBAL_ZONENAME) == 0)
784 		return (Z_BOGUS_ZONE_NAME);
785 
786 	if (strlen(zone) >= ZONENAME_MAX)
787 		return (Z_BOGUS_ZONE_NAME);
788 
789 	if (!((zone[0] >= 'a' && zone[0] <= 'z') ||
790 	    (zone[0] >= 'A' && zone[0] <= 'Z') ||
791 	    (zone[0] >= '0' && zone[0] <= '9')))
792 		return (Z_BOGUS_ZONE_NAME);
793 
794 	for (i = 1; zone[i] != '\0'; i++) {
795 		if (!((zone[i] >= 'a' && zone[i] <= 'z') ||
796 		    (zone[i] >= 'A' && zone[i] <= 'Z') ||
797 		    (zone[i] >= '0' && zone[i] <= '9') ||
798 		    (zone[i] == '-') || (zone[i] == '_') || (zone[i] == '.')))
799 			return (Z_BOGUS_ZONE_NAME);
800 	}
801 
802 	return (Z_OK);
803 }
804 
805 /*
806  * Changing the zone name requires us to track both the old and new
807  * name of the zone until commit time.
808  */
809 int
810 zonecfg_get_name(zone_dochandle_t handle, char *name, size_t namesize)
811 {
812 	return (getrootattr(handle, DTD_ATTR_NAME, name, namesize));
813 }
814 
815 static int
816 insert_admins(zone_dochandle_t handle, char *zonename)
817 {
818 	int err;
819 	struct zone_admintab admintab;
820 
821 	if ((err = zonecfg_setadminent(handle)) != Z_OK) {
822 		return (err);
823 	}
824 	while (zonecfg_getadminent(handle, &admintab) == Z_OK) {
825 		err = zonecfg_insert_userauths(handle,
826 		    admintab.zone_admin_user, zonename);
827 		if (err != Z_OK) {
828 			(void) zonecfg_endadminent(handle);
829 			return (err);
830 		}
831 	}
832 	(void) zonecfg_endadminent(handle);
833 	return (Z_OK);
834 }
835 
836 int
837 zonecfg_set_name(zone_dochandle_t handle, char *name)
838 {
839 	zone_state_t state;
840 	char curname[ZONENAME_MAX], old_delname[ZONENAME_MAX];
841 	int err;
842 
843 	if ((err = getrootattr(handle, DTD_ATTR_NAME, curname,
844 	    sizeof (curname))) != Z_OK)
845 		return (err);
846 
847 	if (strcmp(name, curname) == 0)
848 		return (Z_OK);
849 
850 	/*
851 	 * Switching zone names to one beginning with SUNW is not permitted.
852 	 */
853 	if (strncmp(name, "SUNW", 4) == 0)
854 		return (Z_BOGUS_ZONE_NAME);
855 
856 	if ((err = zonecfg_validate_zonename(name)) != Z_OK)
857 		return (err);
858 
859 	/*
860 	 * Setting the name back to the original name (effectively a revert of
861 	 * the name) is fine.  But if we carry on, we'll falsely identify the
862 	 * name as "in use," so special case here.
863 	 */
864 	if (strcmp(name, handle->zone_dh_delete_name) == 0) {
865 		err = setrootattr(handle, DTD_ATTR_NAME, name);
866 		handle->zone_dh_delete_name[0] = '\0';
867 		return (err);
868 	}
869 
870 	/* Check to see if new name chosen is already in use */
871 	if (zone_get_state(name, &state) != Z_NO_ZONE)
872 		return (Z_NAME_IN_USE);
873 
874 	/*
875 	 * If this isn't already "new" or in a renaming transition, then
876 	 * we're initiating a rename here; so stash the "delete name"
877 	 * (i.e. the name of the zone we'll be removing) for the rename.
878 	 */
879 	(void) strlcpy(old_delname, handle->zone_dh_delete_name,
880 	    sizeof (old_delname));
881 	if (!is_new(handle) && !is_renaming(handle)) {
882 		/*
883 		 * Name change is allowed only when the zone we're altering
884 		 * is not ready or running.
885 		 */
886 		err = zone_get_state(curname, &state);
887 		if (err == Z_OK) {
888 			if (state > ZONE_STATE_INSTALLED)
889 				return (Z_BAD_ZONE_STATE);
890 		} else if (err != Z_NO_ZONE) {
891 			return (err);
892 		}
893 
894 		(void) strlcpy(handle->zone_dh_delete_name, curname,
895 		    sizeof (handle->zone_dh_delete_name));
896 		assert(is_renaming(handle));
897 	} else if (is_renaming(handle)) {
898 		err = zone_get_state(handle->zone_dh_delete_name, &state);
899 		if (err == Z_OK) {
900 			if (state > ZONE_STATE_INSTALLED)
901 				return (Z_BAD_ZONE_STATE);
902 		} else if (err != Z_NO_ZONE) {
903 			return (err);
904 		}
905 	}
906 
907 	if ((err = setrootattr(handle, DTD_ATTR_NAME, name)) != Z_OK) {
908 		/*
909 		 * Restore the deletename to whatever it was at the
910 		 * top of the routine, since we've had a failure.
911 		 */
912 		(void) strlcpy(handle->zone_dh_delete_name, old_delname,
913 		    sizeof (handle->zone_dh_delete_name));
914 		return (err);
915 	}
916 
917 	/*
918 	 * Record the old admins from the old zonename
919 	 * so that they can be deleted when the operation is committed.
920 	 */
921 	if ((err = insert_admins(handle, curname)) != Z_OK)
922 		return (err);
923 	else
924 		return (Z_OK);
925 }
926 
927 int
928 zonecfg_get_zonepath(zone_dochandle_t handle, char *path, size_t pathsize)
929 {
930 	size_t len;
931 
932 	if ((len = strlcpy(path, zonecfg_root, pathsize)) >= pathsize)
933 		return (Z_TOO_BIG);
934 	return (getrootattr(handle, DTD_ATTR_ZONEPATH, path + len,
935 	    pathsize - len));
936 }
937 
938 int
939 zonecfg_set_zonepath(zone_dochandle_t handle, char *zonepath)
940 {
941 	size_t len;
942 	char *modpath, *copy_mp, *curr_mp;	/* modified path ptrs */
943 	char last_copied;
944 	int ret;
945 
946 	/*
947 	 * Collapse multiple contiguous slashes and remove trailing slash.
948 	 */
949 	modpath = strdup(zonepath);
950 	if (modpath == NULL)
951 		return (Z_NOMEM);
952 	last_copied = '\0';
953 	for (copy_mp = curr_mp = modpath; *curr_mp != '\0'; curr_mp++) {
954 		if (*curr_mp != '/' || last_copied != '/') {
955 			last_copied = *copy_mp = *curr_mp;
956 			copy_mp++;
957 		}
958 	}
959 	if (last_copied == '/')
960 		copy_mp--;
961 	*copy_mp = '\0';
962 
963 	/*
964 	 * The user deals in absolute paths in the running global zone, but the
965 	 * internal configuration files deal with boot environment relative
966 	 * paths.  Strip out the alternate root when specified.
967 	 */
968 	len = strlen(zonecfg_root);
969 	if (strncmp(modpath, zonecfg_root, len) != 0 || modpath[len] != '/') {
970 		free(modpath);
971 		return (Z_BAD_PROPERTY);
972 	}
973 	curr_mp = modpath + len;
974 	ret = setrootattr(handle, DTD_ATTR_ZONEPATH, curr_mp);
975 	free(modpath);
976 	return (ret);
977 }
978 
979 static int
980 i_zonecfg_get_brand(zone_dochandle_t handle, char *brand, size_t brandsize,
981     boolean_t default_query)
982 {
983 	int ret, sz;
984 
985 	ret = getrootattr(handle, DTD_ATTR_BRAND, brand, brandsize);
986 
987 	/*
988 	 * If the lookup failed, or succeeded in finding a non-null brand
989 	 * string then return.
990 	 */
991 	if (ret != Z_OK || brand[0] != '\0')
992 		return (ret);
993 
994 	if (!default_query) {
995 		/* If the zone has no brand, it is the default brand. */
996 		return (zonecfg_default_brand(brand, brandsize));
997 	}
998 
999 	/* if SUNWdefault didn't specify a brand, fallback to "native" */
1000 	sz = strlcpy(brand, NATIVE_BRAND_NAME, brandsize);
1001 	if (sz >= brandsize)
1002 		return (Z_TOO_BIG);
1003 	return (Z_OK);
1004 }
1005 
1006 int
1007 zonecfg_get_brand(zone_dochandle_t handle, char *brand, size_t brandsize)
1008 {
1009 	return (i_zonecfg_get_brand(handle, brand, brandsize, B_FALSE));
1010 }
1011 
1012 int
1013 zonecfg_set_brand(zone_dochandle_t handle, char *brand)
1014 {
1015 	return (setrootattr(handle, DTD_ATTR_BRAND, brand));
1016 }
1017 
1018 int
1019 zonecfg_get_autoboot(zone_dochandle_t handle, boolean_t *autoboot)
1020 {
1021 	char autobootstr[DTD_ENTITY_BOOL_LEN];
1022 	int ret;
1023 
1024 	if ((ret = getrootattr(handle, DTD_ATTR_AUTOBOOT, autobootstr,
1025 	    sizeof (autobootstr))) != Z_OK)
1026 		return (ret);
1027 
1028 	if (strcmp(autobootstr, DTD_ENTITY_TRUE) == 0)
1029 		*autoboot = B_TRUE;
1030 	else if (strcmp(autobootstr, DTD_ENTITY_FALSE) == 0)
1031 		*autoboot = B_FALSE;
1032 	else
1033 		ret = Z_BAD_PROPERTY;
1034 	return (ret);
1035 }
1036 
1037 int
1038 zonecfg_set_autoboot(zone_dochandle_t handle, boolean_t autoboot)
1039 {
1040 	return (setrootattr(handle, DTD_ATTR_AUTOBOOT,
1041 	    autoboot ? DTD_ENTITY_TRUE : DTD_ENTITY_FALSE));
1042 }
1043 
1044 int
1045 zonecfg_get_pool(zone_dochandle_t handle, char *pool, size_t poolsize)
1046 {
1047 	return (getrootattr(handle, DTD_ATTR_POOL, pool, poolsize));
1048 }
1049 
1050 int
1051 zonecfg_set_pool(zone_dochandle_t handle, char *pool)
1052 {
1053 	return (setrootattr(handle, DTD_ATTR_POOL, pool));
1054 }
1055 
1056 int
1057 zonecfg_get_limitpriv(zone_dochandle_t handle, char **limitpriv)
1058 {
1059 	return (get_alloc_rootattr(handle, DTD_ATTR_LIMITPRIV, limitpriv));
1060 }
1061 
1062 int
1063 zonecfg_set_limitpriv(zone_dochandle_t handle, char *limitpriv)
1064 {
1065 	return (setrootattr(handle, DTD_ATTR_LIMITPRIV, limitpriv));
1066 }
1067 
1068 int
1069 zonecfg_get_bootargs(zone_dochandle_t handle, char *bargs, size_t bargssize)
1070 {
1071 	return (getrootattr(handle, DTD_ATTR_BOOTARGS, bargs, bargssize));
1072 }
1073 
1074 int
1075 zonecfg_set_bootargs(zone_dochandle_t handle, char *bargs)
1076 {
1077 	return (setrootattr(handle, DTD_ATTR_BOOTARGS, bargs));
1078 }
1079 
1080 int
1081 zonecfg_get_sched_class(zone_dochandle_t handle, char *sched, size_t schedsize)
1082 {
1083 	return (getrootattr(handle, DTD_ATTR_SCHED, sched, schedsize));
1084 }
1085 
1086 int
1087 zonecfg_set_sched(zone_dochandle_t handle, char *sched)
1088 {
1089 	return (setrootattr(handle, DTD_ATTR_SCHED, sched));
1090 }
1091 
1092 /*
1093  * /etc/zones/index caches a vital piece of information which is also
1094  * in the <zonename>.xml file: the path to the zone.  This is for performance,
1095  * since we need to walk all zonepath's in order to be able to detect conflicts
1096  * (see crosscheck_zonepaths() in the zoneadm command).
1097  *
1098  * An additional complexity is that when doing a rename, we'd like the entire
1099  * index update operation (rename, and potential state changes) to be atomic.
1100  * In general, the operation of this function should succeed or fail as
1101  * a unit.
1102  */
1103 int
1104 zonecfg_refresh_index_file(zone_dochandle_t handle)
1105 {
1106 	char name[ZONENAME_MAX], zonepath[MAXPATHLEN];
1107 	struct zoneent ze;
1108 	int err;
1109 	int opcode;
1110 	char *zn;
1111 
1112 	bzero(&ze, sizeof (ze));
1113 	ze.zone_state = -1;	/* Preserve existing state in index */
1114 
1115 	if ((err = zonecfg_get_name(handle, name, sizeof (name))) != Z_OK)
1116 		return (err);
1117 	(void) strlcpy(ze.zone_name, name, sizeof (ze.zone_name));
1118 
1119 	if ((err = zonecfg_get_zonepath(handle, zonepath,
1120 	    sizeof (zonepath))) != Z_OK)
1121 		return (err);
1122 	(void) strlcpy(ze.zone_path, zonepath + strlen(zonecfg_root),
1123 	    sizeof (ze.zone_path));
1124 
1125 	if (is_renaming(handle)) {
1126 		opcode = PZE_MODIFY;
1127 		(void) strlcpy(ze.zone_name, handle->zone_dh_delete_name,
1128 		    sizeof (ze.zone_name));
1129 		(void) strlcpy(ze.zone_newname, name, sizeof (ze.zone_newname));
1130 	} else if (is_new(handle)) {
1131 		FILE *cookie;
1132 		/*
1133 		 * Be tolerant of the zone already existing in the index file,
1134 		 * since we might be forcibly overwriting an existing
1135 		 * configuration with a new one (for example 'create -F'
1136 		 * in zonecfg).
1137 		 */
1138 		opcode = PZE_ADD;
1139 		cookie = setzoneent();
1140 		while ((zn = getzoneent(cookie)) != NULL) {
1141 			if (strcmp(zn, name) == 0) {
1142 				opcode = PZE_MODIFY;
1143 				free(zn);
1144 				break;
1145 			}
1146 			free(zn);
1147 		}
1148 		endzoneent(cookie);
1149 		ze.zone_state = ZONE_STATE_CONFIGURED;
1150 	} else {
1151 		opcode = PZE_MODIFY;
1152 	}
1153 
1154 	if ((err = putzoneent(&ze, opcode)) != Z_OK)
1155 		return (err);
1156 
1157 	return (Z_OK);
1158 }
1159 
1160 /*
1161  * The goal of this routine is to cause the index file update and the
1162  * document save to happen as an atomic operation.  We do the document
1163  * first, saving a backup copy using a hard link; if that succeeds, we go
1164  * on to the index.  If that fails, we roll the document back into place.
1165  *
1166  * Strategy:
1167  *
1168  * New zone 'foo' configuration:
1169  * 	Create tmpfile (zonecfg.xxxxxx)
1170  * 	Write XML to tmpfile
1171  * 	Rename tmpfile to xmlfile (zonecfg.xxxxxx -> foo.xml)
1172  * 	Add entry to index file
1173  * 	If it fails, delete foo.xml, leaving nothing behind.
1174  *
1175  * Save existing zone 'foo':
1176  * 	Make backup of foo.xml -> .backup
1177  * 	Create tmpfile (zonecfg.xxxxxx)
1178  * 	Write XML to tmpfile
1179  * 	Rename tmpfile to xmlfile (zonecfg.xxxxxx -> foo.xml)
1180  * 	Modify index file as needed
1181  * 	If it fails, recover from .backup -> foo.xml
1182  *
1183  * Rename 'foo' to 'bar':
1184  * 	Create tmpfile (zonecfg.xxxxxx)
1185  * 	Write XML to tmpfile
1186  * 	Rename tmpfile to xmlfile (zonecfg.xxxxxx -> bar.xml)
1187  * 	Add entry for 'bar' to index file, Remove entry for 'foo' (refresh)
1188  * 	If it fails, delete bar.xml; foo.xml is left behind.
1189  */
1190 static int
1191 zonecfg_save_impl(zone_dochandle_t handle, char *filename)
1192 {
1193 	char tmpfile[MAXPATHLEN];
1194 	char bakdir[MAXPATHLEN], bakbase[MAXPATHLEN], bakfile[MAXPATHLEN];
1195 	int tmpfd, err, valid;
1196 	xmlValidCtxt cvp = { NULL };
1197 	boolean_t backup;
1198 
1199 	(void) strlcpy(tmpfile, filename, sizeof (tmpfile));
1200 	(void) dirname(tmpfile);
1201 	(void) strlcat(tmpfile, _PATH_TMPFILE, sizeof (tmpfile));
1202 
1203 	tmpfd = mkstemp(tmpfile);
1204 	if (tmpfd == -1) {
1205 		(void) unlink(tmpfile);
1206 		return (Z_TEMP_FILE);
1207 	}
1208 	(void) close(tmpfd);
1209 
1210 	cvp.error = zonecfg_error_func;
1211 	cvp.warning = zonecfg_error_func;
1212 
1213 	/*
1214 	 * We do a final validation of the document.  Since the library has
1215 	 * malfunctioned if it fails to validate, we follow-up with an
1216 	 * assert() that the doc is valid.
1217 	 */
1218 	valid = xmlValidateDocument(&cvp, handle->zone_dh_doc);
1219 	assert(valid != 0);
1220 
1221 	if (xmlSaveFormatFile(tmpfile, handle->zone_dh_doc, 1) <= 0)
1222 		goto err;
1223 
1224 	(void) chmod(tmpfile, 0644);
1225 
1226 	/*
1227 	 * In the event we are doing a standard save, hard link a copy of the
1228 	 * original file in .backup.<pid>.filename so we can restore it if
1229 	 * something goes wrong.
1230 	 */
1231 	if (!is_new(handle) && !is_renaming(handle)) {
1232 		backup = B_TRUE;
1233 
1234 		(void) strlcpy(bakdir, filename, sizeof (bakdir));
1235 		(void) strlcpy(bakbase, filename, sizeof (bakbase));
1236 		(void) snprintf(bakfile, sizeof (bakfile), "%s/.backup.%d.%s",
1237 		    dirname(bakdir), getpid(), basename(bakbase));
1238 
1239 		if (link(filename, bakfile) == -1) {
1240 			err = errno;
1241 			(void) unlink(tmpfile);
1242 			if (errno == EACCES)
1243 				return (Z_ACCES);
1244 			return (Z_MISC_FS);
1245 		}
1246 	}
1247 
1248 	/*
1249 	 * Move the new document over top of the old.
1250 	 * i.e.:   zonecfg.XXXXXX  ->  myzone.xml
1251 	 */
1252 	if (rename(tmpfile, filename) == -1) {
1253 		err = errno;
1254 		(void) unlink(tmpfile);
1255 		if (backup)
1256 			(void) unlink(bakfile);
1257 		if (err == EACCES)
1258 			return (Z_ACCES);
1259 		return (Z_MISC_FS);
1260 	}
1261 
1262 	/*
1263 	 * If this is a snapshot, we're done-- don't add an index entry.
1264 	 */
1265 	if (is_snapshot(handle))
1266 		return (Z_OK);
1267 
1268 	/* now update the index file to reflect whatever we just did */
1269 	if ((err = zonecfg_refresh_index_file(handle)) != Z_OK) {
1270 		if (backup) {
1271 			/*
1272 			 * Try to restore from our backup.
1273 			 */
1274 			(void) unlink(filename);
1275 			(void) rename(bakfile, filename);
1276 		} else {
1277 			/*
1278 			 * Either the zone is new, in which case we can delete
1279 			 * new.xml, or we're doing a rename, so ditto.
1280 			 */
1281 			assert(is_new(handle) || is_renaming(handle));
1282 			(void) unlink(filename);
1283 		}
1284 		return (Z_UPDATING_INDEX);
1285 	}
1286 
1287 	if (backup)
1288 		(void) unlink(bakfile);
1289 
1290 	return (Z_OK);
1291 
1292 err:
1293 	(void) unlink(tmpfile);
1294 	return (Z_SAVING_FILE);
1295 }
1296 
1297 int
1298 zonecfg_save(zone_dochandle_t handle)
1299 {
1300 	char zname[ZONENAME_MAX], path[MAXPATHLEN];
1301 	char delpath[MAXPATHLEN];
1302 	int err = Z_SAVING_FILE;
1303 
1304 	if (zonecfg_check_handle(handle) != Z_OK)
1305 		return (Z_BAD_HANDLE);
1306 
1307 	/*
1308 	 * We don't support saving snapshots or a tree containing a sw
1309 	 * inventory at this time.
1310 	 */
1311 	if (handle->zone_dh_snapshot || handle->zone_dh_sw_inv)
1312 		return (Z_INVAL);
1313 
1314 	if ((err = zonecfg_get_name(handle, zname, sizeof (zname))) != Z_OK)
1315 		return (err);
1316 
1317 	if (!config_file_path(zname, path))
1318 		return (Z_MISC_FS);
1319 
1320 	addcomment(handle, "\n    DO NOT EDIT THIS "
1321 	    "FILE.  Use zonecfg(1M) instead.\n");
1322 
1323 	/*
1324 	 * Update user_attr first so that it will be older
1325 	 * than the config file.
1326 	 */
1327 	(void) zonecfg_authorize_users(handle, zname);
1328 	err = zonecfg_save_impl(handle, path);
1329 
1330 	stripcomments(handle);
1331 
1332 	if (err != Z_OK)
1333 		return (err);
1334 
1335 	handle->zone_dh_newzone = B_FALSE;
1336 
1337 	if (is_renaming(handle)) {
1338 		if (config_file_path(handle->zone_dh_delete_name, delpath))
1339 			(void) unlink(delpath);
1340 		handle->zone_dh_delete_name[0] = '\0';
1341 	}
1342 
1343 	return (Z_OK);
1344 }
1345 
1346 int
1347 zonecfg_verify_save(zone_dochandle_t handle, char *filename)
1348 {
1349 	int valid;
1350 
1351 	xmlValidCtxt cvp = { NULL };
1352 
1353 	if (zonecfg_check_handle(handle) != Z_OK)
1354 		return (Z_BAD_HANDLE);
1355 
1356 	cvp.error = zonecfg_error_func;
1357 	cvp.warning = zonecfg_error_func;
1358 
1359 	/*
1360 	 * We do a final validation of the document.  Since the library has
1361 	 * malfunctioned if it fails to validate, we follow-up with an
1362 	 * assert() that the doc is valid.
1363 	 */
1364 	valid = xmlValidateDocument(&cvp, handle->zone_dh_doc);
1365 	assert(valid != 0);
1366 
1367 	if (xmlSaveFormatFile(filename, handle->zone_dh_doc, 1) <= 0)
1368 		return (Z_SAVING_FILE);
1369 
1370 	return (Z_OK);
1371 }
1372 
1373 int
1374 zonecfg_detach_save(zone_dochandle_t handle, uint_t flags)
1375 {
1376 	char zname[ZONENAME_MAX];
1377 	char path[MAXPATHLEN];
1378 	char migpath[MAXPATHLEN];
1379 	xmlValidCtxt cvp = { NULL };
1380 	int err = Z_SAVING_FILE;
1381 	int valid;
1382 
1383 	if (zonecfg_check_handle(handle) != Z_OK)
1384 		return (Z_BAD_HANDLE);
1385 
1386 	if (flags & ZONE_DRY_RUN) {
1387 		(void) strlcpy(migpath, "-", sizeof (migpath));
1388 	} else {
1389 		if ((err = zonecfg_get_name(handle, zname, sizeof (zname)))
1390 		    != Z_OK)
1391 			return (err);
1392 
1393 		if ((err = zone_get_zonepath(zname, path, sizeof (path)))
1394 		    != Z_OK)
1395 			return (err);
1396 
1397 		if (snprintf(migpath, sizeof (migpath), "%s/%s", path,
1398 		    ZONE_DETACHED) >= sizeof (migpath))
1399 			return (Z_NOMEM);
1400 	}
1401 
1402 	if ((err = operation_prep(handle)) != Z_OK)
1403 		return (err);
1404 
1405 	addcomment(handle, "\n    DO NOT EDIT THIS FILE.  "
1406 	    "Use zonecfg(1M) and zoneadm(1M) attach.\n");
1407 
1408 	cvp.error = zonecfg_error_func;
1409 	cvp.warning = zonecfg_error_func;
1410 
1411 	/*
1412 	 * We do a final validation of the document.  Since the library has
1413 	 * malfunctioned if it fails to validate, we follow-up with an
1414 	 * assert() that the doc is valid.
1415 	 */
1416 	valid = xmlValidateDocument(&cvp, handle->zone_dh_doc);
1417 	assert(valid != 0);
1418 
1419 	if (xmlSaveFormatFile(migpath, handle->zone_dh_doc, 1) <= 0)
1420 		return (Z_SAVING_FILE);
1421 
1422 	if (!(flags & ZONE_DRY_RUN))
1423 		(void) chmod(migpath, 0644);
1424 
1425 	stripcomments(handle);
1426 
1427 	handle->zone_dh_newzone = B_FALSE;
1428 
1429 	return (Z_OK);
1430 }
1431 
1432 boolean_t
1433 zonecfg_detached(const char *path)
1434 {
1435 	char		migpath[MAXPATHLEN];
1436 	struct stat	buf;
1437 
1438 	if (snprintf(migpath, sizeof (migpath), "%s/%s", path, ZONE_DETACHED) >=
1439 	    sizeof (migpath))
1440 		return (B_FALSE);
1441 
1442 	if (stat(migpath, &buf) != -1)
1443 		return (B_TRUE);
1444 
1445 	return (B_FALSE);
1446 }
1447 
1448 void
1449 zonecfg_rm_detached(zone_dochandle_t handle, boolean_t forced)
1450 {
1451 	char zname[ZONENAME_MAX];
1452 	char path[MAXPATHLEN];
1453 	char detached[MAXPATHLEN];
1454 	char attached[MAXPATHLEN];
1455 
1456 	if (zonecfg_check_handle(handle) != Z_OK)
1457 		return;
1458 
1459 	if (zonecfg_get_name(handle, zname, sizeof (zname)) != Z_OK)
1460 		return;
1461 
1462 	if (zone_get_zonepath(zname, path, sizeof (path)) != Z_OK)
1463 		return;
1464 
1465 	(void) snprintf(detached, sizeof (detached), "%s/%s", path,
1466 	    ZONE_DETACHED);
1467 	(void) snprintf(attached, sizeof (attached), "%s/%s", path,
1468 	    ATTACH_FORCED);
1469 
1470 	if (forced) {
1471 		(void) rename(detached, attached);
1472 	} else {
1473 		(void) unlink(attached);
1474 		(void) unlink(detached);
1475 	}
1476 }
1477 
1478 /*
1479  * Special case: if access(2) fails with ENOENT, then try again using
1480  * ZONE_CONFIG_ROOT instead of config_file_path(zonename).  This is how we
1481  * work around the case of a config file which has not been created yet:
1482  * the user will need access to the directory so use that as a heuristic.
1483  */
1484 
1485 int
1486 zonecfg_access(const char *zonename, int amode)
1487 {
1488 	char path[MAXPATHLEN];
1489 
1490 	if (!config_file_path(zonename, path))
1491 		return (Z_INVAL);
1492 	if (access(path, amode) == 0)
1493 		return (Z_OK);
1494 	if (errno == ENOENT) {
1495 		if (snprintf(path, sizeof (path), "%s%s", zonecfg_root,
1496 		    ZONE_CONFIG_ROOT) >= sizeof (path))
1497 			return (Z_INVAL);
1498 		if (access(path, amode) == 0)
1499 			return (Z_OK);
1500 	}
1501 	if (errno == EACCES)
1502 		return (Z_ACCES);
1503 	if (errno == EINVAL)
1504 		return (Z_INVAL);
1505 	return (Z_MISC_FS);
1506 }
1507 
1508 int
1509 zonecfg_create_snapshot(const char *zonename)
1510 {
1511 	zone_dochandle_t handle;
1512 	char path[MAXPATHLEN], zonepath[MAXPATHLEN], rpath[MAXPATHLEN];
1513 	int error = Z_OK, res;
1514 
1515 	if ((handle = zonecfg_init_handle()) == NULL) {
1516 		return (Z_NOMEM);
1517 	}
1518 
1519 	handle->zone_dh_newzone = B_TRUE;
1520 	handle->zone_dh_snapshot = B_TRUE;
1521 
1522 	if ((error = zonecfg_get_handle(zonename, handle)) != Z_OK)
1523 		goto out;
1524 	if ((error = operation_prep(handle)) != Z_OK)
1525 		goto out;
1526 	error = zonecfg_get_zonepath(handle, zonepath, sizeof (zonepath));
1527 	if (error != Z_OK)
1528 		goto out;
1529 	if ((res = resolvepath(zonepath, rpath, sizeof (rpath))) == -1) {
1530 		error = Z_RESOLVED_PATH;
1531 		goto out;
1532 	}
1533 	/*
1534 	 * If the resolved path is not the same as the original path, then
1535 	 * save the resolved path in the snapshot, thus preventing any
1536 	 * potential problems down the line when zoneadmd goes to unmount
1537 	 * file systems and depends on initial string matches with resolved
1538 	 * paths.
1539 	 */
1540 	rpath[res] = '\0';
1541 	if (strcmp(zonepath, rpath) != 0) {
1542 		if ((error = zonecfg_set_zonepath(handle, rpath)) != Z_OK)
1543 			goto out;
1544 	}
1545 	if (snprintf(path, sizeof (path), "%s%s", zonecfg_root,
1546 	    ZONE_SNAPSHOT_ROOT) >= sizeof (path)) {
1547 		error = Z_MISC_FS;
1548 		goto out;
1549 	}
1550 	if ((mkdir(path, S_IRWXU) == -1) && (errno != EEXIST)) {
1551 		error = Z_MISC_FS;
1552 		goto out;
1553 	}
1554 
1555 	if (!snap_file_path(zonename, path)) {
1556 		error = Z_MISC_FS;
1557 		goto out;
1558 	}
1559 
1560 	addcomment(handle, "\n    DO NOT EDIT THIS FILE.  "
1561 	    "It is a snapshot of running zone state.\n");
1562 
1563 	error = zonecfg_save_impl(handle, path);
1564 
1565 	stripcomments(handle);
1566 
1567 out:
1568 	zonecfg_fini_handle(handle);
1569 	return (error);
1570 }
1571 
1572 int
1573 zonecfg_get_iptype(zone_dochandle_t handle, zone_iptype_t *iptypep)
1574 {
1575 	char property[10]; /* 10 is big enough for "shared"/"exclusive" */
1576 	int err;
1577 
1578 	err = getrootattr(handle, DTD_ATTR_IPTYPE, property, sizeof (property));
1579 	if (err == Z_BAD_PROPERTY) {
1580 		/* Return default value */
1581 		*iptypep = ZS_SHARED;
1582 		return (Z_OK);
1583 	} else if (err != Z_OK) {
1584 		return (err);
1585 	}
1586 
1587 	if (strlen(property) == 0 ||
1588 	    strcmp(property, "shared") == 0)
1589 		*iptypep = ZS_SHARED;
1590 	else if (strcmp(property, "exclusive") == 0)
1591 		*iptypep = ZS_EXCLUSIVE;
1592 	else
1593 		return (Z_INVAL);
1594 
1595 	return (Z_OK);
1596 }
1597 
1598 int
1599 zonecfg_set_iptype(zone_dochandle_t handle, zone_iptype_t iptype)
1600 {
1601 	xmlNodePtr cur;
1602 
1603 	if (handle == NULL)
1604 		return (Z_INVAL);
1605 
1606 	cur = xmlDocGetRootElement(handle->zone_dh_doc);
1607 	if (cur == NULL) {
1608 		return (Z_EMPTY_DOCUMENT);
1609 	}
1610 
1611 	if (xmlStrcmp(cur->name, DTD_ELEM_ZONE) != 0) {
1612 		return (Z_WRONG_DOC_TYPE);
1613 	}
1614 	switch (iptype) {
1615 	case ZS_SHARED:
1616 		/*
1617 		 * Since "shared" is the default, we don't write it to the
1618 		 * configuration file, so that it's easier to migrate those
1619 		 * zones elsewhere, eg., to systems which are not IP-Instances
1620 		 * aware.
1621 		 * xmlUnsetProp only fails when the attribute doesn't exist,
1622 		 * which we don't care.
1623 		 */
1624 		(void) xmlUnsetProp(cur, DTD_ATTR_IPTYPE);
1625 		break;
1626 	case ZS_EXCLUSIVE:
1627 		if (xmlSetProp(cur, DTD_ATTR_IPTYPE,
1628 		    (const xmlChar *) "exclusive") == NULL)
1629 			return (Z_INVAL);
1630 		break;
1631 	}
1632 	return (Z_OK);
1633 }
1634 
1635 static int
1636 newprop(xmlNodePtr node, const xmlChar *attrname, char *src)
1637 {
1638 	xmlAttrPtr newattr;
1639 
1640 	newattr = xmlNewProp(node, attrname, (xmlChar *)src);
1641 	if (newattr == NULL) {
1642 		xmlUnlinkNode(node);
1643 		xmlFreeNode(node);
1644 		return (Z_BAD_PROPERTY);
1645 	}
1646 	return (Z_OK);
1647 }
1648 
1649 static int
1650 zonecfg_add_filesystem_core(zone_dochandle_t handle, struct zone_fstab *tabptr)
1651 {
1652 	xmlNodePtr newnode, cur = handle->zone_dh_cur, options_node;
1653 	zone_fsopt_t *ptr;
1654 	int err;
1655 
1656 	newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_FS, NULL);
1657 	if ((err = newprop(newnode, DTD_ATTR_SPECIAL,
1658 	    tabptr->zone_fs_special)) != Z_OK)
1659 		return (err);
1660 	if (tabptr->zone_fs_raw[0] != '\0' &&
1661 	    (err = newprop(newnode, DTD_ATTR_RAW, tabptr->zone_fs_raw)) != Z_OK)
1662 		return (err);
1663 	if ((err = newprop(newnode, DTD_ATTR_DIR, tabptr->zone_fs_dir)) != Z_OK)
1664 		return (err);
1665 	if ((err = newprop(newnode, DTD_ATTR_TYPE,
1666 	    tabptr->zone_fs_type)) != Z_OK)
1667 		return (err);
1668 	if (tabptr->zone_fs_options != NULL) {
1669 		for (ptr = tabptr->zone_fs_options; ptr != NULL;
1670 		    ptr = ptr->zone_fsopt_next) {
1671 			options_node = xmlNewTextChild(newnode, NULL,
1672 			    DTD_ELEM_FSOPTION, NULL);
1673 			if ((err = newprop(options_node, DTD_ATTR_NAME,
1674 			    ptr->zone_fsopt_opt)) != Z_OK)
1675 				return (err);
1676 		}
1677 	}
1678 	return (Z_OK);
1679 }
1680 
1681 int
1682 zonecfg_add_filesystem(zone_dochandle_t handle, struct zone_fstab *tabptr)
1683 {
1684 	int err;
1685 
1686 	if (tabptr == NULL)
1687 		return (Z_INVAL);
1688 
1689 	if ((err = operation_prep(handle)) != Z_OK)
1690 		return (err);
1691 
1692 	if ((err = zonecfg_add_filesystem_core(handle, tabptr)) != Z_OK)
1693 		return (err);
1694 
1695 	return (Z_OK);
1696 }
1697 
1698 static int
1699 zonecfg_add_ipd_core(zone_dochandle_t handle, struct zone_fstab *tabptr)
1700 {
1701 	xmlNodePtr newnode, cur = handle->zone_dh_cur;
1702 	int err;
1703 
1704 	newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_IPD, NULL);
1705 	if ((err = newprop(newnode, DTD_ATTR_DIR, tabptr->zone_fs_dir)) != Z_OK)
1706 		return (err);
1707 	return (Z_OK);
1708 }
1709 
1710 int
1711 zonecfg_add_ipd(zone_dochandle_t handle, struct zone_fstab *tabptr)
1712 {
1713 	int err;
1714 
1715 	if (tabptr == NULL)
1716 		return (Z_INVAL);
1717 
1718 	if ((err = operation_prep(handle)) != Z_OK)
1719 		return (err);
1720 
1721 	if ((err = zonecfg_add_ipd_core(handle, tabptr)) != Z_OK)
1722 		return (err);
1723 
1724 	return (Z_OK);
1725 }
1726 
1727 int
1728 zonecfg_add_fs_option(struct zone_fstab *tabptr, char *option)
1729 {
1730 	zone_fsopt_t *last, *old, *new;
1731 
1732 	last = tabptr->zone_fs_options;
1733 	for (old = last; old != NULL; old = old->zone_fsopt_next)
1734 		last = old;	/* walk to the end of the list */
1735 	new = (zone_fsopt_t *)malloc(sizeof (zone_fsopt_t));
1736 	if (new == NULL)
1737 		return (Z_NOMEM);
1738 	(void) strlcpy(new->zone_fsopt_opt, option,
1739 	    sizeof (new->zone_fsopt_opt));
1740 	new->zone_fsopt_next = NULL;
1741 	if (last == NULL)
1742 		tabptr->zone_fs_options = new;
1743 	else
1744 		last->zone_fsopt_next = new;
1745 	return (Z_OK);
1746 }
1747 
1748 int
1749 zonecfg_remove_fs_option(struct zone_fstab *tabptr, char *option)
1750 {
1751 	zone_fsopt_t *last, *this, *next;
1752 
1753 	last = tabptr->zone_fs_options;
1754 	for (this = last; this != NULL; this = this->zone_fsopt_next) {
1755 		if (strcmp(this->zone_fsopt_opt, option) == 0) {
1756 			next = this->zone_fsopt_next;
1757 			if (this == tabptr->zone_fs_options)
1758 				tabptr->zone_fs_options = next;
1759 			else
1760 				last->zone_fsopt_next = next;
1761 			free(this);
1762 			return (Z_OK);
1763 		} else
1764 			last = this;
1765 	}
1766 	return (Z_NO_PROPERTY_ID);
1767 }
1768 
1769 void
1770 zonecfg_free_fs_option_list(zone_fsopt_t *list)
1771 {
1772 	zone_fsopt_t *this, *next;
1773 
1774 	for (this = list; this != NULL; this = next) {
1775 		next = this->zone_fsopt_next;
1776 		free(this);
1777 	}
1778 }
1779 
1780 void
1781 zonecfg_free_rctl_value_list(struct zone_rctlvaltab *valtab)
1782 {
1783 	if (valtab == NULL)
1784 		return;
1785 	zonecfg_free_rctl_value_list(valtab->zone_rctlval_next);
1786 	free(valtab);
1787 }
1788 
1789 static boolean_t
1790 match_prop(xmlNodePtr cur, const xmlChar *attr, char *user_prop)
1791 {
1792 	xmlChar *gotten_prop;
1793 	int prop_result;
1794 
1795 	gotten_prop = xmlGetProp(cur, attr);
1796 	if (gotten_prop == NULL)	/* shouldn't happen */
1797 		return (B_FALSE);
1798 	prop_result = xmlStrcmp(gotten_prop, (const xmlChar *) user_prop);
1799 	xmlFree(gotten_prop);
1800 	return ((prop_result == 0));
1801 }
1802 
1803 static int
1804 zonecfg_delete_filesystem_core(zone_dochandle_t handle,
1805     struct zone_fstab *tabptr)
1806 {
1807 	xmlNodePtr cur = handle->zone_dh_cur;
1808 	boolean_t dir_match, spec_match, raw_match, type_match;
1809 
1810 	for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
1811 		if (xmlStrcmp(cur->name, DTD_ELEM_FS))
1812 			continue;
1813 		dir_match = match_prop(cur, DTD_ATTR_DIR, tabptr->zone_fs_dir);
1814 		spec_match = match_prop(cur, DTD_ATTR_SPECIAL,
1815 		    tabptr->zone_fs_special);
1816 		raw_match = match_prop(cur, DTD_ATTR_RAW,
1817 		    tabptr->zone_fs_raw);
1818 		type_match = match_prop(cur, DTD_ATTR_TYPE,
1819 		    tabptr->zone_fs_type);
1820 		if (dir_match && spec_match && raw_match && type_match) {
1821 			xmlUnlinkNode(cur);
1822 			xmlFreeNode(cur);
1823 			return (Z_OK);
1824 		}
1825 	}
1826 	return (Z_NO_RESOURCE_ID);
1827 }
1828 
1829 int
1830 zonecfg_delete_filesystem(zone_dochandle_t handle, struct zone_fstab *tabptr)
1831 {
1832 	int err;
1833 
1834 	if (tabptr == NULL)
1835 		return (Z_INVAL);
1836 
1837 	if ((err = operation_prep(handle)) != Z_OK)
1838 		return (err);
1839 
1840 	if ((err = zonecfg_delete_filesystem_core(handle, tabptr)) != Z_OK)
1841 		return (err);
1842 
1843 	return (Z_OK);
1844 }
1845 
1846 int
1847 zonecfg_modify_filesystem(
1848 	zone_dochandle_t handle,
1849 	struct zone_fstab *oldtabptr,
1850 	struct zone_fstab *newtabptr)
1851 {
1852 	int err;
1853 
1854 	if (oldtabptr == NULL || newtabptr == NULL)
1855 		return (Z_INVAL);
1856 
1857 	if ((err = operation_prep(handle)) != Z_OK)
1858 		return (err);
1859 
1860 	if ((err = zonecfg_delete_filesystem_core(handle, oldtabptr)) != Z_OK)
1861 		return (err);
1862 
1863 	if ((err = zonecfg_add_filesystem_core(handle, newtabptr)) != Z_OK)
1864 		return (err);
1865 
1866 	return (Z_OK);
1867 }
1868 
1869 static int
1870 zonecfg_delete_ipd_core(zone_dochandle_t handle, struct zone_fstab *tabptr)
1871 {
1872 	xmlNodePtr cur = handle->zone_dh_cur;
1873 
1874 	for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
1875 		if (xmlStrcmp(cur->name, DTD_ELEM_IPD))
1876 			continue;
1877 		if (match_prop(cur, DTD_ATTR_DIR, tabptr->zone_fs_dir)) {
1878 			xmlUnlinkNode(cur);
1879 			xmlFreeNode(cur);
1880 			return (Z_OK);
1881 		}
1882 	}
1883 	return (Z_NO_RESOURCE_ID);
1884 }
1885 
1886 int
1887 zonecfg_delete_ipd(zone_dochandle_t handle, struct zone_fstab *tabptr)
1888 {
1889 	int err;
1890 
1891 	if (tabptr == NULL)
1892 		return (Z_INVAL);
1893 
1894 	if ((err = operation_prep(handle)) != Z_OK)
1895 		return (err);
1896 
1897 	if ((err = zonecfg_delete_ipd_core(handle, tabptr)) != Z_OK)
1898 		return (err);
1899 
1900 	return (Z_OK);
1901 }
1902 
1903 int
1904 zonecfg_modify_ipd(zone_dochandle_t handle, struct zone_fstab *oldtabptr,
1905     struct zone_fstab *newtabptr)
1906 {
1907 	int err;
1908 
1909 	if (oldtabptr == NULL || newtabptr == NULL)
1910 		return (Z_INVAL);
1911 
1912 	if ((err = operation_prep(handle)) != Z_OK)
1913 		return (err);
1914 
1915 	if ((err = zonecfg_delete_ipd_core(handle, oldtabptr)) != Z_OK)
1916 		return (err);
1917 
1918 	if ((err = zonecfg_add_ipd_core(handle, newtabptr)) != Z_OK)
1919 		return (err);
1920 
1921 	return (Z_OK);
1922 }
1923 
1924 int
1925 zonecfg_lookup_filesystem(
1926 	zone_dochandle_t handle,
1927 	struct zone_fstab *tabptr)
1928 {
1929 	xmlNodePtr cur, options, firstmatch;
1930 	int err;
1931 	char dirname[MAXPATHLEN], special[MAXPATHLEN], raw[MAXPATHLEN];
1932 	char type[FSTYPSZ];
1933 	char options_str[MAX_MNTOPT_STR];
1934 
1935 	if (tabptr == NULL)
1936 		return (Z_INVAL);
1937 
1938 	if ((err = operation_prep(handle)) != Z_OK)
1939 		return (err);
1940 
1941 	/*
1942 	 * Walk the list of children looking for matches on any properties
1943 	 * specified in the fstab parameter.  If more than one resource
1944 	 * matches, we return Z_INSUFFICIENT_SPEC; if none match, we return
1945 	 * Z_NO_RESOURCE_ID.
1946 	 */
1947 	cur = handle->zone_dh_cur;
1948 	firstmatch = NULL;
1949 	for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
1950 		if (xmlStrcmp(cur->name, DTD_ELEM_FS))
1951 			continue;
1952 		if (strlen(tabptr->zone_fs_dir) > 0) {
1953 			if ((fetchprop(cur, DTD_ATTR_DIR, dirname,
1954 			    sizeof (dirname)) == Z_OK) &&
1955 			    (strcmp(tabptr->zone_fs_dir, dirname) == 0)) {
1956 				if (firstmatch == NULL)
1957 					firstmatch = cur;
1958 				else
1959 					return (Z_INSUFFICIENT_SPEC);
1960 			}
1961 		}
1962 		if (strlen(tabptr->zone_fs_special) > 0) {
1963 			if ((fetchprop(cur, DTD_ATTR_SPECIAL, special,
1964 			    sizeof (special)) == Z_OK)) {
1965 				if (strcmp(tabptr->zone_fs_special,
1966 				    special) == 0) {
1967 					if (firstmatch == NULL)
1968 						firstmatch = cur;
1969 					else if (firstmatch != cur)
1970 						return (Z_INSUFFICIENT_SPEC);
1971 				} else {
1972 					/*
1973 					 * If another property matched but this
1974 					 * one doesn't then reset firstmatch.
1975 					 */
1976 					if (firstmatch == cur)
1977 						firstmatch = NULL;
1978 				}
1979 			}
1980 		}
1981 		if (strlen(tabptr->zone_fs_raw) > 0) {
1982 			if ((fetchprop(cur, DTD_ATTR_RAW, raw,
1983 			    sizeof (raw)) == Z_OK)) {
1984 				if (strcmp(tabptr->zone_fs_raw, raw) == 0) {
1985 					if (firstmatch == NULL)
1986 						firstmatch = cur;
1987 					else if (firstmatch != cur)
1988 						return (Z_INSUFFICIENT_SPEC);
1989 				} else {
1990 					/*
1991 					 * If another property matched but this
1992 					 * one doesn't then reset firstmatch.
1993 					 */
1994 					if (firstmatch == cur)
1995 						firstmatch = NULL;
1996 				}
1997 			}
1998 		}
1999 		if (strlen(tabptr->zone_fs_type) > 0) {
2000 			if ((fetchprop(cur, DTD_ATTR_TYPE, type,
2001 			    sizeof (type)) == Z_OK)) {
2002 				if (strcmp(tabptr->zone_fs_type, type) == 0) {
2003 					if (firstmatch == NULL)
2004 						firstmatch = cur;
2005 					else if (firstmatch != cur)
2006 						return (Z_INSUFFICIENT_SPEC);
2007 				} else {
2008 					/*
2009 					 * If another property matched but this
2010 					 * one doesn't then reset firstmatch.
2011 					 */
2012 					if (firstmatch == cur)
2013 						firstmatch = NULL;
2014 				}
2015 			}
2016 		}
2017 	}
2018 
2019 	if (firstmatch == NULL)
2020 		return (Z_NO_RESOURCE_ID);
2021 
2022 	cur = firstmatch;
2023 
2024 	if ((err = fetchprop(cur, DTD_ATTR_DIR, tabptr->zone_fs_dir,
2025 	    sizeof (tabptr->zone_fs_dir))) != Z_OK)
2026 		return (err);
2027 
2028 	if ((err = fetchprop(cur, DTD_ATTR_SPECIAL, tabptr->zone_fs_special,
2029 	    sizeof (tabptr->zone_fs_special))) != Z_OK)
2030 		return (err);
2031 
2032 	if ((err = fetchprop(cur, DTD_ATTR_RAW, tabptr->zone_fs_raw,
2033 	    sizeof (tabptr->zone_fs_raw))) != Z_OK)
2034 		return (err);
2035 
2036 	if ((err = fetchprop(cur, DTD_ATTR_TYPE, tabptr->zone_fs_type,
2037 	    sizeof (tabptr->zone_fs_type))) != Z_OK)
2038 		return (err);
2039 
2040 	/* options are optional */
2041 	tabptr->zone_fs_options = NULL;
2042 	for (options = cur->xmlChildrenNode; options != NULL;
2043 	    options = options->next) {
2044 		if ((fetchprop(options, DTD_ATTR_NAME, options_str,
2045 		    sizeof (options_str)) != Z_OK))
2046 			break;
2047 		if (zonecfg_add_fs_option(tabptr, options_str) != Z_OK)
2048 			break;
2049 	}
2050 	return (Z_OK);
2051 }
2052 
2053 int
2054 zonecfg_lookup_ipd(zone_dochandle_t handle, struct zone_fstab *tabptr)
2055 {
2056 	xmlNodePtr cur, match;
2057 	int err;
2058 	char dirname[MAXPATHLEN];
2059 
2060 	if (tabptr == NULL)
2061 		return (Z_INVAL);
2062 
2063 	if ((err = operation_prep(handle)) != Z_OK)
2064 		return (err);
2065 
2066 	/*
2067 	 * General algorithm:
2068 	 * Walk the list of children looking for matches on any properties
2069 	 * specified in the fstab parameter.  If more than one resource
2070 	 * matches, we return Z_INSUFFICIENT_SPEC; if none match, we return
2071 	 * Z_NO_RESOURCE_ID.
2072 	 */
2073 	cur = handle->zone_dh_cur;
2074 	match = NULL;
2075 	for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
2076 		if (xmlStrcmp(cur->name, DTD_ELEM_IPD))
2077 			continue;
2078 		if (strlen(tabptr->zone_fs_dir) > 0) {
2079 			if ((fetchprop(cur, DTD_ATTR_DIR, dirname,
2080 			    sizeof (dirname)) == Z_OK) &&
2081 			    (strcmp(tabptr->zone_fs_dir, dirname) == 0)) {
2082 				if (match == NULL)
2083 					match = cur;
2084 				else
2085 					return (Z_INSUFFICIENT_SPEC);
2086 			}
2087 		}
2088 	}
2089 
2090 	if (match == NULL)
2091 		return (Z_NO_RESOURCE_ID);
2092 
2093 	cur = match;
2094 
2095 	if ((err = fetchprop(cur, DTD_ATTR_DIR, tabptr->zone_fs_dir,
2096 	    sizeof (tabptr->zone_fs_dir))) != Z_OK)
2097 		return (err);
2098 
2099 	return (Z_OK);
2100 }
2101 
2102 /*
2103  * Compare two IP addresses in string form.  Allow for the possibility that
2104  * one might have "/<prefix-length>" at the end: allow a match on just the
2105  * IP address (or host name) part.
2106  */
2107 
2108 boolean_t
2109 zonecfg_same_net_address(char *a1, char *a2)
2110 {
2111 	char *slashp, *slashp1, *slashp2;
2112 	int result;
2113 
2114 	if (strcmp(a1, a2) == 0)
2115 		return (B_TRUE);
2116 
2117 	/*
2118 	 * If neither has a slash or both do, they need to match to be
2119 	 * considered the same, but they did not match above, so fail.
2120 	 */
2121 	slashp1 = strchr(a1, '/');
2122 	slashp2 = strchr(a2, '/');
2123 	if ((slashp1 == NULL && slashp2 == NULL) ||
2124 	    (slashp1 != NULL && slashp2 != NULL))
2125 		return (B_FALSE);
2126 
2127 	/*
2128 	 * Only one had a slash: pick that one, zero out the slash, compare
2129 	 * the "address only" strings, restore the slash, and return the
2130 	 * result of the comparison.
2131 	 */
2132 	slashp = (slashp1 == NULL) ? slashp2 : slashp1;
2133 	*slashp = '\0';
2134 	result = strcmp(a1, a2);
2135 	*slashp = '/';
2136 	return ((result == 0));
2137 }
2138 
2139 int
2140 zonecfg_valid_net_address(char *address, struct lifreq *lifr)
2141 {
2142 	struct sockaddr_in *sin4;
2143 	struct sockaddr_in6 *sin6;
2144 	struct addrinfo hints, *result;
2145 	char *slashp = strchr(address, '/');
2146 
2147 	bzero(lifr, sizeof (struct lifreq));
2148 	sin4 = (struct sockaddr_in *)&lifr->lifr_addr;
2149 	sin6 = (struct sockaddr_in6 *)&lifr->lifr_addr;
2150 	if (slashp != NULL)
2151 		*slashp = '\0';
2152 	if (inet_pton(AF_INET, address, &sin4->sin_addr) == 1) {
2153 		sin4->sin_family = AF_INET;
2154 	} else if (inet_pton(AF_INET6, address, &sin6->sin6_addr) == 1) {
2155 		if (slashp == NULL)
2156 			return (Z_IPV6_ADDR_PREFIX_LEN);
2157 		sin6->sin6_family = AF_INET6;
2158 	} else {
2159 		/* "address" may be a host name */
2160 		(void) memset(&hints, 0, sizeof (hints));
2161 		hints.ai_family = PF_INET;
2162 		if (getaddrinfo(address, NULL, &hints, &result) != 0)
2163 			return (Z_BOGUS_ADDRESS);
2164 		sin4->sin_family = result->ai_family;
2165 
2166 		(void) memcpy(&sin4->sin_addr,
2167 		    /* LINTED E_BAD_PTR_CAST_ALIGN */
2168 		    &((struct sockaddr_in *)result->ai_addr)->sin_addr,
2169 		    sizeof (struct in_addr));
2170 
2171 		freeaddrinfo(result);
2172 	}
2173 	return (Z_OK);
2174 }
2175 
2176 boolean_t
2177 zonecfg_ifname_exists(sa_family_t af, char *ifname)
2178 {
2179 	struct lifreq lifr;
2180 	int so;
2181 	int save_errno;
2182 
2183 	(void) memset(&lifr, 0, sizeof (lifr));
2184 	(void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
2185 	lifr.lifr_addr.ss_family = af;
2186 	if ((so = socket(af, SOCK_DGRAM, 0)) < 0) {
2187 		/* Odd - can't tell if the ifname exists */
2188 		return (B_FALSE);
2189 	}
2190 	if (ioctl(so, SIOCGLIFFLAGS, (caddr_t)&lifr) < 0) {
2191 		save_errno = errno;
2192 		(void) close(so);
2193 		errno = save_errno;
2194 		return (B_FALSE);
2195 	}
2196 	(void) close(so);
2197 	return (B_TRUE);
2198 }
2199 
2200 /*
2201  * Determines whether there is a net resource with the physical interface, IP
2202  * address, and default router specified by 'tabptr' in the zone configuration
2203  * to which 'handle' refers.  'tabptr' must have an interface, an address, a
2204  * default router, or a combination of the three.  This function returns Z_OK
2205  * iff there is exactly one net resource matching the query specified by
2206  * 'tabptr'.  The function returns Z_INSUFFICIENT_SPEC if there are multiple
2207  * matches or 'tabptr' does not specify a physical interface, address, or
2208  * default router.  The function returns Z_NO_RESOURCE_ID if are no matches.
2209  *
2210  * Errors might also be returned if the entry that exactly matches the
2211  * query lacks critical network resource information.
2212  *
2213  * If there is a single match, then the matching entry's physical interface, IP
2214  * address, and default router information are stored in 'tabptr'.
2215  */
2216 int
2217 zonecfg_lookup_nwif(zone_dochandle_t handle, struct zone_nwiftab *tabptr)
2218 {
2219 	xmlNodePtr cur;
2220 	xmlNodePtr firstmatch;
2221 	int err;
2222 	char address[INET6_ADDRSTRLEN];
2223 	char physical[LIFNAMSIZ];
2224 	size_t addrspec;		/* nonzero if tabptr has IP addr */
2225 	size_t physspec;		/* nonzero if tabptr has interface */
2226 	size_t defrouterspec;		/* nonzero if tabptr has def. router */
2227 
2228 	if (tabptr == NULL)
2229 		return (Z_INVAL);
2230 
2231 	/*
2232 	 * Determine the fields that will be searched.  There must be at least
2233 	 * one.
2234 	 *
2235 	 * zone_nwif_address, zone_nwif_physical, and zone_nwif_defrouter are
2236 	 * arrays, so no NULL checks are necessary.
2237 	 */
2238 	addrspec = strlen(tabptr->zone_nwif_address);
2239 	physspec = strlen(tabptr->zone_nwif_physical);
2240 	defrouterspec = strlen(tabptr->zone_nwif_defrouter);
2241 	if (addrspec == 0 && physspec == 0 && defrouterspec == 0)
2242 		return (Z_INSUFFICIENT_SPEC);
2243 
2244 	if ((err = operation_prep(handle)) != Z_OK)
2245 		return (err);
2246 
2247 	/*
2248 	 * Iterate over the configuration's elements and look for net elements
2249 	 * that match the query.
2250 	 */
2251 	firstmatch = NULL;
2252 	cur = handle->zone_dh_cur;
2253 	for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
2254 		/* Skip non-net elements */
2255 		if (xmlStrcmp(cur->name, DTD_ELEM_NET))
2256 			continue;
2257 
2258 		/*
2259 		 * If any relevant fields don't match the query, then skip
2260 		 * the current net element.
2261 		 */
2262 		if (physspec != 0 && (fetchprop(cur, DTD_ATTR_PHYSICAL,
2263 		    physical, sizeof (physical)) != Z_OK ||
2264 		    strcmp(tabptr->zone_nwif_physical, physical) != 0))
2265 			continue;
2266 		if (addrspec != 0 && (fetchprop(cur, DTD_ATTR_ADDRESS, address,
2267 		    sizeof (address)) != Z_OK ||
2268 		    !zonecfg_same_net_address(tabptr->zone_nwif_address,
2269 		    address)))
2270 			continue;
2271 		if (defrouterspec != 0 && (fetchprop(cur, DTD_ATTR_DEFROUTER,
2272 		    address, sizeof (address)) != Z_OK ||
2273 		    !zonecfg_same_net_address(tabptr->zone_nwif_defrouter,
2274 		    address)))
2275 			continue;
2276 
2277 		/*
2278 		 * The current net element matches the query.  Select it if
2279 		 * it's the first match; otherwise, abort the search.
2280 		 */
2281 		if (firstmatch == NULL)
2282 			firstmatch = cur;
2283 		else
2284 			return (Z_INSUFFICIENT_SPEC);
2285 	}
2286 	if (firstmatch == NULL)
2287 		return (Z_NO_RESOURCE_ID);
2288 
2289 	cur = firstmatch;
2290 
2291 	if ((err = fetchprop(cur, DTD_ATTR_PHYSICAL, tabptr->zone_nwif_physical,
2292 	    sizeof (tabptr->zone_nwif_physical))) != Z_OK)
2293 		return (err);
2294 
2295 	if ((err = fetchprop(cur, DTD_ATTR_ADDRESS, tabptr->zone_nwif_address,
2296 	    sizeof (tabptr->zone_nwif_address))) != Z_OK)
2297 		return (err);
2298 
2299 	if ((err = fetchprop(cur, DTD_ATTR_DEFROUTER,
2300 	    tabptr->zone_nwif_defrouter,
2301 	    sizeof (tabptr->zone_nwif_defrouter))) != Z_OK)
2302 		return (err);
2303 
2304 	return (Z_OK);
2305 }
2306 
2307 static int
2308 zonecfg_add_nwif_core(zone_dochandle_t handle, struct zone_nwiftab *tabptr)
2309 {
2310 	xmlNodePtr newnode, cur = handle->zone_dh_cur;
2311 	int err;
2312 
2313 	newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_NET, NULL);
2314 	if ((err = newprop(newnode, DTD_ATTR_ADDRESS,
2315 	    tabptr->zone_nwif_address)) != Z_OK)
2316 		return (err);
2317 	if ((err = newprop(newnode, DTD_ATTR_PHYSICAL,
2318 	    tabptr->zone_nwif_physical)) != Z_OK)
2319 		return (err);
2320 	/*
2321 	 * Do not add this property when it is not set, for backwards
2322 	 * compatibility and because it is optional.
2323 	 */
2324 	if ((strlen(tabptr->zone_nwif_defrouter) > 0) &&
2325 	    ((err = newprop(newnode, DTD_ATTR_DEFROUTER,
2326 	    tabptr->zone_nwif_defrouter)) != Z_OK))
2327 		return (err);
2328 	return (Z_OK);
2329 }
2330 
2331 int
2332 zonecfg_add_nwif(zone_dochandle_t handle, struct zone_nwiftab *tabptr)
2333 {
2334 	int err;
2335 
2336 	if (tabptr == NULL)
2337 		return (Z_INVAL);
2338 
2339 	if ((err = operation_prep(handle)) != Z_OK)
2340 		return (err);
2341 
2342 	if ((err = zonecfg_add_nwif_core(handle, tabptr)) != Z_OK)
2343 		return (err);
2344 
2345 	return (Z_OK);
2346 }
2347 
2348 static int
2349 zonecfg_delete_nwif_core(zone_dochandle_t handle, struct zone_nwiftab *tabptr)
2350 {
2351 	xmlNodePtr cur = handle->zone_dh_cur;
2352 	boolean_t addr_match, phys_match;
2353 
2354 	for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
2355 		if (xmlStrcmp(cur->name, DTD_ELEM_NET))
2356 			continue;
2357 
2358 		addr_match = match_prop(cur, DTD_ATTR_ADDRESS,
2359 		    tabptr->zone_nwif_address);
2360 		phys_match = match_prop(cur, DTD_ATTR_PHYSICAL,
2361 		    tabptr->zone_nwif_physical);
2362 
2363 		if (addr_match && phys_match) {
2364 			xmlUnlinkNode(cur);
2365 			xmlFreeNode(cur);
2366 			return (Z_OK);
2367 		}
2368 	}
2369 	return (Z_NO_RESOURCE_ID);
2370 }
2371 
2372 int
2373 zonecfg_delete_nwif(zone_dochandle_t handle, struct zone_nwiftab *tabptr)
2374 {
2375 	int err;
2376 
2377 	if (tabptr == NULL)
2378 		return (Z_INVAL);
2379 
2380 	if ((err = operation_prep(handle)) != Z_OK)
2381 		return (err);
2382 
2383 	if ((err = zonecfg_delete_nwif_core(handle, tabptr)) != Z_OK)
2384 		return (err);
2385 
2386 	return (Z_OK);
2387 }
2388 
2389 int
2390 zonecfg_modify_nwif(
2391 	zone_dochandle_t handle,
2392 	struct zone_nwiftab *oldtabptr,
2393 	struct zone_nwiftab *newtabptr)
2394 {
2395 	int err;
2396 
2397 	if (oldtabptr == NULL || newtabptr == NULL)
2398 		return (Z_INVAL);
2399 
2400 	if ((err = operation_prep(handle)) != Z_OK)
2401 		return (err);
2402 
2403 	if ((err = zonecfg_delete_nwif_core(handle, oldtabptr)) != Z_OK)
2404 		return (err);
2405 
2406 	if ((err = zonecfg_add_nwif_core(handle, newtabptr)) != Z_OK)
2407 		return (err);
2408 
2409 	return (Z_OK);
2410 }
2411 
2412 /*
2413  * Must be a comma-separated list of alpha-numeric file system names.
2414  */
2415 static int
2416 zonecfg_valid_fs_allowed(const char *fsallowedp)
2417 {
2418 	char tmp[ZONE_FS_ALLOWED_MAX];
2419 	char *cp = tmp;
2420 	char *p;
2421 
2422 	if (strlen(fsallowedp) > ZONE_FS_ALLOWED_MAX)
2423 		return (Z_TOO_BIG);
2424 
2425 	(void) strlcpy(tmp, fsallowedp, sizeof (tmp));
2426 
2427 	while (*cp != '\0') {
2428 		p = cp;
2429 		while (*p != '\0' && *p != ',') {
2430 			if (!isalnum(*p))
2431 				return (Z_INVALID_PROPERTY);
2432 			p++;
2433 		}
2434 
2435 		if (*p == ',') {
2436 			if (p == cp)
2437 				return (Z_INVALID_PROPERTY);
2438 
2439 			p++;
2440 
2441 			if (*p == '\0')
2442 				return (Z_INVALID_PROPERTY);
2443 		}
2444 
2445 		cp = p;
2446 	}
2447 
2448 	return (Z_OK);
2449 }
2450 
2451 int
2452 zonecfg_get_fs_allowed(zone_dochandle_t handle, char *bufp, size_t buflen)
2453 {
2454 	int err;
2455 
2456 	if ((err = getrootattr(handle, DTD_ATTR_FS_ALLOWED,
2457 	    bufp, buflen)) != Z_OK)
2458 		return (err);
2459 	if (bufp[0] == '\0')
2460 		return (Z_BAD_PROPERTY);
2461 	return (zonecfg_valid_fs_allowed(bufp));
2462 }
2463 
2464 int
2465 zonecfg_set_fs_allowed(zone_dochandle_t handle, const char *bufp)
2466 {
2467 	int err;
2468 
2469 	if (bufp == NULL || (err = zonecfg_valid_fs_allowed(bufp)) == Z_OK)
2470 		return (setrootattr(handle, DTD_ATTR_FS_ALLOWED, bufp));
2471 	return (err);
2472 }
2473 
2474 /*
2475  * Determines if the specified string is a valid hostid string.  This function
2476  * returns Z_OK if the string is a valid hostid string.  It returns Z_INVAL if
2477  * 'hostidp' is NULL, Z_TOO_BIG if 'hostidp' refers to a string buffer
2478  * containing a hex string with more than 8 digits, and Z_INVALID_PROPERTY if
2479  * the string has an invalid format.
2480  */
2481 static int
2482 zonecfg_valid_hostid(const char *hostidp)
2483 {
2484 	char *currentp;
2485 	u_longlong_t hostidval;
2486 	size_t len;
2487 
2488 	if (hostidp == NULL)
2489 		return (Z_INVAL);
2490 
2491 	/* Empty strings and strings with whitespace are invalid. */
2492 	if (*hostidp == '\0')
2493 		return (Z_INVALID_PROPERTY);
2494 	for (currentp = (char *)hostidp; *currentp != '\0'; ++currentp) {
2495 		if (isspace(*currentp))
2496 			return (Z_INVALID_PROPERTY);
2497 	}
2498 	len = (size_t)(currentp - hostidp);
2499 
2500 	/*
2501 	 * The caller might pass a hostid that is larger than the maximum
2502 	 * unsigned 32-bit integral value.  Check for this!  Also, make sure
2503 	 * that the whole string is converted (this helps us find illegal
2504 	 * characters) and that the whole string fits within a buffer of size
2505 	 * HW_HOSTID_LEN.
2506 	 */
2507 	currentp = (char *)hostidp;
2508 	if (strncmp(hostidp, "0x", 2) == 0 || strncmp(hostidp, "0X", 2) == 0)
2509 		currentp += 2;
2510 	hostidval = strtoull(currentp, &currentp, 16);
2511 	if ((size_t)(currentp - hostidp) >= HW_HOSTID_LEN)
2512 		return (Z_TOO_BIG);
2513 	if (hostidval > UINT_MAX || hostidval == HW_INVALID_HOSTID ||
2514 	    currentp != hostidp + len)
2515 		return (Z_INVALID_PROPERTY);
2516 	return (Z_OK);
2517 }
2518 
2519 /*
2520  * Gets the zone hostid string stored in the specified zone configuration
2521  * document.  This function returns Z_OK on success.  Z_BAD_PROPERTY is returned
2522  * if the config file doesn't specify a hostid or if the hostid is blank.
2523  *
2524  * Note that buflen should be at least HW_HOSTID_LEN.
2525  */
2526 int
2527 zonecfg_get_hostid(zone_dochandle_t handle, char *bufp, size_t buflen)
2528 {
2529 	int err;
2530 
2531 	if ((err = getrootattr(handle, DTD_ATTR_HOSTID, bufp, buflen)) != Z_OK)
2532 		return (err);
2533 	if (bufp[0] == '\0')
2534 		return (Z_BAD_PROPERTY);
2535 	return (zonecfg_valid_hostid(bufp));
2536 }
2537 
2538 /*
2539  * Sets the hostid string in the specified zone config document to the given
2540  * string value.  If 'hostidp' is NULL, then the config document's hostid
2541  * attribute is cleared.  Non-NULL hostids are validated.  This function returns
2542  * Z_OK on success.  Any other return value indicates failure.
2543  */
2544 int
2545 zonecfg_set_hostid(zone_dochandle_t handle, const char *hostidp)
2546 {
2547 	int err;
2548 
2549 	/*
2550 	 * A NULL hostid string is interpreted as a request to clear the
2551 	 * hostid.
2552 	 */
2553 	if (hostidp == NULL || (err = zonecfg_valid_hostid(hostidp)) == Z_OK)
2554 		return (setrootattr(handle, DTD_ATTR_HOSTID, hostidp));
2555 	return (err);
2556 }
2557 
2558 int
2559 zonecfg_lookup_dev(zone_dochandle_t handle, struct zone_devtab *tabptr)
2560 {
2561 	xmlNodePtr cur, firstmatch;
2562 	int err;
2563 	char match[MAXPATHLEN];
2564 
2565 	if (tabptr == NULL)
2566 		return (Z_INVAL);
2567 
2568 	if ((err = operation_prep(handle)) != Z_OK)
2569 		return (err);
2570 
2571 	cur = handle->zone_dh_cur;
2572 	firstmatch = NULL;
2573 	for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
2574 		if (xmlStrcmp(cur->name, DTD_ELEM_DEVICE))
2575 			continue;
2576 		if (strlen(tabptr->zone_dev_match) == 0)
2577 			continue;
2578 
2579 		if ((fetchprop(cur, DTD_ATTR_MATCH, match,
2580 		    sizeof (match)) == Z_OK)) {
2581 			if (strcmp(tabptr->zone_dev_match,
2582 			    match) == 0) {
2583 				if (firstmatch == NULL)
2584 					firstmatch = cur;
2585 				else if (firstmatch != cur)
2586 					return (Z_INSUFFICIENT_SPEC);
2587 			} else {
2588 				/*
2589 				 * If another property matched but this
2590 				 * one doesn't then reset firstmatch.
2591 				 */
2592 				if (firstmatch == cur)
2593 					firstmatch = NULL;
2594 			}
2595 		}
2596 	}
2597 	if (firstmatch == NULL)
2598 		return (Z_NO_RESOURCE_ID);
2599 
2600 	cur = firstmatch;
2601 
2602 	if ((err = fetchprop(cur, DTD_ATTR_MATCH, tabptr->zone_dev_match,
2603 	    sizeof (tabptr->zone_dev_match))) != Z_OK)
2604 		return (err);
2605 
2606 	return (Z_OK);
2607 }
2608 
2609 static int
2610 zonecfg_add_dev_core(zone_dochandle_t handle, struct zone_devtab *tabptr)
2611 {
2612 	xmlNodePtr newnode, cur = handle->zone_dh_cur;
2613 	int err;
2614 
2615 	newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_DEVICE, NULL);
2616 
2617 	if ((err = newprop(newnode, DTD_ATTR_MATCH,
2618 	    tabptr->zone_dev_match)) != Z_OK)
2619 		return (err);
2620 
2621 	return (Z_OK);
2622 }
2623 
2624 int
2625 zonecfg_add_dev(zone_dochandle_t handle, struct zone_devtab *tabptr)
2626 {
2627 	int err;
2628 
2629 	if (tabptr == NULL)
2630 		return (Z_INVAL);
2631 
2632 	if ((err = operation_prep(handle)) != Z_OK)
2633 		return (err);
2634 
2635 	if ((err = zonecfg_add_dev_core(handle, tabptr)) != Z_OK)
2636 		return (err);
2637 
2638 	return (Z_OK);
2639 }
2640 
2641 static int
2642 zonecfg_delete_dev_core(zone_dochandle_t handle, struct zone_devtab *tabptr)
2643 {
2644 	xmlNodePtr cur = handle->zone_dh_cur;
2645 	int match_match;
2646 
2647 	for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
2648 		if (xmlStrcmp(cur->name, DTD_ELEM_DEVICE))
2649 			continue;
2650 
2651 		match_match = match_prop(cur, DTD_ATTR_MATCH,
2652 		    tabptr->zone_dev_match);
2653 
2654 		if (match_match) {
2655 			xmlUnlinkNode(cur);
2656 			xmlFreeNode(cur);
2657 			return (Z_OK);
2658 		}
2659 	}
2660 	return (Z_NO_RESOURCE_ID);
2661 }
2662 
2663 int
2664 zonecfg_delete_dev(zone_dochandle_t handle, struct zone_devtab *tabptr)
2665 {
2666 	int err;
2667 
2668 	if (tabptr == NULL)
2669 		return (Z_INVAL);
2670 
2671 	if ((err = operation_prep(handle)) != Z_OK)
2672 		return (err);
2673 
2674 	if ((err = zonecfg_delete_dev_core(handle, tabptr)) != Z_OK)
2675 		return (err);
2676 
2677 	return (Z_OK);
2678 }
2679 
2680 int
2681 zonecfg_modify_dev(
2682 	zone_dochandle_t handle,
2683 	struct zone_devtab *oldtabptr,
2684 	struct zone_devtab *newtabptr)
2685 {
2686 	int err;
2687 
2688 	if (oldtabptr == NULL || newtabptr == NULL)
2689 		return (Z_INVAL);
2690 
2691 	if ((err = operation_prep(handle)) != Z_OK)
2692 		return (err);
2693 
2694 	if ((err = zonecfg_delete_dev_core(handle, oldtabptr)) != Z_OK)
2695 		return (err);
2696 
2697 	if ((err = zonecfg_add_dev_core(handle, newtabptr)) != Z_OK)
2698 		return (err);
2699 
2700 	return (Z_OK);
2701 }
2702 
2703 static int
2704 zonecfg_add_auth_core(zone_dochandle_t handle, struct zone_admintab *tabptr,
2705     char *zonename)
2706 {
2707 	xmlNodePtr newnode, cur = handle->zone_dh_cur;
2708 	int err;
2709 
2710 	newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_ADMIN, NULL);
2711 	err = newprop(newnode, DTD_ATTR_USER, tabptr->zone_admin_user);
2712 	if (err != Z_OK)
2713 		return (err);
2714 	err = newprop(newnode, DTD_ATTR_AUTHS, tabptr->zone_admin_auths);
2715 	if (err != Z_OK)
2716 		return (err);
2717 	if ((err = zonecfg_remove_userauths(
2718 	    handle, tabptr->zone_admin_user, zonename, B_FALSE)) != Z_OK)
2719 		return (err);
2720 	return (Z_OK);
2721 }
2722 
2723 int
2724 zonecfg_add_admin(zone_dochandle_t handle, struct zone_admintab *tabptr,
2725     char *zonename)
2726 {
2727 	int err;
2728 
2729 	if (tabptr == NULL)
2730 		return (Z_INVAL);
2731 
2732 	if ((err = operation_prep(handle)) != Z_OK)
2733 		return (err);
2734 
2735 	if ((err = zonecfg_add_auth_core(handle, tabptr,
2736 	    zonename)) != Z_OK)
2737 		return (err);
2738 
2739 	return (Z_OK);
2740 }
2741 static int
2742 zonecfg_delete_auth_core(zone_dochandle_t handle, struct zone_admintab *tabptr,
2743     char *zonename)
2744 {
2745 	xmlNodePtr cur = handle->zone_dh_cur;
2746 	boolean_t auth_match;
2747 	int err;
2748 
2749 	for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
2750 		if (xmlStrcmp(cur->name, DTD_ELEM_ADMIN))
2751 			continue;
2752 		auth_match = match_prop(cur, DTD_ATTR_USER,
2753 		    tabptr->zone_admin_user);
2754 		if (auth_match) {
2755 			if ((err = zonecfg_insert_userauths(
2756 			    handle, tabptr->zone_admin_user,
2757 			    zonename)) != Z_OK)
2758 				return (err);
2759 			xmlUnlinkNode(cur);
2760 			xmlFreeNode(cur);
2761 			return (Z_OK);
2762 		}
2763 	}
2764 	return (Z_NO_RESOURCE_ID);
2765 }
2766 
2767 int
2768 zonecfg_delete_admin(zone_dochandle_t handle, struct zone_admintab *tabptr,
2769     char *zonename)
2770 {
2771 	int err;
2772 
2773 	if (tabptr == NULL)
2774 		return (Z_INVAL);
2775 
2776 	if ((err = operation_prep(handle)) != Z_OK)
2777 		return (err);
2778 
2779 	if ((err = zonecfg_delete_auth_core(handle, tabptr, zonename)) != Z_OK)
2780 		return (err);
2781 
2782 	return (Z_OK);
2783 }
2784 
2785 int
2786 zonecfg_modify_admin(zone_dochandle_t handle, struct zone_admintab *oldtabptr,
2787     struct zone_admintab *newtabptr, char *zonename)
2788 {
2789 	int err;
2790 
2791 	if (oldtabptr == NULL || newtabptr == NULL)
2792 		return (Z_INVAL);
2793 
2794 	if ((err = operation_prep(handle)) != Z_OK)
2795 		return (err);
2796 
2797 	if ((err = zonecfg_delete_auth_core(handle, oldtabptr, zonename))
2798 	    != Z_OK)
2799 		return (err);
2800 
2801 	if ((err = zonecfg_add_auth_core(handle, newtabptr,
2802 	    zonename)) != Z_OK)
2803 		return (err);
2804 
2805 	return (Z_OK);
2806 }
2807 
2808 int
2809 zonecfg_lookup_admin(zone_dochandle_t handle, struct zone_admintab *tabptr)
2810 {
2811 	xmlNodePtr cur, firstmatch;
2812 	int err;
2813 	char user[MAXUSERNAME];
2814 
2815 	if (tabptr == NULL)
2816 		return (Z_INVAL);
2817 
2818 	if ((err = operation_prep(handle)) != Z_OK)
2819 		return (err);
2820 
2821 	cur = handle->zone_dh_cur;
2822 	firstmatch = NULL;
2823 	for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
2824 		if (xmlStrcmp(cur->name, DTD_ELEM_ADMIN))
2825 			continue;
2826 		if (strlen(tabptr->zone_admin_user) > 0) {
2827 			if ((fetchprop(cur, DTD_ATTR_USER, user,
2828 			    sizeof (user)) == Z_OK) &&
2829 			    (strcmp(tabptr->zone_admin_user, user) == 0)) {
2830 				if (firstmatch == NULL)
2831 					firstmatch = cur;
2832 				else
2833 					return (Z_INSUFFICIENT_SPEC);
2834 			}
2835 		}
2836 	}
2837 	if (firstmatch == NULL)
2838 		return (Z_NO_RESOURCE_ID);
2839 
2840 	cur = firstmatch;
2841 
2842 	if ((err = fetchprop(cur, DTD_ATTR_USER, tabptr->zone_admin_user,
2843 	    sizeof (tabptr->zone_admin_user))) != Z_OK)
2844 		return (err);
2845 
2846 	if ((err = fetchprop(cur, DTD_ATTR_AUTHS, tabptr->zone_admin_auths,
2847 	    sizeof (tabptr->zone_admin_auths))) != Z_OK)
2848 		return (err);
2849 
2850 	return (Z_OK);
2851 }
2852 
2853 
2854 /* Lock to serialize all devwalks */
2855 static pthread_mutex_t zonecfg_devwalk_lock = PTHREAD_MUTEX_INITIALIZER;
2856 /*
2857  * Global variables used to pass data from zonecfg_dev_manifest to the nftw
2858  * call-back (zonecfg_devwalk_cb).  g_devwalk_data is really the void*
2859  * parameter and g_devwalk_cb is really the *cb parameter from
2860  * zonecfg_dev_manifest.
2861  */
2862 typedef struct __g_devwalk_data *g_devwalk_data_t;
2863 static g_devwalk_data_t g_devwalk_data;
2864 static int (*g_devwalk_cb)(const char *, uid_t, gid_t, mode_t, const char *,
2865     void *);
2866 static size_t g_devwalk_skip_prefix;
2867 
2868 /*
2869  * zonecfg_dev_manifest call-back function used during detach to generate the
2870  * dev info in the manifest.
2871  */
2872 static int
2873 get_detach_dev_entry(const char *name, uid_t uid, gid_t gid, mode_t mode,
2874     const char *acl, void *hdl)
2875 {
2876 	zone_dochandle_t handle = (zone_dochandle_t)hdl;
2877 	xmlNodePtr newnode;
2878 	xmlNodePtr cur;
2879 	int err;
2880 	char buf[128];
2881 
2882 	if ((err = operation_prep(handle)) != Z_OK)
2883 		return (err);
2884 
2885 	cur = handle->zone_dh_cur;
2886 	newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_DEV_PERM, NULL);
2887 	if ((err = newprop(newnode, DTD_ATTR_NAME, (char *)name)) != Z_OK)
2888 		return (err);
2889 	(void) snprintf(buf, sizeof (buf), "%lu", uid);
2890 	if ((err = newprop(newnode, DTD_ATTR_UID, buf)) != Z_OK)
2891 		return (err);
2892 	(void) snprintf(buf, sizeof (buf), "%lu", gid);
2893 	if ((err = newprop(newnode, DTD_ATTR_GID, buf)) != Z_OK)
2894 		return (err);
2895 	(void) snprintf(buf, sizeof (buf), "%o", mode);
2896 	if ((err = newprop(newnode, DTD_ATTR_MODE, buf)) != Z_OK)
2897 		return (err);
2898 	if ((err = newprop(newnode, DTD_ATTR_ACL, (char *)acl)) != Z_OK)
2899 		return (err);
2900 	return (Z_OK);
2901 }
2902 
2903 /*
2904  * This is the nftw call-back function used by zonecfg_dev_manifest.  It is
2905  * responsible for calling the actual call-back.
2906  */
2907 /* ARGSUSED2 */
2908 static int
2909 zonecfg_devwalk_cb(const char *path, const struct stat *st, int f,
2910     struct FTW *ftw)
2911 {
2912 	acl_t *acl;
2913 	char *acl_txt = NULL;
2914 
2915 	/* skip all but character and block devices */
2916 	if (!S_ISBLK(st->st_mode) && !S_ISCHR(st->st_mode))
2917 		return (0);
2918 
2919 	if ((acl_get(path, ACL_NO_TRIVIAL, &acl) == 0) &&
2920 	    acl != NULL) {
2921 		acl_txt = acl_totext(acl, ACL_NORESOLVE);
2922 		acl_free(acl);
2923 	}
2924 
2925 	if (strlen(path) <= g_devwalk_skip_prefix)
2926 		return (0);
2927 
2928 	g_devwalk_cb(path + g_devwalk_skip_prefix, st->st_uid, st->st_gid,
2929 	    st->st_mode & S_IAMB, acl_txt != NULL ? acl_txt : "",
2930 	    g_devwalk_data);
2931 	free(acl_txt);
2932 	return (0);
2933 }
2934 
2935 /*
2936  * Walk the dev tree for the zone specified by hdl and call the
2937  * get_detach_dev_entry call-back function for each entry in the tree.  The
2938  * call-back will be passed the name, uid, gid, mode, acl string and the
2939  * handle input parameter for each dev entry.
2940  *
2941  * Data is passed to get_detach_dev_entry through the global variables
2942  * g_devwalk_data, *g_devwalk_cb, and g_devwalk_skip_prefix.  The
2943  * zonecfg_devwalk_cb function will actually call get_detach_dev_entry.
2944  */
2945 int
2946 zonecfg_dev_manifest(zone_dochandle_t hdl)
2947 {
2948 	char path[MAXPATHLEN];
2949 	int ret;
2950 
2951 	if ((ret = zonecfg_get_zonepath(hdl, path, sizeof (path))) != Z_OK)
2952 		return (ret);
2953 
2954 	if (strlcat(path, "/dev", sizeof (path)) >= sizeof (path))
2955 		return (Z_TOO_BIG);
2956 
2957 	/*
2958 	 * We have to serialize all devwalks in the same process
2959 	 * (which should be fine), since nftw() is so badly designed.
2960 	 */
2961 	(void) pthread_mutex_lock(&zonecfg_devwalk_lock);
2962 
2963 	g_devwalk_skip_prefix = strlen(path) + 1;
2964 	g_devwalk_data = (g_devwalk_data_t)hdl;
2965 	g_devwalk_cb = get_detach_dev_entry;
2966 	(void) nftw(path, zonecfg_devwalk_cb, 0, FTW_PHYS);
2967 
2968 	(void) pthread_mutex_unlock(&zonecfg_devwalk_lock);
2969 	return (Z_OK);
2970 }
2971 
2972 /*
2973  * Update the owner, group, mode and acl on the specified dev (inpath) for
2974  * the zone (hdl).  This function can be used to fix up the dev tree after
2975  * attaching a migrated zone.
2976  */
2977 int
2978 zonecfg_devperms_apply(zone_dochandle_t hdl, const char *inpath, uid_t owner,
2979     gid_t group, mode_t mode, const char *acltxt)
2980 {
2981 	int ret;
2982 	char path[MAXPATHLEN];
2983 	struct stat st;
2984 	acl_t *aclp;
2985 
2986 	if ((ret = zonecfg_get_zonepath(hdl, path, sizeof (path))) != Z_OK)
2987 		return (ret);
2988 
2989 	if (strlcat(path, "/dev/", sizeof (path)) >= sizeof (path))
2990 		return (Z_TOO_BIG);
2991 	if (strlcat(path, inpath, sizeof (path)) >= sizeof (path))
2992 		return (Z_TOO_BIG);
2993 
2994 	if (stat(path, &st) == -1)
2995 		return (Z_INVAL);
2996 
2997 	/* make sure we're only touching device nodes */
2998 	if (!S_ISCHR(st.st_mode) && !S_ISBLK(st.st_mode))
2999 		return (Z_INVAL);
3000 
3001 	if (chown(path, owner, group) == -1)
3002 		return (Z_SYSTEM);
3003 
3004 	if (chmod(path, mode) == -1)
3005 		return (Z_SYSTEM);
3006 
3007 	if ((acltxt == NULL) || (strcmp(acltxt, "") == 0))
3008 		return (Z_OK);
3009 
3010 	if (acl_fromtext(acltxt, &aclp) != 0) {
3011 		errno = EINVAL;
3012 		return (Z_SYSTEM);
3013 	}
3014 
3015 	errno = 0;
3016 	if (acl_set(path, aclp) == -1) {
3017 		free(aclp);
3018 		return (Z_SYSTEM);
3019 	}
3020 
3021 	free(aclp);
3022 	return (Z_OK);
3023 }
3024 
3025 /*
3026  * This function finds everything mounted under a zone's rootpath.
3027  * This returns the number of mounts under rootpath, or -1 on error.
3028  * callback is called once per mount found with the first argument
3029  * pointing to a mnttab structure containing the mount's information.
3030  *
3031  * If the callback function returns non-zero zonecfg_find_mounts
3032  * aborts with an error.
3033  */
3034 int
3035 zonecfg_find_mounts(char *rootpath, int (*callback)(const struct mnttab *,
3036     void *), void *priv) {
3037 	FILE *mnttab;
3038 	struct mnttab m;
3039 	size_t l;
3040 	int zfsl;
3041 	int rv = 0;
3042 	char zfs_path[MAXPATHLEN];
3043 
3044 	assert(rootpath != NULL);
3045 
3046 	if ((zfsl = snprintf(zfs_path, sizeof (zfs_path), "%s/.zfs/", rootpath))
3047 	    >= sizeof (zfs_path))
3048 		return (-1);
3049 
3050 	l = strlen(rootpath);
3051 
3052 	mnttab = fopen("/etc/mnttab", "r");
3053 
3054 	if (mnttab == NULL)
3055 		return (-1);
3056 
3057 	if (ioctl(fileno(mnttab), MNTIOC_SHOWHIDDEN, NULL) < 0)  {
3058 		rv = -1;
3059 		goto out;
3060 	}
3061 
3062 	while (!getmntent(mnttab, &m)) {
3063 		if ((strncmp(rootpath, m.mnt_mountp, l) == 0) &&
3064 		    (m.mnt_mountp[l] == '/') &&
3065 		    (strncmp(zfs_path, m.mnt_mountp, zfsl) != 0)) {
3066 			rv++;
3067 			if (callback == NULL)
3068 				continue;
3069 			if (callback(&m, priv)) {
3070 				rv = -1;
3071 				goto out;
3072 
3073 			}
3074 		}
3075 	}
3076 
3077 out:
3078 	(void) fclose(mnttab);
3079 	return (rv);
3080 }
3081 
3082 int
3083 zonecfg_lookup_attr(zone_dochandle_t handle, struct zone_attrtab *tabptr)
3084 {
3085 	xmlNodePtr cur, firstmatch;
3086 	int err;
3087 	char name[MAXNAMELEN], type[MAXNAMELEN], value[MAXNAMELEN];
3088 
3089 	if (tabptr == NULL)
3090 		return (Z_INVAL);
3091 
3092 	if ((err = operation_prep(handle)) != Z_OK)
3093 		return (err);
3094 
3095 	cur = handle->zone_dh_cur;
3096 	firstmatch = NULL;
3097 	for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
3098 		if (xmlStrcmp(cur->name, DTD_ELEM_ATTR))
3099 			continue;
3100 		if (strlen(tabptr->zone_attr_name) > 0) {
3101 			if ((fetchprop(cur, DTD_ATTR_NAME, name,
3102 			    sizeof (name)) == Z_OK) &&
3103 			    (strcmp(tabptr->zone_attr_name, name) == 0)) {
3104 				if (firstmatch == NULL)
3105 					firstmatch = cur;
3106 				else
3107 					return (Z_INSUFFICIENT_SPEC);
3108 			}
3109 		}
3110 		if (strlen(tabptr->zone_attr_type) > 0) {
3111 			if ((fetchprop(cur, DTD_ATTR_TYPE, type,
3112 			    sizeof (type)) == Z_OK)) {
3113 				if (strcmp(tabptr->zone_attr_type, type) == 0) {
3114 					if (firstmatch == NULL)
3115 						firstmatch = cur;
3116 					else if (firstmatch != cur)
3117 						return (Z_INSUFFICIENT_SPEC);
3118 				} else {
3119 					/*
3120 					 * If another property matched but this
3121 					 * one doesn't then reset firstmatch.
3122 					 */
3123 					if (firstmatch == cur)
3124 						firstmatch = NULL;
3125 				}
3126 			}
3127 		}
3128 		if (strlen(tabptr->zone_attr_value) > 0) {
3129 			if ((fetchprop(cur, DTD_ATTR_VALUE, value,
3130 			    sizeof (value)) == Z_OK)) {
3131 				if (strcmp(tabptr->zone_attr_value, value) ==
3132 				    0) {
3133 					if (firstmatch == NULL)
3134 						firstmatch = cur;
3135 					else if (firstmatch != cur)
3136 						return (Z_INSUFFICIENT_SPEC);
3137 				} else {
3138 					/*
3139 					 * If another property matched but this
3140 					 * one doesn't then reset firstmatch.
3141 					 */
3142 					if (firstmatch == cur)
3143 						firstmatch = NULL;
3144 				}
3145 			}
3146 		}
3147 	}
3148 	if (firstmatch == NULL)
3149 		return (Z_NO_RESOURCE_ID);
3150 
3151 	cur = firstmatch;
3152 
3153 	if ((err = fetchprop(cur, DTD_ATTR_NAME, tabptr->zone_attr_name,
3154 	    sizeof (tabptr->zone_attr_name))) != Z_OK)
3155 		return (err);
3156 
3157 	if ((err = fetchprop(cur, DTD_ATTR_TYPE, tabptr->zone_attr_type,
3158 	    sizeof (tabptr->zone_attr_type))) != Z_OK)
3159 		return (err);
3160 
3161 	if ((err = fetchprop(cur, DTD_ATTR_VALUE, tabptr->zone_attr_value,
3162 	    sizeof (tabptr->zone_attr_value))) != Z_OK)
3163 		return (err);
3164 
3165 	return (Z_OK);
3166 }
3167 
3168 static int
3169 zonecfg_add_attr_core(zone_dochandle_t handle, struct zone_attrtab *tabptr)
3170 {
3171 	xmlNodePtr newnode, cur = handle->zone_dh_cur;
3172 	int err;
3173 
3174 	newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_ATTR, NULL);
3175 	err = newprop(newnode, DTD_ATTR_NAME, tabptr->zone_attr_name);
3176 	if (err != Z_OK)
3177 		return (err);
3178 	err = newprop(newnode, DTD_ATTR_TYPE, tabptr->zone_attr_type);
3179 	if (err != Z_OK)
3180 		return (err);
3181 	err = newprop(newnode, DTD_ATTR_VALUE, tabptr->zone_attr_value);
3182 	if (err != Z_OK)
3183 		return (err);
3184 	return (Z_OK);
3185 }
3186 
3187 int
3188 zonecfg_add_attr(zone_dochandle_t handle, struct zone_attrtab *tabptr)
3189 {
3190 	int err;
3191 
3192 	if (tabptr == NULL)
3193 		return (Z_INVAL);
3194 
3195 	if ((err = operation_prep(handle)) != Z_OK)
3196 		return (err);
3197 
3198 	if ((err = zonecfg_add_attr_core(handle, tabptr)) != Z_OK)
3199 		return (err);
3200 
3201 	return (Z_OK);
3202 }
3203 
3204 static int
3205 zonecfg_delete_attr_core(zone_dochandle_t handle, struct zone_attrtab *tabptr)
3206 {
3207 	xmlNodePtr cur = handle->zone_dh_cur;
3208 	int name_match, type_match, value_match;
3209 
3210 	for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
3211 		if (xmlStrcmp(cur->name, DTD_ELEM_ATTR))
3212 			continue;
3213 
3214 		name_match = match_prop(cur, DTD_ATTR_NAME,
3215 		    tabptr->zone_attr_name);
3216 		type_match = match_prop(cur, DTD_ATTR_TYPE,
3217 		    tabptr->zone_attr_type);
3218 		value_match = match_prop(cur, DTD_ATTR_VALUE,
3219 		    tabptr->zone_attr_value);
3220 
3221 		if (name_match && type_match && value_match) {
3222 			xmlUnlinkNode(cur);
3223 			xmlFreeNode(cur);
3224 			return (Z_OK);
3225 		}
3226 	}
3227 	return (Z_NO_RESOURCE_ID);
3228 }
3229 
3230 int
3231 zonecfg_delete_attr(zone_dochandle_t handle, struct zone_attrtab *tabptr)
3232 {
3233 	int err;
3234 
3235 	if (tabptr == NULL)
3236 		return (Z_INVAL);
3237 
3238 	if ((err = operation_prep(handle)) != Z_OK)
3239 		return (err);
3240 
3241 	if ((err = zonecfg_delete_attr_core(handle, tabptr)) != Z_OK)
3242 		return (err);
3243 
3244 	return (Z_OK);
3245 }
3246 
3247 int
3248 zonecfg_modify_attr(
3249 	zone_dochandle_t handle,
3250 	struct zone_attrtab *oldtabptr,
3251 	struct zone_attrtab *newtabptr)
3252 {
3253 	int err;
3254 
3255 	if (oldtabptr == NULL || newtabptr == NULL)
3256 		return (Z_INVAL);
3257 
3258 	if ((err = operation_prep(handle)) != Z_OK)
3259 		return (err);
3260 
3261 	if ((err = zonecfg_delete_attr_core(handle, oldtabptr)) != Z_OK)
3262 		return (err);
3263 
3264 	if ((err = zonecfg_add_attr_core(handle, newtabptr)) != Z_OK)
3265 		return (err);
3266 
3267 	return (Z_OK);
3268 }
3269 
3270 int
3271 zonecfg_get_attr_boolean(const struct zone_attrtab *attr, boolean_t *value)
3272 {
3273 	if (attr == NULL)
3274 		return (Z_INVAL);
3275 
3276 	if (strcmp(attr->zone_attr_type, DTD_ENTITY_BOOLEAN) != 0)
3277 		return (Z_INVAL);
3278 
3279 	if (strcmp(attr->zone_attr_value, DTD_ENTITY_TRUE) == 0) {
3280 		*value = B_TRUE;
3281 		return (Z_OK);
3282 	}
3283 	if (strcmp(attr->zone_attr_value, DTD_ENTITY_FALSE) == 0) {
3284 		*value = B_FALSE;
3285 		return (Z_OK);
3286 	}
3287 	return (Z_INVAL);
3288 }
3289 
3290 int
3291 zonecfg_get_attr_int(const struct zone_attrtab *attr, int64_t *value)
3292 {
3293 	long long result;
3294 	char *endptr;
3295 
3296 	if (attr == NULL)
3297 		return (Z_INVAL);
3298 
3299 	if (strcmp(attr->zone_attr_type, DTD_ENTITY_INT) != 0)
3300 		return (Z_INVAL);
3301 
3302 	errno = 0;
3303 	result = strtoll(attr->zone_attr_value, &endptr, 10);
3304 	if (errno != 0 || *endptr != '\0')
3305 		return (Z_INVAL);
3306 	*value = result;
3307 	return (Z_OK);
3308 }
3309 
3310 int
3311 zonecfg_get_attr_string(const struct zone_attrtab *attr, char *value,
3312     size_t val_sz)
3313 {
3314 	if (attr == NULL)
3315 		return (Z_INVAL);
3316 
3317 	if (strcmp(attr->zone_attr_type, DTD_ENTITY_STRING) != 0)
3318 		return (Z_INVAL);
3319 
3320 	if (strlcpy(value, attr->zone_attr_value, val_sz) >= val_sz)
3321 		return (Z_TOO_BIG);
3322 	return (Z_OK);
3323 }
3324 
3325 int
3326 zonecfg_get_attr_uint(const struct zone_attrtab *attr, uint64_t *value)
3327 {
3328 	unsigned long long result;
3329 	long long neg_result;
3330 	char *endptr;
3331 
3332 	if (attr == NULL)
3333 		return (Z_INVAL);
3334 
3335 	if (strcmp(attr->zone_attr_type, DTD_ENTITY_UINT) != 0)
3336 		return (Z_INVAL);
3337 
3338 	errno = 0;
3339 	result = strtoull(attr->zone_attr_value, &endptr, 10);
3340 	if (errno != 0 || *endptr != '\0')
3341 		return (Z_INVAL);
3342 	errno = 0;
3343 	neg_result = strtoll(attr->zone_attr_value, &endptr, 10);
3344 	/*
3345 	 * Incredibly, strtoull("<negative number>", ...) will not fail but
3346 	 * return whatever (negative) number cast as a u_longlong_t, so we
3347 	 * need to look for this here.
3348 	 */
3349 	if (errno == 0 && neg_result < 0)
3350 		return (Z_INVAL);
3351 	*value = result;
3352 	return (Z_OK);
3353 }
3354 
3355 int
3356 zonecfg_lookup_rctl(zone_dochandle_t handle, struct zone_rctltab *tabptr)
3357 {
3358 	xmlNodePtr cur, val;
3359 	char savedname[MAXNAMELEN];
3360 	struct zone_rctlvaltab *valptr;
3361 	int err;
3362 
3363 	if (strlen(tabptr->zone_rctl_name) == 0)
3364 		return (Z_INVAL);
3365 
3366 	if ((err = operation_prep(handle)) != Z_OK)
3367 		return (err);
3368 
3369 	cur = handle->zone_dh_cur;
3370 	for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
3371 		if (xmlStrcmp(cur->name, DTD_ELEM_RCTL))
3372 			continue;
3373 		if ((fetchprop(cur, DTD_ATTR_NAME, savedname,
3374 		    sizeof (savedname)) == Z_OK) &&
3375 		    (strcmp(savedname, tabptr->zone_rctl_name) == 0)) {
3376 			tabptr->zone_rctl_valptr = NULL;
3377 			for (val = cur->xmlChildrenNode; val != NULL;
3378 			    val = val->next) {
3379 				valptr = (struct zone_rctlvaltab *)malloc(
3380 				    sizeof (struct zone_rctlvaltab));
3381 				if (valptr == NULL)
3382 					return (Z_NOMEM);
3383 				if ((fetchprop(val, DTD_ATTR_PRIV,
3384 				    valptr->zone_rctlval_priv,
3385 				    sizeof (valptr->zone_rctlval_priv)) !=
3386 				    Z_OK))
3387 					break;
3388 				if ((fetchprop(val, DTD_ATTR_LIMIT,
3389 				    valptr->zone_rctlval_limit,
3390 				    sizeof (valptr->zone_rctlval_limit)) !=
3391 				    Z_OK))
3392 					break;
3393 				if ((fetchprop(val, DTD_ATTR_ACTION,
3394 				    valptr->zone_rctlval_action,
3395 				    sizeof (valptr->zone_rctlval_action)) !=
3396 				    Z_OK))
3397 					break;
3398 				if (zonecfg_add_rctl_value(tabptr, valptr) !=
3399 				    Z_OK)
3400 					break;
3401 			}
3402 			return (Z_OK);
3403 		}
3404 	}
3405 	return (Z_NO_RESOURCE_ID);
3406 }
3407 
3408 static int
3409 zonecfg_add_rctl_core(zone_dochandle_t handle, struct zone_rctltab *tabptr)
3410 {
3411 	xmlNodePtr newnode, cur = handle->zone_dh_cur, valnode;
3412 	struct zone_rctlvaltab *valptr;
3413 	int err;
3414 
3415 	newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_RCTL, NULL);
3416 	err = newprop(newnode, DTD_ATTR_NAME, tabptr->zone_rctl_name);
3417 	if (err != Z_OK)
3418 		return (err);
3419 	for (valptr = tabptr->zone_rctl_valptr; valptr != NULL;
3420 	    valptr = valptr->zone_rctlval_next) {
3421 		valnode = xmlNewTextChild(newnode, NULL,
3422 		    DTD_ELEM_RCTLVALUE, NULL);
3423 		err = newprop(valnode, DTD_ATTR_PRIV,
3424 		    valptr->zone_rctlval_priv);
3425 		if (err != Z_OK)
3426 			return (err);
3427 		err = newprop(valnode, DTD_ATTR_LIMIT,
3428 		    valptr->zone_rctlval_limit);
3429 		if (err != Z_OK)
3430 			return (err);
3431 		err = newprop(valnode, DTD_ATTR_ACTION,
3432 		    valptr->zone_rctlval_action);
3433 		if (err != Z_OK)
3434 			return (err);
3435 	}
3436 	return (Z_OK);
3437 }
3438 
3439 int
3440 zonecfg_add_rctl(zone_dochandle_t handle, struct zone_rctltab *tabptr)
3441 {
3442 	int err;
3443 
3444 	if (tabptr == NULL)
3445 		return (Z_INVAL);
3446 
3447 	if ((err = operation_prep(handle)) != Z_OK)
3448 		return (err);
3449 
3450 	if ((err = zonecfg_add_rctl_core(handle, tabptr)) != Z_OK)
3451 		return (err);
3452 
3453 	return (Z_OK);
3454 }
3455 
3456 static int
3457 zonecfg_delete_rctl_core(zone_dochandle_t handle, struct zone_rctltab *tabptr)
3458 {
3459 	xmlNodePtr cur = handle->zone_dh_cur;
3460 	xmlChar *savedname;
3461 	int name_result;
3462 
3463 	for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
3464 		if (xmlStrcmp(cur->name, DTD_ELEM_RCTL))
3465 			continue;
3466 
3467 		savedname = xmlGetProp(cur, DTD_ATTR_NAME);
3468 		if (savedname == NULL)	/* shouldn't happen */
3469 			continue;
3470 		name_result = xmlStrcmp(savedname,
3471 		    (const xmlChar *) tabptr->zone_rctl_name);
3472 		xmlFree(savedname);
3473 
3474 		if (name_result == 0) {
3475 			xmlUnlinkNode(cur);
3476 			xmlFreeNode(cur);
3477 			return (Z_OK);
3478 		}
3479 	}
3480 	return (Z_NO_RESOURCE_ID);
3481 }
3482 
3483 int
3484 zonecfg_delete_rctl(zone_dochandle_t handle, struct zone_rctltab *tabptr)
3485 {
3486 	int err;
3487 
3488 	if (tabptr == NULL)
3489 		return (Z_INVAL);
3490 
3491 	if ((err = operation_prep(handle)) != Z_OK)
3492 		return (err);
3493 
3494 	if ((err = zonecfg_delete_rctl_core(handle, tabptr)) != Z_OK)
3495 		return (err);
3496 
3497 	return (Z_OK);
3498 }
3499 
3500 int
3501 zonecfg_modify_rctl(
3502 	zone_dochandle_t handle,
3503 	struct zone_rctltab *oldtabptr,
3504 	struct zone_rctltab *newtabptr)
3505 {
3506 	int err;
3507 
3508 	if (oldtabptr == NULL || newtabptr == NULL)
3509 		return (Z_INVAL);
3510 
3511 	if ((err = operation_prep(handle)) != Z_OK)
3512 		return (err);
3513 
3514 	if ((err = zonecfg_delete_rctl_core(handle, oldtabptr)) != Z_OK)
3515 		return (err);
3516 
3517 	if ((err = zonecfg_add_rctl_core(handle, newtabptr)) != Z_OK)
3518 		return (err);
3519 
3520 	return (Z_OK);
3521 }
3522 
3523 int
3524 zonecfg_add_rctl_value(
3525 	struct zone_rctltab *tabptr,
3526 	struct zone_rctlvaltab *valtabptr)
3527 {
3528 	struct zone_rctlvaltab *last, *old, *new;
3529 	rctlblk_t *rctlblk = alloca(rctlblk_size());
3530 
3531 	last = tabptr->zone_rctl_valptr;
3532 	for (old = last; old != NULL; old = old->zone_rctlval_next)
3533 		last = old;	/* walk to the end of the list */
3534 	new = valtabptr;	/* alloc'd by caller */
3535 	new->zone_rctlval_next = NULL;
3536 	if (zonecfg_construct_rctlblk(valtabptr, rctlblk) != Z_OK)
3537 		return (Z_INVAL);
3538 	if (!zonecfg_valid_rctlblk(rctlblk))
3539 		return (Z_INVAL);
3540 	if (last == NULL)
3541 		tabptr->zone_rctl_valptr = new;
3542 	else
3543 		last->zone_rctlval_next = new;
3544 	return (Z_OK);
3545 }
3546 
3547 int
3548 zonecfg_remove_rctl_value(
3549 	struct zone_rctltab *tabptr,
3550 	struct zone_rctlvaltab *valtabptr)
3551 {
3552 	struct zone_rctlvaltab *last, *this, *next;
3553 
3554 	last = tabptr->zone_rctl_valptr;
3555 	for (this = last; this != NULL; this = this->zone_rctlval_next) {
3556 		if (strcmp(this->zone_rctlval_priv,
3557 		    valtabptr->zone_rctlval_priv) == 0 &&
3558 		    strcmp(this->zone_rctlval_limit,
3559 		    valtabptr->zone_rctlval_limit) == 0 &&
3560 		    strcmp(this->zone_rctlval_action,
3561 		    valtabptr->zone_rctlval_action) == 0) {
3562 			next = this->zone_rctlval_next;
3563 			if (this == tabptr->zone_rctl_valptr)
3564 				tabptr->zone_rctl_valptr = next;
3565 			else
3566 				last->zone_rctlval_next = next;
3567 			free(this);
3568 			return (Z_OK);
3569 		} else
3570 			last = this;
3571 	}
3572 	return (Z_NO_PROPERTY_ID);
3573 }
3574 
3575 void
3576 zonecfg_set_swinv(zone_dochandle_t handle)
3577 {
3578 	handle->zone_dh_sw_inv = B_TRUE;
3579 }
3580 
3581 /*
3582  * Add the pkg to the sw inventory on the handle.
3583  */
3584 int
3585 zonecfg_add_pkg(zone_dochandle_t handle, char *name, char *version)
3586 {
3587 	xmlNodePtr newnode;
3588 	xmlNodePtr cur;
3589 	int err;
3590 
3591 	if ((err = operation_prep(handle)) != Z_OK)
3592 		return (err);
3593 
3594 	cur = handle->zone_dh_cur;
3595 	newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_PACKAGE, NULL);
3596 	if ((err = newprop(newnode, DTD_ATTR_NAME, name)) != Z_OK)
3597 		return (err);
3598 	if ((err = newprop(newnode, DTD_ATTR_VERSION, version)) != Z_OK)
3599 		return (err);
3600 	return (Z_OK);
3601 }
3602 
3603 int
3604 zonecfg_add_patch(zone_dochandle_t handle, char *id, void **pnode)
3605 {
3606 	xmlNodePtr node = (xmlNodePtr)*pnode;
3607 	xmlNodePtr cur;
3608 	int err;
3609 
3610 	if ((err = operation_prep(handle)) != Z_OK)
3611 		return (err);
3612 
3613 	cur = handle->zone_dh_cur;
3614 	node = xmlNewTextChild(cur, NULL, DTD_ELEM_PATCH, NULL);
3615 	if ((err = newprop(node, DTD_ATTR_ID, id)) != Z_OK)
3616 		return (err);
3617 	*pnode = (void *)node;
3618 	return (Z_OK);
3619 }
3620 
3621 int
3622 zonecfg_add_patch_obs(char *id, void *cur)
3623 {
3624 	xmlNodePtr	node;
3625 	int err;
3626 
3627 	node = xmlNewTextChild((xmlNodePtr)cur, NULL, DTD_ELEM_OBSOLETES, NULL);
3628 	if ((err = newprop(node, DTD_ATTR_ID, id)) != Z_OK)
3629 		return (err);
3630 	return (Z_OK);
3631 }
3632 
3633 char *
3634 zonecfg_strerror(int errnum)
3635 {
3636 	switch (errnum) {
3637 	case Z_OK:
3638 		return (dgettext(TEXT_DOMAIN, "OK"));
3639 	case Z_EMPTY_DOCUMENT:
3640 		return (dgettext(TEXT_DOMAIN, "Empty document"));
3641 	case Z_WRONG_DOC_TYPE:
3642 		return (dgettext(TEXT_DOMAIN, "Wrong document type"));
3643 	case Z_BAD_PROPERTY:
3644 		return (dgettext(TEXT_DOMAIN, "Bad document property"));
3645 	case Z_TEMP_FILE:
3646 		return (dgettext(TEXT_DOMAIN,
3647 		    "Problem creating temporary file"));
3648 	case Z_SAVING_FILE:
3649 		return (dgettext(TEXT_DOMAIN, "Problem saving file"));
3650 	case Z_NO_ENTRY:
3651 		return (dgettext(TEXT_DOMAIN, "No such entry"));
3652 	case Z_BOGUS_ZONE_NAME:
3653 		return (dgettext(TEXT_DOMAIN, "Bogus zone name"));
3654 	case Z_REQD_RESOURCE_MISSING:
3655 		return (dgettext(TEXT_DOMAIN, "Required resource missing"));
3656 	case Z_REQD_PROPERTY_MISSING:
3657 		return (dgettext(TEXT_DOMAIN, "Required property missing"));
3658 	case Z_BAD_HANDLE:
3659 		return (dgettext(TEXT_DOMAIN, "Bad handle"));
3660 	case Z_NOMEM:
3661 		return (dgettext(TEXT_DOMAIN, "Out of memory"));
3662 	case Z_INVAL:
3663 		return (dgettext(TEXT_DOMAIN, "Invalid argument"));
3664 	case Z_ACCES:
3665 		return (dgettext(TEXT_DOMAIN, "Permission denied"));
3666 	case Z_TOO_BIG:
3667 		return (dgettext(TEXT_DOMAIN, "Argument list too long"));
3668 	case Z_MISC_FS:
3669 		return (dgettext(TEXT_DOMAIN,
3670 		    "Miscellaneous file system error"));
3671 	case Z_NO_ZONE:
3672 		return (dgettext(TEXT_DOMAIN, "No such zone configured"));
3673 	case Z_NO_RESOURCE_TYPE:
3674 		return (dgettext(TEXT_DOMAIN, "No such resource type"));
3675 	case Z_NO_RESOURCE_ID:
3676 		return (dgettext(TEXT_DOMAIN, "No such resource with that id"));
3677 	case Z_NO_PROPERTY_TYPE:
3678 		return (dgettext(TEXT_DOMAIN, "No such property type"));
3679 	case Z_NO_PROPERTY_ID:
3680 		return (dgettext(TEXT_DOMAIN, "No such property with that id"));
3681 	case Z_BAD_ZONE_STATE:
3682 		return (dgettext(TEXT_DOMAIN,
3683 		    "Zone state is invalid for the requested operation"));
3684 	case Z_INVALID_DOCUMENT:
3685 		return (dgettext(TEXT_DOMAIN, "Invalid document"));
3686 	case Z_NAME_IN_USE:
3687 		return (dgettext(TEXT_DOMAIN, "Zone name already in use"));
3688 	case Z_NO_SUCH_ID:
3689 		return (dgettext(TEXT_DOMAIN, "No such zone ID"));
3690 	case Z_UPDATING_INDEX:
3691 		return (dgettext(TEXT_DOMAIN, "Problem updating index file"));
3692 	case Z_LOCKING_FILE:
3693 		return (dgettext(TEXT_DOMAIN, "Locking index file"));
3694 	case Z_UNLOCKING_FILE:
3695 		return (dgettext(TEXT_DOMAIN, "Unlocking index file"));
3696 	case Z_INSUFFICIENT_SPEC:
3697 		return (dgettext(TEXT_DOMAIN, "Insufficient specification"));
3698 	case Z_RESOLVED_PATH:
3699 		return (dgettext(TEXT_DOMAIN, "Resolved path mismatch"));
3700 	case Z_IPV6_ADDR_PREFIX_LEN:
3701 		return (dgettext(TEXT_DOMAIN,
3702 		    "IPv6 address missing required prefix length"));
3703 	case Z_BOGUS_ADDRESS:
3704 		return (dgettext(TEXT_DOMAIN,
3705 		    "Neither an IPv4 nor an IPv6 address nor a host name"));
3706 	case Z_PRIV_PROHIBITED:
3707 		return (dgettext(TEXT_DOMAIN,
3708 		    "Specified privilege is prohibited"));
3709 	case Z_PRIV_REQUIRED:
3710 		return (dgettext(TEXT_DOMAIN,
3711 		    "Required privilege is missing"));
3712 	case Z_PRIV_UNKNOWN:
3713 		return (dgettext(TEXT_DOMAIN,
3714 		    "Specified privilege is unknown"));
3715 	case Z_BRAND_ERROR:
3716 		return (dgettext(TEXT_DOMAIN,
3717 		    "Brand-specific error"));
3718 	case Z_INCOMPATIBLE:
3719 		return (dgettext(TEXT_DOMAIN, "Incompatible settings"));
3720 	case Z_ALIAS_DISALLOW:
3721 		return (dgettext(TEXT_DOMAIN,
3722 		    "An incompatible rctl already exists for this property"));
3723 	case Z_CLEAR_DISALLOW:
3724 		return (dgettext(TEXT_DOMAIN,
3725 		    "Clearing this property is not allowed"));
3726 	case Z_POOL:
3727 		return (dgettext(TEXT_DOMAIN, "libpool(3LIB) error"));
3728 	case Z_POOLS_NOT_ACTIVE:
3729 		return (dgettext(TEXT_DOMAIN, "Pools facility not active; "
3730 		    "zone will not be bound to pool"));
3731 	case Z_POOL_ENABLE:
3732 		return (dgettext(TEXT_DOMAIN,
3733 		    "Could not enable pools facility"));
3734 	case Z_NO_POOL:
3735 		return (dgettext(TEXT_DOMAIN,
3736 		    "Pool not found; using default pool"));
3737 	case Z_POOL_CREATE:
3738 		return (dgettext(TEXT_DOMAIN,
3739 		    "Could not create a temporary pool"));
3740 	case Z_POOL_BIND:
3741 		return (dgettext(TEXT_DOMAIN, "Could not bind zone to pool"));
3742 	case Z_INVALID_PROPERTY:
3743 		return (dgettext(TEXT_DOMAIN, "Specified property is invalid"));
3744 	case Z_SYSTEM:
3745 		return (strerror(errno));
3746 	default:
3747 		return (dgettext(TEXT_DOMAIN, "Unknown error"));
3748 	}
3749 }
3750 
3751 /*
3752  * Note that the zonecfg_setXent() and zonecfg_endXent() calls are all the
3753  * same, as they just turn around and call zonecfg_setent() / zonecfg_endent().
3754  */
3755 
3756 static int
3757 zonecfg_setent(zone_dochandle_t handle)
3758 {
3759 	xmlNodePtr cur;
3760 	int err;
3761 
3762 	if (handle == NULL)
3763 		return (Z_INVAL);
3764 
3765 	if ((err = operation_prep(handle)) != Z_OK) {
3766 		handle->zone_dh_cur = NULL;
3767 		return (err);
3768 	}
3769 	cur = handle->zone_dh_cur;
3770 	cur = cur->xmlChildrenNode;
3771 	handle->zone_dh_cur = cur;
3772 	return (Z_OK);
3773 }
3774 
3775 static int
3776 zonecfg_endent(zone_dochandle_t handle)
3777 {
3778 	if (handle == NULL)
3779 		return (Z_INVAL);
3780 
3781 	handle->zone_dh_cur = handle->zone_dh_top;
3782 	return (Z_OK);
3783 }
3784 
3785 /*
3786  * Do the work required to manipulate a process through libproc.
3787  * If grab_process() returns no errors (0), then release_process()
3788  * must eventually be called.
3789  *
3790  * Return values:
3791  *      0 Successful creation of agent thread
3792  *      1 Error grabbing
3793  *      2 Error creating agent
3794  */
3795 static int
3796 grab_process(pr_info_handle_t *p)
3797 {
3798 	int ret;
3799 
3800 	if ((p->pr = Pgrab(p->pid, 0, &ret)) != NULL) {
3801 
3802 		if (Psetflags(p->pr, PR_RLC) != 0) {
3803 			Prelease(p->pr, 0);
3804 			return (1);
3805 		}
3806 		if (Pcreate_agent(p->pr) == 0) {
3807 			return (0);
3808 
3809 		} else {
3810 			Prelease(p->pr, 0);
3811 			return (2);
3812 		}
3813 	} else {
3814 		return (1);
3815 	}
3816 }
3817 
3818 /*
3819  * Release the specified process. This destroys the agent
3820  * and releases the process. If the process is NULL, nothing
3821  * is done. This function should only be called if grab_process()
3822  * has previously been called and returned success.
3823  *
3824  * This function is Pgrab-safe.
3825  */
3826 static void
3827 release_process(struct ps_prochandle *Pr)
3828 {
3829 	if (Pr == NULL)
3830 		return;
3831 
3832 	Pdestroy_agent(Pr);
3833 	Prelease(Pr, 0);
3834 }
3835 
3836 static boolean_t
3837 grab_zone_proc(char *zonename, pr_info_handle_t *p)
3838 {
3839 	DIR *dirp;
3840 	struct dirent *dentp;
3841 	zoneid_t zoneid;
3842 	int pid_self;
3843 	psinfo_t psinfo;
3844 
3845 	if (zone_get_id(zonename, &zoneid) != 0)
3846 		return (B_FALSE);
3847 
3848 	pid_self = getpid();
3849 
3850 	if ((dirp = opendir("/proc")) == NULL)
3851 		return (B_FALSE);
3852 
3853 	while (dentp = readdir(dirp)) {
3854 		p->pid = atoi(dentp->d_name);
3855 
3856 		/* Skip self */
3857 		if (p->pid == pid_self)
3858 			continue;
3859 
3860 		if (proc_get_psinfo(p->pid, &psinfo) != 0)
3861 			continue;
3862 
3863 		if (psinfo.pr_zoneid != zoneid)
3864 			continue;
3865 
3866 		/* attempt to grab process */
3867 		if (grab_process(p) != 0)
3868 			continue;
3869 
3870 		if (pr_getzoneid(p->pr) != zoneid) {
3871 			release_process(p->pr);
3872 			continue;
3873 		}
3874 
3875 		(void) closedir(dirp);
3876 		return (B_TRUE);
3877 	}
3878 
3879 	(void) closedir(dirp);
3880 	return (B_FALSE);
3881 }
3882 
3883 static boolean_t
3884 get_priv_rctl(struct ps_prochandle *pr, char *name, rctlblk_t *rblk)
3885 {
3886 	if (pr_getrctl(pr, name, NULL, rblk, RCTL_FIRST))
3887 		return (B_FALSE);
3888 
3889 	if (rctlblk_get_privilege(rblk) == RCPRIV_PRIVILEGED)
3890 		return (B_TRUE);
3891 
3892 	while (pr_getrctl(pr, name, rblk, rblk, RCTL_NEXT) == 0) {
3893 		if (rctlblk_get_privilege(rblk) == RCPRIV_PRIVILEGED)
3894 			return (B_TRUE);
3895 	}
3896 
3897 	return (B_FALSE);
3898 }
3899 
3900 /*
3901  * Apply the current rctl settings to the specified, running zone.
3902  */
3903 int
3904 zonecfg_apply_rctls(char *zone_name, zone_dochandle_t handle)
3905 {
3906 	int err;
3907 	int res = Z_OK;
3908 	rctlblk_t *rblk;
3909 	pr_info_handle_t p;
3910 	struct zone_rctltab rctl;
3911 
3912 	if ((err = zonecfg_setrctlent(handle)) != Z_OK)
3913 		return (err);
3914 
3915 	if ((rblk = (rctlblk_t *)malloc(rctlblk_size())) == NULL) {
3916 		(void) zonecfg_endrctlent(handle);
3917 		return (Z_NOMEM);
3918 	}
3919 
3920 	if (!grab_zone_proc(zone_name, &p)) {
3921 		(void) zonecfg_endrctlent(handle);
3922 		free(rblk);
3923 		return (Z_SYSTEM);
3924 	}
3925 
3926 	while (zonecfg_getrctlent(handle, &rctl) == Z_OK) {
3927 		char *rname;
3928 		struct zone_rctlvaltab *valptr;
3929 
3930 		rname = rctl.zone_rctl_name;
3931 
3932 		/* first delete all current privileged settings for this rctl */
3933 		while (get_priv_rctl(p.pr, rname, rblk)) {
3934 			if (pr_setrctl(p.pr, rname, NULL, rblk, RCTL_DELETE) !=
3935 			    0) {
3936 				res = Z_SYSTEM;
3937 				goto done;
3938 			}
3939 		}
3940 
3941 		/* now set each new value for the rctl */
3942 		for (valptr = rctl.zone_rctl_valptr; valptr != NULL;
3943 		    valptr = valptr->zone_rctlval_next) {
3944 			if ((err = zonecfg_construct_rctlblk(valptr, rblk))
3945 			    != Z_OK) {
3946 				res = errno = err;
3947 				goto done;
3948 			}
3949 
3950 			if (pr_setrctl(p.pr, rname, NULL, rblk, RCTL_INSERT)) {
3951 				res = Z_SYSTEM;
3952 				goto done;
3953 			}
3954 		}
3955 	}
3956 
3957 done:
3958 	release_process(p.pr);
3959 	free(rblk);
3960 	(void) zonecfg_endrctlent(handle);
3961 
3962 	return (res);
3963 }
3964 
3965 static const xmlChar *
3966 nm_to_dtd(char *nm)
3967 {
3968 	if (strcmp(nm, "device") == 0)
3969 		return (DTD_ELEM_DEVICE);
3970 	if (strcmp(nm, "fs") == 0)
3971 		return (DTD_ELEM_FS);
3972 	if (strcmp(nm, "inherit-pkg-dir") == 0)
3973 		return (DTD_ELEM_IPD);
3974 	if (strcmp(nm, "net") == 0)
3975 		return (DTD_ELEM_NET);
3976 	if (strcmp(nm, "attr") == 0)
3977 		return (DTD_ELEM_ATTR);
3978 	if (strcmp(nm, "rctl") == 0)
3979 		return (DTD_ELEM_RCTL);
3980 	if (strcmp(nm, "dataset") == 0)
3981 		return (DTD_ELEM_DATASET);
3982 	if (strcmp(nm, "admin") == 0)
3983 		return (DTD_ELEM_ADMIN);
3984 
3985 	return (NULL);
3986 }
3987 
3988 int
3989 zonecfg_num_resources(zone_dochandle_t handle, char *rsrc)
3990 {
3991 	int num = 0;
3992 	const xmlChar *dtd;
3993 	xmlNodePtr cur;
3994 
3995 	if ((dtd = nm_to_dtd(rsrc)) == NULL)
3996 		return (num);
3997 
3998 	if (zonecfg_setent(handle) != Z_OK)
3999 		return (num);
4000 
4001 	for (cur = handle->zone_dh_cur; cur != NULL; cur = cur->next)
4002 		if (xmlStrcmp(cur->name, dtd) == 0)
4003 			num++;
4004 
4005 	(void) zonecfg_endent(handle);
4006 
4007 	return (num);
4008 }
4009 
4010 int
4011 zonecfg_del_all_resources(zone_dochandle_t handle, char *rsrc)
4012 {
4013 	int err;
4014 	const xmlChar *dtd;
4015 	xmlNodePtr cur;
4016 
4017 	if ((dtd = nm_to_dtd(rsrc)) == NULL)
4018 		return (Z_NO_RESOURCE_TYPE);
4019 
4020 	if ((err = zonecfg_setent(handle)) != Z_OK)
4021 		return (err);
4022 
4023 	cur = handle->zone_dh_cur;
4024 	while (cur != NULL) {
4025 		xmlNodePtr tmp;
4026 
4027 		if (xmlStrcmp(cur->name, dtd)) {
4028 			cur = cur->next;
4029 			continue;
4030 		}
4031 
4032 		tmp = cur->next;
4033 		xmlUnlinkNode(cur);
4034 		xmlFreeNode(cur);
4035 		cur = tmp;
4036 	}
4037 
4038 	(void) zonecfg_endent(handle);
4039 	return (Z_OK);
4040 }
4041 
4042 static boolean_t
4043 valid_uint(char *s, uint64_t *n)
4044 {
4045 	char *endp;
4046 
4047 	/* strtoull accepts '-'?! so we want to flag that as an error */
4048 	if (strchr(s, '-') != NULL)
4049 		return (B_FALSE);
4050 
4051 	errno = 0;
4052 	*n = strtoull(s, &endp, 10);
4053 
4054 	if (errno != 0 || *endp != '\0')
4055 		return (B_FALSE);
4056 	return (B_TRUE);
4057 }
4058 
4059 /*
4060  * Convert a string representing a number (possibly a fraction) into an integer.
4061  * The string can have a modifier (K, M, G or T).   The modifiers are treated
4062  * as powers of two (not 10).
4063  */
4064 int
4065 zonecfg_str_to_bytes(char *str, uint64_t *bytes)
4066 {
4067 	long double val;
4068 	char *unitp;
4069 	uint64_t scale;
4070 
4071 	if ((val = strtold(str, &unitp)) < 0)
4072 		return (-1);
4073 
4074 	/* remove any leading white space from units string */
4075 	while (isspace(*unitp) != 0)
4076 		++unitp;
4077 
4078 	/* if no units explicitly set, error */
4079 	if (unitp == NULL || *unitp == '\0') {
4080 		scale = 1;
4081 	} else {
4082 		int i;
4083 		char *units[] = {"K", "M", "G", "T", NULL};
4084 
4085 		scale = 1024;
4086 
4087 		/* update scale based on units */
4088 		for (i = 0; units[i] != NULL; i++) {
4089 			if (strcasecmp(unitp, units[i]) == 0)
4090 				break;
4091 			scale <<= 10;
4092 		}
4093 
4094 		if (units[i] == NULL)
4095 			return (-1);
4096 	}
4097 
4098 	*bytes = (uint64_t)(val * scale);
4099 	return (0);
4100 }
4101 
4102 boolean_t
4103 zonecfg_valid_ncpus(char *lowstr, char *highstr)
4104 {
4105 	uint64_t low, high;
4106 
4107 	if (!valid_uint(lowstr, &low) || !valid_uint(highstr, &high) ||
4108 	    low < 1 || low > high)
4109 		return (B_FALSE);
4110 
4111 	return (B_TRUE);
4112 }
4113 
4114 boolean_t
4115 zonecfg_valid_importance(char *impstr)
4116 {
4117 	uint64_t num;
4118 
4119 	if (!valid_uint(impstr, &num))
4120 		return (B_FALSE);
4121 
4122 	return (B_TRUE);
4123 }
4124 
4125 boolean_t
4126 zonecfg_valid_alias_limit(char *name, char *limitstr, uint64_t *limit)
4127 {
4128 	int i;
4129 
4130 	for (i = 0; aliases[i].shortname != NULL; i++)
4131 		if (strcmp(name, aliases[i].shortname) == 0)
4132 			break;
4133 
4134 	if (aliases[i].shortname == NULL)
4135 		return (B_FALSE);
4136 
4137 	if (!valid_uint(limitstr, limit) || *limit < aliases[i].low_limit)
4138 		return (B_FALSE);
4139 
4140 	return (B_TRUE);
4141 }
4142 
4143 boolean_t
4144 zonecfg_valid_memlimit(char *memstr, uint64_t *mem_val)
4145 {
4146 	if (zonecfg_str_to_bytes(memstr, mem_val) != 0)
4147 		return (B_FALSE);
4148 
4149 	return (B_TRUE);
4150 }
4151 
4152 static int
4153 zerr_pool(char *pool_err, int err_size, int res)
4154 {
4155 	(void) strlcpy(pool_err, pool_strerror(pool_error()), err_size);
4156 	return (res);
4157 }
4158 
4159 static int
4160 create_tmp_pset(char *pool_err, int err_size, pool_conf_t *pconf, pool_t *pool,
4161     char *name, int min, int max)
4162 {
4163 	pool_resource_t *res;
4164 	pool_elem_t *elem;
4165 	pool_value_t *val;
4166 
4167 	if ((res = pool_resource_create(pconf, "pset", name)) == NULL)
4168 		return (zerr_pool(pool_err, err_size, Z_POOL));
4169 
4170 	if (pool_associate(pconf, pool, res) != PO_SUCCESS)
4171 		return (zerr_pool(pool_err, err_size, Z_POOL));
4172 
4173 	if ((elem = pool_resource_to_elem(pconf, res)) == NULL)
4174 		return (zerr_pool(pool_err, err_size, Z_POOL));
4175 
4176 	if ((val = pool_value_alloc()) == NULL)
4177 		return (zerr_pool(pool_err, err_size, Z_POOL));
4178 
4179 	/* set the maximum number of cpus for the pset */
4180 	pool_value_set_uint64(val, (uint64_t)max);
4181 
4182 	if (pool_put_property(pconf, elem, "pset.max", val) != PO_SUCCESS) {
4183 		pool_value_free(val);
4184 		return (zerr_pool(pool_err, err_size, Z_POOL));
4185 	}
4186 
4187 	/* set the minimum number of cpus for the pset */
4188 	pool_value_set_uint64(val, (uint64_t)min);
4189 
4190 	if (pool_put_property(pconf, elem, "pset.min", val) != PO_SUCCESS) {
4191 		pool_value_free(val);
4192 		return (zerr_pool(pool_err, err_size, Z_POOL));
4193 	}
4194 
4195 	pool_value_free(val);
4196 
4197 	return (Z_OK);
4198 }
4199 
4200 static int
4201 create_tmp_pool(char *pool_err, int err_size, pool_conf_t *pconf, char *name,
4202     struct zone_psettab *pset_tab)
4203 {
4204 	pool_t *pool;
4205 	int res = Z_OK;
4206 
4207 	/* create a temporary pool configuration */
4208 	if (pool_conf_open(pconf, NULL, PO_TEMP) != PO_SUCCESS) {
4209 		res = zerr_pool(pool_err, err_size, Z_POOL);
4210 		return (res);
4211 	}
4212 
4213 	if ((pool = pool_create(pconf, name)) == NULL) {
4214 		res = zerr_pool(pool_err, err_size, Z_POOL_CREATE);
4215 		goto done;
4216 	}
4217 
4218 	/* set pool importance */
4219 	if (pset_tab->zone_importance[0] != '\0') {
4220 		pool_elem_t *elem;
4221 		pool_value_t *val;
4222 
4223 		if ((elem = pool_to_elem(pconf, pool)) == NULL) {
4224 			res = zerr_pool(pool_err, err_size, Z_POOL);
4225 			goto done;
4226 		}
4227 
4228 		if ((val = pool_value_alloc()) == NULL) {
4229 			res = zerr_pool(pool_err, err_size, Z_POOL);
4230 			goto done;
4231 		}
4232 
4233 		pool_value_set_int64(val,
4234 		    (int64_t)atoi(pset_tab->zone_importance));
4235 
4236 		if (pool_put_property(pconf, elem, "pool.importance", val)
4237 		    != PO_SUCCESS) {
4238 			res = zerr_pool(pool_err, err_size, Z_POOL);
4239 			pool_value_free(val);
4240 			goto done;
4241 		}
4242 
4243 		pool_value_free(val);
4244 	}
4245 
4246 	if ((res = create_tmp_pset(pool_err, err_size, pconf, pool, name,
4247 	    atoi(pset_tab->zone_ncpu_min),
4248 	    atoi(pset_tab->zone_ncpu_max))) != Z_OK)
4249 		goto done;
4250 
4251 	/* validation */
4252 	if (pool_conf_status(pconf) == POF_INVALID) {
4253 		res = zerr_pool(pool_err, err_size, Z_POOL);
4254 		goto done;
4255 	}
4256 
4257 	/*
4258 	 * This validation is the one we expect to fail if the user specified
4259 	 * an invalid configuration (too many cpus) for this system.
4260 	 */
4261 	if (pool_conf_validate(pconf, POV_RUNTIME) != PO_SUCCESS) {
4262 		res = zerr_pool(pool_err, err_size, Z_POOL_CREATE);
4263 		goto done;
4264 	}
4265 
4266 	/*
4267 	 * Commit the dynamic configuration but not the pool configuration
4268 	 * file.
4269 	 */
4270 	if (pool_conf_commit(pconf, 1) != PO_SUCCESS)
4271 		res = zerr_pool(pool_err, err_size, Z_POOL);
4272 
4273 done:
4274 	(void) pool_conf_close(pconf);
4275 	return (res);
4276 }
4277 
4278 static int
4279 get_running_tmp_pset(pool_conf_t *pconf, pool_t *pool, pool_resource_t *pset,
4280     struct zone_psettab *pset_tab)
4281 {
4282 	int nfound = 0;
4283 	pool_elem_t *pe;
4284 	pool_value_t *pv = pool_value_alloc();
4285 	uint64_t val_uint;
4286 
4287 	if (pool != NULL) {
4288 		pe = pool_to_elem(pconf, pool);
4289 		if (pool_get_property(pconf, pe, "pool.importance", pv)
4290 		    != POC_INVAL) {
4291 			int64_t val_int;
4292 
4293 			(void) pool_value_get_int64(pv, &val_int);
4294 			(void) snprintf(pset_tab->zone_importance,
4295 			    sizeof (pset_tab->zone_importance), "%d", val_int);
4296 			nfound++;
4297 		}
4298 	}
4299 
4300 	if (pset != NULL) {
4301 		pe = pool_resource_to_elem(pconf, pset);
4302 		if (pool_get_property(pconf, pe, "pset.min", pv) != POC_INVAL) {
4303 			(void) pool_value_get_uint64(pv, &val_uint);
4304 			(void) snprintf(pset_tab->zone_ncpu_min,
4305 			    sizeof (pset_tab->zone_ncpu_min), "%u", val_uint);
4306 			nfound++;
4307 		}
4308 
4309 		if (pool_get_property(pconf, pe, "pset.max", pv) != POC_INVAL) {
4310 			(void) pool_value_get_uint64(pv, &val_uint);
4311 			(void) snprintf(pset_tab->zone_ncpu_max,
4312 			    sizeof (pset_tab->zone_ncpu_max), "%u", val_uint);
4313 			nfound++;
4314 		}
4315 	}
4316 
4317 	pool_value_free(pv);
4318 
4319 	if (nfound == 3)
4320 		return (PO_SUCCESS);
4321 
4322 	return (PO_FAIL);
4323 }
4324 
4325 /*
4326  * Determine if a tmp pool is configured and if so, if the configuration is
4327  * still valid or if it has been changed since the tmp pool was created.
4328  * If the tmp pool configuration is no longer valid, delete the tmp pool.
4329  *
4330  * Set *valid=B_TRUE if there is an existing, valid tmp pool configuration.
4331  */
4332 static int
4333 verify_del_tmp_pool(pool_conf_t *pconf, char *tmp_name, char *pool_err,
4334     int err_size, struct zone_psettab *pset_tab, boolean_t *exists)
4335 {
4336 	int res = Z_OK;
4337 	pool_t *pool;
4338 	pool_resource_t *pset;
4339 	struct zone_psettab pset_current;
4340 
4341 	*exists = B_FALSE;
4342 
4343 	if (pool_conf_open(pconf, pool_dynamic_location(), PO_RDWR)
4344 	    != PO_SUCCESS) {
4345 		res = zerr_pool(pool_err, err_size, Z_POOL);
4346 		return (res);
4347 	}
4348 
4349 	pool = pool_get_pool(pconf, tmp_name);
4350 	pset = pool_get_resource(pconf, "pset", tmp_name);
4351 
4352 	if (pool == NULL && pset == NULL) {
4353 		/* no tmp pool configured */
4354 		goto done;
4355 	}
4356 
4357 	/*
4358 	 * If an existing tmp pool for this zone is configured with the proper
4359 	 * settings, then the tmp pool is valid.
4360 	 */
4361 	if (get_running_tmp_pset(pconf, pool, pset, &pset_current)
4362 	    == PO_SUCCESS &&
4363 	    strcmp(pset_tab->zone_ncpu_min,
4364 	    pset_current.zone_ncpu_min) == 0 &&
4365 	    strcmp(pset_tab->zone_ncpu_max,
4366 	    pset_current.zone_ncpu_max) == 0 &&
4367 	    strcmp(pset_tab->zone_importance,
4368 	    pset_current.zone_importance) == 0) {
4369 		*exists = B_TRUE;
4370 
4371 	} else {
4372 		/*
4373 		 * An out-of-date tmp pool configuration exists.  Delete it
4374 		 * so that we can create the correct tmp pool config.
4375 		 */
4376 		if (pset != NULL &&
4377 		    pool_resource_destroy(pconf, pset) != PO_SUCCESS) {
4378 			res = zerr_pool(pool_err, err_size, Z_POOL);
4379 			goto done;
4380 		}
4381 
4382 		if (pool != NULL &&
4383 		    pool_destroy(pconf, pool) != PO_SUCCESS) {
4384 			res = zerr_pool(pool_err, err_size, Z_POOL);
4385 			goto done;
4386 		}
4387 
4388 		/* commit dynamic config */
4389 		if (pool_conf_commit(pconf, 0) != PO_SUCCESS)
4390 			res = zerr_pool(pool_err, err_size, Z_POOL);
4391 	}
4392 
4393 done:
4394 	(void) pool_conf_close(pconf);
4395 
4396 	return (res);
4397 }
4398 
4399 /*
4400  * Destroy any existing tmp pool.
4401  */
4402 int
4403 zonecfg_destroy_tmp_pool(char *zone_name, char *pool_err, int err_size)
4404 {
4405 	int status;
4406 	int res = Z_OK;
4407 	pool_conf_t *pconf;
4408 	pool_t *pool;
4409 	pool_resource_t *pset;
4410 	char tmp_name[MAX_TMP_POOL_NAME];
4411 
4412 	/* if pools not enabled then nothing to do */
4413 	if (pool_get_status(&status) != PO_SUCCESS || status != POOL_ENABLED)
4414 		return (Z_OK);
4415 
4416 	if ((pconf = pool_conf_alloc()) == NULL)
4417 		return (zerr_pool(pool_err, err_size, Z_POOL));
4418 
4419 	(void) snprintf(tmp_name, sizeof (tmp_name), TMP_POOL_NAME, zone_name);
4420 
4421 	if (pool_conf_open(pconf, pool_dynamic_location(), PO_RDWR)
4422 	    != PO_SUCCESS) {
4423 		res = zerr_pool(pool_err, err_size, Z_POOL);
4424 		pool_conf_free(pconf);
4425 		return (res);
4426 	}
4427 
4428 	pool = pool_get_pool(pconf, tmp_name);
4429 	pset = pool_get_resource(pconf, "pset", tmp_name);
4430 
4431 	if (pool == NULL && pset == NULL) {
4432 		/* nothing to destroy, we're done */
4433 		goto done;
4434 	}
4435 
4436 	if (pset != NULL && pool_resource_destroy(pconf, pset) != PO_SUCCESS) {
4437 		res = zerr_pool(pool_err, err_size, Z_POOL);
4438 		goto done;
4439 	}
4440 
4441 	if (pool != NULL && pool_destroy(pconf, pool) != PO_SUCCESS) {
4442 		res = zerr_pool(pool_err, err_size, Z_POOL);
4443 		goto done;
4444 	}
4445 
4446 	/* commit dynamic config */
4447 	if (pool_conf_commit(pconf, 0) != PO_SUCCESS)
4448 		res = zerr_pool(pool_err, err_size, Z_POOL);
4449 
4450 done:
4451 	(void) pool_conf_close(pconf);
4452 	pool_conf_free(pconf);
4453 
4454 	return (res);
4455 }
4456 
4457 /*
4458  * Attempt to bind to a tmp pool for this zone.  If there is no tmp pool
4459  * configured, we just return Z_OK.
4460  *
4461  * We either attempt to create the tmp pool for this zone or rebind to an
4462  * existing tmp pool for this zone.
4463  *
4464  * Rebinding is used when a zone with a tmp pool reboots so that we don't have
4465  * to recreate the tmp pool.  To do this we need to be sure we work correctly
4466  * for the following cases:
4467  *
4468  *	- there is an existing, properly configured tmp pool.
4469  *	- zonecfg added tmp pool after zone was booted, must now create.
4470  *	- zonecfg updated tmp pool config after zone was booted, in this case
4471  *	  we destroy the old tmp pool and create a new one.
4472  */
4473 int
4474 zonecfg_bind_tmp_pool(zone_dochandle_t handle, zoneid_t zoneid, char *pool_err,
4475     int err_size)
4476 {
4477 	struct zone_psettab pset_tab;
4478 	int err;
4479 	int status;
4480 	pool_conf_t *pconf;
4481 	boolean_t exists;
4482 	char zone_name[ZONENAME_MAX];
4483 	char tmp_name[MAX_TMP_POOL_NAME];
4484 
4485 	(void) getzonenamebyid(zoneid, zone_name, sizeof (zone_name));
4486 
4487 	err = zonecfg_lookup_pset(handle, &pset_tab);
4488 
4489 	/* if no temporary pool configured, we're done */
4490 	if (err == Z_NO_ENTRY)
4491 		return (Z_OK);
4492 
4493 	/*
4494 	 * importance might not have a value but we need to validate it here,
4495 	 * so set the default.
4496 	 */
4497 	if (pset_tab.zone_importance[0] == '\0')
4498 		(void) strlcpy(pset_tab.zone_importance, "1",
4499 		    sizeof (pset_tab.zone_importance));
4500 
4501 	/* if pools not enabled, enable them now */
4502 	if (pool_get_status(&status) != PO_SUCCESS || status != POOL_ENABLED) {
4503 		if (pool_set_status(POOL_ENABLED) != PO_SUCCESS)
4504 			return (Z_POOL_ENABLE);
4505 	}
4506 
4507 	if ((pconf = pool_conf_alloc()) == NULL)
4508 		return (zerr_pool(pool_err, err_size, Z_POOL));
4509 
4510 	(void) snprintf(tmp_name, sizeof (tmp_name), TMP_POOL_NAME, zone_name);
4511 
4512 	/*
4513 	 * Check if a valid tmp pool/pset already exists.  If so, we just
4514 	 * reuse it.
4515 	 */
4516 	if ((err = verify_del_tmp_pool(pconf, tmp_name, pool_err, err_size,
4517 	    &pset_tab, &exists)) != Z_OK) {
4518 		pool_conf_free(pconf);
4519 		return (err);
4520 	}
4521 
4522 	if (!exists)
4523 		err = create_tmp_pool(pool_err, err_size, pconf, tmp_name,
4524 		    &pset_tab);
4525 
4526 	pool_conf_free(pconf);
4527 
4528 	if (err != Z_OK)
4529 		return (err);
4530 
4531 	/* Bind the zone to the pool. */
4532 	if (pool_set_binding(tmp_name, P_ZONEID, zoneid) != PO_SUCCESS)
4533 		return (zerr_pool(pool_err, err_size, Z_POOL_BIND));
4534 
4535 	return (Z_OK);
4536 }
4537 
4538 /*
4539  * Attempt to bind to a permanent pool for this zone.  If there is no
4540  * permanent pool configured, we just return Z_OK.
4541  */
4542 int
4543 zonecfg_bind_pool(zone_dochandle_t handle, zoneid_t zoneid, char *pool_err,
4544     int err_size)
4545 {
4546 	pool_conf_t *poolconf;
4547 	pool_t *pool;
4548 	char poolname[MAXPATHLEN];
4549 	int status;
4550 	int error;
4551 
4552 	/*
4553 	 * Find the pool mentioned in the zone configuration, and bind to it.
4554 	 */
4555 	error = zonecfg_get_pool(handle, poolname, sizeof (poolname));
4556 	if (error == Z_NO_ENTRY || (error == Z_OK && strlen(poolname) == 0)) {
4557 		/*
4558 		 * The property is not set on the zone, so the pool
4559 		 * should be bound to the default pool.  But that's
4560 		 * already done by the kernel, so we can just return.
4561 		 */
4562 		return (Z_OK);
4563 	}
4564 	if (error != Z_OK) {
4565 		/*
4566 		 * Not an error, even though it shouldn't be happening.
4567 		 */
4568 		return (Z_OK);
4569 	}
4570 	/*
4571 	 * Don't do anything if pools aren't enabled.
4572 	 */
4573 	if (pool_get_status(&status) != PO_SUCCESS || status != POOL_ENABLED)
4574 		return (Z_POOLS_NOT_ACTIVE);
4575 
4576 	/*
4577 	 * Try to provide a sane error message if the requested pool doesn't
4578 	 * exist.
4579 	 */
4580 	if ((poolconf = pool_conf_alloc()) == NULL)
4581 		return (zerr_pool(pool_err, err_size, Z_POOL));
4582 
4583 	if (pool_conf_open(poolconf, pool_dynamic_location(), PO_RDONLY) !=
4584 	    PO_SUCCESS) {
4585 		pool_conf_free(poolconf);
4586 		return (zerr_pool(pool_err, err_size, Z_POOL));
4587 	}
4588 	pool = pool_get_pool(poolconf, poolname);
4589 	(void) pool_conf_close(poolconf);
4590 	pool_conf_free(poolconf);
4591 	if (pool == NULL)
4592 		return (Z_NO_POOL);
4593 
4594 	/*
4595 	 * Bind the zone to the pool.
4596 	 */
4597 	if (pool_set_binding(poolname, P_ZONEID, zoneid) != PO_SUCCESS) {
4598 		/* if bind fails, return poolname for the error msg */
4599 		(void) strlcpy(pool_err, poolname, err_size);
4600 		return (Z_POOL_BIND);
4601 	}
4602 
4603 	return (Z_OK);
4604 }
4605 
4606 int
4607 zonecfg_get_poolname(zone_dochandle_t handle, char *zone, char *pool,
4608     size_t poolsize)
4609 {
4610 	int err;
4611 	struct zone_psettab pset_tab;
4612 
4613 	err = zonecfg_lookup_pset(handle, &pset_tab);
4614 	if ((err != Z_NO_ENTRY) && (err != Z_OK))
4615 		return (err);
4616 
4617 	/* pset was found so a temporary pool was created */
4618 	if (err == Z_OK) {
4619 		(void) snprintf(pool, poolsize, TMP_POOL_NAME, zone);
4620 		return (Z_OK);
4621 	}
4622 
4623 	/* lookup the poolname in zonecfg */
4624 	return (zonecfg_get_pool(handle, pool, poolsize));
4625 }
4626 
4627 static boolean_t
4628 svc_enabled(char *svc_name)
4629 {
4630 	scf_simple_prop_t	*prop;
4631 	boolean_t		found = B_FALSE;
4632 
4633 	prop = scf_simple_prop_get(NULL, svc_name, SCF_PG_GENERAL,
4634 	    SCF_PROPERTY_ENABLED);
4635 
4636 	if (scf_simple_prop_numvalues(prop) == 1 &&
4637 	    *scf_simple_prop_next_boolean(prop) != 0)
4638 		found = B_TRUE;
4639 
4640 	scf_simple_prop_free(prop);
4641 
4642 	return (found);
4643 }
4644 
4645 /*
4646  * If the zone has capped-memory, make sure the rcap service is enabled.
4647  */
4648 int
4649 zonecfg_enable_rcapd(char *err, int size)
4650 {
4651 	if (!svc_enabled(RCAP_SERVICE) &&
4652 	    smf_enable_instance(RCAP_SERVICE, 0) == -1) {
4653 		(void) strlcpy(err, scf_strerror(scf_error()), size);
4654 		return (Z_SYSTEM);
4655 	}
4656 
4657 	return (Z_OK);
4658 }
4659 
4660 /*
4661  * Return true if pset has cpu range specified and poold is not enabled.
4662  */
4663 boolean_t
4664 zonecfg_warn_poold(zone_dochandle_t handle)
4665 {
4666 	struct zone_psettab pset_tab;
4667 	int min, max;
4668 	int err;
4669 
4670 	err = zonecfg_lookup_pset(handle, &pset_tab);
4671 
4672 	/* if no temporary pool configured, we're done */
4673 	if (err == Z_NO_ENTRY)
4674 		return (B_FALSE);
4675 
4676 	min = atoi(pset_tab.zone_ncpu_min);
4677 	max = atoi(pset_tab.zone_ncpu_max);
4678 
4679 	/* range not specified, no need for poold */
4680 	if (min == max)
4681 		return (B_FALSE);
4682 
4683 	/* we have a range, check if poold service is enabled */
4684 	if (svc_enabled(POOLD_SERVICE))
4685 		return (B_FALSE);
4686 
4687 	return (B_TRUE);
4688 }
4689 
4690 /*
4691  * Retrieve the specified pool's thread scheduling class.  'poolname' must
4692  * refer to the name of a configured resource pool.  The thread scheduling
4693  * class specified by the pool will be stored in the buffer to which 'class'
4694  * points.  'clsize' is the byte size of the buffer to which 'class' points.
4695  *
4696  * This function returns Z_OK if it successfully stored the specified pool's
4697  * thread scheduling class into the buffer to which 'class' points.  It returns
4698  * Z_NO_POOL if resource pools are not enabled, the function is unable to
4699  * access the system's resource pools configuration, or the specified pool
4700  * does not exist.  The function returns Z_TOO_BIG if the buffer to which
4701  * 'class' points is not large enough to contain the thread scheduling class'
4702  * name.  The function returns Z_NO_ENTRY if the pool does not specify a thread
4703  * scheduling class.
4704  */
4705 static int
4706 get_pool_sched_class(char *poolname, char *class, int clsize)
4707 {
4708 	int status;
4709 	pool_conf_t *poolconf;
4710 	pool_t *pool;
4711 	pool_elem_t *pe;
4712 	pool_value_t *pv = pool_value_alloc();
4713 	const char *sched_str;
4714 
4715 	if (pool_get_status(&status) != PO_SUCCESS || status != POOL_ENABLED)
4716 		return (Z_NO_POOL);
4717 
4718 	if ((poolconf = pool_conf_alloc()) == NULL)
4719 		return (Z_NO_POOL);
4720 
4721 	if (pool_conf_open(poolconf, pool_dynamic_location(), PO_RDONLY) !=
4722 	    PO_SUCCESS) {
4723 		pool_conf_free(poolconf);
4724 		return (Z_NO_POOL);
4725 	}
4726 
4727 	if ((pool = pool_get_pool(poolconf, poolname)) == NULL) {
4728 		(void) pool_conf_close(poolconf);
4729 		pool_conf_free(poolconf);
4730 		return (Z_NO_POOL);
4731 	}
4732 
4733 	pe = pool_to_elem(poolconf, pool);
4734 	if (pool_get_property(poolconf, pe, "pool.scheduler", pv) !=
4735 	    POC_STRING) {
4736 		(void) pool_conf_close(poolconf);
4737 		pool_conf_free(poolconf);
4738 		return (Z_NO_ENTRY);
4739 	}
4740 	(void) pool_value_get_string(pv, &sched_str);
4741 	(void) pool_conf_close(poolconf);
4742 	pool_conf_free(poolconf);
4743 	if (strlcpy(class, sched_str, clsize) >= clsize)
4744 		return (Z_TOO_BIG);
4745 	return (Z_OK);
4746 }
4747 
4748 /*
4749  * Get the default scheduling class for the zone.  This will either be the
4750  * class set on the zone's pool or the system default scheduling class.
4751  */
4752 int
4753 zonecfg_get_dflt_sched_class(zone_dochandle_t handle, char *class, int clsize)
4754 {
4755 	char poolname[MAXPATHLEN];
4756 
4757 	if (zonecfg_get_pool(handle, poolname, sizeof (poolname)) == Z_OK) {
4758 		/* check if the zone's pool specified a sched class */
4759 		if (get_pool_sched_class(poolname, class, clsize) == Z_OK)
4760 			return (Z_OK);
4761 	}
4762 
4763 	if (priocntl(0, 0, PC_GETDFLCL, class, (uint64_t)clsize) == -1)
4764 		return (Z_TOO_BIG);
4765 
4766 	return (Z_OK);
4767 }
4768 
4769 int
4770 zonecfg_setfsent(zone_dochandle_t handle)
4771 {
4772 	return (zonecfg_setent(handle));
4773 }
4774 
4775 int
4776 zonecfg_getfsent(zone_dochandle_t handle, struct zone_fstab *tabptr)
4777 {
4778 	xmlNodePtr cur, options;
4779 	char options_str[MAX_MNTOPT_STR];
4780 	int err;
4781 
4782 	if (handle == NULL)
4783 		return (Z_INVAL);
4784 
4785 	if ((cur = handle->zone_dh_cur) == NULL)
4786 		return (Z_NO_ENTRY);
4787 
4788 	for (; cur != NULL; cur = cur->next)
4789 		if (!xmlStrcmp(cur->name, DTD_ELEM_FS))
4790 			break;
4791 	if (cur == NULL) {
4792 		handle->zone_dh_cur = handle->zone_dh_top;
4793 		return (Z_NO_ENTRY);
4794 	}
4795 
4796 	if ((err = fetchprop(cur, DTD_ATTR_SPECIAL, tabptr->zone_fs_special,
4797 	    sizeof (tabptr->zone_fs_special))) != Z_OK) {
4798 		handle->zone_dh_cur = handle->zone_dh_top;
4799 		return (err);
4800 	}
4801 
4802 	if ((err = fetchprop(cur, DTD_ATTR_RAW, tabptr->zone_fs_raw,
4803 	    sizeof (tabptr->zone_fs_raw))) != Z_OK) {
4804 		handle->zone_dh_cur = handle->zone_dh_top;
4805 		return (err);
4806 	}
4807 
4808 	if ((err = fetchprop(cur, DTD_ATTR_DIR, tabptr->zone_fs_dir,
4809 	    sizeof (tabptr->zone_fs_dir))) != Z_OK) {
4810 		handle->zone_dh_cur = handle->zone_dh_top;
4811 		return (err);
4812 	}
4813 
4814 	if ((err = fetchprop(cur, DTD_ATTR_TYPE, tabptr->zone_fs_type,
4815 	    sizeof (tabptr->zone_fs_type))) != Z_OK) {
4816 		handle->zone_dh_cur = handle->zone_dh_top;
4817 		return (err);
4818 	}
4819 
4820 	/* OK for options to be NULL */
4821 	tabptr->zone_fs_options = NULL;
4822 	for (options = cur->xmlChildrenNode; options != NULL;
4823 	    options = options->next) {
4824 		if (fetchprop(options, DTD_ATTR_NAME, options_str,
4825 		    sizeof (options_str)) != Z_OK)
4826 			break;
4827 		if (zonecfg_add_fs_option(tabptr, options_str) != Z_OK)
4828 			break;
4829 	}
4830 
4831 	handle->zone_dh_cur = cur->next;
4832 	return (Z_OK);
4833 }
4834 
4835 int
4836 zonecfg_endfsent(zone_dochandle_t handle)
4837 {
4838 	return (zonecfg_endent(handle));
4839 }
4840 
4841 int
4842 zonecfg_setipdent(zone_dochandle_t handle)
4843 {
4844 	return (zonecfg_setent(handle));
4845 }
4846 
4847 int
4848 zonecfg_getipdent(zone_dochandle_t handle, struct zone_fstab *tabptr)
4849 {
4850 	xmlNodePtr cur;
4851 	int err;
4852 
4853 	if (handle == NULL)
4854 		return (Z_INVAL);
4855 
4856 	if ((cur = handle->zone_dh_cur) == NULL)
4857 		return (Z_NO_ENTRY);
4858 
4859 	for (; cur != NULL; cur = cur->next)
4860 		if (!xmlStrcmp(cur->name, DTD_ELEM_IPD))
4861 			break;
4862 	if (cur == NULL) {
4863 		handle->zone_dh_cur = handle->zone_dh_top;
4864 		return (Z_NO_ENTRY);
4865 	}
4866 
4867 	if ((err = fetchprop(cur, DTD_ATTR_DIR, tabptr->zone_fs_dir,
4868 	    sizeof (tabptr->zone_fs_dir))) != Z_OK) {
4869 		handle->zone_dh_cur = handle->zone_dh_top;
4870 		return (err);
4871 	}
4872 
4873 	handle->zone_dh_cur = cur->next;
4874 	return (Z_OK);
4875 }
4876 
4877 int
4878 zonecfg_endipdent(zone_dochandle_t handle)
4879 {
4880 	return (zonecfg_endent(handle));
4881 }
4882 
4883 int
4884 zonecfg_setnwifent(zone_dochandle_t handle)
4885 {
4886 	return (zonecfg_setent(handle));
4887 }
4888 
4889 int
4890 zonecfg_getnwifent(zone_dochandle_t handle, struct zone_nwiftab *tabptr)
4891 {
4892 	xmlNodePtr cur;
4893 	int err;
4894 
4895 	if (handle == NULL)
4896 		return (Z_INVAL);
4897 
4898 	if ((cur = handle->zone_dh_cur) == NULL)
4899 		return (Z_NO_ENTRY);
4900 
4901 	for (; cur != NULL; cur = cur->next)
4902 		if (!xmlStrcmp(cur->name, DTD_ELEM_NET))
4903 			break;
4904 	if (cur == NULL) {
4905 		handle->zone_dh_cur = handle->zone_dh_top;
4906 		return (Z_NO_ENTRY);
4907 	}
4908 
4909 	if ((err = fetchprop(cur, DTD_ATTR_ADDRESS, tabptr->zone_nwif_address,
4910 	    sizeof (tabptr->zone_nwif_address))) != Z_OK) {
4911 		handle->zone_dh_cur = handle->zone_dh_top;
4912 		return (err);
4913 	}
4914 
4915 	if ((err = fetchprop(cur, DTD_ATTR_PHYSICAL, tabptr->zone_nwif_physical,
4916 	    sizeof (tabptr->zone_nwif_physical))) != Z_OK) {
4917 		handle->zone_dh_cur = handle->zone_dh_top;
4918 		return (err);
4919 	}
4920 
4921 	if ((err = fetchprop(cur, DTD_ATTR_DEFROUTER,
4922 	    tabptr->zone_nwif_defrouter,
4923 	    sizeof (tabptr->zone_nwif_defrouter))) != Z_OK) {
4924 		handle->zone_dh_cur = handle->zone_dh_top;
4925 		return (err);
4926 	}
4927 
4928 	handle->zone_dh_cur = cur->next;
4929 	return (Z_OK);
4930 }
4931 
4932 int
4933 zonecfg_endnwifent(zone_dochandle_t handle)
4934 {
4935 	return (zonecfg_endent(handle));
4936 }
4937 
4938 int
4939 zonecfg_setdevent(zone_dochandle_t handle)
4940 {
4941 	return (zonecfg_setent(handle));
4942 }
4943 
4944 int
4945 zonecfg_getdevent(zone_dochandle_t handle, struct zone_devtab *tabptr)
4946 {
4947 	xmlNodePtr cur;
4948 	int err;
4949 
4950 	if (handle == NULL)
4951 		return (Z_INVAL);
4952 
4953 	if ((cur = handle->zone_dh_cur) == NULL)
4954 		return (Z_NO_ENTRY);
4955 
4956 	for (; cur != NULL; cur = cur->next)
4957 		if (!xmlStrcmp(cur->name, DTD_ELEM_DEVICE))
4958 			break;
4959 	if (cur == NULL) {
4960 		handle->zone_dh_cur = handle->zone_dh_top;
4961 		return (Z_NO_ENTRY);
4962 	}
4963 
4964 	if ((err = fetchprop(cur, DTD_ATTR_MATCH, tabptr->zone_dev_match,
4965 	    sizeof (tabptr->zone_dev_match))) != Z_OK) {
4966 		handle->zone_dh_cur = handle->zone_dh_top;
4967 		return (err);
4968 	}
4969 
4970 	handle->zone_dh_cur = cur->next;
4971 	return (Z_OK);
4972 }
4973 
4974 int
4975 zonecfg_enddevent(zone_dochandle_t handle)
4976 {
4977 	return (zonecfg_endent(handle));
4978 }
4979 
4980 int
4981 zonecfg_setrctlent(zone_dochandle_t handle)
4982 {
4983 	return (zonecfg_setent(handle));
4984 }
4985 
4986 int
4987 zonecfg_getrctlent(zone_dochandle_t handle, struct zone_rctltab *tabptr)
4988 {
4989 	xmlNodePtr cur, val;
4990 	struct zone_rctlvaltab *valptr;
4991 	int err;
4992 
4993 	if (handle == NULL)
4994 		return (Z_INVAL);
4995 
4996 	if ((cur = handle->zone_dh_cur) == NULL)
4997 		return (Z_NO_ENTRY);
4998 
4999 	for (; cur != NULL; cur = cur->next)
5000 		if (!xmlStrcmp(cur->name, DTD_ELEM_RCTL))
5001 			break;
5002 	if (cur == NULL) {
5003 		handle->zone_dh_cur = handle->zone_dh_top;
5004 		return (Z_NO_ENTRY);
5005 	}
5006 
5007 	if ((err = fetchprop(cur, DTD_ATTR_NAME, tabptr->zone_rctl_name,
5008 	    sizeof (tabptr->zone_rctl_name))) != Z_OK) {
5009 		handle->zone_dh_cur = handle->zone_dh_top;
5010 		return (err);
5011 	}
5012 
5013 	tabptr->zone_rctl_valptr = NULL;
5014 	for (val = cur->xmlChildrenNode; val != NULL; val = val->next) {
5015 		valptr = (struct zone_rctlvaltab *)malloc(
5016 		    sizeof (struct zone_rctlvaltab));
5017 		if (valptr == NULL)
5018 			return (Z_NOMEM);
5019 		if (fetchprop(val, DTD_ATTR_PRIV, valptr->zone_rctlval_priv,
5020 		    sizeof (valptr->zone_rctlval_priv)) != Z_OK)
5021 			break;
5022 		if (fetchprop(val, DTD_ATTR_LIMIT, valptr->zone_rctlval_limit,
5023 		    sizeof (valptr->zone_rctlval_limit)) != Z_OK)
5024 			break;
5025 		if (fetchprop(val, DTD_ATTR_ACTION, valptr->zone_rctlval_action,
5026 		    sizeof (valptr->zone_rctlval_action)) != Z_OK)
5027 			break;
5028 		if (zonecfg_add_rctl_value(tabptr, valptr) != Z_OK)
5029 			break;
5030 	}
5031 
5032 	handle->zone_dh_cur = cur->next;
5033 	return (Z_OK);
5034 }
5035 
5036 int
5037 zonecfg_endrctlent(zone_dochandle_t handle)
5038 {
5039 	return (zonecfg_endent(handle));
5040 }
5041 
5042 int
5043 zonecfg_setattrent(zone_dochandle_t handle)
5044 {
5045 	return (zonecfg_setent(handle));
5046 }
5047 
5048 int
5049 zonecfg_getattrent(zone_dochandle_t handle, struct zone_attrtab *tabptr)
5050 {
5051 	xmlNodePtr cur;
5052 	int err;
5053 
5054 	if (handle == NULL)
5055 		return (Z_INVAL);
5056 
5057 	if ((cur = handle->zone_dh_cur) == NULL)
5058 		return (Z_NO_ENTRY);
5059 
5060 	for (; cur != NULL; cur = cur->next)
5061 		if (!xmlStrcmp(cur->name, DTD_ELEM_ATTR))
5062 			break;
5063 	if (cur == NULL) {
5064 		handle->zone_dh_cur = handle->zone_dh_top;
5065 		return (Z_NO_ENTRY);
5066 	}
5067 
5068 	if ((err = fetchprop(cur, DTD_ATTR_NAME, tabptr->zone_attr_name,
5069 	    sizeof (tabptr->zone_attr_name))) != Z_OK) {
5070 		handle->zone_dh_cur = handle->zone_dh_top;
5071 		return (err);
5072 	}
5073 
5074 	if ((err = fetchprop(cur, DTD_ATTR_TYPE, tabptr->zone_attr_type,
5075 	    sizeof (tabptr->zone_attr_type))) != Z_OK) {
5076 		handle->zone_dh_cur = handle->zone_dh_top;
5077 		return (err);
5078 	}
5079 
5080 	if ((err = fetchprop(cur, DTD_ATTR_VALUE, tabptr->zone_attr_value,
5081 	    sizeof (tabptr->zone_attr_value))) != Z_OK) {
5082 		handle->zone_dh_cur = handle->zone_dh_top;
5083 		return (err);
5084 	}
5085 
5086 	handle->zone_dh_cur = cur->next;
5087 	return (Z_OK);
5088 }
5089 
5090 int
5091 zonecfg_endattrent(zone_dochandle_t handle)
5092 {
5093 	return (zonecfg_endent(handle));
5094 }
5095 
5096 int
5097 zonecfg_setadminent(zone_dochandle_t handle)
5098 {
5099 	return (zonecfg_setent(handle));
5100 }
5101 
5102 int
5103 zonecfg_getadminent(zone_dochandle_t handle, struct zone_admintab *tabptr)
5104 {
5105 	xmlNodePtr cur;
5106 	int err;
5107 
5108 	if (handle == NULL)
5109 		return (Z_INVAL);
5110 
5111 	if ((cur = handle->zone_dh_cur) == NULL)
5112 		return (Z_NO_ENTRY);
5113 
5114 	for (; cur != NULL; cur = cur->next)
5115 		if (!xmlStrcmp(cur->name, DTD_ELEM_ADMIN))
5116 			break;
5117 	if (cur == NULL) {
5118 		handle->zone_dh_cur = handle->zone_dh_top;
5119 		return (Z_NO_ENTRY);
5120 	}
5121 
5122 	if ((err = fetchprop(cur, DTD_ATTR_USER, tabptr->zone_admin_user,
5123 	    sizeof (tabptr->zone_admin_user))) != Z_OK) {
5124 		handle->zone_dh_cur = handle->zone_dh_top;
5125 		return (err);
5126 	}
5127 
5128 
5129 	if ((err = fetchprop(cur, DTD_ATTR_AUTHS, tabptr->zone_admin_auths,
5130 	    sizeof (tabptr->zone_admin_auths))) != Z_OK) {
5131 		handle->zone_dh_cur = handle->zone_dh_top;
5132 		return (err);
5133 	}
5134 
5135 	handle->zone_dh_cur = cur->next;
5136 	return (Z_OK);
5137 }
5138 
5139 int
5140 zonecfg_endadminent(zone_dochandle_t handle)
5141 {
5142 	return (zonecfg_endent(handle));
5143 }
5144 
5145 /*
5146  * The privileges available on the system and described in privileges(5)
5147  * fall into four categories with respect to non-global zones:
5148  *
5149  *      Default set of privileges considered safe for all non-global
5150  *      zones.  These privileges are "safe" in the sense that a
5151  *      privileged process in the zone cannot affect processes in any
5152  *      other zone on the system.
5153  *
5154  *      Set of privileges not currently permitted within a non-global
5155  *      zone.  These privileges are considered by default, "unsafe,"
5156  *      and include ones which affect global resources (such as the
5157  *      system clock or physical memory) or are overly broad and cover
5158  *      more than one mechanism in the system.  In other cases, there
5159  *      has not been sufficient virtualization in the parts of the
5160  *      system the privilege covers to allow its use within a
5161  *      non-global zone.
5162  *
5163  *      Set of privileges required in order to get a zone booted and
5164  *      init(1M) started.  These cannot be removed from the zone's
5165  *      privilege set.
5166  *
5167  * All other privileges are optional and are potentially useful for
5168  * processes executing inside a non-global zone.
5169  *
5170  * When privileges are added to the system, a determination needs to be
5171  * made as to which category the privilege belongs to.  Ideally,
5172  * privileges should be fine-grained enough and the mechanisms they cover
5173  * virtualized enough so that they can be made available to non-global
5174  * zones.
5175  */
5176 
5177 /*
5178  * Define some of the tokens that priv_str_to_set(3C) recognizes.  Since
5179  * the privilege string separator can be any character, although it is
5180  * usually a comma character, define these here as well in the event that
5181  * they change or are augmented in the future.
5182  */
5183 #define	BASIC_TOKEN		"basic"
5184 #define	DEFAULT_TOKEN		"default"
5185 #define	ZONE_TOKEN		"zone"
5186 #define	TOKEN_PRIV_CHAR		','
5187 #define	TOKEN_PRIV_STR		","
5188 
5189 typedef struct priv_node {
5190 	struct priv_node	*pn_next;	/* Next privilege */
5191 	char			*pn_priv;	/* Privileges name */
5192 } priv_node_t;
5193 
5194 /* Privileges lists can differ across brands */
5195 typedef struct priv_lists {
5196 	/* Privileges considered safe for all non-global zones of a brand */
5197 	struct priv_node	*pl_default;
5198 
5199 	/* Privileges not permitted for all non-global zones of a brand */
5200 	struct priv_node	*pl_prohibited;
5201 
5202 	/* Privileges required for all non-global zones of a brand */
5203 	struct priv_node	*pl_required;
5204 
5205 	/*
5206 	 * ip-type of the zone these privileges lists apply to.
5207 	 * It is used to pass ip-type to the callback function,
5208 	 * priv_lists_cb, which has no way of getting the ip-type.
5209 	 */
5210 	const char		*pl_iptype;
5211 } priv_lists_t;
5212 
5213 static int
5214 priv_lists_cb(void *data, priv_iter_t *priv_iter)
5215 {
5216 	priv_lists_t *plp = (priv_lists_t *)data;
5217 	priv_node_t *pnp;
5218 
5219 	/* Skip this privilege if ip-type does not match */
5220 	if ((strcmp(priv_iter->pi_iptype, "all") != 0) &&
5221 	    (strcmp(priv_iter->pi_iptype, plp->pl_iptype) != 0))
5222 		return (0);
5223 
5224 	/* Allocate a new priv list node. */
5225 	if ((pnp = malloc(sizeof (*pnp))) == NULL)
5226 		return (-1);
5227 	if ((pnp->pn_priv = strdup(priv_iter->pi_name)) == NULL) {
5228 		free(pnp);
5229 		return (-1);
5230 	}
5231 
5232 	/* Insert the new priv list node into the right list */
5233 	if (strcmp(priv_iter->pi_set, "default") == 0) {
5234 		pnp->pn_next = plp->pl_default;
5235 		plp->pl_default = pnp;
5236 	} else if (strcmp(priv_iter->pi_set, "prohibited") == 0) {
5237 		pnp->pn_next = plp->pl_prohibited;
5238 		plp->pl_prohibited = pnp;
5239 	} else if (strcmp(priv_iter->pi_set, "required") == 0) {
5240 		pnp->pn_next = plp->pl_required;
5241 		plp->pl_required = pnp;
5242 	} else {
5243 		free(pnp->pn_priv);
5244 		free(pnp);
5245 		return (-1);
5246 	}
5247 	return (0);
5248 }
5249 
5250 static void
5251 priv_lists_destroy(priv_lists_t *plp)
5252 {
5253 	priv_node_t *pnp;
5254 
5255 	assert(plp != NULL);
5256 
5257 	while ((pnp = plp->pl_default) != NULL) {
5258 		plp->pl_default = pnp->pn_next;
5259 		free(pnp->pn_priv);
5260 		free(pnp);
5261 	}
5262 	while ((pnp = plp->pl_prohibited) != NULL) {
5263 		plp->pl_prohibited = pnp->pn_next;
5264 		free(pnp->pn_priv);
5265 		free(pnp);
5266 	}
5267 	while ((pnp = plp->pl_required) != NULL) {
5268 		plp->pl_required = pnp->pn_next;
5269 		free(pnp->pn_priv);
5270 		free(pnp);
5271 	}
5272 	free(plp);
5273 }
5274 
5275 static int
5276 priv_lists_create(zone_dochandle_t handle, char *brand, priv_lists_t **plpp,
5277     const char *curr_iptype)
5278 {
5279 	priv_lists_t *plp;
5280 	brand_handle_t bh;
5281 	char brand_str[MAXNAMELEN];
5282 
5283 	/* handle or brand must be set, but never both */
5284 	assert((handle != NULL) || (brand != NULL));
5285 	assert((handle == NULL) || (brand == NULL));
5286 
5287 	if (handle != NULL) {
5288 		brand = brand_str;
5289 		if (zonecfg_get_brand(handle, brand, sizeof (brand_str)) != 0)
5290 			return (Z_BRAND_ERROR);
5291 	}
5292 
5293 	if ((bh = brand_open(brand)) == NULL)
5294 		return (Z_BRAND_ERROR);
5295 
5296 	if ((plp = calloc(1, sizeof (priv_lists_t))) == NULL) {
5297 		brand_close(bh);
5298 		return (Z_NOMEM);
5299 	}
5300 
5301 	plp->pl_iptype = curr_iptype;
5302 
5303 	/* construct the privilege lists */
5304 	if (brand_config_iter_privilege(bh, priv_lists_cb, plp) != 0) {
5305 		priv_lists_destroy(plp);
5306 		brand_close(bh);
5307 		return (Z_BRAND_ERROR);
5308 	}
5309 
5310 	brand_close(bh);
5311 	*plpp = plp;
5312 	return (Z_OK);
5313 }
5314 
5315 static int
5316 get_default_privset(priv_set_t *privs, priv_lists_t *plp)
5317 {
5318 	priv_node_t *pnp;
5319 	priv_set_t *basic;
5320 
5321 	basic = priv_str_to_set(BASIC_TOKEN, TOKEN_PRIV_STR, NULL);
5322 	if (basic == NULL)
5323 		return (errno == ENOMEM ? Z_NOMEM : Z_INVAL);
5324 
5325 	priv_union(basic, privs);
5326 	priv_freeset(basic);
5327 
5328 	for (pnp = plp->pl_default; pnp != NULL; pnp = pnp->pn_next) {
5329 		if (priv_addset(privs, pnp->pn_priv) != 0)
5330 			return (Z_INVAL);
5331 	}
5332 
5333 	return (Z_OK);
5334 }
5335 
5336 int
5337 zonecfg_default_brand(char *brand, size_t brandsize)
5338 {
5339 	zone_dochandle_t handle;
5340 	int myzoneid = getzoneid();
5341 	int ret;
5342 
5343 	/*
5344 	 * If we're running within a zone, then the default brand is the
5345 	 * current zone's brand.
5346 	 */
5347 	if (myzoneid != GLOBAL_ZONEID) {
5348 		ret = zone_getattr(myzoneid, ZONE_ATTR_BRAND, brand, brandsize);
5349 		if (ret < 0)
5350 			return ((errno == EFAULT) ? Z_TOO_BIG : Z_INVAL);
5351 		return (Z_OK);
5352 	}
5353 
5354 	if ((handle = zonecfg_init_handle()) == NULL)
5355 		return (Z_NOMEM);
5356 	if ((ret = zonecfg_get_handle("SUNWdefault", handle)) == Z_OK) {
5357 		ret = i_zonecfg_get_brand(handle, brand, brandsize, B_TRUE);
5358 		zonecfg_fini_handle(handle);
5359 		return (ret);
5360 	}
5361 	return (ret);
5362 }
5363 
5364 int
5365 zonecfg_default_privset(priv_set_t *privs, const char *curr_iptype)
5366 {
5367 	priv_lists_t *plp;
5368 	char buf[MAXNAMELEN];
5369 	int ret;
5370 
5371 	if ((ret = zonecfg_default_brand(buf, sizeof (buf))) != Z_OK)
5372 		return (ret);
5373 	if ((ret = priv_lists_create(NULL, buf, &plp, curr_iptype)) != Z_OK)
5374 		return (ret);
5375 	ret = get_default_privset(privs, plp);
5376 	priv_lists_destroy(plp);
5377 	return (ret);
5378 }
5379 
5380 void
5381 append_priv_token(char *priv, char *str, size_t strlen)
5382 {
5383 	if (*str != '\0')
5384 		(void) strlcat(str, TOKEN_PRIV_STR, strlen);
5385 	(void) strlcat(str, priv, strlen);
5386 }
5387 
5388 /*
5389  * Verify that the supplied string is a valid privilege limit set for a
5390  * non-global zone.  This string must not only be acceptable to
5391  * priv_str_to_set(3C) which parses it, but it also must resolve to a
5392  * privilege set that includes certain required privileges and lacks
5393  * certain prohibited privileges.
5394  */
5395 static int
5396 verify_privset(char *privbuf, priv_set_t *privs, char **privname,
5397     boolean_t add_default, priv_lists_t *plp)
5398 {
5399 	priv_node_t *pnp;
5400 	char *tmp, *cp, *lasts;
5401 	size_t len;
5402 	priv_set_t *mergeset;
5403 	const char *token;
5404 
5405 	/*
5406 	 * The verification of the privilege string occurs in several
5407 	 * phases.  In the first phase, the supplied string is scanned for
5408 	 * the ZONE_TOKEN token which is not support as part of the
5409 	 * "limitpriv" property.
5410 	 *
5411 	 * Duplicate the supplied privilege string since strtok_r(3C)
5412 	 * tokenizes its input by null-terminating the tokens.
5413 	 */
5414 	if ((tmp = strdup(privbuf)) == NULL)
5415 		return (Z_NOMEM);
5416 	for (cp = strtok_r(tmp, TOKEN_PRIV_STR, &lasts); cp != NULL;
5417 	    cp = strtok_r(NULL, TOKEN_PRIV_STR, &lasts)) {
5418 		if (strcmp(cp, ZONE_TOKEN) == 0) {
5419 			free(tmp);
5420 			if ((*privname = strdup(ZONE_TOKEN)) == NULL)
5421 				return (Z_NOMEM);
5422 			else
5423 				return (Z_PRIV_UNKNOWN);
5424 		}
5425 	}
5426 	free(tmp);
5427 
5428 	if (add_default) {
5429 		/*
5430 		 * If DEFAULT_TOKEN was specified, a string needs to be
5431 		 * built containing the privileges from the default, safe
5432 		 * set along with those of the "limitpriv" property.
5433 		 */
5434 		len = strlen(privbuf) + sizeof (BASIC_TOKEN) + 2;
5435 
5436 		for (pnp = plp->pl_default; pnp != NULL; pnp = pnp->pn_next)
5437 			len += strlen(pnp->pn_priv) + 1;
5438 		tmp = alloca(len);
5439 		*tmp = '\0';
5440 
5441 		append_priv_token(BASIC_TOKEN, tmp, len);
5442 		for (pnp = plp->pl_default; pnp != NULL; pnp = pnp->pn_next)
5443 			append_priv_token(pnp->pn_priv, tmp, len);
5444 		(void) strlcat(tmp, TOKEN_PRIV_STR, len);
5445 		(void) strlcat(tmp, privbuf, len);
5446 	} else {
5447 		tmp = privbuf;
5448 	}
5449 
5450 
5451 	/*
5452 	 * In the next phase, attempt to convert the merged privilege
5453 	 * string into a privilege set.  In the case of an error, either
5454 	 * there was a memory allocation failure or there was an invalid
5455 	 * privilege token in the string.  In either case, return an
5456 	 * appropriate error code but in the event of an invalid token,
5457 	 * allocate a string containing its name and return that back to
5458 	 * the caller.
5459 	 */
5460 	mergeset = priv_str_to_set(tmp, TOKEN_PRIV_STR, &token);
5461 	if (mergeset == NULL) {
5462 		if (token == NULL)
5463 			return (Z_NOMEM);
5464 		if ((cp = strchr(token, TOKEN_PRIV_CHAR)) != NULL)
5465 			*cp = '\0';
5466 		if ((*privname = strdup(token)) == NULL)
5467 			return (Z_NOMEM);
5468 		else
5469 			return (Z_PRIV_UNKNOWN);
5470 	}
5471 
5472 	/*
5473 	 * Next, verify that none of the prohibited zone privileges are
5474 	 * present in the merged privilege set.
5475 	 */
5476 	for (pnp = plp->pl_prohibited; pnp != NULL; pnp = pnp->pn_next) {
5477 		if (priv_ismember(mergeset, pnp->pn_priv)) {
5478 			priv_freeset(mergeset);
5479 			if ((*privname = strdup(pnp->pn_priv)) == NULL)
5480 				return (Z_NOMEM);
5481 			else
5482 				return (Z_PRIV_PROHIBITED);
5483 		}
5484 	}
5485 
5486 	/*
5487 	 * Finally, verify that all of the required zone privileges are
5488 	 * present in the merged privilege set.
5489 	 */
5490 	for (pnp = plp->pl_required; pnp != NULL; pnp = pnp->pn_next) {
5491 		if (!priv_ismember(mergeset, pnp->pn_priv)) {
5492 			priv_freeset(mergeset);
5493 			if ((*privname = strdup(pnp->pn_priv)) == NULL)
5494 				return (Z_NOMEM);
5495 			else
5496 				return (Z_PRIV_REQUIRED);
5497 		}
5498 	}
5499 
5500 	priv_copyset(mergeset, privs);
5501 	priv_freeset(mergeset);
5502 	return (Z_OK);
5503 }
5504 
5505 /*
5506  * Fill in the supplied privilege set with either the default, safe set of
5507  * privileges suitable for a non-global zone, or one based on the
5508  * "limitpriv" property in the zone's configuration.
5509  *
5510  * In the event of an invalid privilege specification in the
5511  * configuration, a string is allocated and returned containing the
5512  * "privilege" causing the issue.  It is the caller's responsibility to
5513  * free this memory when it is done with it.
5514  */
5515 int
5516 zonecfg_get_privset(zone_dochandle_t handle, priv_set_t *privs,
5517     char **privname)
5518 {
5519 	priv_lists_t *plp;
5520 	char *cp, *limitpriv = NULL;
5521 	int err, limitlen;
5522 	zone_iptype_t iptype;
5523 	const char *curr_iptype;
5524 
5525 	/*
5526 	 * Attempt to lookup the "limitpriv" property.  If it does not
5527 	 * exist or matches the string DEFAULT_TOKEN exactly, then the
5528 	 * default, safe privilege set is returned.
5529 	 */
5530 	if ((err = zonecfg_get_limitpriv(handle, &limitpriv)) != Z_OK)
5531 		return (err);
5532 
5533 	if ((err = zonecfg_get_iptype(handle, &iptype)) != Z_OK)
5534 		return (err);
5535 
5536 	switch (iptype) {
5537 	case ZS_SHARED:
5538 		curr_iptype = "shared";
5539 		break;
5540 	case ZS_EXCLUSIVE:
5541 		curr_iptype = "exclusive";
5542 		break;
5543 	}
5544 
5545 	if ((err = priv_lists_create(handle, NULL, &plp, curr_iptype)) != Z_OK)
5546 		return (err);
5547 
5548 	limitlen = strlen(limitpriv);
5549 	if (limitlen == 0 || strcmp(limitpriv, DEFAULT_TOKEN) == 0) {
5550 		free(limitpriv);
5551 		err = get_default_privset(privs, plp);
5552 		priv_lists_destroy(plp);
5553 		return (err);
5554 	}
5555 
5556 	/*
5557 	 * Check if the string DEFAULT_TOKEN is the first token in a list
5558 	 * of privileges.
5559 	 */
5560 	cp = strchr(limitpriv, TOKEN_PRIV_CHAR);
5561 	if (cp != NULL &&
5562 	    strncmp(limitpriv, DEFAULT_TOKEN, cp - limitpriv) == 0)
5563 		err = verify_privset(cp + 1, privs, privname, B_TRUE, plp);
5564 	else
5565 		err = verify_privset(limitpriv, privs, privname, B_FALSE, plp);
5566 
5567 	free(limitpriv);
5568 	priv_lists_destroy(plp);
5569 	return (err);
5570 }
5571 
5572 int
5573 zone_get_zonepath(char *zone_name, char *zonepath, size_t rp_sz)
5574 {
5575 	zone_dochandle_t handle;
5576 	boolean_t found = B_FALSE;
5577 	struct zoneent *ze;
5578 	FILE *cookie;
5579 	int err;
5580 	char *cp;
5581 
5582 	if (zone_name == NULL)
5583 		return (Z_INVAL);
5584 
5585 	(void) strlcpy(zonepath, zonecfg_root, rp_sz);
5586 	cp = zonepath + strlen(zonepath);
5587 	while (cp > zonepath && cp[-1] == '/')
5588 		*--cp = '\0';
5589 
5590 	if (strcmp(zone_name, GLOBAL_ZONENAME) == 0) {
5591 		if (zonepath[0] == '\0')
5592 			(void) strlcpy(zonepath, "/", rp_sz);
5593 		return (Z_OK);
5594 	}
5595 
5596 	/*
5597 	 * First check the index file.  Because older versions did not have
5598 	 * a copy of the zone path, allow for it to be zero length, in which
5599 	 * case we ignore this result and fall back to the XML files.
5600 	 */
5601 	cookie = setzoneent();
5602 	while ((ze = getzoneent_private(cookie)) != NULL) {
5603 		if (strcmp(ze->zone_name, zone_name) == 0) {
5604 			found = B_TRUE;
5605 			if (ze->zone_path[0] != '\0')
5606 				(void) strlcpy(cp, ze->zone_path,
5607 				    rp_sz - (cp - zonepath));
5608 		}
5609 		free(ze);
5610 		if (found)
5611 			break;
5612 	}
5613 	endzoneent(cookie);
5614 	if (found && *cp != '\0')
5615 		return (Z_OK);
5616 
5617 	/* Fall back to the XML files. */
5618 	if ((handle = zonecfg_init_handle()) == NULL)
5619 		return (Z_NOMEM);
5620 
5621 	/*
5622 	 * Check the snapshot first: if a zone is running, its zonepath
5623 	 * may have changed.
5624 	 */
5625 	if (zonecfg_get_snapshot_handle(zone_name, handle) != Z_OK) {
5626 		if ((err = zonecfg_get_handle(zone_name, handle)) != Z_OK) {
5627 			zonecfg_fini_handle(handle);
5628 			return (err);
5629 		}
5630 	}
5631 	err = zonecfg_get_zonepath(handle, zonepath, rp_sz);
5632 	zonecfg_fini_handle(handle);
5633 	return (err);
5634 }
5635 
5636 int
5637 zone_get_rootpath(char *zone_name, char *rootpath, size_t rp_sz)
5638 {
5639 	int err;
5640 
5641 	/* This function makes sense for non-global zones only. */
5642 	if (strcmp(zone_name, GLOBAL_ZONENAME) == 0)
5643 		return (Z_BOGUS_ZONE_NAME);
5644 	if ((err = zone_get_zonepath(zone_name, rootpath, rp_sz)) != Z_OK)
5645 		return (err);
5646 	if (strlcat(rootpath, "/root", rp_sz) >= rp_sz)
5647 		return (Z_TOO_BIG);
5648 	return (Z_OK);
5649 }
5650 
5651 int
5652 zone_get_brand(char *zone_name, char *brandname, size_t rp_sz)
5653 {
5654 	int err;
5655 	zone_dochandle_t handle;
5656 	char myzone[MAXNAMELEN];
5657 	int myzoneid = getzoneid();
5658 
5659 	/*
5660 	 * If we are not in the global zone, then we don't have the zone
5661 	 * .xml files with the brand name available.  Thus, we are going to
5662 	 * have to ask the kernel for the information.
5663 	 */
5664 	if (myzoneid != GLOBAL_ZONEID) {
5665 		if (is_system_labeled()) {
5666 			(void) strlcpy(brandname, NATIVE_BRAND_NAME, rp_sz);
5667 			return (Z_OK);
5668 		}
5669 		if (zone_getattr(myzoneid, ZONE_ATTR_NAME, myzone,
5670 		    sizeof (myzone)) < 0)
5671 			return (Z_NO_ZONE);
5672 		if (!zonecfg_is_scratch(myzone)) {
5673 			if (strncmp(zone_name, myzone, MAXNAMELEN) != 0)
5674 				return (Z_NO_ZONE);
5675 		}
5676 		err = zone_getattr(myzoneid, ZONE_ATTR_BRAND, brandname, rp_sz);
5677 		if (err < 0)
5678 			return ((errno == EFAULT) ? Z_TOO_BIG : Z_INVAL);
5679 
5680 		return (Z_OK);
5681 	}
5682 
5683 	if (strcmp(zone_name, "global") == 0)
5684 		return (zonecfg_default_brand(brandname, rp_sz));
5685 
5686 	if ((handle = zonecfg_init_handle()) == NULL)
5687 		return (Z_NOMEM);
5688 
5689 	err = zonecfg_get_handle((char *)zone_name, handle);
5690 	if (err == Z_OK)
5691 		err = zonecfg_get_brand(handle, brandname, rp_sz);
5692 
5693 	zonecfg_fini_handle(handle);
5694 	return (err);
5695 }
5696 
5697 /*
5698  * Return the appropriate root for the active /dev.
5699  * For normal zone, the path is $ZONEPATH/root;
5700  * for scratch zone, the dev path is $ZONEPATH/lu.
5701  */
5702 int
5703 zone_get_devroot(char *zone_name, char *devroot, size_t rp_sz)
5704 {
5705 	int err;
5706 	char *suffix;
5707 	zone_state_t state;
5708 
5709 	/* This function makes sense for non-global zones only. */
5710 	if (strcmp(zone_name, GLOBAL_ZONENAME) == 0)
5711 		return (Z_BOGUS_ZONE_NAME);
5712 	if ((err = zone_get_zonepath(zone_name, devroot, rp_sz)) != Z_OK)
5713 		return (err);
5714 
5715 	if (zone_get_state(zone_name, &state) == Z_OK &&
5716 	    state == ZONE_STATE_MOUNTED)
5717 		suffix = "/lu";
5718 	else
5719 		suffix = "/root";
5720 	if (strlcat(devroot, suffix, rp_sz) >= rp_sz)
5721 		return (Z_TOO_BIG);
5722 	return (Z_OK);
5723 }
5724 
5725 static zone_state_t
5726 kernel_state_to_user_state(zoneid_t zoneid, zone_status_t kernel_state)
5727 {
5728 	char zoneroot[MAXPATHLEN];
5729 	size_t zlen;
5730 
5731 	assert(kernel_state <= ZONE_MAX_STATE);
5732 	switch (kernel_state) {
5733 		case ZONE_IS_UNINITIALIZED:
5734 		case ZONE_IS_INITIALIZED:
5735 			/* The kernel will not return these two states */
5736 			return (ZONE_STATE_READY);
5737 		case ZONE_IS_READY:
5738 			/*
5739 			 * If the zone's root is mounted on $ZONEPATH/lu, then
5740 			 * it's a mounted scratch zone.
5741 			 */
5742 			if (zone_getattr(zoneid, ZONE_ATTR_ROOT, zoneroot,
5743 			    sizeof (zoneroot)) >= 0) {
5744 				zlen = strlen(zoneroot);
5745 				if (zlen > 3 &&
5746 				    strcmp(zoneroot + zlen - 3, "/lu") == 0)
5747 					return (ZONE_STATE_MOUNTED);
5748 			}
5749 			return (ZONE_STATE_READY);
5750 		case ZONE_IS_BOOTING:
5751 		case ZONE_IS_RUNNING:
5752 			return (ZONE_STATE_RUNNING);
5753 		case ZONE_IS_SHUTTING_DOWN:
5754 		case ZONE_IS_EMPTY:
5755 			return (ZONE_STATE_SHUTTING_DOWN);
5756 		case ZONE_IS_DOWN:
5757 		case ZONE_IS_DYING:
5758 		case ZONE_IS_DEAD:
5759 		default:
5760 			return (ZONE_STATE_DOWN);
5761 	}
5762 	/* NOTREACHED */
5763 }
5764 
5765 int
5766 zone_get_state(char *zone_name, zone_state_t *state_num)
5767 {
5768 	zone_status_t status;
5769 	zoneid_t zone_id;
5770 	struct zoneent *ze;
5771 	boolean_t found = B_FALSE;
5772 	FILE *cookie;
5773 	char kernzone[ZONENAME_MAX];
5774 	FILE *fp;
5775 
5776 	if (zone_name == NULL)
5777 		return (Z_INVAL);
5778 
5779 	/*
5780 	 * If we're looking at an alternate root, then we need to query the
5781 	 * kernel using the scratch zone name.
5782 	 */
5783 	zone_id = -1;
5784 	if (*zonecfg_root != '\0' && !zonecfg_is_scratch(zone_name)) {
5785 		if ((fp = zonecfg_open_scratch("", B_FALSE)) != NULL) {
5786 			if (zonecfg_find_scratch(fp, zone_name, zonecfg_root,
5787 			    kernzone, sizeof (kernzone)) == 0)
5788 				zone_id = getzoneidbyname(kernzone);
5789 			zonecfg_close_scratch(fp);
5790 		}
5791 	} else {
5792 		zone_id = getzoneidbyname(zone_name);
5793 	}
5794 
5795 	/* check to see if zone is running */
5796 	if (zone_id != -1 &&
5797 	    zone_getattr(zone_id, ZONE_ATTR_STATUS, &status,
5798 	    sizeof (status)) >= 0) {
5799 		*state_num = kernel_state_to_user_state(zone_id, status);
5800 		return (Z_OK);
5801 	}
5802 
5803 	cookie = setzoneent();
5804 	while ((ze = getzoneent_private(cookie)) != NULL) {
5805 		if (strcmp(ze->zone_name, zone_name) == 0) {
5806 			found = B_TRUE;
5807 			*state_num = ze->zone_state;
5808 		}
5809 		free(ze);
5810 		if (found)
5811 			break;
5812 	}
5813 	endzoneent(cookie);
5814 	return ((found) ? Z_OK : Z_NO_ZONE);
5815 }
5816 
5817 int
5818 zone_set_state(char *zone, zone_state_t state)
5819 {
5820 	struct zoneent ze;
5821 
5822 	if (state != ZONE_STATE_CONFIGURED && state != ZONE_STATE_INSTALLED &&
5823 	    state != ZONE_STATE_INCOMPLETE)
5824 		return (Z_INVAL);
5825 
5826 	bzero(&ze, sizeof (ze));
5827 	(void) strlcpy(ze.zone_name, zone, sizeof (ze.zone_name));
5828 	ze.zone_state = state;
5829 	(void) strlcpy(ze.zone_path, "", sizeof (ze.zone_path));
5830 	return (putzoneent(&ze, PZE_MODIFY));
5831 }
5832 
5833 /*
5834  * Get id (if any) for specified zone.  There are four possible outcomes:
5835  * - If the string corresponds to the numeric id of an active (booted)
5836  *   zone, sets *zip to the zone id and returns 0.
5837  * - If the string corresponds to the name of an active (booted) zone,
5838  *   sets *zip to the zone id and returns 0.
5839  * - If the string is a name in the configuration but is not booted,
5840  *   sets *zip to ZONE_ID_UNDEFINED and returns 0.
5841  * - Otherwise, leaves *zip unchanged and returns -1.
5842  *
5843  * This function acts as an auxiliary filter on the function of the same
5844  * name in libc; the linker binds to this version if libzonecfg exists,
5845  * and the libc version if it doesn't.  Any changes to this version of
5846  * the function should probably be reflected in the libc version as well.
5847  */
5848 int
5849 zone_get_id(const char *str, zoneid_t *zip)
5850 {
5851 	zone_dochandle_t hdl;
5852 	zoneid_t zoneid;
5853 	char *cp;
5854 	int err;
5855 
5856 	/* first try looking for active zone by id */
5857 	errno = 0;
5858 	zoneid = (zoneid_t)strtol(str, &cp, 0);
5859 	if (errno == 0 && cp != str && *cp == '\0' &&
5860 	    getzonenamebyid(zoneid, NULL, 0) != -1) {
5861 		*zip = zoneid;
5862 		return (0);
5863 	}
5864 
5865 	/* then look for active zone by name */
5866 	if ((zoneid = getzoneidbyname(str)) != -1) {
5867 		*zip = zoneid;
5868 		return (0);
5869 	}
5870 
5871 	/* if in global zone, try looking up name in configuration database */
5872 	if (getzoneid() != GLOBAL_ZONEID ||
5873 	    (hdl = zonecfg_init_handle()) == NULL)
5874 		return (-1);
5875 
5876 	if (zonecfg_get_handle(str, hdl) == Z_OK) {
5877 		/* zone exists but isn't active */
5878 		*zip = ZONE_ID_UNDEFINED;
5879 		err = 0;
5880 	} else {
5881 		err = -1;
5882 	}
5883 
5884 	zonecfg_fini_handle(hdl);
5885 	return (err);
5886 }
5887 
5888 char *
5889 zone_state_str(zone_state_t state_num)
5890 {
5891 	switch (state_num) {
5892 	case ZONE_STATE_CONFIGURED:
5893 		return (ZONE_STATE_STR_CONFIGURED);
5894 	case ZONE_STATE_INCOMPLETE:
5895 		return (ZONE_STATE_STR_INCOMPLETE);
5896 	case ZONE_STATE_INSTALLED:
5897 		return (ZONE_STATE_STR_INSTALLED);
5898 	case ZONE_STATE_READY:
5899 		return (ZONE_STATE_STR_READY);
5900 	case ZONE_STATE_MOUNTED:
5901 		return (ZONE_STATE_STR_MOUNTED);
5902 	case ZONE_STATE_RUNNING:
5903 		return (ZONE_STATE_STR_RUNNING);
5904 	case ZONE_STATE_SHUTTING_DOWN:
5905 		return (ZONE_STATE_STR_SHUTTING_DOWN);
5906 	case ZONE_STATE_DOWN:
5907 		return (ZONE_STATE_STR_DOWN);
5908 	default:
5909 		return ("unknown");
5910 	}
5911 }
5912 
5913 /*
5914  * Given a UUID value, find an associated zone name.  This is intended to be
5915  * used by callers who set up some 'default' name (corresponding to the
5916  * expected name for the zone) in the zonename buffer, and thus the function
5917  * doesn't touch this buffer on failure.
5918  */
5919 int
5920 zonecfg_get_name_by_uuid(const uuid_t uuidin, char *zonename, size_t namelen)
5921 {
5922 	FILE *fp;
5923 	struct zoneent *ze;
5924 	uchar_t *uuid;
5925 
5926 	/*
5927 	 * A small amount of subterfuge via casts is necessary here because
5928 	 * libuuid doesn't use const correctly, but we don't want to export
5929 	 * this brokenness to our clients.
5930 	 */
5931 	uuid = (uchar_t *)uuidin;
5932 	if (uuid_is_null(uuid))
5933 		return (Z_NO_ZONE);
5934 	if ((fp = setzoneent()) == NULL)
5935 		return (Z_NO_ZONE);
5936 	while ((ze = getzoneent_private(fp)) != NULL) {
5937 		if (uuid_compare(uuid, ze->zone_uuid) == 0)
5938 			break;
5939 		free(ze);
5940 	}
5941 	endzoneent(fp);
5942 	if (ze != NULL) {
5943 		(void) strlcpy(zonename, ze->zone_name, namelen);
5944 		free(ze);
5945 		return (Z_OK);
5946 	} else {
5947 		return (Z_NO_ZONE);
5948 	}
5949 }
5950 
5951 /*
5952  * Given a zone name, get its UUID.  Returns a "NULL" UUID value if the zone
5953  * exists but the file doesn't have a value set yet.  Returns an error if the
5954  * zone cannot be located.
5955  */
5956 int
5957 zonecfg_get_uuid(const char *zonename, uuid_t uuid)
5958 {
5959 	FILE *fp;
5960 	struct zoneent *ze;
5961 
5962 	if ((fp = setzoneent()) == NULL)
5963 		return (Z_NO_ZONE);
5964 	while ((ze = getzoneent_private(fp)) != NULL) {
5965 		if (strcmp(ze->zone_name, zonename) == 0)
5966 			break;
5967 		free(ze);
5968 	}
5969 	endzoneent(fp);
5970 	if (ze != NULL) {
5971 		uuid_copy(uuid, ze->zone_uuid);
5972 		free(ze);
5973 		return (Z_OK);
5974 	} else {
5975 		return (Z_NO_ZONE);
5976 	}
5977 }
5978 
5979 /*
5980  * File-system convenience functions.
5981  */
5982 boolean_t
5983 zonecfg_valid_fs_type(const char *type)
5984 {
5985 	/*
5986 	 * We already know which FS types don't work.
5987 	 */
5988 	if (strcmp(type, "proc") == 0 ||
5989 	    strcmp(type, "mntfs") == 0 ||
5990 	    strcmp(type, "autofs") == 0 ||
5991 	    strncmp(type, "nfs", sizeof ("nfs") - 1) == 0 ||
5992 	    strcmp(type, "cachefs") == 0)
5993 		return (B_FALSE);
5994 	/*
5995 	 * The caller may do more detailed verification to make sure other
5996 	 * aspects of this filesystem type make sense.
5997 	 */
5998 	return (B_TRUE);
5999 }
6000 
6001 /*
6002  * Generally uninteresting rctl convenience functions.
6003  */
6004 
6005 int
6006 zonecfg_construct_rctlblk(const struct zone_rctlvaltab *rctlval,
6007     rctlblk_t *rctlblk)
6008 {
6009 	unsigned long long ull;
6010 	char *endp;
6011 	rctl_priv_t priv;
6012 	rctl_qty_t limit;
6013 	uint_t action;
6014 
6015 	/* Get the privilege */
6016 	if (strcmp(rctlval->zone_rctlval_priv, "basic") == 0) {
6017 		priv = RCPRIV_BASIC;
6018 	} else if (strcmp(rctlval->zone_rctlval_priv, "privileged") == 0) {
6019 		priv = RCPRIV_PRIVILEGED;
6020 	} else {
6021 		/* Invalid privilege */
6022 		return (Z_INVAL);
6023 	}
6024 
6025 	/* deal with negative input; strtoull(3c) doesn't do what we want */
6026 	if (rctlval->zone_rctlval_limit[0] == '-')
6027 		return (Z_INVAL);
6028 	/* Get the limit */
6029 	errno = 0;
6030 	ull = strtoull(rctlval->zone_rctlval_limit, &endp, 0);
6031 	if (errno != 0 || *endp != '\0') {
6032 		/* parse failed */
6033 		return (Z_INVAL);
6034 	}
6035 	limit = (rctl_qty_t)ull;
6036 
6037 	/* Get the action */
6038 	if (strcmp(rctlval->zone_rctlval_action, "none") == 0) {
6039 		action = RCTL_LOCAL_NOACTION;
6040 	} else if (strcmp(rctlval->zone_rctlval_action, "signal") == 0) {
6041 		action = RCTL_LOCAL_SIGNAL;
6042 	} else if (strcmp(rctlval->zone_rctlval_action, "deny") == 0) {
6043 		action = RCTL_LOCAL_DENY;
6044 	} else {
6045 		/* Invalid Action */
6046 		return (Z_INVAL);
6047 	}
6048 	rctlblk_set_local_action(rctlblk, action, 0);
6049 	rctlblk_set_privilege(rctlblk, priv);
6050 	rctlblk_set_value(rctlblk, limit);
6051 	return (Z_OK);
6052 }
6053 
6054 static int
6055 rctl_check(const char *rctlname, void *arg)
6056 {
6057 	const char *attrname = arg;
6058 
6059 	/*
6060 	 * Returning 1 here is our signal to zonecfg_is_rctl() that it is
6061 	 * indeed an rctl name recognized by the system.
6062 	 */
6063 	return (strcmp(rctlname, attrname) == 0 ? 1 : 0);
6064 }
6065 
6066 boolean_t
6067 zonecfg_is_rctl(const char *name)
6068 {
6069 	return (rctl_walk(rctl_check, (void *)name) == 1);
6070 }
6071 
6072 boolean_t
6073 zonecfg_valid_rctlname(const char *name)
6074 {
6075 	const char *c;
6076 
6077 	if (strncmp(name, "zone.", sizeof ("zone.") - 1) != 0)
6078 		return (B_FALSE);
6079 	if (strlen(name) == sizeof ("zone.") - 1)
6080 		return (B_FALSE);
6081 	for (c = name + sizeof ("zone.") - 1; *c != '\0'; c++) {
6082 		if (!isalpha(*c) && *c != '-')
6083 			return (B_FALSE);
6084 	}
6085 	return (B_TRUE);
6086 }
6087 
6088 boolean_t
6089 zonecfg_valid_rctlblk(const rctlblk_t *rctlblk)
6090 {
6091 	rctl_priv_t priv = rctlblk_get_privilege((rctlblk_t *)rctlblk);
6092 	uint_t action = rctlblk_get_local_action((rctlblk_t *)rctlblk, NULL);
6093 
6094 	if (priv != RCPRIV_PRIVILEGED)
6095 		return (B_FALSE);
6096 	if (action != RCTL_LOCAL_NOACTION && action != RCTL_LOCAL_DENY)
6097 		return (B_FALSE);
6098 	return (B_TRUE);
6099 }
6100 
6101 boolean_t
6102 zonecfg_valid_rctl(const char *name, const rctlblk_t *rctlblk)
6103 {
6104 	rctlblk_t *current, *next;
6105 	rctl_qty_t limit = rctlblk_get_value((rctlblk_t *)rctlblk);
6106 	uint_t action = rctlblk_get_local_action((rctlblk_t *)rctlblk, NULL);
6107 	uint_t global_flags;
6108 
6109 	if (!zonecfg_valid_rctlblk(rctlblk))
6110 		return (B_FALSE);
6111 	if (!zonecfg_valid_rctlname(name))
6112 		return (B_FALSE);
6113 
6114 	current = alloca(rctlblk_size());
6115 	if (getrctl(name, NULL, current, RCTL_FIRST) != 0)
6116 		return (B_TRUE);	/* not an rctl on this system */
6117 	/*
6118 	 * Make sure the proposed value isn't greater than the current system
6119 	 * value.
6120 	 */
6121 	next = alloca(rctlblk_size());
6122 	while (rctlblk_get_privilege(current) != RCPRIV_SYSTEM) {
6123 		rctlblk_t *tmp;
6124 
6125 		if (getrctl(name, current, next, RCTL_NEXT) != 0)
6126 			return (B_FALSE);	/* shouldn't happen */
6127 		tmp = current;
6128 		current = next;
6129 		next = tmp;
6130 	}
6131 	if (limit > rctlblk_get_value(current))
6132 		return (B_FALSE);
6133 
6134 	/*
6135 	 * Make sure the proposed action is allowed.
6136 	 */
6137 	global_flags = rctlblk_get_global_flags(current);
6138 	if ((global_flags & RCTL_GLOBAL_DENY_NEVER) &&
6139 	    action == RCTL_LOCAL_DENY)
6140 		return (B_FALSE);
6141 	if ((global_flags & RCTL_GLOBAL_DENY_ALWAYS) &&
6142 	    action == RCTL_LOCAL_NOACTION)
6143 		return (B_FALSE);
6144 
6145 	return (B_TRUE);
6146 }
6147 
6148 /*
6149  * There is always a race condition between reading the initial copy of
6150  * a zones state and its state changing.  We address this by providing
6151  * zonecfg_notify_critical_enter and zonecfg_noticy_critical_exit functions.
6152  * When zonecfg_critical_enter is called, sets the state field to LOCKED
6153  * and aquires biglock. Biglock protects against other threads executing
6154  * critical_enter and the state field protects against state changes during
6155  * the critical period.
6156  *
6157  * If any state changes occur, zn_cb will set the failed field of the znotify
6158  * structure.  This will cause the critical_exit function to re-lock the
6159  * channel and return an error. Since evsnts may be delayed, the critical_exit
6160  * function "flushes" the queue by putting an event on the queue and waiting for
6161  * zn_cb to notify critical_exit that it received the ping event.
6162  */
6163 static const char *
6164 string_get_tok(const char *in, char delim, int num)
6165 {
6166 	int i = 0;
6167 
6168 	for (; i < num; in++) {
6169 		if (*in == delim)
6170 			i++;
6171 		if (*in == 0)
6172 			return (NULL);
6173 	}
6174 	return (in);
6175 }
6176 
6177 static boolean_t
6178 is_ping(sysevent_t *ev)
6179 {
6180 	if (strcmp(sysevent_get_subclass_name(ev),
6181 	    ZONE_EVENT_PING_SUBCLASS) == 0) {
6182 		return (B_TRUE);
6183 	} else {
6184 		return (B_FALSE);
6185 	}
6186 }
6187 
6188 static boolean_t
6189 is_my_ping(sysevent_t *ev)
6190 {
6191 	const char *sender;
6192 	char mypid[sizeof (pid_t) * 3 + 1];
6193 
6194 	(void) snprintf(mypid, sizeof (mypid), "%i", getpid());
6195 	sender = string_get_tok(sysevent_get_pub(ev), ':', 3);
6196 	if (sender == NULL)
6197 		return (B_FALSE);
6198 	if (strcmp(sender, mypid) != 0)
6199 		return (B_FALSE);
6200 	return (B_TRUE);
6201 }
6202 
6203 static int
6204 do_callback(struct znotify *zevtchan, sysevent_t *ev)
6205 {
6206 	nvlist_t *l;
6207 	int zid;
6208 	char *zonename;
6209 	char *newstate;
6210 	char *oldstate;
6211 	int ret;
6212 	hrtime_t when;
6213 
6214 	if (strcmp(sysevent_get_subclass_name(ev),
6215 	    ZONE_EVENT_STATUS_SUBCLASS) == 0) {
6216 
6217 		if (sysevent_get_attr_list(ev, &l) != 0) {
6218 			if (errno == ENOMEM) {
6219 				zevtchan->zn_failure_count++;
6220 				return (EAGAIN);
6221 			}
6222 			return (0);
6223 		}
6224 		ret = 0;
6225 
6226 		if ((nvlist_lookup_string(l, ZONE_CB_NAME, &zonename) == 0) &&
6227 		    (nvlist_lookup_string(l, ZONE_CB_NEWSTATE, &newstate)
6228 		    == 0) &&
6229 		    (nvlist_lookup_string(l, ZONE_CB_OLDSTATE, &oldstate)
6230 		    == 0) &&
6231 		    (nvlist_lookup_uint64(l, ZONE_CB_TIMESTAMP,
6232 		    (uint64_t *)&when) == 0) &&
6233 		    (nvlist_lookup_int32(l, ZONE_CB_ZONEID, &zid) == 0)) {
6234 			ret = zevtchan->zn_callback(zonename, zid, newstate,
6235 			    oldstate, when, zevtchan->zn_private);
6236 		}
6237 
6238 		zevtchan->zn_failure_count = 0;
6239 		nvlist_free(l);
6240 		return (ret);
6241 	} else {
6242 		/*
6243 		 * We have received an event in an unknown subclass. Ignore.
6244 		 */
6245 		zevtchan->zn_failure_count = 0;
6246 		return (0);
6247 	}
6248 }
6249 
6250 static int
6251 zn_cb(sysevent_t *ev, void *p)
6252 {
6253 	struct znotify *zevtchan = p;
6254 	int error;
6255 
6256 	(void) pthread_mutex_lock(&(zevtchan->zn_mutex));
6257 
6258 	if (is_ping(ev) && !is_my_ping(ev)) {
6259 		(void) pthread_mutex_unlock((&zevtchan->zn_mutex));
6260 		return (0);
6261 	}
6262 
6263 	if (zevtchan->zn_state == ZN_LOCKED) {
6264 		assert(!is_ping(ev));
6265 		zevtchan->zn_failed = B_TRUE;
6266 		(void) pthread_mutex_unlock(&(zevtchan->zn_mutex));
6267 		return (0);
6268 	}
6269 
6270 	if (zevtchan->zn_state == ZN_PING_INFLIGHT) {
6271 		if (is_ping(ev)) {
6272 			zevtchan->zn_state = ZN_PING_RECEIVED;
6273 			(void) pthread_cond_signal(&(zevtchan->zn_cond));
6274 			(void) pthread_mutex_unlock(&(zevtchan->zn_mutex));
6275 			return (0);
6276 		} else {
6277 			zevtchan->zn_failed = B_TRUE;
6278 			(void) pthread_mutex_unlock(&(zevtchan->zn_mutex));
6279 			return (0);
6280 		}
6281 	}
6282 
6283 	if (zevtchan->zn_state == ZN_UNLOCKED) {
6284 
6285 		error = do_callback(zevtchan, ev);
6286 		(void) pthread_mutex_unlock(&(zevtchan->zn_mutex));
6287 		/*
6288 		 * Every ENOMEM failure causes do_callback to increment
6289 		 * zn_failure_count and every success causes it to
6290 		 * set zn_failure_count to zero.  If we got EAGAIN,
6291 		 * we will sleep for zn_failure_count seconds and return
6292 		 * EAGAIN to gpec to try again.
6293 		 *
6294 		 * After 55 seconds, or 10 try's we give up and drop the
6295 		 * event.
6296 		 */
6297 		if (error == EAGAIN) {
6298 			if (zevtchan->zn_failure_count > ZONE_CB_RETRY_COUNT) {
6299 				return (0);
6300 			}
6301 			(void) sleep(zevtchan->zn_failure_count);
6302 		}
6303 		return (error);
6304 	}
6305 
6306 	if (zevtchan->zn_state == ZN_PING_RECEIVED) {
6307 		(void) pthread_mutex_unlock(&(zevtchan->zn_mutex));
6308 		return (0);
6309 	}
6310 
6311 	abort();
6312 	return (0);
6313 }
6314 
6315 void
6316 zonecfg_notify_critical_enter(void *h)
6317 {
6318 	struct znotify *zevtchan = h;
6319 
6320 	(void) pthread_mutex_lock(&(zevtchan->zn_bigmutex));
6321 	zevtchan->zn_state = ZN_LOCKED;
6322 }
6323 
6324 int
6325 zonecfg_notify_critical_exit(void * h)
6326 {
6327 
6328 	struct znotify *zevtchan = h;
6329 
6330 	if (zevtchan->zn_state == ZN_UNLOCKED)
6331 		return (0);
6332 
6333 	(void) pthread_mutex_lock(&(zevtchan->zn_mutex));
6334 	zevtchan->zn_state = ZN_PING_INFLIGHT;
6335 
6336 	(void) sysevent_evc_publish(zevtchan->zn_eventchan,
6337 	    ZONE_EVENT_STATUS_CLASS,
6338 	    ZONE_EVENT_PING_SUBCLASS, ZONE_EVENT_PING_PUBLISHER,
6339 	    zevtchan->zn_subscriber_id, NULL, EVCH_SLEEP);
6340 
6341 	while (zevtchan->zn_state != ZN_PING_RECEIVED) {
6342 		(void) pthread_cond_wait(&(zevtchan->zn_cond),
6343 		    &(zevtchan->zn_mutex));
6344 	}
6345 
6346 	if (zevtchan->zn_failed == B_TRUE) {
6347 		zevtchan->zn_state = ZN_LOCKED;
6348 		zevtchan->zn_failed = B_FALSE;
6349 		(void) pthread_mutex_unlock(&(zevtchan->zn_mutex));
6350 		return (1);
6351 	}
6352 
6353 	zevtchan->zn_state = ZN_UNLOCKED;
6354 	(void) pthread_mutex_unlock(&(zevtchan->zn_mutex));
6355 	(void) pthread_mutex_unlock(&(zevtchan->zn_bigmutex));
6356 	return (0);
6357 }
6358 
6359 void
6360 zonecfg_notify_critical_abort(void *h)
6361 {
6362 	struct znotify *zevtchan = h;
6363 
6364 	zevtchan->zn_state = ZN_UNLOCKED;
6365 	zevtchan->zn_failed = B_FALSE;
6366 	/*
6367 	 * Don't do anything about zn_lock. If it is held, it could only be
6368 	 * held by zn_cb and it will be unlocked soon.
6369 	 */
6370 	(void) pthread_mutex_unlock(&(zevtchan->zn_bigmutex));
6371 }
6372 
6373 void *
6374 zonecfg_notify_bind(int(*func)(const char *zonename, zoneid_t zid,
6375     const char *newstate, const char *oldstate, hrtime_t when, void *p),
6376     void *p)
6377 {
6378 	struct znotify *zevtchan;
6379 	int i = 1;
6380 	int r;
6381 
6382 	zevtchan = malloc(sizeof (struct znotify));
6383 
6384 	if (zevtchan == NULL)
6385 		return (NULL);
6386 
6387 	zevtchan->zn_private = p;
6388 	zevtchan->zn_callback = func;
6389 	zevtchan->zn_state = ZN_UNLOCKED;
6390 	zevtchan->zn_failed = B_FALSE;
6391 
6392 	if (pthread_mutex_init(&(zevtchan->zn_mutex), NULL))
6393 		goto out3;
6394 	if (pthread_cond_init(&(zevtchan->zn_cond), NULL)) {
6395 		(void) pthread_mutex_destroy(&(zevtchan->zn_mutex));
6396 		goto out3;
6397 	}
6398 	if (pthread_mutex_init(&(zevtchan->zn_bigmutex), NULL)) {
6399 		(void) pthread_mutex_destroy(&(zevtchan->zn_mutex));
6400 		(void) pthread_cond_destroy(&(zevtchan->zn_cond));
6401 		goto out3;
6402 	}
6403 
6404 	if (sysevent_evc_bind(ZONE_EVENT_CHANNEL, &(zevtchan->zn_eventchan),
6405 	    0) != 0)
6406 		goto out2;
6407 
6408 	do {
6409 		/*
6410 		 * At 4 digits the subscriber ID gets too long and we have
6411 		 * no chance of successfully registering.
6412 		 */
6413 		if (i > 999)
6414 			goto out1;
6415 
6416 		(void) sprintf(zevtchan->zn_subscriber_id, "zone_%li_%i",
6417 		    getpid() % 999999l, i);
6418 
6419 		r = sysevent_evc_subscribe(zevtchan->zn_eventchan,
6420 		    zevtchan->zn_subscriber_id, ZONE_EVENT_STATUS_CLASS, zn_cb,
6421 		    zevtchan, 0);
6422 
6423 		i++;
6424 
6425 	} while (r);
6426 
6427 	return (zevtchan);
6428 out1:
6429 	(void) sysevent_evc_unbind(zevtchan->zn_eventchan);
6430 out2:
6431 	(void) pthread_mutex_destroy(&zevtchan->zn_mutex);
6432 	(void) pthread_cond_destroy(&zevtchan->zn_cond);
6433 	(void) pthread_mutex_destroy(&(zevtchan->zn_bigmutex));
6434 out3:
6435 	free(zevtchan);
6436 
6437 	return (NULL);
6438 }
6439 
6440 void
6441 zonecfg_notify_unbind(void *handle)
6442 {
6443 
6444 	int ret;
6445 
6446 	(void) sysevent_evc_unbind(((struct znotify *)handle)->zn_eventchan);
6447 	/*
6448 	 * Check that all evc threads have gone away. This should be
6449 	 * enforced by sysevent_evc_unbind.
6450 	 */
6451 	ret = pthread_mutex_trylock(&((struct znotify *)handle)->zn_mutex);
6452 
6453 	if (ret)
6454 		abort();
6455 
6456 	(void) pthread_mutex_unlock(&((struct znotify *)handle)->zn_mutex);
6457 	(void) pthread_mutex_destroy(&((struct znotify *)handle)->zn_mutex);
6458 	(void) pthread_cond_destroy(&((struct znotify *)handle)->zn_cond);
6459 	(void) pthread_mutex_destroy(&((struct znotify *)handle)->zn_bigmutex);
6460 
6461 	free(handle);
6462 }
6463 
6464 static int
6465 zonecfg_add_ds_core(zone_dochandle_t handle, struct zone_dstab *tabptr)
6466 {
6467 	xmlNodePtr newnode, cur = handle->zone_dh_cur;
6468 	int err;
6469 
6470 	newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_DATASET, NULL);
6471 	if ((err = newprop(newnode, DTD_ATTR_NAME,
6472 	    tabptr->zone_dataset_name)) != Z_OK)
6473 		return (err);
6474 	return (Z_OK);
6475 }
6476 
6477 int
6478 zonecfg_add_ds(zone_dochandle_t handle, struct zone_dstab *tabptr)
6479 {
6480 	int err;
6481 
6482 	if (tabptr == NULL)
6483 		return (Z_INVAL);
6484 
6485 	if ((err = operation_prep(handle)) != Z_OK)
6486 		return (err);
6487 
6488 	if ((err = zonecfg_add_ds_core(handle, tabptr)) != Z_OK)
6489 		return (err);
6490 
6491 	return (Z_OK);
6492 }
6493 
6494 static int
6495 zonecfg_delete_ds_core(zone_dochandle_t handle, struct zone_dstab *tabptr)
6496 {
6497 	xmlNodePtr cur = handle->zone_dh_cur;
6498 
6499 	for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
6500 		if (xmlStrcmp(cur->name, DTD_ELEM_DATASET))
6501 			continue;
6502 
6503 		if (match_prop(cur, DTD_ATTR_NAME,
6504 		    tabptr->zone_dataset_name)) {
6505 			xmlUnlinkNode(cur);
6506 			xmlFreeNode(cur);
6507 			return (Z_OK);
6508 		}
6509 	}
6510 	return (Z_NO_RESOURCE_ID);
6511 }
6512 
6513 int
6514 zonecfg_delete_ds(zone_dochandle_t handle, struct zone_dstab *tabptr)
6515 {
6516 	int err;
6517 
6518 	if (tabptr == NULL)
6519 		return (Z_INVAL);
6520 
6521 	if ((err = operation_prep(handle)) != Z_OK)
6522 		return (err);
6523 
6524 	if ((err = zonecfg_delete_ds_core(handle, tabptr)) != Z_OK)
6525 		return (err);
6526 
6527 	return (Z_OK);
6528 }
6529 
6530 int
6531 zonecfg_modify_ds(
6532 	zone_dochandle_t handle,
6533 	struct zone_dstab *oldtabptr,
6534 	struct zone_dstab *newtabptr)
6535 {
6536 	int err;
6537 
6538 	if (oldtabptr == NULL || newtabptr == NULL)
6539 		return (Z_INVAL);
6540 
6541 	if ((err = operation_prep(handle)) != Z_OK)
6542 		return (err);
6543 
6544 	if ((err = zonecfg_delete_ds_core(handle, oldtabptr)) != Z_OK)
6545 		return (err);
6546 
6547 	if ((err = zonecfg_add_ds_core(handle, newtabptr)) != Z_OK)
6548 		return (err);
6549 
6550 	return (Z_OK);
6551 }
6552 
6553 int
6554 zonecfg_lookup_ds(zone_dochandle_t handle, struct zone_dstab *tabptr)
6555 {
6556 	xmlNodePtr cur, firstmatch;
6557 	int err;
6558 	char dataset[MAXNAMELEN];
6559 
6560 	if (tabptr == NULL)
6561 		return (Z_INVAL);
6562 
6563 	if ((err = operation_prep(handle)) != Z_OK)
6564 		return (err);
6565 
6566 	cur = handle->zone_dh_cur;
6567 	firstmatch = NULL;
6568 	for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
6569 		if (xmlStrcmp(cur->name, DTD_ELEM_DATASET))
6570 			continue;
6571 		if (strlen(tabptr->zone_dataset_name) > 0) {
6572 			if ((fetchprop(cur, DTD_ATTR_NAME, dataset,
6573 			    sizeof (dataset)) == Z_OK) &&
6574 			    (strcmp(tabptr->zone_dataset_name,
6575 			    dataset) == 0)) {
6576 				if (firstmatch == NULL)
6577 					firstmatch = cur;
6578 				else
6579 					return (Z_INSUFFICIENT_SPEC);
6580 			}
6581 		}
6582 	}
6583 	if (firstmatch == NULL)
6584 		return (Z_NO_RESOURCE_ID);
6585 
6586 	cur = firstmatch;
6587 
6588 	if ((err = fetchprop(cur, DTD_ATTR_NAME, tabptr->zone_dataset_name,
6589 	    sizeof (tabptr->zone_dataset_name))) != Z_OK)
6590 		return (err);
6591 
6592 	return (Z_OK);
6593 }
6594 
6595 int
6596 zonecfg_setdsent(zone_dochandle_t handle)
6597 {
6598 	return (zonecfg_setent(handle));
6599 }
6600 
6601 int
6602 zonecfg_getdsent(zone_dochandle_t handle, struct zone_dstab *tabptr)
6603 {
6604 	xmlNodePtr cur;
6605 	int err;
6606 
6607 	if (handle == NULL)
6608 		return (Z_INVAL);
6609 
6610 	if ((cur = handle->zone_dh_cur) == NULL)
6611 		return (Z_NO_ENTRY);
6612 
6613 	for (; cur != NULL; cur = cur->next)
6614 		if (!xmlStrcmp(cur->name, DTD_ELEM_DATASET))
6615 			break;
6616 	if (cur == NULL) {
6617 		handle->zone_dh_cur = handle->zone_dh_top;
6618 		return (Z_NO_ENTRY);
6619 	}
6620 
6621 	if ((err = fetchprop(cur, DTD_ATTR_NAME, tabptr->zone_dataset_name,
6622 	    sizeof (tabptr->zone_dataset_name))) != Z_OK) {
6623 		handle->zone_dh_cur = handle->zone_dh_top;
6624 		return (err);
6625 	}
6626 
6627 	handle->zone_dh_cur = cur->next;
6628 	return (Z_OK);
6629 }
6630 
6631 int
6632 zonecfg_enddsent(zone_dochandle_t handle)
6633 {
6634 	return (zonecfg_endent(handle));
6635 }
6636 
6637 /*
6638  * Support for aliased rctls; that is, rctls that have simplified names in
6639  * zonecfg.  For example, max-lwps is an alias for a well defined zone.max-lwps
6640  * rctl.  If there are multiple existing values for one of these rctls or if
6641  * there is a single value that does not match the well defined template (i.e.
6642  * it has a different action) then we cannot treat the rctl as having an alias
6643  * so we return Z_ALIAS_DISALLOW.  That means that the rctl cannot be
6644  * managed in zonecfg via an alias and that the standard rctl syntax must be
6645  * used.
6646  *
6647  * The possible return values are:
6648  *	Z_NO_PROPERTY_ID - invalid alias name
6649  *	Z_ALIAS_DISALLOW - pre-existing, incompatible rctl definition
6650  *	Z_NO_ENTRY - no rctl is configured for this alias
6651  *	Z_OK - we got a valid rctl for the specified alias
6652  */
6653 int
6654 zonecfg_get_aliased_rctl(zone_dochandle_t handle, char *name, uint64_t *rval)
6655 {
6656 	boolean_t found = B_FALSE;
6657 	boolean_t found_val = B_FALSE;
6658 	xmlNodePtr cur, val;
6659 	char savedname[MAXNAMELEN];
6660 	struct zone_rctlvaltab rctl;
6661 	int i;
6662 	int err;
6663 
6664 	for (i = 0; aliases[i].shortname != NULL; i++)
6665 		if (strcmp(name, aliases[i].shortname) == 0)
6666 			break;
6667 
6668 	if (aliases[i].shortname == NULL)
6669 		return (Z_NO_PROPERTY_ID);
6670 
6671 	if ((err = operation_prep(handle)) != Z_OK)
6672 		return (err);
6673 
6674 	cur = handle->zone_dh_cur;
6675 	for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
6676 		if (xmlStrcmp(cur->name, DTD_ELEM_RCTL) != 0)
6677 			continue;
6678 		if ((fetchprop(cur, DTD_ATTR_NAME, savedname,
6679 		    sizeof (savedname)) == Z_OK) &&
6680 		    (strcmp(savedname, aliases[i].realname) == 0)) {
6681 
6682 			/*
6683 			 * If we already saw one of these, we can't have an
6684 			 * alias since we just found another.
6685 			 */
6686 			if (found)
6687 				return (Z_ALIAS_DISALLOW);
6688 			found = B_TRUE;
6689 
6690 			for (val = cur->xmlChildrenNode; val != NULL;
6691 			    val = val->next) {
6692 				/*
6693 				 * If we already have one value, we can't have
6694 				 * an alias since we just found another.
6695 				 */
6696 				if (found_val)
6697 					return (Z_ALIAS_DISALLOW);
6698 				found_val = B_TRUE;
6699 
6700 				if ((fetchprop(val, DTD_ATTR_PRIV,
6701 				    rctl.zone_rctlval_priv,
6702 				    sizeof (rctl.zone_rctlval_priv)) != Z_OK))
6703 					break;
6704 				if ((fetchprop(val, DTD_ATTR_LIMIT,
6705 				    rctl.zone_rctlval_limit,
6706 				    sizeof (rctl.zone_rctlval_limit)) != Z_OK))
6707 					break;
6708 				if ((fetchprop(val, DTD_ATTR_ACTION,
6709 				    rctl.zone_rctlval_action,
6710 				    sizeof (rctl.zone_rctlval_action)) != Z_OK))
6711 					break;
6712 			}
6713 
6714 			/* check priv and action match the expected vals */
6715 			if (strcmp(rctl.zone_rctlval_priv,
6716 			    aliases[i].priv) != 0 ||
6717 			    strcmp(rctl.zone_rctlval_action,
6718 			    aliases[i].action) != 0)
6719 				return (Z_ALIAS_DISALLOW);
6720 		}
6721 	}
6722 
6723 	if (found) {
6724 		*rval = strtoull(rctl.zone_rctlval_limit, NULL, 10);
6725 		return (Z_OK);
6726 	}
6727 
6728 	return (Z_NO_ENTRY);
6729 }
6730 
6731 int
6732 zonecfg_rm_aliased_rctl(zone_dochandle_t handle, char *name)
6733 {
6734 	int i;
6735 	uint64_t val;
6736 	struct zone_rctltab rctltab;
6737 
6738 	/*
6739 	 * First check that we have a valid aliased rctl to remove.
6740 	 * This will catch an rctl entry with non-standard values or
6741 	 * multiple rctl values for this name.  We need to ignore those
6742 	 * rctl entries.
6743 	 */
6744 	if (zonecfg_get_aliased_rctl(handle, name, &val) != Z_OK)
6745 		return (Z_OK);
6746 
6747 	for (i = 0; aliases[i].shortname != NULL; i++)
6748 		if (strcmp(name, aliases[i].shortname) == 0)
6749 			break;
6750 
6751 	if (aliases[i].shortname == NULL)
6752 		return (Z_NO_RESOURCE_ID);
6753 
6754 	(void) strlcpy(rctltab.zone_rctl_name, aliases[i].realname,
6755 	    sizeof (rctltab.zone_rctl_name));
6756 
6757 	return (zonecfg_delete_rctl(handle, &rctltab));
6758 }
6759 
6760 boolean_t
6761 zonecfg_aliased_rctl_ok(zone_dochandle_t handle, char *name)
6762 {
6763 	uint64_t tmp_val;
6764 
6765 	switch (zonecfg_get_aliased_rctl(handle, name, &tmp_val)) {
6766 	case Z_OK:
6767 		/*FALLTHRU*/
6768 	case Z_NO_ENTRY:
6769 		return (B_TRUE);
6770 	default:
6771 		return (B_FALSE);
6772 	}
6773 }
6774 
6775 int
6776 zonecfg_set_aliased_rctl(zone_dochandle_t handle, char *name, uint64_t val)
6777 {
6778 	int i;
6779 	int err;
6780 	struct zone_rctltab rctltab;
6781 	struct zone_rctlvaltab *rctlvaltab;
6782 	char buf[128];
6783 
6784 	if (!zonecfg_aliased_rctl_ok(handle, name))
6785 		return (Z_ALIAS_DISALLOW);
6786 
6787 	for (i = 0; aliases[i].shortname != NULL; i++)
6788 		if (strcmp(name, aliases[i].shortname) == 0)
6789 			break;
6790 
6791 	if (aliases[i].shortname == NULL)
6792 		return (Z_NO_RESOURCE_ID);
6793 
6794 	/* remove any pre-existing definition for this rctl */
6795 	(void) zonecfg_rm_aliased_rctl(handle, name);
6796 
6797 	(void) strlcpy(rctltab.zone_rctl_name, aliases[i].realname,
6798 	    sizeof (rctltab.zone_rctl_name));
6799 
6800 	rctltab.zone_rctl_valptr = NULL;
6801 
6802 	if ((rctlvaltab = calloc(1, sizeof (struct zone_rctlvaltab))) == NULL)
6803 		return (Z_NOMEM);
6804 
6805 	(void) snprintf(buf, sizeof (buf), "%llu", (long long)val);
6806 
6807 	(void) strlcpy(rctlvaltab->zone_rctlval_priv, aliases[i].priv,
6808 	    sizeof (rctlvaltab->zone_rctlval_priv));
6809 	(void) strlcpy(rctlvaltab->zone_rctlval_limit, buf,
6810 	    sizeof (rctlvaltab->zone_rctlval_limit));
6811 	(void) strlcpy(rctlvaltab->zone_rctlval_action, aliases[i].action,
6812 	    sizeof (rctlvaltab->zone_rctlval_action));
6813 
6814 	rctlvaltab->zone_rctlval_next = NULL;
6815 
6816 	if ((err = zonecfg_add_rctl_value(&rctltab, rctlvaltab)) != Z_OK)
6817 		return (err);
6818 
6819 	return (zonecfg_add_rctl(handle, &rctltab));
6820 }
6821 
6822 static int
6823 delete_tmp_pool(zone_dochandle_t handle)
6824 {
6825 	int err;
6826 	xmlNodePtr cur = handle->zone_dh_cur;
6827 
6828 	if ((err = operation_prep(handle)) != Z_OK)
6829 		return (err);
6830 
6831 	for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
6832 		if (xmlStrcmp(cur->name, DTD_ELEM_TMPPOOL) == 0) {
6833 			xmlUnlinkNode(cur);
6834 			xmlFreeNode(cur);
6835 			return (Z_OK);
6836 		}
6837 	}
6838 
6839 	return (Z_NO_RESOURCE_ID);
6840 }
6841 
6842 static int
6843 modify_tmp_pool(zone_dochandle_t handle, char *pool_importance)
6844 {
6845 	int err;
6846 	xmlNodePtr cur = handle->zone_dh_cur;
6847 	xmlNodePtr newnode;
6848 
6849 	err = delete_tmp_pool(handle);
6850 	if (err != Z_OK && err != Z_NO_RESOURCE_ID)
6851 		return (err);
6852 
6853 	if (*pool_importance != '\0') {
6854 		if ((err = operation_prep(handle)) != Z_OK)
6855 			return (err);
6856 
6857 		newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_TMPPOOL, NULL);
6858 		if ((err = newprop(newnode, DTD_ATTR_IMPORTANCE,
6859 		    pool_importance)) != Z_OK)
6860 			return (err);
6861 	}
6862 
6863 	return (Z_OK);
6864 }
6865 
6866 static int
6867 add_pset_core(zone_dochandle_t handle, struct zone_psettab *tabptr)
6868 {
6869 	xmlNodePtr newnode, cur = handle->zone_dh_cur;
6870 	int err;
6871 
6872 	newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_PSET, NULL);
6873 	if ((err = newprop(newnode, DTD_ATTR_NCPU_MIN,
6874 	    tabptr->zone_ncpu_min)) != Z_OK)
6875 		return (err);
6876 	if ((err = newprop(newnode, DTD_ATTR_NCPU_MAX,
6877 	    tabptr->zone_ncpu_max)) != Z_OK)
6878 		return (err);
6879 
6880 	if ((err = modify_tmp_pool(handle, tabptr->zone_importance)) != Z_OK)
6881 		return (err);
6882 
6883 	return (Z_OK);
6884 }
6885 
6886 int
6887 zonecfg_add_pset(zone_dochandle_t handle, struct zone_psettab *tabptr)
6888 {
6889 	int err;
6890 
6891 	if (tabptr == NULL)
6892 		return (Z_INVAL);
6893 
6894 	if ((err = operation_prep(handle)) != Z_OK)
6895 		return (err);
6896 
6897 	if ((err = add_pset_core(handle, tabptr)) != Z_OK)
6898 		return (err);
6899 
6900 	return (Z_OK);
6901 }
6902 
6903 int
6904 zonecfg_delete_pset(zone_dochandle_t handle)
6905 {
6906 	int err;
6907 	int res = Z_NO_RESOURCE_ID;
6908 	xmlNodePtr cur = handle->zone_dh_cur;
6909 
6910 	if ((err = operation_prep(handle)) != Z_OK)
6911 		return (err);
6912 
6913 	for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
6914 		if (xmlStrcmp(cur->name, DTD_ELEM_PSET) == 0) {
6915 			xmlUnlinkNode(cur);
6916 			xmlFreeNode(cur);
6917 			res = Z_OK;
6918 			break;
6919 		}
6920 	}
6921 
6922 	/*
6923 	 * Once we have msets, we should check that a mset
6924 	 * do not exist before we delete the tmp_pool data.
6925 	 */
6926 	err = delete_tmp_pool(handle);
6927 	if (err != Z_OK && err != Z_NO_RESOURCE_ID)
6928 		return (err);
6929 
6930 	return (res);
6931 }
6932 
6933 int
6934 zonecfg_modify_pset(zone_dochandle_t handle, struct zone_psettab *tabptr)
6935 {
6936 	int err;
6937 
6938 	if (tabptr == NULL)
6939 		return (Z_INVAL);
6940 
6941 	if ((err = zonecfg_delete_pset(handle)) != Z_OK)
6942 		return (err);
6943 
6944 	if ((err = add_pset_core(handle, tabptr)) != Z_OK)
6945 		return (err);
6946 
6947 	return (Z_OK);
6948 }
6949 
6950 int
6951 zonecfg_lookup_pset(zone_dochandle_t handle, struct zone_psettab *tabptr)
6952 {
6953 	xmlNodePtr cur;
6954 	int err;
6955 	int res = Z_NO_ENTRY;
6956 
6957 	if (tabptr == NULL)
6958 		return (Z_INVAL);
6959 
6960 	if ((err = operation_prep(handle)) != Z_OK)
6961 		return (err);
6962 
6963 	/* this is an optional component */
6964 	tabptr->zone_importance[0] = '\0';
6965 
6966 	cur = handle->zone_dh_cur;
6967 	for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
6968 		if (xmlStrcmp(cur->name, DTD_ELEM_PSET) == 0) {
6969 			if ((err = fetchprop(cur, DTD_ATTR_NCPU_MIN,
6970 			    tabptr->zone_ncpu_min,
6971 			    sizeof (tabptr->zone_ncpu_min))) != Z_OK) {
6972 				handle->zone_dh_cur = handle->zone_dh_top;
6973 				return (err);
6974 			}
6975 
6976 			if ((err = fetchprop(cur, DTD_ATTR_NCPU_MAX,
6977 			    tabptr->zone_ncpu_max,
6978 			    sizeof (tabptr->zone_ncpu_max))) != Z_OK) {
6979 				handle->zone_dh_cur = handle->zone_dh_top;
6980 				return (err);
6981 			}
6982 
6983 			res = Z_OK;
6984 
6985 		} else if (xmlStrcmp(cur->name, DTD_ELEM_TMPPOOL) == 0) {
6986 			if ((err = fetchprop(cur, DTD_ATTR_IMPORTANCE,
6987 			    tabptr->zone_importance,
6988 			    sizeof (tabptr->zone_importance))) != Z_OK) {
6989 				handle->zone_dh_cur = handle->zone_dh_top;
6990 				return (err);
6991 			}
6992 		}
6993 	}
6994 
6995 	return (res);
6996 }
6997 
6998 int
6999 zonecfg_getpsetent(zone_dochandle_t handle, struct zone_psettab *tabptr)
7000 {
7001 	int err;
7002 
7003 	if ((err = zonecfg_setent(handle)) != Z_OK)
7004 		return (err);
7005 
7006 	err = zonecfg_lookup_pset(handle, tabptr);
7007 
7008 	(void) zonecfg_endent(handle);
7009 
7010 	return (err);
7011 }
7012 
7013 static int
7014 add_mcap(zone_dochandle_t handle, struct zone_mcaptab *tabptr)
7015 {
7016 	xmlNodePtr newnode, cur = handle->zone_dh_cur;
7017 	int err;
7018 
7019 	newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_MCAP, NULL);
7020 	if ((err = newprop(newnode, DTD_ATTR_PHYSCAP, tabptr->zone_physmem_cap))
7021 	    != Z_OK)
7022 		return (err);
7023 
7024 	return (Z_OK);
7025 }
7026 
7027 int
7028 zonecfg_delete_mcap(zone_dochandle_t handle)
7029 {
7030 	int err;
7031 	xmlNodePtr cur = handle->zone_dh_cur;
7032 
7033 	if ((err = operation_prep(handle)) != Z_OK)
7034 		return (err);
7035 
7036 	for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
7037 		if (xmlStrcmp(cur->name, DTD_ELEM_MCAP) != 0)
7038 			continue;
7039 
7040 		xmlUnlinkNode(cur);
7041 		xmlFreeNode(cur);
7042 		return (Z_OK);
7043 	}
7044 	return (Z_NO_RESOURCE_ID);
7045 }
7046 
7047 int
7048 zonecfg_modify_mcap(zone_dochandle_t handle, struct zone_mcaptab *tabptr)
7049 {
7050 	int err;
7051 
7052 	if (tabptr == NULL)
7053 		return (Z_INVAL);
7054 
7055 	err = zonecfg_delete_mcap(handle);
7056 	/* it is ok if there is no mcap entry */
7057 	if (err != Z_OK && err != Z_NO_RESOURCE_ID)
7058 		return (err);
7059 
7060 	if ((err = add_mcap(handle, tabptr)) != Z_OK)
7061 		return (err);
7062 
7063 	return (Z_OK);
7064 }
7065 
7066 int
7067 zonecfg_lookup_mcap(zone_dochandle_t handle, struct zone_mcaptab *tabptr)
7068 {
7069 	xmlNodePtr cur;
7070 	int err;
7071 
7072 	if (tabptr == NULL)
7073 		return (Z_INVAL);
7074 
7075 	if ((err = operation_prep(handle)) != Z_OK)
7076 		return (err);
7077 
7078 	cur = handle->zone_dh_cur;
7079 	for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
7080 		if (xmlStrcmp(cur->name, DTD_ELEM_MCAP) != 0)
7081 			continue;
7082 		if ((err = fetchprop(cur, DTD_ATTR_PHYSCAP,
7083 		    tabptr->zone_physmem_cap,
7084 		    sizeof (tabptr->zone_physmem_cap))) != Z_OK) {
7085 			handle->zone_dh_cur = handle->zone_dh_top;
7086 			return (err);
7087 		}
7088 
7089 		return (Z_OK);
7090 	}
7091 
7092 	return (Z_NO_ENTRY);
7093 }
7094 
7095 static int
7096 getmcapent_core(zone_dochandle_t handle, struct zone_mcaptab *tabptr)
7097 {
7098 	xmlNodePtr cur;
7099 	int err;
7100 
7101 	if (handle == NULL)
7102 		return (Z_INVAL);
7103 
7104 	if ((cur = handle->zone_dh_cur) == NULL)
7105 		return (Z_NO_ENTRY);
7106 
7107 	for (; cur != NULL; cur = cur->next)
7108 		if (xmlStrcmp(cur->name, DTD_ELEM_MCAP) == 0)
7109 			break;
7110 	if (cur == NULL) {
7111 		handle->zone_dh_cur = handle->zone_dh_top;
7112 		return (Z_NO_ENTRY);
7113 	}
7114 
7115 	if ((err = fetchprop(cur, DTD_ATTR_PHYSCAP, tabptr->zone_physmem_cap,
7116 	    sizeof (tabptr->zone_physmem_cap))) != Z_OK) {
7117 		handle->zone_dh_cur = handle->zone_dh_top;
7118 		return (err);
7119 	}
7120 
7121 	handle->zone_dh_cur = cur->next;
7122 	return (Z_OK);
7123 }
7124 
7125 int
7126 zonecfg_getmcapent(zone_dochandle_t handle, struct zone_mcaptab *tabptr)
7127 {
7128 	int err;
7129 
7130 	if ((err = zonecfg_setent(handle)) != Z_OK)
7131 		return (err);
7132 
7133 	err = getmcapent_core(handle, tabptr);
7134 
7135 	(void) zonecfg_endent(handle);
7136 
7137 	return (err);
7138 }
7139 
7140 /*
7141  * Get the full tree of pkg/patch metadata in a set of nested AVL trees.
7142  * pkgs_avl is an AVL tree of pkgs.  Each pkg element contains a
7143  * zpe_patches_avl member which holds an AVL tree of patches for that pkg.
7144  * The patch elements have the same zpe_patches_avl member, each of which can
7145  * hold an AVL tree of patches that are obsoleted by the patch.
7146  *
7147  * The zone xml data contains DTD_ELEM_PACKAGE elements, followed by
7148  * DTD_ELEM_PATCH elements.  The DTD_ELEM_PATCH patch element applies to the
7149  * DTD_ELEM_PACKAGE that precedes it.  The DTD_ELEM_PATCH element may have
7150  * child DTD_ELEM_OBSOLETES nodes associated with it.  The DTD_ELEM_PACKAGE
7151  * really should have had the DTD_ELEM_PATCH elements as children but it
7152  * was not defined that way initially so we are stuck with the DTD definition
7153  * now.  However, we can safely assume the ordering for compatibility.
7154  */
7155 int
7156 zonecfg_getpkgdata(zone_dochandle_t handle, uu_avl_pool_t *pkg_pool,
7157     uu_avl_t *pkgs_avl)
7158 {
7159 	xmlNodePtr cur;
7160 	int res;
7161 	zone_pkg_entry_t *pkg;
7162 	char name[MAXNAMELEN];
7163 	char version[ZONE_PKG_VERSMAX];
7164 
7165 	if (handle == NULL)
7166 		return (Z_INVAL);
7167 
7168 	if ((res = zonecfg_setent(handle)) != Z_OK)
7169 		return (res);
7170 
7171 	if ((cur = handle->zone_dh_cur) == NULL) {
7172 		res = Z_NO_ENTRY;
7173 		goto done;
7174 	}
7175 
7176 	for (; cur != NULL; cur = cur->next) {
7177 		if (xmlStrcmp(cur->name, DTD_ELEM_PACKAGE) == 0) {
7178 			uu_avl_index_t where;
7179 
7180 			if ((res = fetchprop(cur, DTD_ATTR_NAME, name,
7181 			    sizeof (name))) != Z_OK)
7182 				goto done;
7183 
7184 			if ((res = fetchprop(cur, DTD_ATTR_VERSION, version,
7185 			    sizeof (version))) != Z_OK)
7186 				goto done;
7187 
7188 			if ((pkg = (zone_pkg_entry_t *)
7189 			    malloc(sizeof (zone_pkg_entry_t))) == NULL) {
7190 				res = Z_NOMEM;
7191 				goto done;
7192 			}
7193 
7194 			if ((pkg->zpe_name = strdup(name)) == NULL) {
7195 				free(pkg);
7196 				res = Z_NOMEM;
7197 				goto done;
7198 			}
7199 
7200 			if ((pkg->zpe_vers = strdup(version)) == NULL) {
7201 				free(pkg->zpe_name);
7202 				free(pkg);
7203 				res = Z_NOMEM;
7204 				goto done;
7205 			}
7206 
7207 			pkg->zpe_patches_avl = NULL;
7208 
7209 			uu_avl_node_init(pkg, &pkg->zpe_entry, pkg_pool);
7210 			if (uu_avl_find(pkgs_avl, pkg, NULL, &where) != NULL) {
7211 				free(pkg->zpe_name);
7212 				free(pkg->zpe_vers);
7213 				free(pkg);
7214 			} else {
7215 				uu_avl_insert(pkgs_avl, pkg, where);
7216 			}
7217 
7218 		} else if (xmlStrcmp(cur->name, DTD_ELEM_PATCH) == 0) {
7219 			zone_pkg_entry_t *patch;
7220 			uu_avl_index_t where;
7221 			char *p;
7222 			char *dashp = NULL;
7223 			xmlNodePtr child;
7224 
7225 			if ((res = fetchprop(cur, DTD_ATTR_ID, name,
7226 			    sizeof (name))) != Z_OK)
7227 				goto done;
7228 
7229 			if ((patch = (zone_pkg_entry_t *)
7230 			    malloc(sizeof (zone_pkg_entry_t))) == NULL) {
7231 				res = Z_NOMEM;
7232 				goto done;
7233 			}
7234 
7235 			if ((p = strchr(name, '-')) != NULL) {
7236 				dashp = p;
7237 				*p++ = '\0';
7238 			} else {
7239 				p = "";
7240 			}
7241 
7242 			if ((patch->zpe_name = strdup(name)) == NULL) {
7243 				free(patch);
7244 				res = Z_NOMEM;
7245 				goto done;
7246 			}
7247 
7248 			if ((patch->zpe_vers = strdup(p)) == NULL) {
7249 				free(patch->zpe_name);
7250 				free(patch);
7251 				res = Z_NOMEM;
7252 				goto done;
7253 			}
7254 
7255 			if (dashp != NULL)
7256 				*dashp = '-';
7257 
7258 			patch->zpe_patches_avl = NULL;
7259 
7260 			if (pkg->zpe_patches_avl == NULL) {
7261 				pkg->zpe_patches_avl = uu_avl_create(pkg_pool,
7262 				    NULL, UU_DEFAULT);
7263 				if (pkg->zpe_patches_avl == NULL) {
7264 					free(patch->zpe_name);
7265 					free(patch->zpe_vers);
7266 					free(patch);
7267 					res = Z_NOMEM;
7268 					goto done;
7269 				}
7270 			}
7271 
7272 			uu_avl_node_init(patch, &patch->zpe_entry, pkg_pool);
7273 			if (uu_avl_find(pkg->zpe_patches_avl, patch, NULL,
7274 			    &where) != NULL) {
7275 				free(patch->zpe_name);
7276 				free(patch->zpe_vers);
7277 				free(patch);
7278 			} else {
7279 				uu_avl_insert(pkg->zpe_patches_avl, patch,
7280 				    where);
7281 			}
7282 
7283 			/* Add any patches this patch obsoletes. */
7284 			for (child = cur->xmlChildrenNode; child != NULL;
7285 			    child = child->next) {
7286 				zone_pkg_entry_t *obs;
7287 
7288 				if (xmlStrcmp(child->name, DTD_ELEM_OBSOLETES)
7289 				    != 0)
7290 					continue;
7291 
7292 				if ((res = fetchprop(child, DTD_ATTR_ID,
7293 				    name, sizeof (name))) != Z_OK)
7294 					goto done;
7295 
7296 				if ((obs = (zone_pkg_entry_t *)malloc(
7297 				    sizeof (zone_pkg_entry_t))) == NULL) {
7298 					res = Z_NOMEM;
7299 					goto done;
7300 				}
7301 
7302 				if ((obs->zpe_name = strdup(name)) == NULL) {
7303 					free(obs);
7304 					res = Z_NOMEM;
7305 					goto done;
7306 				}
7307 				/*
7308 				 * The version doesn't matter for obsoleted
7309 				 * patches.
7310 				 */
7311 				obs->zpe_vers = NULL;
7312 				obs->zpe_patches_avl = NULL;
7313 
7314 				/*
7315 				 * If this is the first obsolete patch, add an
7316 				 * AVL tree to the parent patch element.
7317 				 */
7318 				if (patch->zpe_patches_avl == NULL) {
7319 					patch->zpe_patches_avl =
7320 					    uu_avl_create(pkg_pool, NULL,
7321 					    UU_DEFAULT);
7322 					if (patch->zpe_patches_avl == NULL) {
7323 						free(obs->zpe_name);
7324 						free(obs);
7325 						res = Z_NOMEM;
7326 						goto done;
7327 					}
7328 				}
7329 
7330 				/* Insert obsolete patch into the AVL tree. */
7331 				uu_avl_node_init(obs, &obs->zpe_entry,
7332 				    pkg_pool);
7333 				if (uu_avl_find(patch->zpe_patches_avl, obs,
7334 				    NULL, &where) != NULL) {
7335 					free(obs->zpe_name);
7336 					free(obs);
7337 				} else {
7338 					uu_avl_insert(patch->zpe_patches_avl,
7339 					    obs, where);
7340 				}
7341 			}
7342 		}
7343 	}
7344 
7345 done:
7346 	(void) zonecfg_endent(handle);
7347 	return (res);
7348 }
7349 
7350 int
7351 zonecfg_setdevperment(zone_dochandle_t handle)
7352 {
7353 	return (zonecfg_setent(handle));
7354 }
7355 
7356 int
7357 zonecfg_getdevperment(zone_dochandle_t handle, struct zone_devpermtab *tabptr)
7358 {
7359 	xmlNodePtr cur;
7360 	int err;
7361 	char buf[128];
7362 
7363 	tabptr->zone_devperm_acl = NULL;
7364 
7365 	if (handle == NULL)
7366 		return (Z_INVAL);
7367 
7368 	if ((cur = handle->zone_dh_cur) == NULL)
7369 		return (Z_NO_ENTRY);
7370 
7371 	for (; cur != NULL; cur = cur->next)
7372 		if (!xmlStrcmp(cur->name, DTD_ELEM_DEV_PERM))
7373 			break;
7374 	if (cur == NULL) {
7375 		handle->zone_dh_cur = handle->zone_dh_top;
7376 		return (Z_NO_ENTRY);
7377 	}
7378 
7379 	if ((err = fetchprop(cur, DTD_ATTR_NAME, tabptr->zone_devperm_name,
7380 	    sizeof (tabptr->zone_devperm_name))) != Z_OK) {
7381 		handle->zone_dh_cur = handle->zone_dh_top;
7382 		return (err);
7383 	}
7384 
7385 	if ((err = fetchprop(cur, DTD_ATTR_UID, buf, sizeof (buf))) != Z_OK) {
7386 		handle->zone_dh_cur = handle->zone_dh_top;
7387 		return (err);
7388 	}
7389 	tabptr->zone_devperm_uid = (uid_t)atol(buf);
7390 
7391 	if ((err = fetchprop(cur, DTD_ATTR_GID, buf, sizeof (buf))) != Z_OK) {
7392 		handle->zone_dh_cur = handle->zone_dh_top;
7393 		return (err);
7394 	}
7395 	tabptr->zone_devperm_gid = (gid_t)atol(buf);
7396 
7397 	if ((err = fetchprop(cur, DTD_ATTR_MODE, buf, sizeof (buf))) != Z_OK) {
7398 		handle->zone_dh_cur = handle->zone_dh_top;
7399 		return (err);
7400 	}
7401 	tabptr->zone_devperm_mode = (mode_t)strtol(buf, (char **)NULL, 8);
7402 
7403 	if ((err = fetch_alloc_prop(cur, DTD_ATTR_ACL,
7404 	    &(tabptr->zone_devperm_acl))) != Z_OK) {
7405 		handle->zone_dh_cur = handle->zone_dh_top;
7406 		return (err);
7407 	}
7408 
7409 	handle->zone_dh_cur = cur->next;
7410 	return (Z_OK);
7411 }
7412 
7413 int
7414 zonecfg_enddevperment(zone_dochandle_t handle)
7415 {
7416 	return (zonecfg_endent(handle));
7417 }
7418 
7419 /* PRINTFLIKE1 */
7420 static void
7421 zerror(const char *zone_name, const char *fmt, ...)
7422 {
7423 	va_list alist;
7424 
7425 	va_start(alist, fmt);
7426 	(void) fprintf(stderr, "zone '%s': ", zone_name);
7427 	(void) vfprintf(stderr, fmt, alist);
7428 	(void) fprintf(stderr, "\n");
7429 	va_end(alist);
7430 }
7431 
7432 static void
7433 zperror(const char *str)
7434 {
7435 	(void) fprintf(stderr, "%s: %s\n", str, strerror(errno));
7436 }
7437 
7438 /*
7439  * The following three routines implement a simple locking mechanism to
7440  * ensure that only one instance of zoneadm at a time is able to manipulate
7441  * a given zone.  The lock is built on top of an fcntl(2) lock of
7442  * [<altroot>]/var/run/zones/<zonename>.zoneadm.lock.  If a zoneadm instance
7443  * can grab that lock, it is allowed to manipulate the zone.
7444  *
7445  * Since zoneadm may call external applications which in turn invoke
7446  * zoneadm again, we introduce the notion of "lock inheritance".  Any
7447  * instance of zoneadm that has another instance in its ancestry is assumed
7448  * to be acting on behalf of the original zoneadm, and is thus allowed to
7449  * manipulate its zone.
7450  *
7451  * This inheritance is implemented via the _ZONEADM_LOCK_HELD environment
7452  * variable.  When zoneadm is granted a lock on its zone, this environment
7453  * variable is set to 1.  When it releases the lock, the variable is set to
7454  * 0.  Since a child process inherits its parent's environment, checking
7455  * the state of this variable indicates whether or not any ancestor owns
7456  * the lock.
7457  */
7458 void
7459 zonecfg_init_lock_file(const char *zone_name, char **lock_env)
7460 {
7461 	*lock_env = getenv(LOCK_ENV_VAR);
7462 	if (*lock_env == NULL) {
7463 		if (putenv(zoneadm_lock_not_held) != 0) {
7464 			zerror(zone_name, gettext("could not set env: %s"),
7465 			    strerror(errno));
7466 			exit(1);
7467 		}
7468 	} else {
7469 		if (atoi(*lock_env) == 1)
7470 			zone_lock_cnt = 1;
7471 	}
7472 }
7473 
7474 void
7475 zonecfg_release_lock_file(const char *zone_name, int lockfd)
7476 {
7477 	/*
7478 	 * If we are cleaning up from a failed attempt to lock the zone for
7479 	 * the first time, we might have a zone_lock_cnt of 0.  In that
7480 	 * error case, we don't want to do anything but close the lock
7481 	 * file.
7482 	 */
7483 	assert(zone_lock_cnt >= 0);
7484 	if (zone_lock_cnt > 0) {
7485 		assert(getenv(LOCK_ENV_VAR) != NULL);
7486 		assert(atoi(getenv(LOCK_ENV_VAR)) == 1);
7487 		if (--zone_lock_cnt > 0) {
7488 			assert(lockfd == -1);
7489 			return;
7490 		}
7491 		if (putenv(zoneadm_lock_not_held) != 0) {
7492 			zerror(zone_name, gettext("could not set env: %s"),
7493 			    strerror(errno));
7494 			exit(1);
7495 		}
7496 	}
7497 	assert(lockfd >= 0);
7498 	(void) close(lockfd);
7499 }
7500 
7501 int
7502 zonecfg_grab_lock_file(const char *zone_name, int *lockfd)
7503 {
7504 	char pathbuf[PATH_MAX];
7505 	struct flock flock;
7506 
7507 	/*
7508 	 * If we already have the lock, we can skip this expensive song
7509 	 * and dance.
7510 	 */
7511 	assert(zone_lock_cnt >= 0);
7512 	assert(getenv(LOCK_ENV_VAR) != NULL);
7513 	if (zone_lock_cnt > 0) {
7514 		assert(atoi(getenv(LOCK_ENV_VAR)) == 1);
7515 		zone_lock_cnt++;
7516 		*lockfd = -1;
7517 		return (Z_OK);
7518 	}
7519 	assert(getenv(LOCK_ENV_VAR) != NULL);
7520 	assert(atoi(getenv(LOCK_ENV_VAR)) == 0);
7521 
7522 	if (snprintf(pathbuf, sizeof (pathbuf), "%s%s", zonecfg_get_root(),
7523 	    ZONES_TMPDIR) >= sizeof (pathbuf)) {
7524 		zerror(zone_name, gettext("alternate root path is too long"));
7525 		return (-1);
7526 	}
7527 	if (mkdir(pathbuf, S_IRWXU) < 0 && errno != EEXIST) {
7528 		zerror(zone_name, gettext("could not mkdir %s: %s"), pathbuf,
7529 		    strerror(errno));
7530 		return (-1);
7531 	}
7532 	(void) chmod(pathbuf, S_IRWXU);
7533 
7534 	/*
7535 	 * One of these lock files is created for each zone (when needed).
7536 	 * The lock files are not cleaned up (except on system reboot),
7537 	 * but since there is only one per zone, there is no resource
7538 	 * starvation issue.
7539 	 */
7540 	if (snprintf(pathbuf, sizeof (pathbuf), "%s%s/%s.zoneadm.lock",
7541 	    zonecfg_get_root(), ZONES_TMPDIR, zone_name) >= sizeof (pathbuf)) {
7542 		zerror(zone_name, gettext("alternate root path is too long"));
7543 		return (-1);
7544 	}
7545 	if ((*lockfd = open(pathbuf, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR)) < 0) {
7546 		zerror(zone_name, gettext("could not open %s: %s"), pathbuf,
7547 		    strerror(errno));
7548 		return (-1);
7549 	}
7550 	/*
7551 	 * Lock the file to synchronize with other zoneadmds
7552 	 */
7553 	flock.l_type = F_WRLCK;
7554 	flock.l_whence = SEEK_SET;
7555 	flock.l_start = (off_t)0;
7556 	flock.l_len = (off_t)0;
7557 	if ((fcntl(*lockfd, F_SETLKW, &flock) < 0) ||
7558 	    (putenv(zoneadm_lock_held) != 0)) {
7559 		zerror(zone_name, gettext("unable to lock %s: %s"), pathbuf,
7560 		    strerror(errno));
7561 		zonecfg_release_lock_file(zone_name, *lockfd);
7562 		return (-1);
7563 	}
7564 	zone_lock_cnt = 1;
7565 	return (Z_OK);
7566 }
7567 
7568 boolean_t
7569 zonecfg_lock_file_held(int *lockfd)
7570 {
7571 	if (*lockfd >= 0 || zone_lock_cnt > 0)
7572 		return (B_TRUE);
7573 	return (B_FALSE);
7574 }
7575 
7576 static boolean_t
7577 get_doorname(const char *zone_name, char *buffer)
7578 {
7579 	return (snprintf(buffer, PATH_MAX, "%s" ZONE_DOOR_PATH,
7580 	    zonecfg_get_root(), zone_name) < PATH_MAX);
7581 }
7582 
7583 /*
7584  * system daemons are not audited.  For the global zone, this occurs
7585  * "naturally" since init is started with the default audit
7586  * characteristics.  Since zoneadmd is a system daemon and it starts
7587  * init for a zone, it is necessary to clear out the audit
7588  * characteristics inherited from whomever started zoneadmd.  This is
7589  * indicated by the audit id, which is set from the ruid parameter of
7590  * adt_set_user(), below.
7591  */
7592 
7593 static void
7594 prepare_audit_context(const char *zone_name)
7595 {
7596 	adt_session_data_t	*ah;
7597 	char			*failure = gettext("audit failure: %s");
7598 
7599 	if (adt_start_session(&ah, NULL, 0)) {
7600 		zerror(zone_name, failure, strerror(errno));
7601 		return;
7602 	}
7603 	if (adt_set_user(ah, ADT_NO_AUDIT, ADT_NO_AUDIT,
7604 	    ADT_NO_AUDIT, ADT_NO_AUDIT, NULL, ADT_NEW)) {
7605 		zerror(zone_name, failure, strerror(errno));
7606 		(void) adt_end_session(ah);
7607 		return;
7608 	}
7609 	if (adt_set_proc(ah))
7610 		zerror(zone_name, failure, strerror(errno));
7611 
7612 	(void) adt_end_session(ah);
7613 }
7614 
7615 static int
7616 start_zoneadmd(const char *zone_name, boolean_t lock)
7617 {
7618 	char doorpath[PATH_MAX];
7619 	pid_t child_pid;
7620 	int error = -1;
7621 	int doorfd, lockfd;
7622 	struct door_info info;
7623 
7624 	if (!get_doorname(zone_name, doorpath))
7625 		return (-1);
7626 
7627 	if (lock)
7628 		if (zonecfg_grab_lock_file(zone_name, &lockfd) != Z_OK)
7629 			return (-1);
7630 
7631 	/*
7632 	 * Now that we have the lock, re-confirm that the daemon is
7633 	 * *not* up and working fine.  If it is still down, we have a green
7634 	 * light to start it.
7635 	 */
7636 	if ((doorfd = open(doorpath, O_RDONLY)) < 0) {
7637 		if (errno != ENOENT) {
7638 			zperror(doorpath);
7639 			goto out;
7640 		}
7641 	} else {
7642 		if (door_info(doorfd, &info) == 0 &&
7643 		    ((info.di_attributes & DOOR_REVOKED) == 0)) {
7644 			error = Z_OK;
7645 			(void) close(doorfd);
7646 			goto out;
7647 		}
7648 		(void) close(doorfd);
7649 	}
7650 
7651 	if ((child_pid = fork()) == -1) {
7652 		zperror(gettext("could not fork"));
7653 		goto out;
7654 	}
7655 
7656 	if (child_pid == 0) {
7657 		const char *argv[6], **ap;
7658 
7659 		/* child process */
7660 		prepare_audit_context(zone_name);
7661 
7662 		ap = argv;
7663 		*ap++ = "zoneadmd";
7664 		*ap++ = "-z";
7665 		*ap++ = zone_name;
7666 		if (zonecfg_in_alt_root()) {
7667 			*ap++ = "-R";
7668 			*ap++ = zonecfg_get_root();
7669 		}
7670 		*ap = NULL;
7671 
7672 		(void) execv("/usr/lib/zones/zoneadmd", (char * const *)argv);
7673 		/*
7674 		 * TRANSLATION_NOTE
7675 		 * zoneadmd is a literal that should not be translated.
7676 		 */
7677 		zperror(gettext("could not exec zoneadmd"));
7678 		_exit(1);
7679 	} else {
7680 		/* parent process */
7681 		pid_t retval;
7682 		int pstatus = 0;
7683 
7684 		do {
7685 			retval = waitpid(child_pid, &pstatus, 0);
7686 		} while (retval != child_pid);
7687 		if (WIFSIGNALED(pstatus) || (WIFEXITED(pstatus) &&
7688 		    WEXITSTATUS(pstatus) != 0)) {
7689 			zerror(zone_name, gettext("could not start %s"),
7690 			    "zoneadmd");
7691 			goto out;
7692 		}
7693 	}
7694 	error = Z_OK;
7695 out:
7696 	if (lock)
7697 		zonecfg_release_lock_file(zone_name, lockfd);
7698 	return (error);
7699 }
7700 
7701 int
7702 zonecfg_ping_zoneadmd(const char *zone_name)
7703 {
7704 	char doorpath[PATH_MAX];
7705 	int doorfd;
7706 	struct door_info info;
7707 
7708 	if (!get_doorname(zone_name, doorpath))
7709 		return (-1);
7710 
7711 	if ((doorfd = open(doorpath, O_RDONLY)) < 0) {
7712 		return (-1);
7713 	}
7714 	if (door_info(doorfd, &info) == 0 &&
7715 	    ((info.di_attributes & DOOR_REVOKED) == 0)) {
7716 		(void) close(doorfd);
7717 		return (Z_OK);
7718 	}
7719 	(void) close(doorfd);
7720 	return (-1);
7721 }
7722 
7723 int
7724 zonecfg_call_zoneadmd(const char *zone_name, zone_cmd_arg_t *arg, char *locale,
7725     boolean_t lock)
7726 {
7727 	char doorpath[PATH_MAX];
7728 	int doorfd, result;
7729 	door_arg_t darg;
7730 
7731 	zoneid_t zoneid;
7732 	uint64_t uniqid = 0;
7733 
7734 	zone_cmd_rval_t *rvalp;
7735 	size_t rlen;
7736 	char *cp, *errbuf;
7737 
7738 	rlen = getpagesize();
7739 	if ((rvalp = malloc(rlen)) == NULL) {
7740 		zerror(zone_name, gettext("failed to allocate %lu bytes: %s"),
7741 		    rlen, strerror(errno));
7742 		return (-1);
7743 	}
7744 
7745 	if ((zoneid = getzoneidbyname(zone_name)) != ZONE_ID_UNDEFINED) {
7746 		(void) zone_getattr(zoneid, ZONE_ATTR_UNIQID, &uniqid,
7747 		    sizeof (uniqid));
7748 	}
7749 	arg->uniqid = uniqid;
7750 	(void) strlcpy(arg->locale, locale, sizeof (arg->locale));
7751 	if (!get_doorname(zone_name, doorpath)) {
7752 		zerror(zone_name, gettext("alternate root path is too long"));
7753 		free(rvalp);
7754 		return (-1);
7755 	}
7756 
7757 	/*
7758 	 * Loop trying to start zoneadmd; if something goes seriously
7759 	 * wrong we break out and fail.
7760 	 */
7761 	for (;;) {
7762 		if (start_zoneadmd(zone_name, lock) != Z_OK)
7763 			break;
7764 
7765 		if ((doorfd = open(doorpath, O_RDONLY)) < 0) {
7766 			zperror(gettext("failed to open zone door"));
7767 			break;
7768 		}
7769 
7770 		darg.data_ptr = (char *)arg;
7771 		darg.data_size = sizeof (*arg);
7772 		darg.desc_ptr = NULL;
7773 		darg.desc_num = 0;
7774 		darg.rbuf = (char *)rvalp;
7775 		darg.rsize = rlen;
7776 		if (door_call(doorfd, &darg) != 0) {
7777 			(void) close(doorfd);
7778 			/*
7779 			 * We'll get EBADF if the door has been revoked.
7780 			 */
7781 			if (errno != EBADF) {
7782 				zperror(gettext("door_call failed"));
7783 				break;
7784 			}
7785 			continue;	/* take another lap */
7786 		}
7787 		(void) close(doorfd);
7788 
7789 		if (darg.data_size == 0) {
7790 			/* Door server is going away; kick it again. */
7791 			continue;
7792 		}
7793 
7794 		errbuf = rvalp->errbuf;
7795 		while (*errbuf != '\0') {
7796 			/*
7797 			 * Remove any newlines since zerror()
7798 			 * will append one automatically.
7799 			 */
7800 			cp = strchr(errbuf, '\n');
7801 			if (cp != NULL)
7802 				*cp = '\0';
7803 			zerror(zone_name, "%s", errbuf);
7804 			if (cp == NULL)
7805 				break;
7806 			errbuf = cp + 1;
7807 		}
7808 		result = rvalp->rval == 0 ? 0 : -1;
7809 		free(rvalp);
7810 		return (result);
7811 	}
7812 
7813 	free(rvalp);
7814 	return (-1);
7815 }
7816 
7817 boolean_t
7818 zonecfg_valid_auths(const char *auths, const char *zonename)
7819 {
7820 	char *right;
7821 	char *tmpauths;
7822 	char *lasts;
7823 	char authname[MAXAUTHS];
7824 	boolean_t status = B_TRUE;
7825 
7826 	tmpauths = strdup(auths);
7827 	if (tmpauths == NULL) {
7828 		zerror(zonename, gettext("Out of memory"));
7829 		return (B_FALSE);
7830 	}
7831 	right = strtok_r(tmpauths, ",", &lasts);
7832 	while (right != NULL) {
7833 		(void) snprintf(authname, MAXAUTHS, "%s%s",
7834 		    ZONE_AUTH_PREFIX, right);
7835 		if (getauthnam(authname) == NULL) {
7836 			status = B_FALSE;
7837 			zerror(zonename,
7838 			    gettext("'%s' is not a valid authorization"),
7839 			    right);
7840 		}
7841 		right = strtok_r(NULL, ",", &lasts);
7842 	}
7843 	free(tmpauths);
7844 	return (status);
7845 }
7846 
7847 int
7848 zonecfg_delete_admins(zone_dochandle_t handle, char *zonename)
7849 {
7850 	int err;
7851 	struct zone_admintab admintab;
7852 	boolean_t changed = B_FALSE;
7853 
7854 	if ((err = zonecfg_setadminent(handle)) != Z_OK) {
7855 		return (err);
7856 	}
7857 	while (zonecfg_getadminent(handle, &admintab) == Z_OK) {
7858 		err = zonecfg_delete_admin(handle, &admintab,
7859 		    zonename);
7860 		if (err != Z_OK) {
7861 			(void) zonecfg_endadminent(handle);
7862 			return (err);
7863 		} else {
7864 			changed = B_TRUE;
7865 		}
7866 		if ((err = zonecfg_setadminent(handle)) != Z_OK) {
7867 			return (err);
7868 		}
7869 	}
7870 	(void) zonecfg_endadminent(handle);
7871 	return (changed? Z_OK:Z_NO_ENTRY);
7872 }
7873 
7874 /*
7875  * Checks if a long authorization applies to this zone.
7876  * If so, it returns true, after destructively stripping
7877  * the authorization of its prefix and zone suffix.
7878  */
7879 static boolean_t
7880 is_zone_auth(char **auth, char *zonename, char *oldzonename)
7881 {
7882 	char *suffix;
7883 	size_t offset;
7884 
7885 	offset = strlen(ZONE_AUTH_PREFIX);
7886 	if ((strncmp(*auth, ZONE_AUTH_PREFIX, offset) == 0) &&
7887 	    ((suffix = strchr(*auth, '/')) != NULL)) {
7888 		if (strcmp(suffix + 1, zonename) == 0) {
7889 			*auth += offset;
7890 			suffix[0] = '\0';
7891 			return (B_TRUE);
7892 		} else if ((oldzonename != NULL) &&
7893 		    (strcmp(suffix + 1, oldzonename) == 0)) {
7894 			*auth += offset;
7895 			suffix[0] = '\0';
7896 			return (B_TRUE);
7897 		}
7898 	}
7899 	return (B_FALSE);
7900 }
7901 
7902 /*
7903  * This function determines whether the zone-specific authorization
7904  * assignments in /etc/user_attr have been changed more recently
7905  * than the equivalent data stored in the zone's configuration file.
7906  * This should only happen if the zone-specific authorizations in
7907  * the user_attr file were modified using a tool other than zonecfg.
7908  * If the configuration file is out-of-date with respect to these
7909  * authorization assignments, it is updated to match those specified
7910  * in /etc/user_attr.
7911  */
7912 
7913 int
7914 zonecfg_update_userauths(zone_dochandle_t handle, char *zonename)
7915 {
7916 	userattr_t *ua_ptr;
7917 	char *authlist;
7918 	char *lasts;
7919 	FILE  *uaf;
7920 	struct zone_admintab admintab;
7921 	struct stat config_st, ua_st;
7922 	char config_file[MAXPATHLEN];
7923 	boolean_t changed = B_FALSE;
7924 	int err;
7925 
7926 	if ((uaf = fopen(USERATTR_FILENAME, "r")) == NULL) {
7927 		zerror(zonename, gettext("could not open file %s: %s"),
7928 		    USERATTR_FILENAME, strerror(errno));
7929 		if (errno == EACCES)
7930 			return (Z_ACCES);
7931 		if (errno == ENOENT)
7932 			return (Z_NO_ZONE);
7933 		return (Z_MISC_FS);
7934 	}
7935 	if ((err = fstat(fileno(uaf), &ua_st)) != 0) {
7936 		zerror(zonename, gettext("could not stat file %s: %s"),
7937 		    USERATTR_FILENAME, strerror(errno));
7938 		(void) fclose(uaf);
7939 		return (Z_MISC_FS);
7940 	}
7941 	if (!config_file_path(zonename, config_file)) {
7942 		(void) fclose(uaf);
7943 		return (Z_MISC_FS);
7944 	}
7945 
7946 	if ((err = stat(config_file, &config_st)) != 0) {
7947 		zerror(zonename, gettext("could not stat file %s: %s"),
7948 		    config_file, strerror(errno));
7949 		(void) fclose(uaf);
7950 		return (Z_MISC_FS);
7951 	}
7952 	if (config_st.st_mtime >= ua_st.st_mtime) {
7953 		(void) fclose(uaf);
7954 		return (Z_NO_ENTRY);
7955 	}
7956 	if ((err = zonecfg_delete_admins(handle, zonename)) == Z_OK) {
7957 		changed = B_TRUE;
7958 	} else if (err != Z_NO_ENTRY) {
7959 		(void) fclose(uaf);
7960 		return (err);
7961 	}
7962 	while ((ua_ptr = fgetuserattr(uaf)) != NULL) {
7963 		if (ua_ptr->name[0] == '#') {
7964 			continue;
7965 		}
7966 		authlist = kva_match(ua_ptr->attr, USERATTR_AUTHS_KW);
7967 		if (authlist != NULL) {
7968 			char *cur_auth;
7969 			boolean_t first;
7970 
7971 			first = B_TRUE;
7972 			bzero(&admintab.zone_admin_auths, MAXAUTHS);
7973 			cur_auth = strtok_r(authlist, ",", &lasts);
7974 			while (cur_auth != NULL) {
7975 				if (is_zone_auth(&cur_auth, zonename,
7976 				    NULL)) {
7977 					/*
7978 					 * Add auths for this zone
7979 					 */
7980 					if (first) {
7981 						first = B_FALSE;
7982 					} else {
7983 						(void) strlcat(
7984 						    admintab.zone_admin_auths,
7985 						    ",", MAXAUTHS);
7986 					}
7987 					(void) strlcat(
7988 					    admintab.zone_admin_auths,
7989 					    cur_auth, MAXAUTHS);
7990 				}
7991 				cur_auth = strtok_r(NULL, ",", &lasts);
7992 			}
7993 			if (!first) {
7994 				/*
7995 				 * Add this right to config file
7996 				 */
7997 				(void) strlcpy(admintab.zone_admin_user,
7998 				    ua_ptr->name,
7999 				    sizeof (admintab.zone_admin_user));
8000 				err = zonecfg_add_admin(handle,
8001 				    &admintab, zonename);
8002 				if (err != Z_OK) {
8003 					(void) fclose(uaf);
8004 					return (err);
8005 				} else {
8006 					changed = B_TRUE;
8007 				}
8008 			}
8009 		}
8010 	} /* end-of-while-loop */
8011 	(void) fclose(uaf);
8012 	return (changed? Z_OK: Z_NO_ENTRY);
8013 }
8014 
8015 static void
8016 update_profiles(char *rbac_profs, boolean_t add)
8017 {
8018 	char new_profs[MAXPROFS];
8019 	char *cur_prof;
8020 	boolean_t first = B_TRUE;
8021 	boolean_t found = B_FALSE;
8022 	char *lasts;
8023 
8024 	cur_prof = strtok_r(rbac_profs, ",", &lasts);
8025 	while (cur_prof != NULL) {
8026 		if (strcmp(cur_prof, ZONE_MGMT_PROF) == 0) {
8027 			found = B_TRUE;
8028 			if (!add) {
8029 				cur_prof = strtok_r(NULL, ",", &lasts);
8030 				continue;
8031 			}
8032 		}
8033 		if (first) {
8034 			first = B_FALSE;
8035 		} else {
8036 			(void) strlcat(new_profs, ",",
8037 			    MAXPROFS);
8038 		}
8039 		(void) strlcat(new_profs, cur_prof,
8040 		    MAXPROFS);
8041 		cur_prof = strtok_r(NULL, ",", &lasts);
8042 	}
8043 	/*
8044 	 * Now prepend the Zone Management profile at the beginning
8045 	 * of the list if it is needed, and append the rest.
8046 	 * Return the updated list in the original buffer.
8047 	 */
8048 	if (add && !found) {
8049 		first = B_FALSE;
8050 		(void) strlcpy(rbac_profs, ZONE_MGMT_PROF, MAXPROFS);
8051 	} else {
8052 		first = B_TRUE;
8053 		rbac_profs[0] = '\0';
8054 	}
8055 	if (strlen(new_profs) > 0) {
8056 		if (!first)
8057 			(void) strlcat(rbac_profs, ",", MAXPROFS);
8058 		(void) strlcat(rbac_profs, new_profs, MAXPROFS);
8059 	}
8060 }
8061 
8062 #define	MAX_CMD_LEN	1024
8063 
8064 static int
8065 do_subproc(char *zonename, char *cmdbuf)
8066 {
8067 	char inbuf[MAX_CMD_LEN];
8068 	FILE *file;
8069 	int status;
8070 
8071 	file = popen(cmdbuf, "r");
8072 	if (file == NULL) {
8073 		zerror(zonename, gettext("Could not launch: %s"), cmdbuf);
8074 		return (-1);
8075 	}
8076 
8077 	while (fgets(inbuf, sizeof (inbuf), file) != NULL)
8078 		(void) fprintf(stderr, "%s", inbuf);
8079 	status = pclose(file);
8080 
8081 	if (WIFSIGNALED(status)) {
8082 		zerror(zonename, gettext("%s unexpectedly terminated "
8083 		    "due to signal %d"),
8084 		    cmdbuf, WTERMSIG(status));
8085 		return (-1);
8086 	}
8087 	assert(WIFEXITED(status));
8088 	return (WEXITSTATUS(status));
8089 }
8090 
8091 /*
8092  * This function updates the local /etc/user_attr file to
8093  * correspond to the admin settings that are currently being
8094  * committed. The updates are done via usermod and/or rolemod
8095  * depending on the type of the specified user. It is also
8096  * invoked to remove entries from user_attr corresponding to
8097  * removed admin assignments, using an empty auths string.
8098  *
8099  * Because the removed entries are no longer included in the
8100  * cofiguration that is being committed, a linked list of
8101  * removed admin entries is maintained to keep track of such
8102  * transactions. The head of the list is stored in the zone_dh_userauths
8103  * element of the handle strcture.
8104  */
8105 static int
8106 zonecfg_authorize_user_impl(zone_dochandle_t handle, char *user,
8107     char *auths, char *zonename)
8108 {
8109 	char *right;
8110 	char old_auths[MAXAUTHS];
8111 	char new_auths[MAXAUTHS];
8112 	char rbac_profs[MAXPROFS];
8113 	char *lasts;
8114 	userattr_t *u;
8115 	boolean_t first = B_TRUE;
8116 	boolean_t is_zone_admin = B_FALSE;
8117 	char user_cmd[] = "/usr/sbin/usermod";
8118 	char role_cmd[] = "/usr/sbin/rolemod";
8119 	char *auths_cmd = user_cmd;	/* either usermod or rolemod */
8120 	char *new_auth_start;		/* string containing the new auths */
8121 	int new_auth_cnt = 0;		/* delta of changed authorizations */
8122 
8123 	/*
8124 	 * First get the existing authorizations for this user
8125 	 */
8126 
8127 	bzero(&old_auths, sizeof (old_auths));
8128 	bzero(&new_auths, sizeof (new_auths));
8129 	bzero(&rbac_profs, sizeof (rbac_profs));
8130 	if ((u = getusernam(user)) != NULL) {
8131 		char *current_auths;
8132 		char *current_profs;
8133 		char *type;
8134 
8135 		type = kva_match(u->attr, USERATTR_TYPE_KW);
8136 		if (type != NULL) {
8137 			if (strcmp(type, USERATTR_TYPE_NONADMIN_KW) == 0)
8138 				auths_cmd = role_cmd;
8139 		}
8140 
8141 		current_auths = kva_match(u->attr, USERATTR_AUTHS_KW);
8142 		if (current_auths != NULL) {
8143 			char *cur_auth;
8144 			char *delete_name;
8145 			size_t offset;
8146 
8147 			offset = strlen(ZONE_AUTH_PREFIX);
8148 
8149 			(void) strlcpy(old_auths, current_auths, MAXAUTHS);
8150 			cur_auth = strtok_r(current_auths, ",", &lasts);
8151 
8152 			/*
8153 			 * Next, remove any existing authorizations
8154 			 * for this zone, and determine if the
8155 			 * user still needs the Zone Management Profile.
8156 			 */
8157 			if (is_renaming(handle))
8158 				delete_name = handle->zone_dh_delete_name;
8159 			else
8160 				delete_name = NULL;
8161 			while (cur_auth != NULL) {
8162 				if (!is_zone_auth(&cur_auth, zonename,
8163 				    delete_name)) {
8164 					if (first) {
8165 						first = B_FALSE;
8166 					} else {
8167 						(void) strlcat(new_auths, ",",
8168 						    MAXAUTHS);
8169 					}
8170 					(void) strlcat(new_auths, cur_auth,
8171 					    MAXAUTHS);
8172 					/*
8173 					 * If the user has authorizations
8174 					 * for other zones, then set a
8175 					 * flag indicate that the Zone
8176 					 * Management profile should be
8177 					 * preserved in user_attr.
8178 					 */
8179 					if (strncmp(cur_auth,
8180 					    ZONE_AUTH_PREFIX, offset) == 0)
8181 						is_zone_admin = B_TRUE;
8182 				} else {
8183 					new_auth_cnt++;
8184 				}
8185 				cur_auth = strtok_r(NULL, ",", &lasts);
8186 			}
8187 		}
8188 		current_profs = kva_match(u->attr, USERATTR_PROFILES_KW);
8189 		if (current_profs != NULL) {
8190 			(void) strlcpy(rbac_profs, current_profs, MAXPROFS);
8191 		}
8192 		free_userattr(u);
8193 	}
8194 	/*
8195 	 * The following is done to avoid revisiting the
8196 	 * user_attr entry for this user
8197 	 */
8198 	(void) zonecfg_remove_userauths(handle, user, "", B_FALSE);
8199 
8200 	/*
8201 	 * Convert each right into a properly formatted authorization
8202 	 */
8203 	new_auth_start = new_auths + strlen(new_auths);
8204 	if (!first)
8205 		new_auth_start++;
8206 	right = strtok_r(auths, ",", &lasts);
8207 	while (right != NULL) {
8208 		char auth[MAXAUTHS];
8209 
8210 		(void) snprintf(auth, MAXAUTHS, "%s%s/%s",
8211 		    ZONE_AUTH_PREFIX, right, zonename);
8212 		if (first) {
8213 			first = B_FALSE;
8214 		} else {
8215 			(void) strlcat(new_auths, ",", MAXAUTHS);
8216 		}
8217 		(void) strlcat(new_auths, auth, MAXAUTHS);
8218 		is_zone_admin = B_TRUE;
8219 		new_auth_cnt--;
8220 		right = strtok_r(NULL, ",", &lasts);
8221 	}
8222 
8223 	/*
8224 	 * Need to update the authorizations in user_attr unless
8225 	 * the number of old and new authorizations is unchanged
8226 	 * and the new auths are a substrings of the old auths.
8227 	 *
8228 	 * If the user's previous authorizations have changed
8229 	 * execute the usermod progam to update them in user_attr.
8230 	 */
8231 	if ((new_auth_cnt != 0) ||
8232 	    (strstr(old_auths, new_auth_start) == NULL)) {
8233 		char    *cmdbuf;
8234 		size_t  cmd_len;
8235 
8236 		update_profiles(rbac_profs, is_zone_admin);
8237 		cmd_len = snprintf(NULL, 0, "%s -A \"%s\" -P \"%s\" %s",
8238 		    auths_cmd, new_auths, rbac_profs, user) + 1;
8239 		if ((cmdbuf = malloc(cmd_len)) == NULL) {
8240 			return (Z_NOMEM);
8241 		}
8242 		(void) snprintf(cmdbuf, cmd_len, "%s -A \"%s\" -P \"%s\" %s",
8243 		    auths_cmd, new_auths, rbac_profs, user);
8244 		if (do_subproc(zonename, cmdbuf) != 0) {
8245 			free(cmdbuf);
8246 			return (Z_SYSTEM);
8247 		}
8248 		free(cmdbuf);
8249 	}
8250 
8251 	return (Z_OK);
8252 }
8253 
8254 int
8255 zonecfg_authorize_users(zone_dochandle_t handle, char *zonename)
8256 {
8257 	xmlNodePtr cur;
8258 	int err;
8259 	char user[MAXUSERNAME];
8260 	char auths[MAXAUTHS];
8261 
8262 	if ((err = operation_prep(handle)) != Z_OK)
8263 		return (err);
8264 
8265 	cur = handle->zone_dh_cur;
8266 	for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
8267 		if (xmlStrcmp(cur->name, DTD_ELEM_ADMIN))
8268 			continue;
8269 		if (fetchprop(cur, DTD_ATTR_USER, user,
8270 		    sizeof (user)) != Z_OK)
8271 			continue;
8272 		if (fetchprop(cur, DTD_ATTR_AUTHS, auths,
8273 		    sizeof (auths)) != Z_OK)
8274 			continue;
8275 		if (zonecfg_authorize_user_impl(handle, user, auths, zonename)
8276 		    != Z_OK)
8277 			return (Z_SYSTEM);
8278 	}
8279 	(void) zonecfg_remove_userauths(handle, "", "", B_TRUE);
8280 
8281 	return (Z_OK);
8282 }
8283 
8284 int
8285 zonecfg_deauthorize_user(zone_dochandle_t handle, char *user, char *zonename)
8286 {
8287 	return (zonecfg_authorize_user_impl(handle, user, "", zonename));
8288 }
8289 
8290 int
8291 zonecfg_deauthorize_users(zone_dochandle_t handle, char *zonename)
8292 {
8293 	xmlNodePtr cur;
8294 	int err;
8295 	char user[MAXUSERNAME];
8296 
8297 	if ((err = operation_prep(handle)) != Z_OK)
8298 		return (err);
8299 
8300 	cur = handle->zone_dh_cur;
8301 	for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
8302 		if (xmlStrcmp(cur->name, DTD_ELEM_ADMIN))
8303 			continue;
8304 		if (fetchprop(cur, DTD_ATTR_USER, user,
8305 		    sizeof (user)) != Z_OK)
8306 			continue;
8307 		if ((err = zonecfg_deauthorize_user(handle, user,
8308 		    zonename)) != Z_OK)
8309 			return (err);
8310 	}
8311 	return (Z_OK);
8312 }
8313 
8314 int
8315 zonecfg_insert_userauths(zone_dochandle_t handle, char *user, char *zonename)
8316 {
8317 	zone_userauths_t *new, **prev, *next;
8318 
8319 	prev = &handle->zone_dh_userauths;
8320 	next = *prev;
8321 	while (next) {
8322 		if ((strncmp(next->user, user, MAXUSERNAME) == 0) &&
8323 		    (strncmp(next->zonename, zonename,
8324 		    ZONENAME_MAX) == 0)) {
8325 			/*
8326 			 * user is already in list
8327 			 * which isn't supposed to happen!
8328 			 */
8329 			return (Z_OK);
8330 		}
8331 		prev = &next->next;
8332 		next = *prev;
8333 	}
8334 	new = (zone_userauths_t *)malloc(sizeof (zone_userauths_t));
8335 	if (new == NULL)
8336 		return (Z_NOMEM);
8337 
8338 	(void) strlcpy(new->user, user, sizeof (new->user));
8339 	(void) strlcpy(new->zonename, zonename, sizeof (new->zonename));
8340 	new->next = NULL;
8341 	*prev = new;
8342 	return (Z_OK);
8343 }
8344 
8345 int
8346 zonecfg_remove_userauths(zone_dochandle_t handle, char *user, char *zonename,
8347 	boolean_t deauthorize)
8348 {
8349 	zone_userauths_t *new, **prev, *next;
8350 
8351 	prev = &handle->zone_dh_userauths;
8352 	next = *prev;
8353 
8354 	while (next) {
8355 		if ((strlen(user) == 0 ||
8356 		    strncmp(next->user, user, MAXUSERNAME) == 0) &&
8357 		    (strlen(zonename) == 0 ||
8358 		    (strncmp(next->zonename, zonename, ZONENAME_MAX) == 0))) {
8359 			new = next;
8360 			*prev = next->next;
8361 			next =  *prev;
8362 			if (deauthorize)
8363 				(void) zonecfg_deauthorize_user(handle,
8364 				    new->user, new->zonename);
8365 			free(new);
8366 			continue;
8367 		}
8368 		prev = &next->next;
8369 		next = *prev;
8370 	}
8371 	return (Z_OK);
8372 }
8373