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