summaryrefslogtreecommitdiff
path: root/usr/src/lib/libzonecfg/common/libzonecfg.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/libzonecfg/common/libzonecfg.c')
-rw-r--r--usr/src/lib/libzonecfg/common/libzonecfg.c1552
1 files changed, 1547 insertions, 5 deletions
diff --git a/usr/src/lib/libzonecfg/common/libzonecfg.c b/usr/src/lib/libzonecfg/common/libzonecfg.c
index f4fbcde368..1a3fb37c8c 100644
--- a/usr/src/lib/libzonecfg/common/libzonecfg.c
+++ b/usr/src/lib/libzonecfg/common/libzonecfg.c
@@ -46,6 +46,10 @@
#include <sys/nvpair.h>
#include <sys/types.h>
#include <ftw.h>
+#include <pool.h>
+#include <libscf.h>
+#include <libproc.h>
+#include <sys/priocntl.h>
#include <arpa/inet.h>
#include <netdb.h>
@@ -79,6 +83,9 @@
#define DTD_ELEM_RCTLVALUE (const xmlChar *) "rctl-value"
#define DTD_ELEM_ZONE (const xmlChar *) "zone"
#define DTD_ELEM_DATASET (const xmlChar *) "dataset"
+#define DTD_ELEM_TMPPOOL (const xmlChar *) "tmp_pool"
+#define DTD_ELEM_PSET (const xmlChar *) "pset"
+#define DTD_ELEM_MCAP (const xmlChar *) "mcap"
#define DTD_ELEM_PACKAGE (const xmlChar *) "package"
#define DTD_ELEM_PATCH (const xmlChar *) "patch"
#define DTD_ELEM_OBSOLETES (const xmlChar *) "obsoletes"
@@ -92,6 +99,7 @@
#define DTD_ATTR_LIMIT (const xmlChar *) "limit"
#define DTD_ATTR_LIMITPRIV (const xmlChar *) "limitpriv"
#define DTD_ATTR_BOOTARGS (const xmlChar *) "bootargs"
+#define DTD_ATTR_SCHED (const xmlChar *) "scheduling-class"
#define DTD_ATTR_MATCH (const xmlChar *) "match"
#define DTD_ATTR_NAME (const xmlChar *) "name"
#define DTD_ATTR_PHYSICAL (const xmlChar *) "physical"
@@ -102,6 +110,10 @@
#define DTD_ATTR_TYPE (const xmlChar *) "type"
#define DTD_ATTR_VALUE (const xmlChar *) "value"
#define DTD_ATTR_ZONEPATH (const xmlChar *) "zonepath"
+#define DTD_ATTR_NCPU_MIN (const xmlChar *) "ncpu_min"
+#define DTD_ATTR_NCPU_MAX (const xmlChar *) "ncpu_max"
+#define DTD_ATTR_IMPORTANCE (const xmlChar *) "importance"
+#define DTD_ATTR_PHYSCAP (const xmlChar *) "physcap"
#define DTD_ATTR_VERSION (const xmlChar *) "version"
#define DTD_ATTR_ID (const xmlChar *) "id"
#define DTD_ATTR_UID (const xmlChar *) "uid"
@@ -133,6 +145,46 @@
#define PATCHINFO "PATCH_INFO_"
#define PKGINFO_RD_LEN 128
+#define TMP_POOL_NAME "SUNWtmp_%s"
+#define MAX_TMP_POOL_NAME (ZONENAME_MAX + 9)
+#define RCAP_SERVICE "system/rcap:default"
+#define POOLD_SERVICE "system/pools/dynamic:default"
+
+/*
+ * rctl alias definitions
+ *
+ * This holds the alias, the full rctl name, the default priv value, action
+ * and lower limit. The functions that handle rctl aliases step through
+ * this table, matching on the alias, and using the full values for setting
+ * the rctl entry as well the limit for validation.
+ */
+static struct alias {
+ char *shortname;
+ char *realname;
+ char *priv;
+ char *action;
+ uint64_t low_limit;
+} aliases[] = {
+ {ALIAS_MAXLWPS, "zone.max-lwps", "privileged", "deny", 100},
+ {ALIAS_MAXSHMMEM, "zone.max-shm-memory", "privileged", "deny", 0},
+ {ALIAS_MAXSHMIDS, "zone.max-shm-ids", "privileged", "deny", 0},
+ {ALIAS_MAXMSGIDS, "zone.max-msg-ids", "privileged", "deny", 0},
+ {ALIAS_MAXSEMIDS, "zone.max-sem-ids", "privileged", "deny", 0},
+ {ALIAS_MAXLOCKEDMEM, "zone.max-locked-memory", "privileged", "deny", 0},
+ {ALIAS_MAXSWAP, "zone.max-swap", "privileged", "deny", 0},
+ {ALIAS_SHARES, "zone.cpu-shares", "privileged", "none", 0},
+ {NULL, NULL, NULL, NULL, 0}
+};
+
+/*
+ * Structure for applying rctls to a running zone. It allows important
+ * process values to be passed together easily.
+ */
+typedef struct pr_info_handle {
+ struct ps_prochandle *pr;
+ pid_t pid;
+} pr_info_handle_t;
+
struct zone_dochandle {
char *zone_dh_rootdir;
xmlDocPtr zone_dh_doc;
@@ -446,14 +498,20 @@ setrootattr(zone_dochandle_t handle, const xmlChar *propname,
int err;
xmlNodePtr root;
- if (propval == NULL)
- return (Z_INVAL);
-
if ((err = getroot(handle, &root)) != Z_OK)
return (err);
- if (xmlSetProp(root, propname, (const xmlChar *) propval) == NULL)
- return (Z_INVAL);
+ /*
+ * If we get a null propval remove the property (ignore return since it
+ * may not be set to begin with).
+ */
+ if (propval == NULL) {
+ (void) xmlUnsetProp(root, propname);
+ } else {
+ if (xmlSetProp(root, propname, (const xmlChar *) propval)
+ == NULL)
+ return (Z_INVAL);
+ }
return (Z_OK);
}
@@ -947,6 +1005,18 @@ zonecfg_set_bootargs(zone_dochandle_t handle, char *bargs)
return (setrootattr(handle, DTD_ATTR_BOOTARGS, bargs));
}
+int
+zonecfg_get_sched_class(zone_dochandle_t handle, char *sched, size_t schedsize)
+{
+ return (getrootattr(handle, DTD_ATTR_SCHED, sched, schedsize));
+}
+
+int
+zonecfg_set_sched(zone_dochandle_t handle, char *sched)
+{
+ return (setrootattr(handle, DTD_ATTR_SCHED, sched));
+}
+
/*
* /etc/zones/index caches a vital piece of information which is also
* in the <zonename>.xml file: the path to the zone. This is for performance,
@@ -3047,6 +3117,30 @@ zonecfg_strerror(int errnum)
case Z_BRAND_ERROR:
return (dgettext(TEXT_DOMAIN,
"Brand-specific error"));
+ case Z_INCOMPATIBLE:
+ return (dgettext(TEXT_DOMAIN, "Incompatible settings"));
+ case Z_ALIAS_DISALLOW:
+ return (dgettext(TEXT_DOMAIN,
+ "An incompatible rctl already exists for this property"));
+ case Z_CLEAR_DISALLOW:
+ return (dgettext(TEXT_DOMAIN,
+ "Clearing this property is not allowed"));
+ case Z_POOL:
+ return (dgettext(TEXT_DOMAIN, "libpool(3LIB) error"));
+ case Z_POOLS_NOT_ACTIVE:
+ return (dgettext(TEXT_DOMAIN, "Pools facility not active; "
+ "zone will not be bound to pool"));
+ case Z_POOL_ENABLE:
+ return (dgettext(TEXT_DOMAIN,
+ "Could not enable pools facility"));
+ case Z_NO_POOL:
+ return (dgettext(TEXT_DOMAIN,
+ "Pool not found; using default pool"));
+ case Z_POOL_CREATE:
+ return (dgettext(TEXT_DOMAIN,
+ "Could not create a temporary pool"));
+ case Z_POOL_BIND:
+ return (dgettext(TEXT_DOMAIN, "Could not bind zone to pool"));
default:
return (dgettext(TEXT_DOMAIN, "Unknown error"));
}
@@ -3086,6 +3180,951 @@ zonecfg_endent(zone_dochandle_t handle)
return (Z_OK);
}
+/*
+ * Do the work required to manipulate a process through libproc.
+ * If grab_process() returns no errors (0), then release_process()
+ * must eventually be called.
+ *
+ * Return values:
+ * 0 Successful creation of agent thread
+ * 1 Error grabbing
+ * 2 Error creating agent
+ */
+static int
+grab_process(pr_info_handle_t *p)
+{
+ int ret;
+
+ if ((p->pr = Pgrab(p->pid, 0, &ret)) != NULL) {
+
+ if (Psetflags(p->pr, PR_RLC) != 0) {
+ Prelease(p->pr, 0);
+ return (1);
+ }
+ if (Pcreate_agent(p->pr) == 0) {
+ return (0);
+
+ } else {
+ Prelease(p->pr, 0);
+ return (2);
+ }
+ } else {
+ return (1);
+ }
+}
+
+/*
+ * Release the specified process. This destroys the agent
+ * and releases the process. If the process is NULL, nothing
+ * is done. This function should only be called if grab_process()
+ * has previously been called and returned success.
+ *
+ * This function is Pgrab-safe.
+ */
+static void
+release_process(struct ps_prochandle *Pr)
+{
+ if (Pr == NULL)
+ return;
+
+ Pdestroy_agent(Pr);
+ Prelease(Pr, 0);
+}
+
+static boolean_t
+grab_zone_proc(char *zonename, pr_info_handle_t *p)
+{
+ DIR *dirp;
+ struct dirent *dentp;
+ zoneid_t zoneid;
+ int pid_self;
+ psinfo_t psinfo;
+
+ if (zone_get_id(zonename, &zoneid) != 0)
+ return (B_FALSE);
+
+ pid_self = getpid();
+
+ if ((dirp = opendir("/proc")) == NULL)
+ return (B_FALSE);
+
+ while (dentp = readdir(dirp)) {
+ p->pid = atoi(dentp->d_name);
+
+ /* Skip self */
+ if (p->pid == pid_self)
+ continue;
+
+ if (proc_get_psinfo(p->pid, &psinfo) != 0)
+ continue;
+
+ if (psinfo.pr_zoneid != zoneid)
+ continue;
+
+ /* attempt to grab process */
+ if (grab_process(p) != 0)
+ continue;
+
+ if (pr_getzoneid(p->pr) != zoneid) {
+ release_process(p->pr);
+ continue;
+ }
+
+ (void) closedir(dirp);
+ return (B_TRUE);
+ }
+
+ (void) closedir(dirp);
+ return (B_FALSE);
+}
+
+static boolean_t
+get_priv_rctl(struct ps_prochandle *pr, char *name, rctlblk_t *rblk)
+{
+ if (pr_getrctl(pr, name, NULL, rblk, RCTL_FIRST))
+ return (B_FALSE);
+
+ if (rctlblk_get_privilege(rblk) == RCPRIV_PRIVILEGED)
+ return (B_TRUE);
+
+ while (pr_getrctl(pr, name, rblk, rblk, RCTL_NEXT) == 0) {
+ if (rctlblk_get_privilege(rblk) == RCPRIV_PRIVILEGED)
+ return (B_TRUE);
+ }
+
+ return (B_FALSE);
+}
+
+/*
+ * Apply the current rctl settings to the specified, running zone.
+ */
+int
+zonecfg_apply_rctls(char *zone_name, zone_dochandle_t handle)
+{
+ int err;
+ int res = Z_OK;
+ rctlblk_t *rblk;
+ pr_info_handle_t p;
+ struct zone_rctltab rctl;
+
+ if ((err = zonecfg_setrctlent(handle)) != Z_OK)
+ return (err);
+
+ if ((rblk = (rctlblk_t *)malloc(rctlblk_size())) == NULL) {
+ (void) zonecfg_endrctlent(handle);
+ return (Z_NOMEM);
+ }
+
+ if (!grab_zone_proc(zone_name, &p)) {
+ (void) zonecfg_endrctlent(handle);
+ free(rblk);
+ return (Z_SYSTEM);
+ }
+
+ while (zonecfg_getrctlent(handle, &rctl) == Z_OK) {
+ char *rname;
+ struct zone_rctlvaltab *valptr;
+
+ rname = rctl.zone_rctl_name;
+
+ /* first delete all current privileged settings for this rctl */
+ while (get_priv_rctl(p.pr, rname, rblk)) {
+ if (pr_setrctl(p.pr, rname, NULL, rblk, RCTL_DELETE) !=
+ 0) {
+ res = Z_SYSTEM;
+ goto done;
+ }
+ }
+
+ /* now set each new value for the rctl */
+ for (valptr = rctl.zone_rctl_valptr; valptr != NULL;
+ valptr = valptr->zone_rctlval_next) {
+ if ((err = zonecfg_construct_rctlblk(valptr, rblk))
+ != Z_OK) {
+ res = errno = err;
+ goto done;
+ }
+
+ if (pr_setrctl(p.pr, rname, NULL, rblk, RCTL_INSERT)) {
+ res = Z_SYSTEM;
+ goto done;
+ }
+ }
+ }
+
+done:
+ release_process(p.pr);
+ free(rblk);
+ (void) zonecfg_endrctlent(handle);
+
+ return (res);
+}
+
+static const xmlChar *
+nm_to_dtd(char *nm)
+{
+ if (strcmp(nm, "device") == 0)
+ return (DTD_ELEM_DEVICE);
+ if (strcmp(nm, "fs") == 0)
+ return (DTD_ELEM_FS);
+ if (strcmp(nm, "inherit-pkg-dir") == 0)
+ return (DTD_ELEM_IPD);
+ if (strcmp(nm, "net") == 0)
+ return (DTD_ELEM_NET);
+ if (strcmp(nm, "attr") == 0)
+ return (DTD_ELEM_ATTR);
+ if (strcmp(nm, "rctl") == 0)
+ return (DTD_ELEM_RCTL);
+ if (strcmp(nm, "dataset") == 0)
+ return (DTD_ELEM_DATASET);
+
+ return (NULL);
+}
+
+int
+zonecfg_num_resources(zone_dochandle_t handle, char *rsrc)
+{
+ int num = 0;
+ const xmlChar *dtd;
+ xmlNodePtr cur;
+
+ if ((dtd = nm_to_dtd(rsrc)) == NULL)
+ return (num);
+
+ if (zonecfg_setent(handle) != Z_OK)
+ return (num);
+
+ for (cur = handle->zone_dh_cur; cur != NULL; cur = cur->next)
+ if (xmlStrcmp(cur->name, dtd) == 0)
+ num++;
+
+ (void) zonecfg_endent(handle);
+
+ return (num);
+}
+
+int
+zonecfg_del_all_resources(zone_dochandle_t handle, char *rsrc)
+{
+ int err;
+ const xmlChar *dtd;
+ xmlNodePtr cur;
+
+ if ((dtd = nm_to_dtd(rsrc)) == NULL)
+ return (Z_NO_RESOURCE_TYPE);
+
+ if ((err = zonecfg_setent(handle)) != Z_OK)
+ return (err);
+
+ cur = handle->zone_dh_cur;
+ while (cur != NULL) {
+ xmlNodePtr tmp;
+
+ if (xmlStrcmp(cur->name, dtd)) {
+ cur = cur->next;
+ continue;
+ }
+
+ tmp = cur->next;
+ xmlUnlinkNode(cur);
+ xmlFreeNode(cur);
+ cur = tmp;
+ }
+
+ (void) zonecfg_endent(handle);
+ return (Z_OK);
+}
+
+static boolean_t
+valid_uint(char *s, uint64_t *n)
+{
+ char *endp;
+
+ /* strtoull accepts '-'?! so we want to flag that as an error */
+ if (strchr(s, '-') != NULL)
+ return (B_FALSE);
+
+ errno = 0;
+ *n = strtoull(s, &endp, 10);
+
+ if (errno != 0 || *endp != '\0')
+ return (B_FALSE);
+ return (B_TRUE);
+}
+
+/*
+ * Convert a string representing a number (possibly a fraction) into an integer.
+ * The string can have a modifier (K, M, G or T). The modifiers are treated
+ * as powers of two (not 10).
+ */
+int
+zonecfg_str_to_bytes(char *str, uint64_t *bytes)
+{
+ long double val;
+ char *unitp;
+ uint64_t scale;
+
+ if ((val = strtold(str, &unitp)) < 0)
+ return (-1);
+
+ /* remove any leading white space from units string */
+ while (isspace(*unitp) != 0)
+ ++unitp;
+
+ /* if no units explicitly set, error */
+ if (unitp == NULL || *unitp == '\0') {
+ scale = 1;
+ } else {
+ int i;
+ char *units[] = {"K", "M", "G", "T", NULL};
+
+ scale = 1024;
+
+ /* update scale based on units */
+ for (i = 0; units[i] != NULL; i++) {
+ if (strcasecmp(unitp, units[i]) == 0)
+ break;
+ scale <<= 10;
+ }
+
+ if (units[i] == NULL)
+ return (-1);
+ }
+
+ *bytes = (uint64_t)(val * scale);
+ return (0);
+}
+
+boolean_t
+zonecfg_valid_ncpus(char *lowstr, char *highstr)
+{
+ uint64_t low, high;
+
+ if (!valid_uint(lowstr, &low) || !valid_uint(highstr, &high) ||
+ low < 1 || low > high)
+ return (B_FALSE);
+
+ return (B_TRUE);
+}
+
+boolean_t
+zonecfg_valid_importance(char *impstr)
+{
+ uint64_t num;
+
+ if (!valid_uint(impstr, &num))
+ return (B_FALSE);
+
+ return (B_TRUE);
+}
+
+boolean_t
+zonecfg_valid_alias_limit(char *name, char *limitstr, uint64_t *limit)
+{
+ int i;
+
+ for (i = 0; aliases[i].shortname != NULL; i++)
+ if (strcmp(name, aliases[i].shortname) == 0)
+ break;
+
+ if (aliases[i].shortname == NULL)
+ return (B_FALSE);
+
+ if (!valid_uint(limitstr, limit) || *limit < aliases[i].low_limit)
+ return (B_FALSE);
+
+ return (B_TRUE);
+}
+
+boolean_t
+zonecfg_valid_memlimit(char *memstr, uint64_t *mem_val)
+{
+ if (zonecfg_str_to_bytes(memstr, mem_val) != 0)
+ return (B_FALSE);
+
+ return (B_TRUE);
+}
+
+static int
+zerr_pool(char *pool_err, int err_size, int res)
+{
+ (void) strlcpy(pool_err, pool_strerror(pool_error()), err_size);
+ return (res);
+}
+
+static int
+create_tmp_pset(char *pool_err, int err_size, pool_conf_t *pconf, pool_t *pool,
+ char *name, int min, int max)
+{
+ pool_resource_t *res;
+ pool_elem_t *elem;
+ pool_value_t *val;
+
+ if ((res = pool_resource_create(pconf, "pset", name)) == NULL)
+ return (zerr_pool(pool_err, err_size, Z_POOL));
+
+ if (pool_associate(pconf, pool, res) != PO_SUCCESS)
+ return (zerr_pool(pool_err, err_size, Z_POOL));
+
+ if ((elem = pool_resource_to_elem(pconf, res)) == NULL)
+ return (zerr_pool(pool_err, err_size, Z_POOL));
+
+ if ((val = pool_value_alloc()) == NULL)
+ return (zerr_pool(pool_err, err_size, Z_POOL));
+
+ /* set the maximum number of cpus for the pset */
+ pool_value_set_uint64(val, (uint64_t)max);
+
+ if (pool_put_property(pconf, elem, "pset.max", val) != PO_SUCCESS) {
+ pool_value_free(val);
+ return (zerr_pool(pool_err, err_size, Z_POOL));
+ }
+
+ /* set the minimum number of cpus for the pset */
+ pool_value_set_uint64(val, (uint64_t)min);
+
+ if (pool_put_property(pconf, elem, "pset.min", val) != PO_SUCCESS) {
+ pool_value_free(val);
+ return (zerr_pool(pool_err, err_size, Z_POOL));
+ }
+
+ pool_value_free(val);
+
+ return (Z_OK);
+}
+
+static int
+create_tmp_pool(char *pool_err, int err_size, pool_conf_t *pconf, char *name,
+ struct zone_psettab *pset_tab)
+{
+ pool_t *pool;
+ int res = Z_OK;
+
+ /* create a temporary pool configuration */
+ if (pool_conf_open(pconf, NULL, PO_TEMP) != PO_SUCCESS) {
+ res = zerr_pool(pool_err, err_size, Z_POOL);
+ return (res);
+ }
+
+ if ((pool = pool_create(pconf, name)) == NULL) {
+ res = zerr_pool(pool_err, err_size, Z_POOL_CREATE);
+ goto done;
+ }
+
+ /* set pool importance */
+ if (pset_tab->zone_importance[0] != '\0') {
+ pool_elem_t *elem;
+ pool_value_t *val;
+
+ if ((elem = pool_to_elem(pconf, pool)) == NULL) {
+ res = zerr_pool(pool_err, err_size, Z_POOL);
+ goto done;
+ }
+
+ if ((val = pool_value_alloc()) == NULL) {
+ res = zerr_pool(pool_err, err_size, Z_POOL);
+ goto done;
+ }
+
+ pool_value_set_int64(val,
+ (int64_t)atoi(pset_tab->zone_importance));
+
+ if (pool_put_property(pconf, elem, "pool.importance", val)
+ != PO_SUCCESS) {
+ res = zerr_pool(pool_err, err_size, Z_POOL);
+ pool_value_free(val);
+ goto done;
+ }
+
+ pool_value_free(val);
+ }
+
+ if ((res = create_tmp_pset(pool_err, err_size, pconf, pool, name,
+ atoi(pset_tab->zone_ncpu_min),
+ atoi(pset_tab->zone_ncpu_max))) != Z_OK)
+ goto done;
+
+ /* validation */
+ if (pool_conf_status(pconf) == POF_INVALID) {
+ res = zerr_pool(pool_err, err_size, Z_POOL);
+ goto done;
+ }
+
+ /*
+ * This validation is the one we expect to fail if the user specified
+ * an invalid configuration (too many cpus) for this system.
+ */
+ if (pool_conf_validate(pconf, POV_RUNTIME) != PO_SUCCESS) {
+ res = zerr_pool(pool_err, err_size, Z_POOL_CREATE);
+ goto done;
+ }
+
+ /*
+ * Commit the dynamic configuration but not the pool configuration
+ * file.
+ */
+ if (pool_conf_commit(pconf, 1) != PO_SUCCESS)
+ res = zerr_pool(pool_err, err_size, Z_POOL);
+
+done:
+ (void) pool_conf_close(pconf);
+ return (res);
+}
+
+static int
+get_running_tmp_pset(pool_conf_t *pconf, pool_t *pool, pool_resource_t *pset,
+ struct zone_psettab *pset_tab)
+{
+ int nfound = 0;
+ pool_elem_t *pe;
+ pool_value_t *pv = pool_value_alloc();
+ uint64_t val_uint;
+
+ if (pool != NULL) {
+ pe = pool_to_elem(pconf, pool);
+ if (pool_get_property(pconf, pe, "pool.importance", pv)
+ != POC_INVAL) {
+ int64_t val_int;
+
+ (void) pool_value_get_int64(pv, &val_int);
+ (void) snprintf(pset_tab->zone_importance,
+ sizeof (pset_tab->zone_importance), "%d", val_int);
+ nfound++;
+ }
+ }
+
+ if (pset != NULL) {
+ pe = pool_resource_to_elem(pconf, pset);
+ if (pool_get_property(pconf, pe, "pset.min", pv) != POC_INVAL) {
+ (void) pool_value_get_uint64(pv, &val_uint);
+ (void) snprintf(pset_tab->zone_ncpu_min,
+ sizeof (pset_tab->zone_ncpu_min), "%u", val_uint);
+ nfound++;
+ }
+
+ if (pool_get_property(pconf, pe, "pset.max", pv) != POC_INVAL) {
+ (void) pool_value_get_uint64(pv, &val_uint);
+ (void) snprintf(pset_tab->zone_ncpu_max,
+ sizeof (pset_tab->zone_ncpu_max), "%u", val_uint);
+ nfound++;
+ }
+ }
+
+ pool_value_free(pv);
+
+ if (nfound == 3)
+ return (PO_SUCCESS);
+
+ return (PO_FAIL);
+}
+
+/*
+ * Determine if a tmp pool is configured and if so, if the configuration is
+ * still valid or if it has been changed since the tmp pool was created.
+ * If the tmp pool configuration is no longer valid, delete the tmp pool.
+ *
+ * Set *valid=B_TRUE if there is an existing, valid tmp pool configuration.
+ */
+static int
+verify_del_tmp_pool(pool_conf_t *pconf, char *tmp_name, char *pool_err,
+ int err_size, struct zone_psettab *pset_tab, boolean_t *exists)
+{
+ int res = Z_OK;
+ pool_t *pool;
+ pool_resource_t *pset;
+ struct zone_psettab pset_current;
+
+ *exists = B_FALSE;
+
+ if (pool_conf_open(pconf, pool_dynamic_location(), PO_RDWR)
+ != PO_SUCCESS) {
+ res = zerr_pool(pool_err, err_size, Z_POOL);
+ return (res);
+ }
+
+ pool = pool_get_pool(pconf, tmp_name);
+ pset = pool_get_resource(pconf, "pset", tmp_name);
+
+ if (pool == NULL && pset == NULL) {
+ /* no tmp pool configured */
+ goto done;
+ }
+
+ /*
+ * If an existing tmp pool for this zone is configured with the proper
+ * settings, then the tmp pool is valid.
+ */
+ if (get_running_tmp_pset(pconf, pool, pset, &pset_current)
+ == PO_SUCCESS &&
+ strcmp(pset_tab->zone_ncpu_min,
+ pset_current.zone_ncpu_min) == 0 &&
+ strcmp(pset_tab->zone_ncpu_max,
+ pset_current.zone_ncpu_max) == 0 &&
+ strcmp(pset_tab->zone_importance,
+ pset_current.zone_importance) == 0) {
+ *exists = B_TRUE;
+
+ } else {
+ /*
+ * An out-of-date tmp pool configuration exists. Delete it
+ * so that we can create the correct tmp pool config.
+ */
+ if (pset != NULL &&
+ pool_resource_destroy(pconf, pset) != PO_SUCCESS) {
+ res = zerr_pool(pool_err, err_size, Z_POOL);
+ goto done;
+ }
+
+ if (pool != NULL &&
+ pool_destroy(pconf, pool) != PO_SUCCESS) {
+ res = zerr_pool(pool_err, err_size, Z_POOL);
+ goto done;
+ }
+
+ /* commit dynamic config */
+ if (pool_conf_commit(pconf, 0) != PO_SUCCESS)
+ res = zerr_pool(pool_err, err_size, Z_POOL);
+ }
+
+done:
+ (void) pool_conf_close(pconf);
+
+ return (res);
+}
+
+/*
+ * Destroy any existing tmp pool.
+ */
+int
+zonecfg_destroy_tmp_pool(char *zone_name, char *pool_err, int err_size)
+{
+ int status;
+ int res = Z_OK;
+ pool_conf_t *pconf;
+ pool_t *pool;
+ pool_resource_t *pset;
+ char tmp_name[MAX_TMP_POOL_NAME];
+
+ /* if pools not enabled then nothing to do */
+ if (pool_get_status(&status) != PO_SUCCESS || status != POOL_ENABLED)
+ return (Z_OK);
+
+ if ((pconf = pool_conf_alloc()) == NULL)
+ return (zerr_pool(pool_err, err_size, Z_POOL));
+
+ (void) snprintf(tmp_name, sizeof (tmp_name), TMP_POOL_NAME, zone_name);
+
+ if (pool_conf_open(pconf, pool_dynamic_location(), PO_RDWR)
+ != PO_SUCCESS) {
+ res = zerr_pool(pool_err, err_size, Z_POOL);
+ pool_conf_free(pconf);
+ return (res);
+ }
+
+ pool = pool_get_pool(pconf, tmp_name);
+ pset = pool_get_resource(pconf, "pset", tmp_name);
+
+ if (pool == NULL && pset == NULL) {
+ /* nothing to destroy, we're done */
+ goto done;
+ }
+
+ if (pset != NULL && pool_resource_destroy(pconf, pset) != PO_SUCCESS) {
+ res = zerr_pool(pool_err, err_size, Z_POOL);
+ goto done;
+ }
+
+ if (pool != NULL && pool_destroy(pconf, pool) != PO_SUCCESS) {
+ res = zerr_pool(pool_err, err_size, Z_POOL);
+ goto done;
+ }
+
+ /* commit dynamic config */
+ if (pool_conf_commit(pconf, 0) != PO_SUCCESS)
+ res = zerr_pool(pool_err, err_size, Z_POOL);
+
+done:
+ (void) pool_conf_close(pconf);
+ pool_conf_free(pconf);
+
+ return (res);
+}
+
+/*
+ * Attempt to bind to a tmp pool for this zone. If there is no tmp pool
+ * configured, we just return Z_OK.
+ *
+ * We either attempt to create the tmp pool for this zone or rebind to an
+ * existing tmp pool for this zone.
+ *
+ * Rebinding is used when a zone with a tmp pool reboots so that we don't have
+ * to recreate the tmp pool. To do this we need to be sure we work correctly
+ * for the following cases:
+ *
+ * - there is an existing, properly configured tmp pool.
+ * - zonecfg added tmp pool after zone was booted, must now create.
+ * - zonecfg updated tmp pool config after zone was booted, in this case
+ * we destroy the old tmp pool and create a new one.
+ */
+int
+zonecfg_bind_tmp_pool(zone_dochandle_t handle, zoneid_t zoneid, char *pool_err,
+ int err_size)
+{
+ struct zone_psettab pset_tab;
+ int err;
+ int status;
+ pool_conf_t *pconf;
+ boolean_t exists;
+ char zone_name[ZONENAME_MAX];
+ char tmp_name[MAX_TMP_POOL_NAME];
+
+ (void) getzonenamebyid(zoneid, zone_name, sizeof (zone_name));
+
+ err = zonecfg_lookup_pset(handle, &pset_tab);
+
+ /* if no temporary pool configured, we're done */
+ if (err == Z_NO_ENTRY)
+ return (Z_OK);
+
+ /*
+ * importance might not have a value but we need to validate it here,
+ * so set the default.
+ */
+ if (pset_tab.zone_importance[0] == '\0')
+ (void) strlcpy(pset_tab.zone_importance, "1",
+ sizeof (pset_tab.zone_importance));
+
+ /* if pools not enabled, enable them now */
+ if (pool_get_status(&status) != PO_SUCCESS || status != POOL_ENABLED) {
+ if (pool_set_status(POOL_ENABLED) != PO_SUCCESS)
+ return (Z_POOL_ENABLE);
+ }
+
+ if ((pconf = pool_conf_alloc()) == NULL)
+ return (zerr_pool(pool_err, err_size, Z_POOL));
+
+ (void) snprintf(tmp_name, sizeof (tmp_name), TMP_POOL_NAME, zone_name);
+
+ /*
+ * Check if a valid tmp pool/pset already exists. If so, we just
+ * reuse it.
+ */
+ if ((err = verify_del_tmp_pool(pconf, tmp_name, pool_err, err_size,
+ &pset_tab, &exists)) != Z_OK) {
+ pool_conf_free(pconf);
+ return (err);
+ }
+
+ if (!exists)
+ err = create_tmp_pool(pool_err, err_size, pconf, tmp_name,
+ &pset_tab);
+
+ pool_conf_free(pconf);
+
+ if (err != Z_OK)
+ return (err);
+
+ /* Bind the zone to the pool. */
+ if (pool_set_binding(tmp_name, P_ZONEID, zoneid) != PO_SUCCESS)
+ return (zerr_pool(pool_err, err_size, Z_POOL_BIND));
+
+ return (Z_OK);
+}
+
+/*
+ * Attempt to bind to a permanent pool for this zone. If there is no
+ * permanent pool configured, we just return Z_OK.
+ */
+int
+zonecfg_bind_pool(zone_dochandle_t handle, zoneid_t zoneid, char *pool_err,
+ int err_size)
+{
+ pool_conf_t *poolconf;
+ pool_t *pool;
+ char poolname[MAXPATHLEN];
+ int status;
+ int error;
+
+ /*
+ * Find the pool mentioned in the zone configuration, and bind to it.
+ */
+ error = zonecfg_get_pool(handle, poolname, sizeof (poolname));
+ if (error == Z_NO_ENTRY || (error == Z_OK && strlen(poolname) == 0)) {
+ /*
+ * The property is not set on the zone, so the pool
+ * should be bound to the default pool. But that's
+ * already done by the kernel, so we can just return.
+ */
+ return (Z_OK);
+ }
+ if (error != Z_OK) {
+ /*
+ * Not an error, even though it shouldn't be happening.
+ */
+ return (Z_OK);
+ }
+ /*
+ * Don't do anything if pools aren't enabled.
+ */
+ if (pool_get_status(&status) != PO_SUCCESS || status != POOL_ENABLED)
+ return (Z_POOLS_NOT_ACTIVE);
+
+ /*
+ * Try to provide a sane error message if the requested pool doesn't
+ * exist.
+ */
+ if ((poolconf = pool_conf_alloc()) == NULL)
+ return (zerr_pool(pool_err, err_size, Z_POOL));
+
+ if (pool_conf_open(poolconf, pool_dynamic_location(), PO_RDONLY) !=
+ PO_SUCCESS) {
+ pool_conf_free(poolconf);
+ return (zerr_pool(pool_err, err_size, Z_POOL));
+ }
+ pool = pool_get_pool(poolconf, poolname);
+ (void) pool_conf_close(poolconf);
+ pool_conf_free(poolconf);
+ if (pool == NULL)
+ return (Z_NO_POOL);
+
+ /*
+ * Bind the zone to the pool.
+ */
+ if (pool_set_binding(poolname, P_ZONEID, zoneid) != PO_SUCCESS) {
+ /* if bind fails, return poolname for the error msg */
+ (void) strlcpy(pool_err, poolname, err_size);
+ return (Z_POOL_BIND);
+ }
+
+ return (Z_OK);
+}
+
+
+static boolean_t
+svc_enabled(char *svc_name)
+{
+ scf_simple_prop_t *prop;
+ boolean_t found = B_FALSE;
+
+ prop = scf_simple_prop_get(NULL, svc_name, SCF_PG_GENERAL,
+ SCF_PROPERTY_ENABLED);
+
+ if (scf_simple_prop_numvalues(prop) == 1 &&
+ *scf_simple_prop_next_boolean(prop) != 0)
+ found = B_TRUE;
+
+ scf_simple_prop_free(prop);
+
+ return (found);
+}
+
+/*
+ * If the zone has capped-memory, make sure the rcap service is enabled.
+ */
+int
+zonecfg_enable_rcapd(char *err, int size)
+{
+ if (!svc_enabled(RCAP_SERVICE) &&
+ smf_enable_instance(RCAP_SERVICE, 0) == -1) {
+ (void) strlcpy(err, scf_strerror(scf_error()), size);
+ return (Z_SYSTEM);
+ }
+
+ return (Z_OK);
+}
+
+/*
+ * Return true if pset has cpu range specified and poold is not enabled.
+ */
+boolean_t
+zonecfg_warn_poold(zone_dochandle_t handle)
+{
+ struct zone_psettab pset_tab;
+ int min, max;
+ int err;
+
+ err = zonecfg_lookup_pset(handle, &pset_tab);
+
+ /* if no temporary pool configured, we're done */
+ if (err == Z_NO_ENTRY)
+ return (B_FALSE);
+
+ min = atoi(pset_tab.zone_ncpu_min);
+ max = atoi(pset_tab.zone_ncpu_max);
+
+ /* range not specified, no need for poold */
+ if (min == max)
+ return (B_FALSE);
+
+ /* we have a range, check if poold service is enabled */
+ if (svc_enabled(POOLD_SERVICE))
+ return (B_FALSE);
+
+ return (B_TRUE);
+}
+
+static int
+get_pool_sched_class(char *poolname, char *class, int clsize)
+{
+ int status;
+ pool_conf_t *poolconf;
+ pool_t *pool;
+ pool_elem_t *pe;
+ pool_value_t *pv = pool_value_alloc();
+ const char *sched_str;
+
+ if (pool_get_status(&status) != PO_SUCCESS || status != POOL_ENABLED)
+ return (Z_NO_POOL);
+
+ if ((poolconf = pool_conf_alloc()) == NULL)
+ return (Z_NO_POOL);
+
+ if (pool_conf_open(poolconf, pool_dynamic_location(), PO_RDONLY) !=
+ PO_SUCCESS) {
+ pool_conf_free(poolconf);
+ return (Z_NO_POOL);
+ }
+
+ if ((pool = pool_get_pool(poolconf, poolname)) == NULL) {
+ (void) pool_conf_close(poolconf);
+ pool_conf_free(poolconf);
+ return (Z_NO_POOL);
+ }
+
+ pe = pool_to_elem(poolconf, pool);
+ if (pool_get_property(poolconf, pe, "pool.scheduler", pv)
+ != POC_INVAL) {
+ (void) pool_value_get_string(pv, &sched_str);
+ if (strlcpy(class, sched_str, clsize) >= clsize)
+ return (Z_TOO_BIG);
+ }
+
+ (void) pool_conf_close(poolconf);
+ pool_conf_free(poolconf);
+ return (Z_OK);
+}
+
+/*
+ * Get the default scheduling class for the zone. This will either be the
+ * class set on the zone's pool or the system default scheduling class.
+ */
+int
+zonecfg_get_dflt_sched_class(zone_dochandle_t handle, char *class, int clsize)
+{
+ char poolname[MAXPATHLEN];
+
+ if (zonecfg_get_pool(handle, poolname, sizeof (poolname)) == Z_OK) {
+ /* check if the zone's pool specified a sched class */
+ if (get_pool_sched_class(poolname, class, clsize) == Z_OK)
+ return (Z_OK);
+ }
+
+ if (priocntl(0, 0, PC_GETDFLCL, class, (uint64_t)clsize) == -1)
+ return (Z_TOO_BIG);
+
+ return (Z_OK);
+}
+
int
zonecfg_setfsent(zone_dochandle_t handle)
{
@@ -4825,6 +5864,509 @@ zonecfg_enddsent(zone_dochandle_t handle)
return (zonecfg_endent(handle));
}
+/*
+ * Support for aliased rctls; that is, rctls that have simplified names in
+ * zonecfg. For example, max-lwps is an alias for a well defined zone.max-lwps
+ * rctl. If there are multiple existing values for one of these rctls or if
+ * there is a single value that does not match the well defined template (i.e.
+ * it has a different action) then we cannot treat the rctl as having an alias
+ * so we return Z_ALIAS_DISALLOW. That means that the rctl cannot be
+ * managed in zonecfg via an alias and that the standard rctl syntax must be
+ * used.
+ *
+ * The possible return values are:
+ * Z_NO_PROPERTY_ID - invalid alias name
+ * Z_ALIAS_DISALLOW - pre-existing, incompatible rctl definition
+ * Z_NO_ENTRY - no rctl is configured for this alias
+ * Z_OK - we got a valid rctl for the specified alias
+ */
+int
+zonecfg_get_aliased_rctl(zone_dochandle_t handle, char *name, uint64_t *rval)
+{
+ boolean_t found = B_FALSE;
+ boolean_t found_val = B_FALSE;
+ xmlNodePtr cur, val;
+ char savedname[MAXNAMELEN];
+ struct zone_rctlvaltab rctl;
+ int i;
+ int err;
+
+ for (i = 0; aliases[i].shortname != NULL; i++)
+ if (strcmp(name, aliases[i].shortname) == 0)
+ break;
+
+ if (aliases[i].shortname == NULL)
+ return (Z_NO_PROPERTY_ID);
+
+ if ((err = operation_prep(handle)) != Z_OK)
+ return (err);
+
+ cur = handle->zone_dh_cur;
+ for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
+ if (xmlStrcmp(cur->name, DTD_ELEM_RCTL) != 0)
+ continue;
+ if ((fetchprop(cur, DTD_ATTR_NAME, savedname,
+ sizeof (savedname)) == Z_OK) &&
+ (strcmp(savedname, aliases[i].realname) == 0)) {
+
+ /*
+ * If we already saw one of these, we can't have an
+ * alias since we just found another.
+ */
+ if (found)
+ return (Z_ALIAS_DISALLOW);
+ found = B_TRUE;
+
+ for (val = cur->xmlChildrenNode; val != NULL;
+ val = val->next) {
+ /*
+ * If we already have one value, we can't have
+ * an alias since we just found another.
+ */
+ if (found_val)
+ return (Z_ALIAS_DISALLOW);
+ found_val = B_TRUE;
+
+ if ((fetchprop(val, DTD_ATTR_PRIV,
+ rctl.zone_rctlval_priv,
+ sizeof (rctl.zone_rctlval_priv)) != Z_OK))
+ break;
+ if ((fetchprop(val, DTD_ATTR_LIMIT,
+ rctl.zone_rctlval_limit,
+ sizeof (rctl.zone_rctlval_limit)) != Z_OK))
+ break;
+ if ((fetchprop(val, DTD_ATTR_ACTION,
+ rctl.zone_rctlval_action,
+ sizeof (rctl.zone_rctlval_action)) != Z_OK))
+ break;
+ }
+
+ /* check priv and action match the expected vals */
+ if (strcmp(rctl.zone_rctlval_priv,
+ aliases[i].priv) != 0 ||
+ strcmp(rctl.zone_rctlval_action,
+ aliases[i].action) != 0)
+ return (Z_ALIAS_DISALLOW);
+ }
+ }
+
+ if (found) {
+ *rval = strtoull(rctl.zone_rctlval_limit, NULL, 10);
+ return (Z_OK);
+ }
+
+ return (Z_NO_ENTRY);
+}
+
+int
+zonecfg_rm_aliased_rctl(zone_dochandle_t handle, char *name)
+{
+ int i;
+ uint64_t val;
+ struct zone_rctltab rctltab;
+
+ /*
+ * First check that we have a valid aliased rctl to remove.
+ * This will catch an rctl entry with non-standard values or
+ * multiple rctl values for this name. We need to ignore those
+ * rctl entries.
+ */
+ if (zonecfg_get_aliased_rctl(handle, name, &val) != Z_OK)
+ return (Z_OK);
+
+ for (i = 0; aliases[i].shortname != NULL; i++)
+ if (strcmp(name, aliases[i].shortname) == 0)
+ break;
+
+ if (aliases[i].shortname == NULL)
+ return (Z_NO_RESOURCE_ID);
+
+ (void) strlcpy(rctltab.zone_rctl_name, aliases[i].realname,
+ sizeof (rctltab.zone_rctl_name));
+
+ return (zonecfg_delete_rctl(handle, &rctltab));
+}
+
+boolean_t
+zonecfg_aliased_rctl_ok(zone_dochandle_t handle, char *name)
+{
+ uint64_t tmp_val;
+
+ switch (zonecfg_get_aliased_rctl(handle, name, &tmp_val)) {
+ case Z_OK:
+ /*FALLTHRU*/
+ case Z_NO_ENTRY:
+ return (B_TRUE);
+ default:
+ return (B_FALSE);
+ }
+}
+
+int
+zonecfg_set_aliased_rctl(zone_dochandle_t handle, char *name, uint64_t val)
+{
+ int i;
+ int err;
+ struct zone_rctltab rctltab;
+ struct zone_rctlvaltab *rctlvaltab;
+ char buf[128];
+
+ if (!zonecfg_aliased_rctl_ok(handle, name))
+ return (Z_ALIAS_DISALLOW);
+
+ for (i = 0; aliases[i].shortname != NULL; i++)
+ if (strcmp(name, aliases[i].shortname) == 0)
+ break;
+
+ if (aliases[i].shortname == NULL)
+ return (Z_NO_RESOURCE_ID);
+
+ /* remove any pre-existing definition for this rctl */
+ (void) zonecfg_rm_aliased_rctl(handle, name);
+
+ (void) strlcpy(rctltab.zone_rctl_name, aliases[i].realname,
+ sizeof (rctltab.zone_rctl_name));
+
+ rctltab.zone_rctl_valptr = NULL;
+
+ if ((rctlvaltab = calloc(1, sizeof (struct zone_rctlvaltab))) == NULL)
+ return (Z_NOMEM);
+
+ (void) snprintf(buf, sizeof (buf), "%llu", (long long)val);
+
+ (void) strlcpy(rctlvaltab->zone_rctlval_priv, aliases[i].priv,
+ sizeof (rctlvaltab->zone_rctlval_priv));
+ (void) strlcpy(rctlvaltab->zone_rctlval_limit, buf,
+ sizeof (rctlvaltab->zone_rctlval_limit));
+ (void) strlcpy(rctlvaltab->zone_rctlval_action, aliases[i].action,
+ sizeof (rctlvaltab->zone_rctlval_action));
+
+ rctlvaltab->zone_rctlval_next = NULL;
+
+ if ((err = zonecfg_add_rctl_value(&rctltab, rctlvaltab)) != Z_OK)
+ return (err);
+
+ return (zonecfg_add_rctl(handle, &rctltab));
+}
+
+static int
+delete_tmp_pool(zone_dochandle_t handle)
+{
+ int err;
+ xmlNodePtr cur = handle->zone_dh_cur;
+
+ if ((err = operation_prep(handle)) != Z_OK)
+ return (err);
+
+ for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
+ if (xmlStrcmp(cur->name, DTD_ELEM_TMPPOOL) == 0) {
+ xmlUnlinkNode(cur);
+ xmlFreeNode(cur);
+ return (Z_OK);
+ }
+ }
+
+ return (Z_NO_RESOURCE_ID);
+}
+
+static int
+modify_tmp_pool(zone_dochandle_t handle, char *pool_importance)
+{
+ int err;
+ xmlNodePtr cur = handle->zone_dh_cur;
+ xmlNodePtr newnode;
+
+ err = delete_tmp_pool(handle);
+ if (err != Z_OK && err != Z_NO_RESOURCE_ID)
+ return (err);
+
+ if (*pool_importance != '\0') {
+ if ((err = operation_prep(handle)) != Z_OK)
+ return (err);
+
+ newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_TMPPOOL, NULL);
+ if ((err = newprop(newnode, DTD_ATTR_IMPORTANCE,
+ pool_importance)) != Z_OK)
+ return (err);
+ }
+
+ return (Z_OK);
+}
+
+static int
+add_pset_core(zone_dochandle_t handle, struct zone_psettab *tabptr)
+{
+ xmlNodePtr newnode, cur = handle->zone_dh_cur;
+ int err;
+
+ newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_PSET, NULL);
+ if ((err = newprop(newnode, DTD_ATTR_NCPU_MIN,
+ tabptr->zone_ncpu_min)) != Z_OK)
+ return (err);
+ if ((err = newprop(newnode, DTD_ATTR_NCPU_MAX,
+ tabptr->zone_ncpu_max)) != Z_OK)
+ return (err);
+
+ if ((err = modify_tmp_pool(handle, tabptr->zone_importance)) != Z_OK)
+ return (err);
+
+ return (Z_OK);
+}
+
+int
+zonecfg_add_pset(zone_dochandle_t handle, struct zone_psettab *tabptr)
+{
+ int err;
+
+ if (tabptr == NULL)
+ return (Z_INVAL);
+
+ if ((err = operation_prep(handle)) != Z_OK)
+ return (err);
+
+ if ((err = add_pset_core(handle, tabptr)) != Z_OK)
+ return (err);
+
+ return (Z_OK);
+}
+
+int
+zonecfg_delete_pset(zone_dochandle_t handle)
+{
+ int err;
+ int res = Z_NO_RESOURCE_ID;
+ xmlNodePtr cur = handle->zone_dh_cur;
+
+ if ((err = operation_prep(handle)) != Z_OK)
+ return (err);
+
+ for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
+ if (xmlStrcmp(cur->name, DTD_ELEM_PSET) == 0) {
+ xmlUnlinkNode(cur);
+ xmlFreeNode(cur);
+ res = Z_OK;
+ break;
+ }
+ }
+
+ /*
+ * Once we have msets, we should check that a mset
+ * do not exist before we delete the tmp_pool data.
+ */
+ err = delete_tmp_pool(handle);
+ if (err != Z_OK && err != Z_NO_RESOURCE_ID)
+ return (err);
+
+ return (res);
+}
+
+int
+zonecfg_modify_pset(zone_dochandle_t handle, struct zone_psettab *tabptr)
+{
+ int err;
+
+ if (tabptr == NULL)
+ return (Z_INVAL);
+
+ if ((err = zonecfg_delete_pset(handle)) != Z_OK)
+ return (err);
+
+ if ((err = add_pset_core(handle, tabptr)) != Z_OK)
+ return (err);
+
+ return (Z_OK);
+}
+
+int
+zonecfg_lookup_pset(zone_dochandle_t handle, struct zone_psettab *tabptr)
+{
+ xmlNodePtr cur;
+ int err;
+ int res = Z_NO_ENTRY;
+
+ if (tabptr == NULL)
+ return (Z_INVAL);
+
+ if ((err = operation_prep(handle)) != Z_OK)
+ return (err);
+
+ /* this is an optional component */
+ tabptr->zone_importance[0] = '\0';
+
+ cur = handle->zone_dh_cur;
+ for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
+ if (xmlStrcmp(cur->name, DTD_ELEM_PSET) == 0) {
+ if ((err = fetchprop(cur, DTD_ATTR_NCPU_MIN,
+ tabptr->zone_ncpu_min,
+ sizeof (tabptr->zone_ncpu_min))) != Z_OK) {
+ handle->zone_dh_cur = handle->zone_dh_top;
+ return (err);
+ }
+
+ if ((err = fetchprop(cur, DTD_ATTR_NCPU_MAX,
+ tabptr->zone_ncpu_max,
+ sizeof (tabptr->zone_ncpu_max))) != Z_OK) {
+ handle->zone_dh_cur = handle->zone_dh_top;
+ return (err);
+ }
+
+ res = Z_OK;
+
+ } else if (xmlStrcmp(cur->name, DTD_ELEM_TMPPOOL) == 0) {
+ if ((err = fetchprop(cur, DTD_ATTR_IMPORTANCE,
+ tabptr->zone_importance,
+ sizeof (tabptr->zone_importance))) != Z_OK) {
+ handle->zone_dh_cur = handle->zone_dh_top;
+ return (err);
+ }
+ }
+ }
+
+ return (res);
+}
+
+int
+zonecfg_getpsetent(zone_dochandle_t handle, struct zone_psettab *tabptr)
+{
+ int err;
+
+ if ((err = zonecfg_setent(handle)) != Z_OK)
+ return (err);
+
+ err = zonecfg_lookup_pset(handle, tabptr);
+
+ (void) zonecfg_endent(handle);
+
+ return (err);
+}
+
+static int
+add_mcap(zone_dochandle_t handle, struct zone_mcaptab *tabptr)
+{
+ xmlNodePtr newnode, cur = handle->zone_dh_cur;
+ int err;
+
+ newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_MCAP, NULL);
+ if ((err = newprop(newnode, DTD_ATTR_PHYSCAP, tabptr->zone_physmem_cap))
+ != Z_OK)
+ return (err);
+
+ return (Z_OK);
+}
+
+int
+zonecfg_delete_mcap(zone_dochandle_t handle)
+{
+ int err;
+ xmlNodePtr cur = handle->zone_dh_cur;
+
+ if ((err = operation_prep(handle)) != Z_OK)
+ return (err);
+
+ for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
+ if (xmlStrcmp(cur->name, DTD_ELEM_MCAP) != 0)
+ continue;
+
+ xmlUnlinkNode(cur);
+ xmlFreeNode(cur);
+ return (Z_OK);
+ }
+ return (Z_NO_RESOURCE_ID);
+}
+
+int
+zonecfg_modify_mcap(zone_dochandle_t handle, struct zone_mcaptab *tabptr)
+{
+ int err;
+
+ if (tabptr == NULL)
+ return (Z_INVAL);
+
+ err = zonecfg_delete_mcap(handle);
+ /* it is ok if there is no mcap entry */
+ if (err != Z_OK && err != Z_NO_RESOURCE_ID)
+ return (err);
+
+ if ((err = add_mcap(handle, tabptr)) != Z_OK)
+ return (err);
+
+ return (Z_OK);
+}
+
+int
+zonecfg_lookup_mcap(zone_dochandle_t handle, struct zone_mcaptab *tabptr)
+{
+ xmlNodePtr cur;
+ int err;
+
+ if (tabptr == NULL)
+ return (Z_INVAL);
+
+ if ((err = operation_prep(handle)) != Z_OK)
+ return (err);
+
+ cur = handle->zone_dh_cur;
+ for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
+ if (xmlStrcmp(cur->name, DTD_ELEM_MCAP) != 0)
+ continue;
+ if ((err = fetchprop(cur, DTD_ATTR_PHYSCAP,
+ tabptr->zone_physmem_cap,
+ sizeof (tabptr->zone_physmem_cap))) != Z_OK) {
+ handle->zone_dh_cur = handle->zone_dh_top;
+ return (err);
+ }
+
+ return (Z_OK);
+ }
+
+ return (Z_NO_ENTRY);
+}
+
+static int
+getmcapent_core(zone_dochandle_t handle, struct zone_mcaptab *tabptr)
+{
+ xmlNodePtr cur;
+ int err;
+
+ if (handle == NULL)
+ return (Z_INVAL);
+
+ if ((cur = handle->zone_dh_cur) == NULL)
+ return (Z_NO_ENTRY);
+
+ for (; cur != NULL; cur = cur->next)
+ if (xmlStrcmp(cur->name, DTD_ELEM_MCAP) == 0)
+ break;
+ if (cur == NULL) {
+ handle->zone_dh_cur = handle->zone_dh_top;
+ return (Z_NO_ENTRY);
+ }
+
+ if ((err = fetchprop(cur, DTD_ATTR_PHYSCAP, tabptr->zone_physmem_cap,
+ sizeof (tabptr->zone_physmem_cap))) != Z_OK) {
+ handle->zone_dh_cur = handle->zone_dh_top;
+ return (err);
+ }
+
+ handle->zone_dh_cur = cur->next;
+ return (Z_OK);
+}
+
+int
+zonecfg_getmcapent(zone_dochandle_t handle, struct zone_mcaptab *tabptr)
+{
+ int err;
+
+ if ((err = zonecfg_setent(handle)) != Z_OK)
+ return (err);
+
+ err = getmcapent_core(handle, tabptr);
+
+ (void) zonecfg_endent(handle);
+
+ return (err);
+}
+
int
zonecfg_setpkgent(zone_dochandle_t handle)
{