Print this page
OS-200 need a better mechanism for storing persistent zone_did
OS-511 make zonecfg device resource extensible, like the net resource
OS-224 add more zonecfg net properties
OS-216 store all net config info on zone
Reduce lint
OS-5139 cpu_cap is sometimes off by 1 when set with package fss or cap_cap direct update
Reviewed by: Patrick Mooney <patrick.mooney@joyent.com>
OS-399 zone phys. mem. cap should be a rctl and have associated kstat
        
*** 21,30 ****
--- 21,31 ----
  
  /*
   * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
   * Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
   * Copyright 2014 Gary Mills
+  * Copyright 2016, Joyent Inc.
   */
  
  /*
   * zonecfg is a lex/yacc based command interpreter used to manage zone
   * configurations.  The lexer (see zonecfg_lex.l) builds up tokens, which
*** 232,241 ****
--- 233,247 ----
          "user",
          "auths",
          "fs-allowed",
          ALIAS_MAXPROCS,
          "allowed-address",
+         ALIAS_ZFSPRI,
+         "mac-addr",
+         "vlan-id",
+         "global-nic",
+         "property",
          NULL
  };
  
  /* These *must* match the order of the PROP_VAL_ define's from zonecfg.h */
  static char *prop_val_types[] = {
*** 404,425 ****
          "cancel",
          "end",
          "exit",
          "help",
          "info",
          "set address=",
!         "set physical=",
          "set defrouter=",
          NULL
  };
  
  static const char *device_res_scope_cmds[] = {
          "cancel",
          "end",
          "exit",
          "help",
          "info",
          "set match=",
          NULL
  };
  
  static const char *attr_res_scope_cmds[] = {
--- 410,443 ----
          "cancel",
          "end",
          "exit",
          "help",
          "info",
+         "add property ",
+         "clear allowed-address",
+         "clear defrouter",
+         "clear global-nic",
+         "clear mac-addr",
+         "clear vlan-id",
+         "remove property ",
          "set address=",
!         "set allowed-address=",
          "set defrouter=",
+         "set global-nic=",
+         "set mac-addr=",
+         "set physical=",
+         "set vlan-id=",
          NULL
  };
  
  static const char *device_res_scope_cmds[] = {
          "cancel",
          "end",
          "exit",
          "help",
          "info",
+         "add property ",
          "set match=",
          NULL
  };
  
  static const char *attr_res_scope_cmds[] = {
*** 577,587 ****
  static struct zone_devtab       old_devtab, in_progress_devtab;
  static struct zone_rctltab      old_rctltab, in_progress_rctltab;
  static struct zone_attrtab      old_attrtab, in_progress_attrtab;
  static struct zone_dstab        old_dstab, in_progress_dstab;
  static struct zone_psettab      old_psettab, in_progress_psettab;
- static struct zone_mcaptab      old_mcaptab, in_progress_mcaptab;
  static struct zone_admintab     old_admintab, in_progress_admintab;
  
  static GetLine *gl;     /* The gl_get_line() resource object */
  
  static void bytes_to_units(char *str, char *buf, int bufsize);
--- 595,604 ----
*** 1076,1101 ****
                              "used to configure a network interface.\n"),
                              rt_to_str(resource_scope));
                          (void) fprintf(fp, gettext("Valid commands:\n"));
                          (void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
                              pt_to_str(PT_ADDRESS), gettext("<IP-address>"));
                          (void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
                              pt_to_str(PT_ALLOWED_ADDRESS),
                              gettext("<IP-address>"));
                          (void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
                              pt_to_str(PT_PHYSICAL), gettext("<interface>"));
                          (void) fprintf(fp, gettext("See ifconfig(1M) for "
                              "details of the <interface> string.\n"));
                          (void) fprintf(fp, gettext("%s %s is valid "
                              "if the %s property is set to %s, otherwise it "
                              "must not be set.\n"),
                              cmd_to_str(CMD_SET), pt_to_str(PT_ADDRESS),
                              pt_to_str(PT_IPTYPE), gettext("shared"));
!                         (void) fprintf(fp, gettext("%s %s is valid "
!                             "if the %s property is set to %s, otherwise it "
!                             "must not be set.\n"),
!                             cmd_to_str(CMD_SET), pt_to_str(PT_ALLOWED_ADDRESS),
                              pt_to_str(PT_IPTYPE), gettext("exclusive"));
                          (void) fprintf(fp, gettext("\t%s %s=%s\n%s %s "
                              "is valid if the %s or %s property is set, "
                              "otherwise it must not be set\n"),
                              cmd_to_str(CMD_SET),
--- 1093,1129 ----
                              "used to configure a network interface.\n"),
                              rt_to_str(resource_scope));
                          (void) fprintf(fp, gettext("Valid commands:\n"));
                          (void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
                              pt_to_str(PT_ADDRESS), gettext("<IP-address>"));
+                         (void) fprintf(fp, "\t%s %s (%s=<value>,%s=<value>)\n",
+                             cmd_to_str(CMD_ADD), pt_to_str(PT_NPROP),
+                             pt_to_str(PT_NAME), pt_to_str(PT_VALUE));
                          (void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
                              pt_to_str(PT_ALLOWED_ADDRESS),
                              gettext("<IP-address>"));
                          (void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
                              pt_to_str(PT_PHYSICAL), gettext("<interface>"));
+                         (void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
+                             pt_to_str(PT_MAC), gettext("<mac-address>"));
+                         (void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
+                             pt_to_str(PT_GNIC), gettext("<global zone NIC>"));
+                         (void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
+                             pt_to_str(PT_VLANID), gettext("<vlan ID>"));
                          (void) fprintf(fp, gettext("See ifconfig(1M) for "
                              "details of the <interface> string.\n"));
                          (void) fprintf(fp, gettext("%s %s is valid "
                              "if the %s property is set to %s, otherwise it "
                              "must not be set.\n"),
                              cmd_to_str(CMD_SET), pt_to_str(PT_ADDRESS),
                              pt_to_str(PT_IPTYPE), gettext("shared"));
!                         (void) fprintf(fp, gettext("%s (%s, %s, %s, %s) are "
!                             "valid if the %s property is set to %s, otherwise "
!                             "they must not be set.\n"),
!                             cmd_to_str(CMD_SET),
!                             pt_to_str(PT_ALLOWED_ADDRESS), pt_to_str(PT_MAC),
!                             pt_to_str(PT_VLANID), pt_to_str(PT_GNIC),
                              pt_to_str(PT_IPTYPE), gettext("exclusive"));
                          (void) fprintf(fp, gettext("\t%s %s=%s\n%s %s "
                              "is valid if the %s or %s property is set, "
                              "otherwise it must not be set\n"),
                              cmd_to_str(CMD_SET),
*** 1107,1116 ****
--- 1135,1147 ----
                  case RT_DEVICE:
                          (void) fprintf(fp, gettext("The '%s' resource scope is "
                              "used to configure a device node.\n"),
                              rt_to_str(resource_scope));
                          (void) fprintf(fp, gettext("Valid commands:\n"));
+                         (void) fprintf(fp, "\t%s %s (%s=<value>,%s=<value>)\n",
+                             cmd_to_str(CMD_ADD), pt_to_str(PT_NPROP),
+                             pt_to_str(PT_NAME), pt_to_str(PT_VALUE));
                          (void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
                              pt_to_str(PT_MATCH), gettext("<device-path>"));
                          break;
                  case RT_RCTL:
                          (void) fprintf(fp, gettext("The '%s' resource scope is "
*** 1334,1348 ****
                      pt_to_str(PT_SHARES));
                  (void) fprintf(fp, "\t%s\t\t%s, %s, %s, %s, %s\n",
                      rt_to_str(RT_FS), pt_to_str(PT_DIR),
                      pt_to_str(PT_SPECIAL), pt_to_str(PT_RAW),
                      pt_to_str(PT_TYPE), pt_to_str(PT_OPTIONS));
!                 (void) fprintf(fp, "\t%s\t\t%s, %s, %s|%s\n", rt_to_str(RT_NET),
                      pt_to_str(PT_ADDRESS), pt_to_str(PT_ALLOWED_ADDRESS),
!                     pt_to_str(PT_PHYSICAL), pt_to_str(PT_DEFROUTER));
!                 (void) fprintf(fp, "\t%s\t\t%s\n", rt_to_str(RT_DEVICE),
!                     pt_to_str(PT_MATCH));
                  (void) fprintf(fp, "\t%s\t\t%s, %s\n", rt_to_str(RT_RCTL),
                      pt_to_str(PT_NAME), pt_to_str(PT_VALUE));
                  (void) fprintf(fp, "\t%s\t\t%s, %s, %s\n", rt_to_str(RT_ATTR),
                      pt_to_str(PT_NAME), pt_to_str(PT_TYPE),
                      pt_to_str(PT_VALUE));
--- 1365,1382 ----
                      pt_to_str(PT_SHARES));
                  (void) fprintf(fp, "\t%s\t\t%s, %s, %s, %s, %s\n",
                      rt_to_str(RT_FS), pt_to_str(PT_DIR),
                      pt_to_str(PT_SPECIAL), pt_to_str(PT_RAW),
                      pt_to_str(PT_TYPE), pt_to_str(PT_OPTIONS));
!                 (void) fprintf(fp, "\t%s\t\t%s, %s, %s, %s, %s, %s, %s %s\n",
!                     rt_to_str(RT_NET),
                      pt_to_str(PT_ADDRESS), pt_to_str(PT_ALLOWED_ADDRESS),
!                     pt_to_str(PT_GNIC), pt_to_str(PT_MAC),
!                     pt_to_str(PT_PHYSICAL), pt_to_str(PT_NPROP),
!                     pt_to_str(PT_VLANID), pt_to_str(PT_DEFROUTER));
!                 (void) fprintf(fp, "\t%s\t\t%s, %s\n", rt_to_str(RT_DEVICE),
!                     pt_to_str(PT_MATCH), pt_to_str(PT_NPROP));
                  (void) fprintf(fp, "\t%s\t\t%s, %s\n", rt_to_str(RT_RCTL),
                      pt_to_str(PT_NAME), pt_to_str(PT_VALUE));
                  (void) fprintf(fp, "\t%s\t\t%s, %s, %s\n", rt_to_str(RT_ATTR),
                      pt_to_str(PT_NAME), pt_to_str(PT_TYPE),
                      pt_to_str(PT_VALUE));
*** 1393,1402 ****
--- 1427,1439 ----
          char brandname[MAXNAMELEN];
  
          if (zonecfg_check_handle(handle) != Z_OK) {
                  if ((err = zonecfg_get_handle(zone, handle)) == Z_OK) {
                          got_handle = B_TRUE;
+ 
+                         (void) zonecfg_fix_obsolete(handle);
+ 
                          if (zonecfg_get_brand(handle, brandname,
                              sizeof (brandname)) != Z_OK) {
                                  zerr("Zone %s is inconsistent: missing "
                                      "brand attribute", zone);
                                  exit(Z_ERR);
*** 1693,1702 ****
--- 1730,1750 ----
                          break;
                  case 't':
                          (void) strlcpy(zone_template, optarg,
                              sizeof (zone_template));
                          break;
+                 case 'X':
+                         (void) snprintf(zone_template, sizeof (zone_template),
+                             "%s/%s.xml", ZONE_CONFIG_ROOT, zone);
+                         err = zonecfg_get_xml_handle(zone_template, handle);
+                         if (err != Z_OK) {
+                                 zone_perror(execname, err, B_TRUE);
+                                 exit(Z_ERR);
+                         }
+                         got_handle = B_TRUE;
+                         need_to_commit = B_TRUE;
+                         return;
                  default:
                          short_usage(CMD_CREATE);
                          arg_err = B_TRUE;
                          break;
                  }
*** 1795,1806 ****
          struct zone_devtab devtab;
          struct zone_attrtab attrtab;
          struct zone_rctltab rctltab;
          struct zone_dstab dstab;
          struct zone_psettab psettab;
-         struct zone_mcaptab mcaptab;
          struct zone_rctlvaltab *valptr;
          struct zone_admintab admintab;
          int err, arg;
          char zonepath[MAXPATHLEN], outfile[MAXPATHLEN], pool[MAXNAMELEN];
          char bootargs[BOOTARGS_MAX];
          char sched[MAXNAMELEN];
--- 1843,1854 ----
          struct zone_devtab devtab;
          struct zone_attrtab attrtab;
          struct zone_rctltab rctltab;
          struct zone_dstab dstab;
          struct zone_psettab psettab;
          struct zone_rctlvaltab *valptr;
+         struct zone_res_attrtab *rap;
          struct zone_admintab admintab;
          int err, arg;
          char zonepath[MAXPATHLEN], outfile[MAXPATHLEN], pool[MAXNAMELEN];
          char bootargs[BOOTARGS_MAX];
          char sched[MAXNAMELEN];
*** 1966,1976 ****
--- 2014,2034 ----
                      rt_to_str(RT_NET));
                  export_prop(of, PT_ADDRESS, nwiftab.zone_nwif_address);
                  export_prop(of, PT_ALLOWED_ADDRESS,
                      nwiftab.zone_nwif_allowed_address);
                  export_prop(of, PT_PHYSICAL, nwiftab.zone_nwif_physical);
+                 export_prop(of, PT_MAC, nwiftab.zone_nwif_mac);
+                 export_prop(of, PT_VLANID, nwiftab.zone_nwif_vlan_id);
+                 export_prop(of, PT_GNIC, nwiftab.zone_nwif_gnic);
                  export_prop(of, PT_DEFROUTER, nwiftab.zone_nwif_defrouter);
+                 for (rap = nwiftab.zone_nwif_attrp; rap != NULL;
+                     rap = rap->zone_res_attr_next) {
+                         fprintf(of, "%s %s (%s=%s,%s=\"%s\")\n",
+                             cmd_to_str(CMD_ADD), pt_to_str(PT_NPROP),
+                             pt_to_str(PT_NAME), rap->zone_res_attr_name,
+                             pt_to_str(PT_VALUE), rap->zone_res_attr_value);
+                 }
                  (void) fprintf(of, "%s\n", cmd_to_str(CMD_END));
          }
          (void) zonecfg_endnwifent(handle);
  
          if ((err = zonecfg_setdevent(handle)) != Z_OK) {
*** 1979,2003 ****
          }
          while (zonecfg_getdevent(handle, &devtab) == Z_OK) {
                  (void) fprintf(of, "%s %s\n", cmd_to_str(CMD_ADD),
                      rt_to_str(RT_DEVICE));
                  export_prop(of, PT_MATCH, devtab.zone_dev_match);
                  (void) fprintf(of, "%s\n", cmd_to_str(CMD_END));
          }
          (void) zonecfg_enddevent(handle);
  
-         if (zonecfg_getmcapent(handle, &mcaptab) == Z_OK) {
-                 char buf[128];
- 
-                 (void) fprintf(of, "%s %s\n", cmd_to_str(CMD_ADD),
-                     rt_to_str(RT_MCAP));
-                 bytes_to_units(mcaptab.zone_physmem_cap, buf, sizeof (buf));
-                 (void) fprintf(of, "%s %s=%s\n", cmd_to_str(CMD_SET),
-                     pt_to_str(PT_PHYSICAL), buf);
-                 (void) fprintf(of, "%s\n", cmd_to_str(CMD_END));
-         }
- 
          if ((err = zonecfg_setrctlent(handle)) != Z_OK) {
                  zone_perror(zone, err, B_FALSE);
                  goto done;
          }
          while (zonecfg_getrctlent(handle, &rctltab) == Z_OK) {
--- 2037,2057 ----
          }
          while (zonecfg_getdevent(handle, &devtab) == Z_OK) {
                  (void) fprintf(of, "%s %s\n", cmd_to_str(CMD_ADD),
                      rt_to_str(RT_DEVICE));
                  export_prop(of, PT_MATCH, devtab.zone_dev_match);
+                 for (rap = devtab.zone_dev_attrp; rap != NULL;
+                     rap = rap->zone_res_attr_next) {
+                         fprintf(of, "%s %s (%s=%s,%s=\"%s\")\n",
+                             cmd_to_str(CMD_ADD), pt_to_str(PT_NPROP),
+                             pt_to_str(PT_NAME), rap->zone_res_attr_name,
+                             pt_to_str(PT_VALUE), rap->zone_res_attr_value);
+                 }
                  (void) fprintf(of, "%s\n", cmd_to_str(CMD_END));
          }
          (void) zonecfg_enddevent(handle);
  
          if ((err = zonecfg_setrctlent(handle)) != Z_OK) {
                  zone_perror(zone, err, B_FALSE);
                  goto done;
          }
          while (zonecfg_getrctlent(handle, &rctltab) == Z_OK) {
*** 2147,2157 ****
  static void
  add_resource(cmd_t *cmd)
  {
          int type;
          struct zone_psettab tmp_psettab;
-         struct zone_mcaptab tmp_mcaptab;
          uint64_t tmp;
          uint64_t tmp_mcap;
          char pool[MAXNAMELEN];
  
          if ((type = cmd->cmd_res_type) == RT_UNKNOWN) {
--- 2201,2210 ----
*** 2239,2251 ****
          case RT_MCAP:
                  /*
                   * Make sure there isn't already a mem-cap entry or max-swap
                   * or max-locked rctl.
                   */
!                 if (zonecfg_lookup_mcap(handle, &tmp_mcaptab) == Z_OK ||
!                     zonecfg_get_aliased_rctl(handle, ALIAS_MAXSWAP, &tmp_mcap)
!                     == Z_OK ||
                      zonecfg_get_aliased_rctl(handle, ALIAS_MAXLOCKEDMEM,
                      &tmp_mcap) == Z_OK) {
                          zerr(gettext("The %s resource or a related resource "
                              "control already exists."), rt_to_str(RT_MCAP));
                          goto bad;
--- 2292,2305 ----
          case RT_MCAP:
                  /*
                   * Make sure there isn't already a mem-cap entry or max-swap
                   * or max-locked rctl.
                   */
!                 if (zonecfg_get_aliased_rctl(handle, ALIAS_MAXSWAP,
!                     &tmp_mcap) == Z_OK ||
!                     zonecfg_get_aliased_rctl(handle, ALIAS_MAXPHYSMEM,
!                     &tmp_mcap) == Z_OK ||
                      zonecfg_get_aliased_rctl(handle, ALIAS_MAXLOCKEDMEM,
                      &tmp_mcap) == Z_OK) {
                          zerr(gettext("The %s resource or a related resource "
                              "control already exists."), rt_to_str(RT_MCAP));
                          goto bad;
*** 2254,2264 ****
                          zerr(gettext("WARNING: Setting a global zone memory "
                              "cap too low could deny\nservice "
                              "to even the root user; "
                              "this could render the system impossible\n"
                              "to administer.  Please use caution."));
-                 bzero(&in_progress_mcaptab, sizeof (in_progress_mcaptab));
                  return;
          case RT_ADMIN:
                  bzero(&in_progress_admintab, sizeof (in_progress_admintab));
                  return;
          default:
--- 2308,2317 ----
*** 2357,2367 ****
--- 2410,2482 ----
  
  bad:
          zonecfg_free_rctl_value_list(rctlvaltab);
  }
  
+ /*
+  * Resource attribute ("property" resource embedded on net or dev resource)
+  */
  static void
+ do_res_attr(struct zone_res_attrtab **headp, complex_property_ptr_t cpp)
+ {
+         complex_property_ptr_t cp;
+         struct zone_res_attrtab *np;
+         int err;
+         boolean_t seen_name = B_FALSE, seen_value = B_FALSE;
+ 
+         if ((np = calloc(1, sizeof (struct zone_res_attrtab))) == NULL) {
+                 zone_perror(zone, Z_NOMEM, B_TRUE);
+                 exit(Z_ERR);
+         }
+ 
+         for (cp = cpp; cp != NULL; cp = cp->cp_next) {
+                 switch (cp->cp_type) {
+                 case PT_NAME:
+                         if (seen_name) {
+                                 zerr(gettext("%s already specified"),
+                                     pt_to_str(PT_NAME));
+                                 goto bad;
+                         }
+                         (void) strlcpy(np->zone_res_attr_name, cp->cp_value,
+                             sizeof (np->zone_res_attr_name));
+                         seen_name = B_TRUE;
+                         break;
+                 case PT_VALUE:
+                         if (seen_value) {
+                                 zerr(gettext("%s already specified"),
+                                     pt_to_str(PT_VALUE));
+                                 goto bad;
+                         }
+                         (void) strlcpy(np->zone_res_attr_value, cp->cp_value,
+                             sizeof (np->zone_res_attr_value));
+                         seen_value = B_TRUE;
+                         break;
+                 default:
+                         zone_perror(pt_to_str(PT_NPROP), Z_NO_PROPERTY_TYPE,
+                             B_TRUE);
+                         long_usage(CMD_ADD, B_TRUE);
+                         usage(B_FALSE, HELP_PROPS);
+                         zonecfg_free_res_attr_list(np);
+                         return;
+                 }
+         }
+ 
+         if (!seen_name)
+                 zerr(gettext("%s not specified"), pt_to_str(PT_NAME));
+         if (!seen_value)
+                 zerr(gettext("%s not specified"), pt_to_str(PT_VALUE));
+ 
+         err = zonecfg_add_res_attr(headp, np);
+         if (err != Z_OK)
+                 zone_perror(pt_to_str(PT_NPROP), err, B_TRUE);
+         return;
+ 
+ bad:
+         zonecfg_free_res_attr_list(np);
+ }
+ 
+ static void
  add_property(cmd_t *cmd)
  {
          char *prop_id;
          int err, res_type, prop_type;
          property_value_ptr_t pp;
*** 2424,2433 ****
--- 2539,2586 ----
                                          zone_perror(pt_to_str(prop_type), err,
                                              B_TRUE);
                          }
                  }
                  return;
+         case RT_NET:
+                 if (prop_type != PT_NPROP) {
+                         zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE,
+                             B_TRUE);
+                         long_usage(CMD_ADD, B_TRUE);
+                         usage(B_FALSE, HELP_PROPS);
+                         return;
+                 }
+                 pp = cmd->cmd_property_ptr[0];
+                 if (pp->pv_type != PROP_VAL_COMPLEX) {
+                         zerr(gettext("A %s value was expected here."),
+                             pvt_to_str(PROP_VAL_COMPLEX));
+                         saw_error = B_TRUE;
+                         return;
+                 }
+ 
+                 do_res_attr(&(in_progress_nwiftab.zone_nwif_attrp),
+                     pp->pv_complex);
+                 return;
+         case RT_DEVICE:
+                 if (prop_type != PT_NPROP) {
+                         zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE,
+                             B_TRUE);
+                         long_usage(CMD_ADD, B_TRUE);
+                         usage(B_FALSE, HELP_PROPS);
+                         return;
+                 }
+                 pp = cmd->cmd_property_ptr[0];
+                 if (pp->pv_type != PROP_VAL_COMPLEX) {
+                         zerr(gettext("A %s value was expected here."),
+                             pvt_to_str(PROP_VAL_COMPLEX));
+                         saw_error = B_TRUE;
+                         return;
+                 }
+ 
+                 do_res_attr(&(in_progress_devtab.zone_dev_attrp),
+                     pp->pv_complex);
+                 return;
          case RT_RCTL:
                  if (prop_type != PT_VALUE) {
                          zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE,
                              B_TRUE);
                          long_usage(CMD_ADD, B_TRUE);
*** 2528,2539 ****
  
                  global_scope = B_FALSE;
                  resource_scope = cmd->cmd_res_type;
                  end_op = CMD_ADD;
                  add_resource(cmd);
!         } else
                  add_property(cmd);
  }
  
  /*
   * This routine has an unusual implementation, because it tries very
   * hard to succeed in the face of a variety of failure modes.
--- 2681,2693 ----
  
                  global_scope = B_FALSE;
                  resource_scope = cmd->cmd_res_type;
                  end_op = CMD_ADD;
                  add_resource(cmd);
!         } else {
                  add_property(cmd);
+         }
  }
  
  /*
   * This routine has an unusual implementation, because it tries very
   * hard to succeed in the face of a variety of failure modes.
*** 2727,2736 ****
--- 2881,2905 ----
                  case PT_PHYSICAL:
                          (void) strlcpy(nwiftab->zone_nwif_physical,
                              pp->pv_simple,
                              sizeof (nwiftab->zone_nwif_physical));
                          break;
+                 case PT_MAC:
+                         (void) strlcpy(nwiftab->zone_nwif_mac,
+                             pp->pv_simple,
+                             sizeof (nwiftab->zone_nwif_mac));
+                         break;
+                 case PT_VLANID:
+                         (void) strlcpy(nwiftab->zone_nwif_vlan_id,
+                             pp->pv_simple,
+                             sizeof (nwiftab->zone_nwif_vlan_id));
+                         break;
+                 case PT_GNIC:
+                         (void) strlcpy(nwiftab->zone_nwif_gnic,
+                             pp->pv_simple,
+                             sizeof (nwiftab->zone_nwif_gnic));
+                         break;
                  case PT_DEFROUTER:
                          (void) strlcpy(nwiftab->zone_nwif_defrouter,
                              pp->pv_simple,
                              sizeof (nwiftab->zone_nwif_defrouter));
                          break;
*** 3244,3257 ****
  static void
  remove_mcap()
  {
          int err, res1, res2, res3;
          uint64_t tmp;
-         struct zone_mcaptab mcaptab;
          boolean_t revert = B_FALSE;
  
!         res1 = zonecfg_lookup_mcap(handle, &mcaptab);
          res2 = zonecfg_get_aliased_rctl(handle, ALIAS_MAXSWAP, &tmp);
          res3 = zonecfg_get_aliased_rctl(handle, ALIAS_MAXLOCKEDMEM, &tmp);
  
          /* if none of these exist, there is no resource to remove */
          if (res1 != Z_OK && res2 != Z_OK && res3 != Z_OK) {
--- 3413,3425 ----
  static void
  remove_mcap()
  {
          int err, res1, res2, res3;
          uint64_t tmp;
          boolean_t revert = B_FALSE;
  
!         res1 = zonecfg_get_aliased_rctl(handle, ALIAS_MAXPHYSMEM, &tmp);
          res2 = zonecfg_get_aliased_rctl(handle, ALIAS_MAXSWAP, &tmp);
          res3 = zonecfg_get_aliased_rctl(handle, ALIAS_MAXLOCKEDMEM, &tmp);
  
          /* if none of these exist, there is no resource to remove */
          if (res1 != Z_OK && res2 != Z_OK && res3 != Z_OK) {
*** 3259,3275 ****
                      zonecfg_strerror(Z_NO_RESOURCE_TYPE));
                  saw_error = B_TRUE;
                  return;
          }
          if (res1 == Z_OK) {
!                 if ((err = zonecfg_delete_mcap(handle)) != Z_OK) {
                          z_cmd_rt_perror(CMD_REMOVE, RT_MCAP, err, B_TRUE);
                          revert = B_TRUE;
                  } else {
                          need_to_commit = B_TRUE;
                  }
          }
          if (res2 == Z_OK) {
                  if ((err = zonecfg_rm_aliased_rctl(handle, ALIAS_MAXSWAP))
                      != Z_OK) {
                          z_cmd_rt_perror(CMD_REMOVE, RT_MCAP, err, B_TRUE);
                          revert = B_TRUE;
--- 3427,3445 ----
                      zonecfg_strerror(Z_NO_RESOURCE_TYPE));
                  saw_error = B_TRUE;
                  return;
          }
          if (res1 == Z_OK) {
!                 if ((err = zonecfg_rm_aliased_rctl(handle, ALIAS_MAXPHYSMEM))
!                     != Z_OK) {
                          z_cmd_rt_perror(CMD_REMOVE, RT_MCAP, err, B_TRUE);
                          revert = B_TRUE;
                  } else {
                          need_to_commit = B_TRUE;
                  }
          }
+ 
          if (res2 == Z_OK) {
                  if ((err = zonecfg_rm_aliased_rctl(handle, ALIAS_MAXSWAP))
                      != Z_OK) {
                          z_cmd_rt_perror(CMD_REMOVE, RT_MCAP, err, B_TRUE);
                          revert = B_TRUE;
*** 3408,3417 ****
--- 3578,3588 ----
  {
          char *prop_id;
          int err, res_type, prop_type;
          property_value_ptr_t pp;
          struct zone_rctlvaltab *rctlvaltab;
+         struct zone_res_attrtab *np;
          complex_property_ptr_t cx;
  
          res_type = resource_scope;
          prop_type = cmd->cmd_prop_name[0];
          if (res_type == RT_UNKNOWN || prop_type == PT_UNKNOWN) {
*** 3468,3477 ****
--- 3639,3698 ----
                                          zone_perror(pt_to_str(prop_type), err,
                                              B_TRUE);
                          }
                  }
                  return;
+         case RT_NET:            /* FALLTHRU */
+         case RT_DEVICE:
+                 if (prop_type != PT_NPROP) {
+                         zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE,
+                             B_TRUE);
+                         long_usage(CMD_REMOVE, B_TRUE);
+                         usage(B_FALSE, HELP_PROPS);
+                         return;
+                 }
+                 pp = cmd->cmd_property_ptr[0];
+                 if (pp->pv_type != PROP_VAL_COMPLEX) {
+                         zerr(gettext("A %s value was expected here."),
+                             pvt_to_str(PROP_VAL_COMPLEX));
+                         saw_error = B_TRUE;
+                         return;
+                 }
+ 
+                 np = alloca(sizeof (struct zone_res_attrtab));
+                 for (cx = pp->pv_complex; cx != NULL; cx = cx->cp_next) {
+                         switch (cx->cp_type) {
+                         case PT_NAME:
+                                 (void) strlcpy(np->zone_res_attr_name,
+                                     cx->cp_value,
+                                     sizeof (np->zone_res_attr_name));
+                                 break;
+                         case PT_VALUE:
+                                 (void) strlcpy(np->zone_res_attr_value,
+                                     cx->cp_value,
+                                     sizeof (np->zone_res_attr_value));
+                                 break;
+                         default:
+                                 zone_perror(pt_to_str(prop_type),
+                                     Z_NO_PROPERTY_TYPE, B_TRUE);
+                                 long_usage(CMD_REMOVE, B_TRUE);
+                                 usage(B_FALSE, HELP_PROPS);
+                                 return;
+                         }
+                 }
+                 np->zone_res_attr_next = NULL;
+ 
+                 if (res_type == RT_NET) {
+                         err = zonecfg_remove_res_attr(
+                             &(in_progress_nwiftab.zone_nwif_attrp), np);
+                 } else {                                /* RT_DEVICE */
+                         err = zonecfg_remove_res_attr(
+                             &(in_progress_devtab.zone_dev_attrp), np);
+                 }
+                 if (err != Z_OK)
+                         zone_perror(pt_to_str(prop_type), err, B_TRUE);
+                 return;
          case RT_RCTL:
                  if (prop_type != PT_VALUE) {
                          zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE,
                              B_TRUE);
                          long_usage(CMD_REMOVE, B_TRUE);
*** 3520,3541 ****
                      rctlvaltab);
                  if (err != Z_OK)
                          zone_perror(pt_to_str(prop_type), err, B_TRUE);
                  zonecfg_free_rctl_value_list(rctlvaltab);
                  return;
-         case RT_NET:
-                 if (prop_type != PT_DEFROUTER) {
-                         zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE,
-                             B_TRUE);
-                         long_usage(CMD_REMOVE, B_TRUE);
-                         usage(B_FALSE, HELP_PROPS);
-                         return;
-                 } else {
-                         bzero(&in_progress_nwiftab.zone_nwif_defrouter,
-                             sizeof (in_progress_nwiftab.zone_nwif_defrouter));
-                         return;
-                 }
          default:
                  zone_perror(rt_to_str(res_type), Z_NO_RESOURCE_TYPE, B_TRUE);
                  long_usage(CMD_REMOVE, B_TRUE);
                  usage(B_FALSE, HELP_RESOURCES);
                  return;
--- 3741,3750 ----
*** 3594,3614 ****
                  }
                  break;
          case RT_MCAP:
                  switch (prop_type) {
                  case PT_PHYSICAL:
!                         in_progress_mcaptab.zone_physmem_cap[0] = '\0';
!                         need_to_commit = B_TRUE;
                          return;
                  case PT_SWAP:
                          remove_aliased_rctl(PT_SWAP, ALIAS_MAXSWAP);
                          return;
                  case PT_LOCKED:
                          remove_aliased_rctl(PT_LOCKED, ALIAS_MAXLOCKEDMEM);
                          return;
                  }
                  break;
          default:
                  break;
          }
  
          zone_perror(pt_to_str(prop_type), Z_CLEAR_DISALLOW, B_TRUE);
--- 3803,3846 ----
                  }
                  break;
          case RT_MCAP:
                  switch (prop_type) {
                  case PT_PHYSICAL:
!                         remove_aliased_rctl(PT_PHYSICAL, ALIAS_MAXPHYSMEM);
                          return;
                  case PT_SWAP:
                          remove_aliased_rctl(PT_SWAP, ALIAS_MAXSWAP);
                          return;
                  case PT_LOCKED:
                          remove_aliased_rctl(PT_LOCKED, ALIAS_MAXLOCKEDMEM);
                          return;
                  }
                  break;
+         case RT_NET:
+                 switch (prop_type) {
+                 case PT_ALLOWED_ADDRESS:
+                         in_progress_nwiftab.zone_nwif_allowed_address[0] = '\0';
+                         need_to_commit = B_TRUE;
+                         return;
+                 case PT_DEFROUTER:
+                         in_progress_nwiftab.zone_nwif_defrouter[0] = '\0';
+                         need_to_commit = B_TRUE;
+                         return;
+                 case PT_GNIC:
+                         in_progress_nwiftab.zone_nwif_gnic[0] = '\0';
+                         need_to_commit = B_TRUE;
+                         return;
+                 case PT_MAC:
+                         in_progress_nwiftab.zone_nwif_mac[0] = '\0';
+                         need_to_commit = B_TRUE;
+                         return;
+                 case PT_VLANID:
+                         in_progress_nwiftab.zone_nwif_vlan_id[0] = '\0';
+                         need_to_commit = B_TRUE;
+                         return;
+                 }
+                 break;
          default:
                  break;
          }
  
          zone_perror(pt_to_str(prop_type), Z_CLEAR_DISALLOW, B_TRUE);
*** 3737,3747 ****
  }
  
  void
  select_func(cmd_t *cmd)
  {
!         int type, err, res;
          uint64_t limit;
          uint64_t tmp;
  
          if (zone_is_read_only(CMD_SELECT))
                  return;
--- 3969,3979 ----
  }
  
  void
  select_func(cmd_t *cmd)
  {
!         int type, err;
          uint64_t limit;
          uint64_t tmp;
  
          if (zone_is_read_only(CMD_SELECT))
                  return;
*** 3832,3856 ****
                          global_scope = B_TRUE;
                  }
                  return;
          case RT_MCAP:
                  /* if none of these exist, there is no resource to select */
!                 if ((res = zonecfg_lookup_mcap(handle, &old_mcaptab)) != Z_OK &&
                      zonecfg_get_aliased_rctl(handle, ALIAS_MAXSWAP, &limit)
                      != Z_OK &&
                      zonecfg_get_aliased_rctl(handle, ALIAS_MAXLOCKEDMEM, &limit)
                      != Z_OK) {
                          z_cmd_rt_perror(CMD_SELECT, RT_MCAP, Z_NO_RESOURCE_TYPE,
                              B_TRUE);
                          global_scope = B_TRUE;
                  }
-                 if (res == Z_OK)
-                         bcopy(&old_mcaptab, &in_progress_mcaptab,
-                             sizeof (struct zone_mcaptab));
-                 else
-                         bzero(&in_progress_mcaptab,
-                             sizeof (in_progress_mcaptab));
                  return;
          case RT_ADMIN:
                  if ((err = fill_in_admintab(cmd, &old_admintab, B_FALSE))
                      != Z_OK) {
                          z_cmd_rt_perror(CMD_SELECT, RT_ADMIN, err,
--- 4064,4083 ----
                          global_scope = B_TRUE;
                  }
                  return;
          case RT_MCAP:
                  /* if none of these exist, there is no resource to select */
!                 if (zonecfg_get_aliased_rctl(handle, ALIAS_MAXPHYSMEM, &limit)
!                     != Z_OK &&
                      zonecfg_get_aliased_rctl(handle, ALIAS_MAXSWAP, &limit)
                      != Z_OK &&
                      zonecfg_get_aliased_rctl(handle, ALIAS_MAXLOCKEDMEM, &limit)
                      != Z_OK) {
                          z_cmd_rt_perror(CMD_SELECT, RT_MCAP, Z_NO_RESOURCE_TYPE,
                              B_TRUE);
                          global_scope = B_TRUE;
                  }
                  return;
          case RT_ADMIN:
                  if ((err = fill_in_admintab(cmd, &old_admintab, B_FALSE))
                      != Z_OK) {
                          z_cmd_rt_perror(CMD_SELECT, RT_ADMIN, err,
*** 4113,4125 ****
          int arg, err, res_type, prop_type;
          property_value_ptr_t pp;
          boolean_t autoboot;
          zone_iptype_t iptype;
          boolean_t force_set = B_FALSE;
-         size_t physmem_size = sizeof (in_progress_mcaptab.zone_physmem_cap);
          uint64_t mem_cap, mem_limit;
!         float cap;
          char *unitp;
          struct zone_psettab tmp_psettab;
          boolean_t arg_err = B_FALSE;
  
          if (zone_is_read_only(CMD_SET))
--- 4340,4351 ----
          int arg, err, res_type, prop_type;
          property_value_ptr_t pp;
          boolean_t autoboot;
          zone_iptype_t iptype;
          boolean_t force_set = B_FALSE;
          uint64_t mem_cap, mem_limit;
!         double cap;
          char *unitp;
          struct zone_psettab tmp_psettab;
          boolean_t arg_err = B_FALSE;
  
          if (zone_is_read_only(CMD_SET))
*** 4217,4230 ****
--- 4443,4458 ----
          pp = cmd->cmd_property_ptr[0];
          /*
           * A nasty expression but not that complicated:
           * 1. fs options are simple or list (tested below)
           * 2. rctl value's are complex or list (tested below)
+          * 3. net attr's are complex (tested below)
           * Anything else should be simple.
           */
          if (!(res_type == RT_FS && prop_type == PT_OPTIONS) &&
              !(res_type == RT_RCTL && prop_type == PT_VALUE) &&
+             !(res_type == RT_NET && prop_type == PT_NPROP) &&
              (pp->pv_type != PROP_VAL_SIMPLE ||
              (prop_id = pp->pv_simple) == NULL)) {
                  zerr(gettext("A %s value was expected here."),
                      pvt_to_str(PROP_VAL_SIMPLE));
                  saw_error = B_TRUE;
*** 4480,4489 ****
--- 4708,4732 ----
                          }
                          (void) strlcpy(in_progress_nwiftab.zone_nwif_physical,
                              prop_id,
                              sizeof (in_progress_nwiftab.zone_nwif_physical));
                          break;
+                 case PT_MAC:
+                         (void) strlcpy(in_progress_nwiftab.zone_nwif_mac,
+                             prop_id,
+                             sizeof (in_progress_nwiftab.zone_nwif_mac));
+                         break;
+                 case PT_VLANID:
+                         (void) strlcpy(in_progress_nwiftab.zone_nwif_vlan_id,
+                             prop_id,
+                             sizeof (in_progress_nwiftab.zone_nwif_vlan_id));
+                         break;
+                 case PT_GNIC:
+                         (void) strlcpy(in_progress_nwiftab.zone_nwif_gnic,
+                             prop_id,
+                             sizeof (in_progress_nwiftab.zone_nwif_gnic));
+                         break;
                  case PT_DEFROUTER:
                          if (validate_net_address_syntax(prop_id, B_TRUE)
                              != Z_OK) {
                                  saw_error = B_TRUE;
                                  return;
*** 4490,4499 ****
--- 4733,4756 ----
                          }
                          (void) strlcpy(in_progress_nwiftab.zone_nwif_defrouter,
                              prop_id,
                              sizeof (in_progress_nwiftab.zone_nwif_defrouter));
                          break;
+                 case PT_NPROP:
+                         if (pp->pv_type != PROP_VAL_COMPLEX) {
+                                 zerr(gettext("A %s value was expected here."),
+                                     pvt_to_str(PROP_VAL_COMPLEX));
+                                 saw_error = B_TRUE;
+                                 return;
+                         }
+                         zonecfg_free_res_attr_list(
+                             in_progress_nwiftab.zone_nwif_attrp);
+                         in_progress_nwiftab.zone_nwif_attrp = NULL;
+                         if (!(pp->pv_type == PROP_VAL_LIST &&
+                             pp->pv_list == NULL))
+                                 add_property(cmd);
+                         break;
                  default:
                          zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE,
                              B_TRUE);
                          long_usage(CMD_SET, B_TRUE);
                          usage(B_FALSE, HELP_PROPS);
*** 4505,4514 ****
--- 4762,4785 ----
                  case PT_MATCH:
                          (void) strlcpy(in_progress_devtab.zone_dev_match,
                              prop_id,
                              sizeof (in_progress_devtab.zone_dev_match));
                          break;
+                 case PT_NPROP:
+                         if (pp->pv_type != PROP_VAL_COMPLEX) {
+                                 zerr(gettext("A %s value was expected here."),
+                                     pvt_to_str(PROP_VAL_COMPLEX));
+                                 saw_error = B_TRUE;
+                                 return;
+                         }
+                         zonecfg_free_res_attr_list(
+                             in_progress_devtab.zone_dev_attrp);
+                         in_progress_devtab.zone_dev_attrp = NULL;
+                         if (!(pp->pv_type == PROP_VAL_LIST &&
+                             pp->pv_list == NULL))
+                                 add_property(cmd);
+                         break;
                  default:
                          zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE,
                              B_TRUE);
                          long_usage(CMD_SET, B_TRUE);
                          usage(B_FALSE, HELP_PROPS);
*** 4648,4686 ****
                  /*
                   * We already checked that an rctl alias is allowed in
                   * the add_resource() function.
                   */
  
!                 if ((cap = strtof(prop_id, &unitp)) <= 0 || *unitp != '\0' ||
!                     (int)(cap * 100) < 1) {
                          zerr(gettext("%s property is out of range."),
                              pt_to_str(PT_NCPUS));
                          saw_error = B_TRUE;
                          return;
                  }
  
                  if ((err = zonecfg_set_aliased_rctl(handle, ALIAS_CPUCAP,
!                     (int)(cap * 100))) != Z_OK)
                          zone_perror(zone, err, B_TRUE);
!                 else
                          need_to_commit = B_TRUE;
                  return;
          case RT_MCAP:
                  switch (prop_type) {
                  case PT_PHYSICAL:
                          if (!zonecfg_valid_memlimit(prop_id, &mem_cap)) {
!                                 zerr(gettext("A positive number with a "
                                      "required scale suffix (K, M, G or T) was "
!                                     "expected here."));
                                  saw_error = B_TRUE;
-                         } else if (mem_cap < ONE_MB) {
-                                 zerr(gettext("%s value is too small.  It must "
-                                     "be at least 1M."), pt_to_str(PT_PHYSICAL));
-                                 saw_error = B_TRUE;
                          } else {
!                                 snprintf(in_progress_mcaptab.zone_physmem_cap,
!                                     physmem_size, "%llu", mem_cap);
                          }
                          break;
                  case PT_SWAP:
                          /*
                           * We have to check if an rctl is allowed here since
--- 4919,4972 ----
                  /*
                   * We already checked that an rctl alias is allowed in
                   * the add_resource() function.
                   */
  
!                 if ((cap = strtod(prop_id, &unitp)) <= 0 || *unitp != '\0' ||
!                     (cap * 100.0) < 1) {
                          zerr(gettext("%s property is out of range."),
                              pt_to_str(PT_NCPUS));
                          saw_error = B_TRUE;
                          return;
                  }
+                 cap *= 100.0;
  
+                 /* To avoid rounding issues add .5 to force correct value. */
                  if ((err = zonecfg_set_aliased_rctl(handle, ALIAS_CPUCAP,
!                     (uint_t)(cap + 0.5))) != Z_OK) {
                          zone_perror(zone, err, B_TRUE);
!                 } else {
                          need_to_commit = B_TRUE;
+                 }
                  return;
          case RT_MCAP:
                  switch (prop_type) {
                  case PT_PHYSICAL:
+                         /*
+                          * We have to check if an rctl is allowed here since
+                          * there might already be a rctl defined that blocks
+                          * the alias.
+                          */
+                         if (!zonecfg_aliased_rctl_ok(handle,
+                             ALIAS_MAXPHYSMEM)) {
+                                 zone_perror(pt_to_str(PT_LOCKED),
+                                     Z_ALIAS_DISALLOW, B_FALSE);
+                                 saw_error = B_TRUE;
+                                 return;
+                         }
+ 
                          if (!zonecfg_valid_memlimit(prop_id, &mem_cap)) {
!                                 zerr(gettext("A non-negative number with a "
                                      "required scale suffix (K, M, G or T) was "
!                                     "expected\nhere."));
                                  saw_error = B_TRUE;
                          } else {
!                                 if ((err = zonecfg_set_aliased_rctl(handle,
!                                     ALIAS_MAXPHYSMEM, mem_cap)) != Z_OK)
!                                         zone_perror(zone, err, B_TRUE);
!                                 else
!                                         need_to_commit = B_TRUE;
                          }
                          break;
                  case PT_SWAP:
                          /*
                           * We have to check if an rctl is allowed here since
*** 5029,5044 ****
  }
  
  static void
  output_net(FILE *fp, struct zone_nwiftab *nwiftab)
  {
          (void) fprintf(fp, "%s:\n", rt_to_str(RT_NET));
          output_prop(fp, PT_ADDRESS, nwiftab->zone_nwif_address, B_TRUE);
          output_prop(fp, PT_ALLOWED_ADDRESS,
              nwiftab->zone_nwif_allowed_address, B_TRUE);
-         output_prop(fp, PT_PHYSICAL, nwiftab->zone_nwif_physical, B_TRUE);
          output_prop(fp, PT_DEFROUTER, nwiftab->zone_nwif_defrouter, B_TRUE);
  }
  
  static void
  info_net(zone_dochandle_t handle, FILE *fp, cmd_t *cmd)
  {
--- 5315,5343 ----
  }
  
  static void
  output_net(FILE *fp, struct zone_nwiftab *nwiftab)
  {
+         struct zone_res_attrtab *np;
+ 
          (void) fprintf(fp, "%s:\n", rt_to_str(RT_NET));
          output_prop(fp, PT_ADDRESS, nwiftab->zone_nwif_address, B_TRUE);
          output_prop(fp, PT_ALLOWED_ADDRESS,
              nwiftab->zone_nwif_allowed_address, B_TRUE);
          output_prop(fp, PT_DEFROUTER, nwiftab->zone_nwif_defrouter, B_TRUE);
+         output_prop(fp, PT_GNIC, nwiftab->zone_nwif_gnic, B_TRUE);
+         output_prop(fp, PT_MAC, nwiftab->zone_nwif_mac, B_TRUE);
+         output_prop(fp, PT_PHYSICAL, nwiftab->zone_nwif_physical, B_TRUE);
+         output_prop(fp, PT_VLANID, nwiftab->zone_nwif_vlan_id, B_TRUE);
+ 
+         for (np = nwiftab->zone_nwif_attrp; np != NULL;
+             np = np->zone_res_attr_next) {
+                 fprintf(fp, "\t%s: (%s=%s,%s=\"%s\")\n",
+                     pt_to_str(PT_NPROP),
+                     pt_to_str(PT_NAME), np->zone_res_attr_name,
+                     pt_to_str(PT_VALUE), np->zone_res_attr_value);
+         }
  }
  
  static void
  info_net(zone_dochandle_t handle, FILE *fp, cmd_t *cmd)
  {
*** 5077,5088 ****
--- 5376,5397 ----
  }
  
  static void
  output_dev(FILE *fp, struct zone_devtab *devtab)
  {
+         struct zone_res_attrtab *np;
+ 
          (void) fprintf(fp, "%s:\n", rt_to_str(RT_DEVICE));
          output_prop(fp, PT_MATCH, devtab->zone_dev_match, B_TRUE);
+ 
+         for (np = devtab->zone_dev_attrp; np != NULL;
+             np = np->zone_res_attr_next) {
+                 fprintf(fp, "\t%s: (%s=%s,%s=\"%s\")\n",
+                     pt_to_str(PT_NPROP),
+                     pt_to_str(PT_NAME), np->zone_res_attr_name,
+                     pt_to_str(PT_VALUE), np->zone_res_attr_value);
+         }
  }
  
  static void
  info_dev(zone_dochandle_t handle, FILE *fp, cmd_t *cmd)
  {
*** 5337,5355 ****
          else
                  (void) snprintf(buf, bufsize, "%llu%c", num, *up);
  }
  
  static void
! output_mcap(FILE *fp, struct zone_mcaptab *mcaptab, int showswap,
      uint64_t maxswap, int showlocked, uint64_t maxlocked)
  {
          char buf[128];
  
          (void) fprintf(fp, "%s:\n", rt_to_str(RT_MCAP));
!         if (mcaptab->zone_physmem_cap[0] != '\0') {
!                 bytes_to_units(mcaptab->zone_physmem_cap, buf, sizeof (buf));
!                 output_prop(fp, PT_PHYSICAL, buf, B_TRUE);
          }
  
          if (showswap == Z_OK) {
                  (void) snprintf(buf, sizeof (buf), "%llu", maxswap);
                  bytes_to_units(buf, buf, sizeof (buf));
--- 5646,5667 ----
          else
                  (void) snprintf(buf, bufsize, "%llu%c", num, *up);
  }
  
  static void
! output_mcap(FILE *fp, int showphys, uint64_t maxphys, int showswap,
      uint64_t maxswap, int showlocked, uint64_t maxlocked)
  {
          char buf[128];
  
          (void) fprintf(fp, "%s:\n", rt_to_str(RT_MCAP));
! 
!         if (showphys == Z_OK) {
!                 (void) snprintf(buf, sizeof (buf), "%llu", maxphys);
!                 bytes_to_units(buf, buf, sizeof (buf));
!                 /* Print directly since "physical" also is a net property. */
!                 (void) fprintf(fp, "\t[%s: %s]\n", pt_to_str(PT_PHYSICAL), buf);
          }
  
          if (showswap == Z_OK) {
                  (void) snprintf(buf, sizeof (buf), "%llu", maxswap);
                  bytes_to_units(buf, buf, sizeof (buf));
*** 5367,5386 ****
  info_mcap(zone_dochandle_t handle, FILE *fp)
  {
          int res1, res2, res3;
          uint64_t swap_limit;
          uint64_t locked_limit;
!         struct zone_mcaptab lookup;
  
!         bzero(&lookup, sizeof (lookup));
!         res1 = zonecfg_getmcapent(handle, &lookup);
          res2 = zonecfg_get_aliased_rctl(handle, ALIAS_MAXSWAP, &swap_limit);
          res3 = zonecfg_get_aliased_rctl(handle, ALIAS_MAXLOCKEDMEM,
              &locked_limit);
  
          if (res1 == Z_OK || res2 == Z_OK || res3 == Z_OK)
!                 output_mcap(fp, &lookup, res2, swap_limit, res3, locked_limit);
  }
  
  static void
  output_auth(FILE *fp, struct zone_admintab *admintab)
  {
--- 5679,5698 ----
  info_mcap(zone_dochandle_t handle, FILE *fp)
  {
          int res1, res2, res3;
          uint64_t swap_limit;
          uint64_t locked_limit;
!         uint64_t phys_limit;
  
!         res1 = zonecfg_get_aliased_rctl(handle, ALIAS_MAXPHYSMEM, &phys_limit);
          res2 = zonecfg_get_aliased_rctl(handle, ALIAS_MAXSWAP, &swap_limit);
          res3 = zonecfg_get_aliased_rctl(handle, ALIAS_MAXLOCKEDMEM,
              &locked_limit);
  
          if (res1 == Z_OK || res2 == Z_OK || res3 == Z_OK)
!                 output_mcap(fp, res1, phys_limit, res2, swap_limit,
!                     res3, locked_limit);
  }
  
  static void
  output_auth(FILE *fp, struct zone_admintab *admintab)
  {
*** 5427,5439 ****
  info_func(cmd_t *cmd)
  {
          FILE *fp = stdout;
          boolean_t need_to_close = B_FALSE;
          int type;
!         int res1, res2;
          uint64_t swap_limit;
          uint64_t locked_limit;
  
          assert(cmd != NULL);
  
          if (initialize(B_TRUE) != Z_OK)
                  return;
--- 5739,5752 ----
  info_func(cmd_t *cmd)
  {
          FILE *fp = stdout;
          boolean_t need_to_close = B_FALSE;
          int type;
!         int res1, res2, res3;
          uint64_t swap_limit;
          uint64_t locked_limit;
+         uint64_t phys_limit;
  
          assert(cmd != NULL);
  
          if (initialize(B_TRUE) != Z_OK)
                  return;
*** 5477,5487 ****
                  case RT_MCAP:
                          res1 = zonecfg_get_aliased_rctl(handle, ALIAS_MAXSWAP,
                              &swap_limit);
                          res2 = zonecfg_get_aliased_rctl(handle,
                              ALIAS_MAXLOCKEDMEM, &locked_limit);
!                         output_mcap(fp, &in_progress_mcaptab, res1, swap_limit,
                              res2, locked_limit);
                          break;
                  case RT_ADMIN:
                          output_auth(fp, &in_progress_admintab);
                          break;
--- 5790,5802 ----
                  case RT_MCAP:
                          res1 = zonecfg_get_aliased_rctl(handle, ALIAS_MAXSWAP,
                              &swap_limit);
                          res2 = zonecfg_get_aliased_rctl(handle,
                              ALIAS_MAXLOCKEDMEM, &locked_limit);
!                         res3 = zonecfg_get_aliased_rctl(handle,
!                             ALIAS_MAXPHYSMEM, &phys_limit);
!                         output_mcap(fp, res3, phys_limit, res1, swap_limit,
                              res2, locked_limit);
                          break;
                  case RT_ADMIN:
                          output_auth(fp, &in_progress_admintab);
                          break;
*** 6099,6108 ****
--- 6414,6429 ----
                          ret_val = Z_INSUFFICIENT_SPEC;
          }
  
          if (save) {
                  if (ret_val == Z_OK) {
+                         /*
+                          * If the zone doesn't yet have a debug ID, set one now.
+                          */
+                         if (zonecfg_get_did(handle) == -1)
+                                 zonecfg_set_did(handle);
+ 
                          if ((ret_val = zonecfg_save(handle)) == Z_OK) {
                                  need_to_commit = B_FALSE;
                                  (void) strlcpy(revert_zone, zone,
                                      sizeof (revert_zone));
                          }
*** 6273,6282 ****
--- 6594,6604 ----
          struct zone_dstab tmp_dstab;
          struct zone_admintab tmp_admintab;
          int err, arg, res1, res2, res3;
          uint64_t swap_limit;
          uint64_t locked_limit;
+         uint64_t phys_limit;
          uint64_t proc_cap;
  
          assert(cmd != NULL);
  
          optind = 0;
*** 6576,6587 ****
                  }
                  err = Z_OK;
                  break;
          case RT_MCAP:
                  /* Make sure everything was filled in. */
!                 res1 = strlen(in_progress_mcaptab.zone_physmem_cap) == 0 ?
!                     Z_ERR : Z_OK;
                  res2 = zonecfg_get_aliased_rctl(handle, ALIAS_MAXSWAP,
                      &swap_limit);
                  res3 = zonecfg_get_aliased_rctl(handle, ALIAS_MAXLOCKEDMEM,
                      &locked_limit);
  
--- 6898,6909 ----
                  }
                  err = Z_OK;
                  break;
          case RT_MCAP:
                  /* Make sure everything was filled in. */
!                 res1 = zonecfg_get_aliased_rctl(handle, ALIAS_MAXPHYSMEM,
!                     &phys_limit);
                  res2 = zonecfg_get_aliased_rctl(handle, ALIAS_MAXSWAP,
                      &swap_limit);
                  res3 = zonecfg_get_aliased_rctl(handle, ALIAS_MAXLOCKEDMEM,
                      &locked_limit);
  
*** 6593,6607 ****
                          return;
                  }
  
                  /* if phys & locked are both set, verify locked <= phys */
                  if (res1 == Z_OK && res3 == Z_OK) {
-                         uint64_t phys_limit;
-                         char *endp;
- 
-                         phys_limit = strtoull(
-                             in_progress_mcaptab.zone_physmem_cap, &endp, 10);
                          if (phys_limit < locked_limit) {
                                  zerr(gettext("The %s cap must be less than or "
                                      "equal to the %s cap."),
                                      pt_to_str(PT_LOCKED),
                                      pt_to_str(PT_PHYSICAL));
--- 6915,6924 ----
*** 6609,6635 ****
                                  return;
                          }
                  }
  
                  err = Z_OK;
-                 if (res1 == Z_OK) {
-                         /*
-                          * We could be ending from either an add operation
-                          * or a select operation.  Since all of the properties
-                          * within this resource are optional, we always use
-                          * modify on the mcap entry.  zonecfg_modify_mcap()
-                          * will handle both adding and modifying a memory cap.
-                          */
-                         err = zonecfg_modify_mcap(handle, &in_progress_mcaptab);
-                 } else if (end_op == CMD_SELECT) {
-                         /*
-                          * If we're ending from a select and the physical
-                          * memory cap is empty then the user could have cleared
-                          * the physical cap value, so try to delete the entry.
-                          */
-                         (void) zonecfg_delete_mcap(handle);
-                 }
                  break;
          case RT_ADMIN:
                  /* First make sure everything was filled in. */
                  if (end_check_reqd(in_progress_admintab.zone_admin_user,
                      PT_USER, &validation_failed) == Z_OK) {
--- 6926,6935 ----