17421 name key order preservation
authorDavid Powell <david.e.powell@oracle.com>
Tue, 23 Nov 2010 15:54:20 -0800
changeset 604 20d9acfeb7fb
parent 603 71a20acea802
child 605 c5fc1a2832c4
17421 name key order preservation 17422 rad dynamic namespace support 17423 rad kstat module
usr/src/apis/kstat.xml
usr/src/cmd/pyclient/kstat_demo.py
usr/src/cmd/rad/mdb/rad.c
usr/src/cmd/rad/mod/Makefile
usr/src/cmd/rad/mod/kstat/Makefile
usr/src/cmd/rad/mod/kstat/kstat.c
usr/src/cmd/rad/mod/proto_rad/mod_proto_rad.c
usr/src/cmd/rad/mod/smf/mod_smf.c
usr/src/cmd/rad/rad.c
usr/src/cmd/rad/rad_container.c
usr/src/cmd/rad/rad_container.h
usr/src/cmd/rad/rad_modapi.h
usr/src/cmd/rad/rad_object.c
usr/src/cmd/rad/rad_object.h
usr/src/cmd/rad/rad_pam.c
usr/src/cmd/test/libadr/name.c
usr/src/java/rad/org/opensolaris/os/rad/ADRName.java
usr/src/lib/libadr/common/adr.c
usr/src/lib/libadr/common/adr.h
usr/src/lib/libadr/common/adr_name.c
usr/src/lib/pykstat/Makefile
usr/src/lib/pykstat/__init__.py
usr/src/lib/pykstat/rad.py
usr/src/pkg/manifests/system-management-rad-kstat.mf
usr/src/pkg/manifests_wos/system-management-visual-panels.content
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/apis/kstat.xml	Tue Nov 23 15:54:20 2010 -0800
@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!--
+ Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+
+ CDDL HEADER START
+
+  The contents of this file are subject to the terms of the
+  Common Development and Distribution License (the "License").
+  You may not use this file except in compliance with the License.
+ 
+  You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+  or http://www.opensolaris.org/os/licensing.
+  See the License for the specific language governing permissions
+  and limitations under the License.
+ 
+  When distributing Covered Code, include this CDDL HEADER in each
+  file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+  If applicable, add the following below this CDDL HEADER, with the
+  fields enclosed by brackets "[]" replaced with your own identifying
+  information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+-->
+
+<interface xmlns="http://www.opensolaris.org/ns/adr" name="kstat">
+
+	<pragma domain="java" name="package" value="org.opensolaris.os.kstat" />
+
+	<!-- Example of where it would be nice to have a native union type -->
+	<type name="union">
+		<field type="string" name="str" optional="true" />
+		<field type="integer" name="i32" />
+		<field type="uinteger" name="ui32" />
+		<field type="long" name="i64" />
+		<field type="ulong" name="ui64" />
+		<field type="float" name="f" />
+		<field type="double" name="d" />
+	</type>
+
+	<enum name="ksdtype">
+		<value name="char" />
+		<value name="int32" />
+		<value name="uint32" />
+		<value name="int64" />
+		<value name="uint64" />
+		<value name="float" />
+		<value name="double" />
+		<value name="string" />
+	</enum>
+
+	<enum name="kstype">
+		<value name="raw" />
+		<value name="named" />
+		<value name="intr" />
+		<value name="io" />
+		<value name="timer" />
+	</enum>
+
+	<type name="statistic">
+		<field type="string" name="name" />
+		<field typeref="ksdtype" name="type" />
+		<field typeref="union" name="value" />
+	</type>
+
+	<type name="snapshot">
+		<field type="ulong" name="timestamp" />
+		<field name="statistics">
+			<list typeref="statistic" />
+		</field>
+	</type>
+
+	<api name="kstat">
+		<version major="0" minor="1" />
+
+		<property type="string" name="module" access="ro" />
+		<property type="string" name="name" access="ro" />
+		<property type="string" name="class" access="ro" />
+		<property type="integer" name="instance" access="ro" />
+		<property typeref="kstype" name="kstatType" access="ro" />
+		<property type="ulong" name="creationTime" access="ro" />
+		<property type="boolean" name="stale" access="ro" />
+
+		<property typeref="snapshot" name="value" access="ro">
+			<!-- Stale handle or kstatType != named -->
+			<error />
+		</property>
+
+		<method name="get_fresh_value">
+			<result typeref="snapshot" />
+			<!-- Stale handle or kstatType != named -->
+			<error />
+		</method>
+	</api>
+
+	<api name="kstatctl">
+		<version major="0" minor="1" />
+
+		<method name="update_all" />
+	</api>
+</interface>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/pyclient/kstat_demo.py	Tue Nov 23 15:54:20 2010 -0800
@@ -0,0 +1,46 @@
+#!/usr/bin/python2.6
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+#
+
+import kstat.rad
+import time
+
+last = [ 0, 0, 0, 0 ]
+flds = [ "cpu_nsec_idle", "cpu_nsec_user", "cpu_nsec_kernel", "cpu_nsec_intr" ]
+frmt = "Idle: %.2f%%  User: %.2f%%  Kern: %.2f%%  Intr: %.2f%%"
+
+def print_diff(prev, next):
+	diffs = [ next[i] - prev[i] for i in range(4) ]
+	total = sum(diffs)
+	print frmt % tuple([ float(x) * 100 / total for x in diffs ])
+
+stats = kstat.rad.Kstats()			# get kstat connection
+cpu = stats.lookup("cpu", "sys", 0)		# get cpu 0's kstat object
+
+while True:
+	stats.update()				# update kstats
+	s = cpu.value				# fetch new kstat
+	this = [ s.get(x) for x in flds ]	# pick out interesting stats
+	print_diff(last, this)			# pretty-print
+	last = this				# new is now old
+	time.sleep(1)				# wait
--- a/usr/src/cmd/rad/mdb/rad.c	Mon Nov 22 10:50:58 2010 -0500
+++ b/usr/src/cmd/rad/mdb/rad.c	Tue Nov 23 15:54:20 2010 -0800
@@ -23,6 +23,8 @@
  */
 
 #include <sys/mdb_modapi.h>
+#include <sys/list_impl.h>
+#include <stddef.h>
 
 #include "../rad_object.h"
 #include "../rad_container.h"
@@ -75,9 +77,9 @@
 }
 
 typedef struct iwalk_data {
-	int	iwd_n;
-	int	iwd_i;
-	uintptr_t *iwd_instances;
+	size_t	iwd_offset;
+	uintptr_t iwd_next;
+	uintptr_t iwd_start;
 } iwalk_data_t;
 
 static int
@@ -91,18 +93,19 @@
 		return (WALK_ERR);
 	}
 
-	size_t isize = cont.c_ninstances * sizeof (uintptr_t);
-	uintptr_t *instances = mdb_alloc(isize, UM_SLEEP | UM_GC);
-	iwalk_data_t *data = mdb_alloc(sizeof (iwalk_data_t), UM_SLEEP | UM_GC);
-
-	if (mdb_vread(instances, isize, (uintptr_t)cont.c_instances) == -1) {
-		mdb_warn("failed to read instance array");
+	if (cont.c_instances.list_offset != offsetof(rad_instance_t, i_cnode) ||
+	    cont.c_instances.list_size != sizeof (rad_instance_t)) {
+		mdb_warn("invalid/corrupt rad_container_t or "
+		    "mismatched mdb module\n");
 		return (WALK_ERR);
 	}
 
-	data->iwd_i = 0;
-	data->iwd_n = cont.c_ninstances;
-	data->iwd_instances = instances;
+	/* Unfortunately, the list_t walker is buried in the genunix module */
+	iwalk_data_t *data = mdb_alloc(sizeof (iwalk_data_t), UM_SLEEP | UM_GC);
+	data->iwd_offset = cont.c_instances.list_offset;
+	data->iwd_next = (uintptr_t)cont.c_instances.list_head.list_next;
+	data->iwd_start =
+	    wsp->walk_addr + offsetof(rad_container_t, c_instances.list_head);
 	wsp->walk_data = data;
 
 	return (WALK_NEXT);
@@ -115,15 +118,16 @@
 	uintptr_t addr;
 	rad_instance_t inst;
 
-	if (data->iwd_i >= data->iwd_n)
+	if (data->iwd_next == data->iwd_start)
 		return (WALK_DONE);
 
-	addr = data->iwd_instances[data->iwd_i++];
+	addr = data->iwd_next - data->iwd_offset;
 	if (mdb_vread(&inst, sizeof (rad_instance_t), addr) == -1) {
 		mdb_warn("error reading rad_instance_t at %p, skipping\n",
 		    addr);
 		return (WALK_NEXT);
 	}
+	data->iwd_next = (uintptr_t)inst.i_cnode.list_next;
 
 	return (wsp->walk_callback(addr, &inst, wsp->walk_cbdata));
 }
--- a/usr/src/cmd/rad/mod/Makefile	Mon Nov 22 10:50:58 2010 -0500
+++ b/usr/src/cmd/rad/mod/Makefile	Tue Nov 23 15:54:20 2010 -0800
@@ -30,6 +30,7 @@
 MODULE_SUBDIRS = \
 	example-time2 \
 	files \
+	kstat \
         network \
 	panels \
 	sharemgr \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/rad/mod/kstat/Makefile	Tue Nov 23 15:54:20 2010 -0800
@@ -0,0 +1,34 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+#
+
+include ../Makefile.env
+
+LDLIBS += -lkstat
+MOD_APIS=kstat
+MOD_OBJS=kstat.o
+MOD_LIBNAME=kstat.so
+MOD_INSTALLDIR=$(RADDIR_MODULE)
+
+include ../Makefile.com
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/rad/mod/kstat/kstat.c	Tue Nov 23 15:54:20 2010 -0800
@@ -0,0 +1,504 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ */
+
+#include <sys/list.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <kstat.h>
+#include <pthread.h>
+#include <string.h>
+#include <assert.h>
+
+#include <rad/adr.h>
+#include <rad/adr_object.h>
+#include <rad/rad_modapi.h>
+
+#include "api_kstat.h"
+
+#define	KSTAT_DOMAIN	"com.oracle.solaris.kstat"
+
+static pthread_mutex_t kstat_lock = PTHREAD_MUTEX_INITIALIZER;
+static list_t kstat_list;
+static kstat_ctl_t *kc;
+
+typedef struct kstat_inst {
+	adr_name_t *ki_name;	/* full name */
+	int ki_instance;	/* instance number */
+	uchar_t ki_type;	/* last known type */
+	hrtime_t ki_crtime;	/* last known creation time */
+
+	kstat_t *ki_ks;		/* current kstat header, if any */
+	boolean_t ki_read;	/* kstat data has been read */
+	rad_instance_t *ki_inst;
+	list_node_t *ki_node;
+} kstat_inst_t;
+
+static void
+kstat_inst_unread_all(void)
+{
+	for (kstat_inst_t *kid = list_head(&kstat_list); kid;
+	    kid = list_next(&kstat_list, kid))
+		kid->ki_read = B_FALSE;
+}
+
+static void
+kstat_inst_invalidate_all(void)
+{
+	for (kstat_inst_t *kid = list_head(&kstat_list); kid;
+	    kid = list_next(&kstat_list, kid)) {
+		kid->ki_read = B_FALSE;
+		kid->ki_ks = NULL;
+	}
+}
+
+static void
+kstat_inst_update(void)
+{
+	while (kstat_chain_update(kc))
+		kstat_inst_invalidate_all();
+	kstat_inst_unread_all();
+}
+
+static boolean_t
+kstat_inst_validate_int(kstat_inst_t *kid)
+{
+	if (kid->ki_ks == NULL) {
+		const char *kname = adr_name_key(kid->ki_name, "name");
+		const char *kmodule = adr_name_key(kid->ki_name, "module");
+		if ((kid->ki_ks = kstat_lookup(kc, (char *)kmodule,
+		    kid->ki_instance, (char *)kname)) == NULL)
+			return (B_FALSE);
+		kid->ki_crtime = kid->ki_ks->ks_crtime;
+		kid->ki_type = kid->ki_ks->ks_type;
+		assert(!kid->ki_read);
+	}
+
+	return (B_TRUE);
+}
+
+static boolean_t
+kstat_inst_validate(kstat_inst_t *kid)
+{
+	rad_mutex_enter(&kstat_lock);
+	if (!kstat_inst_validate_int(kid)) {
+		rad_mutex_exit(&kstat_lock);
+		return (B_FALSE);
+	}
+
+	return (B_TRUE);
+}
+
+static boolean_t
+kstat_inst_read(kstat_inst_t *kid, boolean_t fresh)
+{
+	rad_mutex_enter(&kstat_lock);
+	do {
+		if (!kstat_inst_validate_int(kid)) {
+			rad_mutex_exit(&kstat_lock);
+			return (B_FALSE);
+		}
+		if (!fresh && kid->ki_read)
+			return (B_TRUE);
+	} while (kstat_read(kc, kid->ki_ks, NULL) == -1);
+
+	kid->ki_read = B_TRUE;
+	return (B_TRUE);
+}
+
+static void
+kstat_inst_free(kstat_inst_t *kid)
+{
+	adr_name_rele(kid->ki_name);
+	free(kid);
+}
+
+static adr_name_t *
+kstat_name_create(kstat_t *ks)
+{
+	char instance[100];
+	(void) snprintf(instance, sizeof (instance), "%d", ks->ks_instance);
+
+	return (adr_name_vcreate(KSTAT_DOMAIN, 5,
+	    "type", "kstat",
+	    "module", ks->ks_module,
+	    "name", ks->ks_name,
+	    "class", ks->ks_class,
+	    "instance", instance));
+}
+
+/*
+ * Dynamic namespace callbacks
+ */
+
+static conerr_t
+kstat_listf(adr_name_t *pattern, data_t **names, void *arg)
+{
+	*names = data_new_array(&t_array_string, 0);
+
+	for (kstat_t *ks = kc->kc_chain; ks; ks = ks->ks_next) {
+		/* XXX: round-about filtering; could be much more efficient */
+		adr_name_t *name = kstat_name_create(ks);
+		if (adr_name_match(name, pattern))
+			array_add(*names,
+			    data_new_string(adr_name_tostr(name), lt_free));
+		adr_name_rele(name);
+	}
+
+	return (*names == NULL ? ce_nomem : ce_ok);
+}
+
+static conerr_t
+kstat_lookupf(adr_name_t **name, rad_instance_t **inst, void *arg)
+{
+	adr_name_t *aname = *name;
+	const char *kclass = adr_name_key(aname, "class");
+	const char *kname = adr_name_key(aname, "name");
+	const char *kmodule = adr_name_key(aname, "module");
+	const char *kinst = adr_name_key(aname, "instance");
+	char *end = NULL;
+	int kinstnum;
+
+	if (kname == NULL || kmodule == NULL || kinst == NULL)
+		return (ce_notfound);
+
+	kinstnum = strtol(kinst, &end, 10);
+	if (*end != '\0')
+		return (ce_notfound);
+
+	rad_mutex_enter(&kstat_lock);
+	kstat_t *ks = kstat_lookup(kc, (char *)kmodule, kinstnum,
+	    (char *)kname);
+	if (ks == NULL ||
+	    (kclass != NULL && strcmp(kclass, ks->ks_class) != 0)) {
+		rad_mutex_exit(&kstat_lock);
+		return (ce_notfound);
+	}
+
+	if (kclass == NULL) {
+		/*
+		 * We permit clients to omit the class when doing lookups.
+		 * This results in a second kstat_lookup, but *shrug*.
+		 */
+		adr_name_t *cname = kstat_name_create(ks);
+		rad_mutex_exit(&kstat_lock);
+		if (cname == NULL)
+			return (ce_nomem);
+		adr_name_rele(aname);
+		*name = cname;
+		*inst = NULL;
+		return (ce_ok);
+	}
+
+	kstat_inst_t *kid = malloc(sizeof (kstat_inst_t));
+	if (kid == NULL) {
+		rad_mutex_exit(&kstat_lock);
+		return (ce_nomem);
+	}
+
+	rad_instance_t *i = instance_create(adr_name_hold(aname),
+	    &api_kstat_svr, kid, (void(*)(void *))kstat_inst_free);
+	if (i == NULL) {
+		rad_mutex_exit(&kstat_lock);
+		return (ce_nomem);
+	}
+
+	kid->ki_name = adr_name_hold(aname);
+	kid->ki_instance = kinstnum;
+	kid->ki_ks = ks;
+	kid->ki_read = B_FALSE;
+	kid->ki_inst = i;
+	list_insert_tail(&kstat_list, kid);
+	*inst = i;
+
+	rad_mutex_exit(&kstat_lock);
+
+	return (ce_ok);
+}
+
+/*
+ * "kstat" interface entry points
+ */
+
+conerr_t
+api_kstat_read_module(rad_instance_t *inst, adr_attribute_t *attr,
+    data_t **data, data_t **error)
+{
+	kstat_inst_t *kid = instance_getdata(inst);
+	*data = data_new_string(adr_name_key(kid->ki_name, "module"), lt_copy);
+	return (*data == NULL ? ce_nomem : ce_ok);
+}
+
+conerr_t
+api_kstat_read_name(rad_instance_t *inst, adr_attribute_t *attr,
+    data_t **data, data_t **error)
+{
+	kstat_inst_t *kid = instance_getdata(inst);
+	*data = data_new_string(adr_name_key(kid->ki_name, "name"), lt_copy);
+	return (*data == NULL ? ce_nomem : ce_ok);
+}
+
+conerr_t
+api_kstat_read_class(rad_instance_t *inst, adr_attribute_t *attr,
+    data_t **data, data_t **error)
+{
+	kstat_inst_t *kid = instance_getdata(inst);
+	*data = data_new_string(adr_name_key(kid->ki_name, "class"), lt_copy);
+	return (*data == NULL ? ce_nomem : ce_ok);
+}
+
+conerr_t
+api_kstat_read_instance(rad_instance_t *inst, adr_attribute_t *attr,
+    data_t **data, data_t **error)
+{
+	kstat_inst_t *kid = instance_getdata(inst);
+	*data = data_new_integer(kid->ki_instance);
+	return (*data == NULL ? ce_nomem : ce_ok);
+}
+
+conerr_t
+api_kstat_read_kstatType(rad_instance_t *inst, adr_attribute_t *attr,
+    data_t **data, data_t **error)
+{
+	kstat_inst_t *kid = instance_getdata(inst);
+	if (kstat_inst_validate(kid))
+		rad_mutex_exit(&kstat_lock);
+
+	switch (kid->ki_type) {
+	case KSTAT_TYPE_RAW:
+		*data = &e__kstype_raw;
+		break;
+	case KSTAT_TYPE_NAMED:
+		*data = &e__kstype_named;
+		break;
+	case KSTAT_TYPE_INTR:
+		*data = &e__kstype_intr;
+		break;
+	case KSTAT_TYPE_IO:
+		*data = &e__kstype_io;
+		break;
+	case KSTAT_TYPE_TIMER:
+		*data = &e__kstype_timer;
+		break;
+	default:
+		/* future? *data = &e__kstype_unknown; */
+		*data = &e__kstype_raw;
+	}
+
+	return (ce_ok);
+}
+
+conerr_t
+api_kstat_read_creationTime(rad_instance_t *inst, adr_attribute_t *attr,
+    data_t **data, data_t **error)
+{
+	kstat_inst_t *kid = instance_getdata(inst);
+	if (kstat_inst_validate(kid))
+		rad_mutex_exit(&kstat_lock);
+
+	*data = data_new_ulong(kid->ki_crtime);
+	return (*data == NULL ? ce_nomem : ce_ok);
+}
+
+conerr_t
+api_kstat_read_stale(rad_instance_t *inst, adr_attribute_t *attr,
+    data_t **data, data_t **error)
+{
+	if (kstat_inst_validate(instance_getdata(inst))) {
+		*data = data_new_boolean(B_FALSE);
+		rad_mutex_exit(&kstat_lock);
+	} else {
+		*data = data_new_boolean(B_TRUE);
+	}
+
+	return (ce_ok);
+}
+
+/* Compensate for lacking a discriminated union type */
+static data_t *
+make_union(void)
+{
+	data_t *value = data_new_struct(&t__union);
+	struct_set(value, "i32", data_new_integer(0));
+	struct_set(value, "ui32", data_new_uinteger(0));
+	struct_set(value, "i64", data_new_long(0));
+	struct_set(value, "ui64", data_new_ulong(0));
+	struct_set(value, "f", data_new_float(0));
+	struct_set(value, "d", data_new_double(0));
+	return (value);
+}
+
+/*
+ * Constructs kstat value.  Future: consider caching.
+ */
+static conerr_t
+read_value(rad_instance_t *inst, data_t **data, boolean_t fresh)
+{
+	kstat_inst_t *kid = instance_getdata(inst);
+	if (!kstat_inst_read(kid, fresh))
+		return (ce_object);
+
+	if (kid->ki_ks->ks_type != KSTAT_TYPE_NAMED) {
+		rad_mutex_exit(&kstat_lock);
+		return (ce_object);
+	}
+
+	int n = kid->ki_ks->ks_ndata;
+	kstat_named_t *stats = kid->ki_ks->ks_data;
+
+	data_t *sarray = data_new_array(&t_array__statistic, n);
+	for (int i = 0; i < n; i++) {
+		data_t *type;
+		data_t *value = make_union();
+		switch (stats[i].data_type) {
+		case KSTAT_DATA_CHAR:
+			type = &e__ksdtype_char;
+			struct_set(value, "str",
+			    data_new_nstring(stats[i].value.c,
+			    sizeof (stats[i].value.c)));
+			break;
+		case KSTAT_DATA_INT32:
+		/* case KSTAT_DATA_LONG: */
+			type = &e__ksdtype_int32;
+			struct_set(value, "i32",
+			    data_new_integer(stats[i].value.i32));
+			break;
+		case KSTAT_DATA_STRING:
+			type = &e__ksdtype_string;
+			struct_set(value, "str",
+			    data_new_string(stats[i].value.str.addr.ptr,
+			    lt_copy));
+			break;
+		case KSTAT_DATA_UINT32:
+		/* case KSTAT_DATA_ULONG: */
+			type = &e__ksdtype_uint32;
+			struct_set(value, "ui32",
+			    data_new_uinteger(stats[i].value.ui32));
+			break;
+		case KSTAT_DATA_INT64:
+		/* case KSTAT_DATA_LONGLONG: */
+			type = &e__ksdtype_int64;
+			struct_set(value, "i64",
+			    data_new_long(stats[i].value.i64));
+			break;
+		case KSTAT_DATA_UINT64:
+		/* case KSTAT_DATA_ULONGLONG: */
+			type = &e__ksdtype_uint64;
+			struct_set(value, "ui64",
+			    data_new_ulong(stats[i].value.ui64));
+			break;
+		case KSTAT_DATA_FLOAT:
+			type = &e__ksdtype_float;
+			struct_set(value, "f",
+			    data_new_float(stats[i].value.f));
+			break;
+		case KSTAT_DATA_DOUBLE:
+			type = &e__ksdtype_double;
+			struct_set(value, "d",
+			    data_new_double(stats[i].value.d));
+			break;
+		default:
+			continue;
+		}
+
+		data_t *stat = data_new_struct(&t__statistic);
+		struct_set(stat, "name",
+		    data_new_string(stats[i].name, lt_copy));
+		struct_set(stat, "type", type);
+		struct_set(stat, "value", value);
+		array_add(sarray, stat);
+	}
+
+	data_t *snap = data_new_struct(&t__snapshot);
+	struct_set(snap, "timestamp", data_new_ulong(kid->ki_ks->ks_snaptime));
+	struct_set(snap, "statistics", sarray);
+
+	if (data_verify(snap, NULL, B_TRUE)) {
+		*data = snap;
+	} else {
+		*data = NULL;
+		data_free(snap);
+	}
+
+	rad_mutex_exit(&kstat_lock);
+	return (*data == NULL ? ce_nomem : ce_ok);
+}
+
+conerr_t
+api_kstat_read_value(rad_instance_t *inst, adr_attribute_t *attr,
+    data_t **data, data_t **error)
+{
+	return (read_value(inst, data, B_FALSE));
+}
+
+conerr_t
+api_kstat_invoke_get_fresh_value(rad_instance_t *inst, adr_method_t *meth,
+    data_t **ret, data_t **args, int count, data_t **error)
+{
+	return (read_value(inst, ret, B_TRUE));
+}
+
+/*
+ * "kstatctl" interface entry point
+ */
+
+conerr_t
+api_kstatctl_invoke_update_all(rad_instance_t *inst, adr_method_t *meth,
+    data_t **ret, data_t **args, int count, data_t **error)
+{
+	rad_mutex_enter(&kstat_lock);
+	kstat_inst_update();
+	rad_mutex_exit(&kstat_lock);
+}
+
+static rad_modinfo_t modinfo = { "kstat", "kernel statistics module" };
+
+int
+_rad_init(void *handle)
+{
+	if (rad_module_register(handle, RAD_MODVERSION, &modinfo) == -1)
+		return (-1);
+
+	if (rad_isproxy)
+		return (0);
+
+	adr_name_t *domain = adr_name_vcreate(KSTAT_DOMAIN, 0);
+	if (domain == NULL)
+		return (0);
+
+	if ((kc = kstat_open()) == NULL)
+		return (0);
+
+	list_create(&kstat_list, sizeof (kstat_inst_t),
+	    offsetof(kstat_inst_t, ki_node));
+	(void) cont_register_dynamic(rad_container, domain,
+	    kstat_listf, kstat_lookupf, NULL);
+
+	adr_name_t *ctlname =
+	    adr_name_vcreate(KSTAT_DOMAIN, 1, "type", "control");
+	(void) cont_insert_singleton(rad_container, ctlname, &api_kstatctl_svr);
+
+	return (0);
+}
--- a/usr/src/cmd/rad/mod/proto_rad/mod_proto_rad.c	Mon Nov 22 10:50:58 2010 -0500
+++ b/usr/src/cmd/rad/mod/proto_rad/mod_proto_rad.c	Tue Nov 23 15:54:20 2010 -0800
@@ -435,7 +435,7 @@
 			    &slavenames);
 			int len = array_size(names);
 			if (ce == ce_ok)
-				names = array_combine(names, slavenames);
+				names = array_addall(names, slavenames);
 
 			/* Inefficiently remove duplicates */
 			for (int i = 0; i < len; i++) {
--- a/usr/src/cmd/rad/mod/smf/mod_smf.c	Mon Nov 22 10:50:58 2010 -0500
+++ b/usr/src/cmd/rad/mod/smf/mod_smf.c	Tue Nov 23 15:54:20 2010 -0800
@@ -1805,7 +1805,7 @@
 		objname = adr_name_vcreate("org.opensolaris.os.smf", 2,
 		    "type", "service", "service", sname);
 	}
-	si->inst = instance_create(objname, &api_serviceInfo_svr, si);
+	si->inst = instance_create(objname, &api_serviceInfo_svr, si, NULL);
 	(void) cont_insert(rad_container, si->inst, INST_ID_PICK);
 
 	if (inst) {
@@ -1867,7 +1867,7 @@
 	scf_handle_destroy(scfhandle);
 
 	agg_inst = instance_create(adr_name_fromstr(aggregator_pattern),
-	    &api_aggregator_svr, NULL);
+	    &api_aggregator_svr, NULL, NULL);
 	if (agg_inst != NULL)
 		(void) cont_insert(rad_container, agg_inst, INST_ID_PICK);
 
--- a/usr/src/cmd/rad/rad.c	Mon Nov 22 10:50:58 2010 -0500
+++ b/usr/src/cmd/rad/rad.c	Tue Nov 23 15:54:20 2010 -0800
@@ -55,10 +55,9 @@
 #define	TEXT_DOMAIN	"SYS_TEST"
 #endif
 
-rad_container_t rad_container_auth = CONTAINER_INITIALIZER;
-rad_container_t rad_container_unauth = CONTAINER_INITIALIZER;
-rad_container_t rad_container_control = CONTAINER_INITIALIZER;
-
+rad_container_t rad_container_auth;
+rad_container_t rad_container_unauth;
+rad_container_t rad_container_control;
 rad_container_t *rad_container = &rad_container_auth;
 
 int rad_exit_failure = 1;
@@ -216,10 +215,10 @@
 			rad_loglevel = RL_ALL;
 		if ((moduledir = struct_get(config, "moduledir")) != NULL)
 			rad_moduledirs =
-			    array_combine(rad_moduledirs, data_ref(moduledir));
+			    array_addall(rad_moduledirs, data_ref(moduledir));
 		if ((modules = struct_get(config, "modules")) != NULL)
 			rad_modules =
-			    array_combine(rad_modules, data_ref(modules));
+			    array_addall(rad_modules, data_ref(modules));
 		data_free(config);
 	}
 
@@ -227,6 +226,10 @@
 	if (smf_startup)
 		svc_fd = rad_service_wait();
 
+	cont_create(&rad_container_auth);
+	cont_create(&rad_container_unauth);
+	cont_create(&rad_container_control);
+
 	xmlInitParser();	/* So libxml consumers are MT safe */
 	adr_ssl_init();		/* So OpenSSL consumers are MT safe */
 	rad_ticket_init();
--- a/usr/src/cmd/rad/rad_container.c	Mon Nov 22 10:50:58 2010 -0500
+++ b/usr/src/cmd/rad/rad_container.c	Tue Nov 23 15:54:20 2010 -0800
@@ -46,24 +46,49 @@
 #include "api_errors.h"
 #include "api_container.h"
 
+void
+cont_create(rad_container_t *c)
+{
+	rad_mutex_init(&c->c_lock);
+	list_create(&c->c_instances, sizeof (rad_instance_t),
+	    offsetof(rad_instance_t, i_cnode));
+	list_create(&c->c_dynamic, sizeof (rad_dynamic_t),
+	    offsetof(rad_dynamic_t, rd_node));
+	c->c_ninstances = 0;
+	c->c_id = 0;
+	c->c_contobj = NULL;
+}
+
 conerr_t
 cont_list(rad_container_t *c, adr_name_t *pattern, data_t **names)
 {
-	data_t *result;
-	int n = 0;
-
 	rad_mutex_enter(&c->c_lock);
-	result = data_new_array(&t_array_string, c->c_ninstances);
-
+	data_t *result = data_new_array(&t_array_string, c->c_ninstances);
 	if (result == NULL)
 		goto out;
 
-	for (int i = 0; i < c->c_ninstances; i++) {
-		rad_instance_t *inst = c->c_instances[i];
-		if (!adr_name_match(inst->i_name, pattern))
+	int n = 0;
+	for (rad_instance_t *i = list_head(&c->c_instances); i != NULL;
+	    i = list_next(&c->c_instances, i)) {
+		if (!i->i_visible || !adr_name_match(i->i_name, pattern))
 			continue;
-		array_set(result, n++,
-		    data_new_string(c->c_instances[i]->i_namestr, lt_copy));
+		array_set(result, n++, data_new_string(i->i_namestr, lt_copy));
+	}
+
+	for (rad_dynamic_t *dyn = list_head(&c->c_dynamic); dyn;
+	    dyn = list_next(&c->c_dynamic, dyn)) {
+		data_t *n = NULL;
+		if (dyn->rd_list == NULL)
+			continue;
+
+		if (dyn->rd_list(pattern, &n, dyn->rd_arg) == ce_ok) {
+			result = array_addall(result, n);
+		} else {
+			data_free(result);
+			result = NULL;
+		}
+		if (result == NULL)
+			break;
 	}
 
 	*names = result;
@@ -76,25 +101,46 @@
 static rad_instance_t *
 cont_lookup_int(rad_container_t *c, adr_name_t *name)
 {
-	for (int i = 0; i < c->c_ninstances; i++)
-		if (adr_name_cmp(c->c_instances[i]->i_name, name) == 0)
-			return (c->c_instances[i]);
+	for (rad_instance_t *inst = list_head(&c->c_instances); inst != NULL;
+	    inst = list_next(&c->c_instances, inst))
+		if (adr_name_cmp(inst->i_name, name) == 0)
+			return (inst);
 	return (NULL);
 }
 
+static conerr_t
+cont_insert_unlocked(rad_container_t *c, rad_instance_t *inst, long long id);
+
 conerr_t
 cont_lookup(rad_container_t *c, const char *name, rad_instance_t **inst,
     data_t **error)
 {
-	adr_name_t *aname = adr_name_fromstr(name);
-	if (aname == NULL)
+	adr_name_t *adrn = adr_name_fromstr(name);
+	if (adrn == NULL)
 		return (error_notfound(error, &e__target_object, name));
 
 	rad_mutex_enter(&c->c_lock);
-	*inst = cont_lookup_int(c, aname);
+retry:
+	*inst = cont_lookup_int(c, adrn);
+	if (*inst == NULL)
+		for (rad_dynamic_t *dyn = list_head(&c->c_dynamic); dyn;
+		    dyn = list_next(&c->c_dynamic, dyn))
+			if (adr_name_match(adrn, dyn->rd_pattern) &&
+			    dyn->rd_lookup != NULL &&
+			    dyn->rd_lookup(&adrn, inst, dyn->rd_arg) == ce_ok) {
+				if (*inst == NULL)
+					goto retry;
+				cont_insert_unlocked(c, *inst, INST_ID_PICK);
+				/*
+				 * We defer to dynamic list for objects
+				 * instantiated by dynamic lookup.
+				 */
+				(*inst)->i_visible = B_FALSE;
+				break;
+			}
 	instance_hold(*inst);
 	rad_mutex_exit(&c->c_lock);
-	adr_name_rele(aname);
+	adr_name_rele(adrn);
 	if (*inst == NULL)
 		return (error_notfound(error, &e__target_object, name));
 
@@ -104,9 +150,10 @@
 static rad_instance_t *
 cont_lookup_id_int(rad_container_t *c, long long id)
 {
-	for (int i = 0; i < c->c_ninstances; i++)
-		if (c->c_instances[i]->i_id == id)
-			return (c->c_instances[i]);
+	for (rad_instance_t *inst = list_head(&c->c_instances); inst != NULL;
+	    inst = list_next(&c->c_instances, inst))
+		if (inst->i_id == id)
+			return (inst);
 	return (NULL);
 }
 
@@ -160,18 +207,8 @@
 	    (id != INST_ID_PICK && cont_lookup_id_int(c, id) != NULL))
 		return (ce_exists);
 
-	newarray = malloc((c->c_ninstances + 1) * sizeof (rad_instance_t *));
-	if (newarray == NULL)
-		return (ce_nomem);
-
-	if (c->c_instances != NULL) {
-		(void) memcpy(newarray, c->c_instances,
-		    c->c_ninstances * sizeof (rad_instance_t *));
-		free(c->c_instances);
-	}
-
-	c->c_instances = newarray;
-	newarray[c->c_ninstances++] = inst;
+	c->c_ninstances++;
+	list_insert_tail(&c->c_instances, inst);
 	if (id == INST_ID_PICK) {
 		inst->i_id = (rad_isproxy ? INST_ID_PROXY : 0) |
 		    (long long)atomic_inc_64_nv(&instance_lastid);
@@ -203,7 +240,7 @@
 cont_insert_singleton_id(rad_container_t *c, adr_name_t *name,
     rad_object_t *obj, long long id)
 {
-	rad_instance_t *inst = instance_create(name, obj, NULL);
+	rad_instance_t *inst = instance_create(name, obj, NULL, NULL);
 	if (inst == NULL)
 		return (ce_nomem);
 
@@ -222,19 +259,11 @@
 cont_remove(rad_container_t *c, rad_instance_t *inst)
 {
 	rad_mutex_enter(&c->c_lock);
-	for (int i = 0; i < c->c_ninstances; i++)
-		if (c->c_instances[i] == inst) {
-			c->c_instances[i] = c->c_instances[--c->c_ninstances];
-			cont_sendevent(c, &e__nschange_remove, inst);
-			rad_mutex_exit(&c->c_lock);
-
-			instance_rele(inst);
-			return (ce_ok);
-		}
-
-	rad_log(RL_FATAL, "cont_remove: instance not found: %s",
-	    inst->i_namestr);
-	return (ce_notfound);
+	list_remove(&c->c_instances, inst);
+	cont_sendevent(c, &e__nschange_remove, inst);
+	rad_mutex_exit(&c->c_lock);
+	instance_rele(inst);
+	return (ce_ok);
 }
 
 /*
@@ -247,7 +276,7 @@
 	adr_name_t *objname = adr_name_vcreate("org.opensolaris.os.rad", 1,
 	    "type", "container");
 	rad_instance_t *inst =
-	    instance_create(objname, &api_container_svr, NULL);
+	    instance_create(objname, &api_container_svr, NULL, NULL);
 	if (inst == NULL)
 		return (B_FALSE);
 
@@ -265,3 +294,22 @@
 
 	return (err == ce_ok);
 }
+
+conerr_t
+cont_register_dynamic(rad_container_t *c, adr_name_t *pattern,
+    rad_dyn_list_t listf, rad_dyn_lookup_t lookupf, void *arg)
+{
+	rad_dynamic_t *dyn = malloc(sizeof (rad_dynamic_t));
+	if (dyn == NULL)
+		return (ce_nomem);
+	dyn->rd_pattern = pattern;
+	dyn->rd_list = listf;
+	dyn->rd_lookup = lookupf;
+	dyn->rd_arg = arg;
+
+	rad_mutex_enter(&c->c_lock);
+	list_insert_tail(&c->c_dynamic, dyn);
+	rad_mutex_exit(&c->c_lock);
+
+	return (ce_ok);
+}
--- a/usr/src/cmd/rad/rad_container.h	Mon Nov 22 10:50:58 2010 -0500
+++ b/usr/src/cmd/rad/rad_container.h	Tue Nov 23 15:54:20 2010 -0800
@@ -41,22 +41,31 @@
 extern "C" {
 #endif
 
+typedef struct rad_dynamic {
+	adr_name_t	*rd_pattern;
+	rad_dyn_list_t	rd_list;
+	rad_dyn_lookup_t rd_lookup;
+	void		*rd_arg;
+	list_node_t	rd_node;
+} rad_dynamic_t;
+
 /* The container itself; contains instances */
 struct rad_container {
 	pthread_mutex_t c_lock;
 	int		c_ninstances;	/* # of instances */
-	rad_instance_t	**c_instances;	/* instances */
+	list_t		c_instances;	/* instances */
+	list_t		c_dynamic;	/* dynamic namespace providers */
 	long long	c_id;		/* last instance ID allocated */
 	rad_instance_t	*c_contobj;	/* container object */
 };
 
-#define	CONTAINER_INITIALIZER	{ PTHREAD_MUTEX_INITIALIZER, 0, NULL, 0, NULL }
-
+void cont_create(rad_container_t *);
 conerr_t cont_list(rad_container_t *, adr_name_t *, data_t **);
 conerr_t cont_lookup(rad_container_t *, const char *, rad_instance_t **,
-	data_t **);
+    data_t **);
 conerr_t cont_lookup_id(rad_container_t *, long long, rad_instance_t **,
-	data_t **);
+    data_t **);
+
 boolean_t cont_insert_contobj(rad_container_t *);
 
 #ifdef	__cplusplus
--- a/usr/src/cmd/rad/rad_modapi.h	Mon Nov 22 10:50:58 2010 -0500
+++ b/usr/src/cmd/rad/rad_modapi.h	Tue Nov 23 15:54:20 2010 -0800
@@ -102,11 +102,19 @@
 };
 
 /*
+ * Dynamic namespace management
+ */
+typedef conerr_t (*rad_dyn_list_t)(adr_name_t *, data_t **, void *arg);
+typedef conerr_t (*rad_dyn_lookup_t)(adr_name_t **, rad_instance_t **,
+    void *arg);
+
+/*
  * Container routines
  */
 #define	INST_ID_PICK	(-1)
 
-rad_instance_t *instance_create(adr_name_t *, rad_object_t *, void *);
+rad_instance_t *instance_create(adr_name_t *, rad_object_t *, void *,
+    void (*)(void *));
 void instance_hold(rad_instance_t *);
 void instance_rele(rad_instance_t *);
 data_t *instance_getname(rad_instance_t *);
@@ -116,8 +124,10 @@
 conerr_t cont_insert(rad_container_t *, rad_instance_t *, long long);
 conerr_t cont_insert_singleton(rad_container_t *, adr_name_t *, rad_object_t *);
 conerr_t cont_insert_singleton_id(rad_container_t *, adr_name_t *,
-	rad_object_t *, long long);
+    rad_object_t *, long long);
 conerr_t cont_remove(rad_container_t *, rad_instance_t *);
+conerr_t cont_register_dynamic(rad_container_t *, adr_name_t *, rad_dyn_list_t,
+    rad_dyn_lookup_t, void *);
 
 /*
  * Logging routines
--- a/usr/src/cmd/rad/rad_object.c	Mon Nov 22 10:50:58 2010 -0500
+++ b/usr/src/cmd/rad/rad_object.c	Tue Nov 23 15:54:20 2010 -0800
@@ -356,7 +356,8 @@
 }
 
 rad_instance_t *
-instance_create(adr_name_t *name, rad_object_t *type, void *data)
+instance_create(adr_name_t *name, rad_object_t *type, void *data,
+    void (*freef)(void *))
 {
 	if (name == NULL)
 		return (NULL);
@@ -377,7 +378,9 @@
 	result->i_namestr = namestr;
 	result->i_type = type;
 	result->i_data = data;
+	result->i_freef = freef;
 	result->i_id = -1;
+	result->i_visible = B_TRUE;
 	list_create(&result->i_interest, sizeof (rad_interest_t),
 	    offsetof(rad_interest_t, ri_instnode));
 
@@ -387,6 +390,8 @@
 static void
 instance_destroy(rad_instance_t *inst)
 {
+	if (inst->i_freef != NULL)
+		inst->i_freef(inst->i_data);
 	adr_name_rele(inst->i_name);
 	free(inst->i_namestr);
 	free(inst);
--- a/usr/src/cmd/rad/rad_object.h	Mon Nov 22 10:50:58 2010 -0500
+++ b/usr/src/cmd/rad/rad_object.h	Tue Nov 23 15:54:20 2010 -0800
@@ -44,12 +44,17 @@
 	pthread_mutex_t	i_lock;		/* protects *i_data */
 	pthread_mutex_t i_reflock;	/* protects references */
 	long long	i_refs;		/* references */
+
+	long long	i_id;		/* instance id */
 	adr_name_t	*i_name;	/* instance name */
 	char		*i_namestr;	/* instance name (as canon. string) */
 	rad_object_t	*i_type;	/* instance's type */
+	boolean_t	i_visible;	/* visible to LIST */
+
 	void		*i_data;	/* instance-specific data */
+	void		(*i_freef)(void *); /* free function for i_data */
 	list_t		i_interest;	/* event listeners */
-	long long	i_id;		/* instance id */
+	list_node_t	i_cnode;	/* container membership */
 };
 
 adr_attribute_t *inst_attr_lookup(rad_instance_t *, const char *, data_t **);
--- a/usr/src/cmd/rad/rad_pam.c	Mon Nov 22 10:50:58 2010 -0500
+++ b/usr/src/cmd/rad/rad_pam.c	Tue Nov 23 15:54:20 2010 -0800
@@ -613,10 +613,10 @@
 {
 	adr_name_t *name = adr_name_vcreate("org.opensolaris.os.rad", 1,
 	    "type", "authentication");
-	rad_instance_t *inst =
-	    instance_create(adr_name_hold(name), &api_authenticator_svr, NULL);
-	rad_instance_t *uinst =
-	    instance_create(adr_name_hold(name), &api_authenticator_svr, NULL);
+	rad_instance_t *inst = instance_create(adr_name_hold(name),
+	    &api_authenticator_svr, NULL, NULL);
+	rad_instance_t *uinst = instance_create(adr_name_hold(name),
+	    &api_authenticator_svr, NULL, NULL);
 
 	conerr_t err = ce_nomem;
 	if (inst == NULL || uinst == NULL ||
--- a/usr/src/cmd/test/libadr/name.c	Mon Nov 22 10:50:58 2010 -0500
+++ b/usr/src/cmd/test/libadr/name.c	Tue Nov 23 15:54:20 2010 -0800
@@ -149,13 +149,6 @@
 	    STR_6KV, STR_7KV, STR_8KV,
 	    STR_9KV, STR_10KV
 	};
-	/* Canonicalized strings */
-	const char *cstrs[] = {
-	    STR_1KV, STR_2KV,
-	    STR_3KV, STR_5KV, STR_5KV,
-	    STR_6KV, STR_8KV, STR_8KV,
-	    STR_9KV, STR_10KV
-	};
 	int t = 0;
 
 	const char *keys1[] = { KEY_1 };
@@ -363,11 +356,11 @@
 	/*
 	 * Test batch 5: Does adr_name_tostr function properly?
 	 */
-	if (comparestrs("adr_name_create", name_create, cstrs, strs))
+	if (comparestrs("adr_name_create", name_create, strs, strs))
 		fail = B_TRUE;
-	if (comparestrs("adr_name_vcreate", name_vcreate, cstrs, strs))
+	if (comparestrs("adr_name_vcreate", name_vcreate, strs, strs))
 		fail = B_TRUE;
-	if (comparestrs("adr_name_fromstr", name_fromstr, cstrs, strs))
+	if (comparestrs("adr_name_fromstr", name_fromstr, strs, strs))
 		fail = B_TRUE;
 
 	/*
--- a/usr/src/java/rad/org/opensolaris/os/rad/ADRName.java	Mon Nov 22 10:50:58 2010 -0500
+++ b/usr/src/java/rad/org/opensolaris/os/rad/ADRName.java	Tue Nov 23 15:54:20 2010 -0800
@@ -25,7 +25,9 @@
 
 package org.opensolaris.os.rad;
 
+import java.util.ArrayList;
 import java.util.Hashtable;
+import java.util.List;
 import java.util.Map;
 import java.util.TreeMap;
 import java.util.logging.Level;
@@ -37,6 +39,9 @@
     private String domain_;
     private TreeMap<String, String> kvs_ = new TreeMap<String, String>();
 
+    /* Wire-ordered keys to play jconsole's silly collation game */
+    private List<String> keys_ = new ArrayList<String>();
+
     public ADRName(ObjectName on) {
 	domain_ = on.getDomain();
 	Hashtable<String, String> keys = on.getKeyPropertyList();
@@ -83,6 +88,7 @@
 		if (pos == start || inkey)
 		    throw new IllegalArgumentException("Empty value: " + str);
 		kvs_.put(key, new String(scratch, 0, dst));
+		keys_.add(key);
 		dst = 0;
 		inkey = true;
 		start = pos + 1;
@@ -118,13 +124,30 @@
 	if (pos == start || inkey)
 	    throw new IllegalArgumentException("Empty value: " + str);
 	kvs_.put(key, new String(scratch, 0, dst));
+	keys_.add(key);
     }
 
     public ObjectName toObjectName() throws MalformedObjectNameException {
-	Hashtable<String, String> keys = new Hashtable<String, String>();
-	for (Map.Entry<String, String> e : kvs_.entrySet())
-	    keys.put(e.getKey(), ObjectName.quote(e.getValue()));
-	return new ObjectName(domain_, keys);
+	/*
+	 * In theory, ObjectName keys are unordered.  In practice, their
+	 * order is used as a hint to improve the appearance of jconsole.
+	 * Discordant as this is, the end result is actually pretty nice.
+	 *
+	 * Unfortunately, the only ObjectName constructor that chooses
+	 * practice over theory is the one that takes the string form, so
+	 * below we cons up a String so that ObjectName(String) can then
+	 * immediately undo our work and parse it.
+	 */
+	StringBuffer sb = new StringBuffer(domain_).append(':');
+	boolean first = true;
+	for (String key : keys_) {
+	    if (first)
+		first = false;
+	    else
+		sb.append(',');
+	    sb.append(key).append('=').append(ObjectName.quote(kvs_.get(key)));
+	}
+	return (new ObjectName(sb.toString()));
     }
 
     @Override
--- a/usr/src/lib/libadr/common/adr.c	Mon Nov 22 10:50:58 2010 -0500
+++ b/usr/src/lib/libadr/common/adr.c	Tue Nov 23 15:54:20 2010 -0800
@@ -789,11 +789,38 @@
 	return (data->d_type->t_aux.t_array);
 }
 
+#define	array_tainted(x)	(x->d_data.array == NULL)
+
+static void
+array_taint(data_t *a)
+{
+	for (int i = 0; i < a->d_size; i++)
+		data_free(a->d_data.array[i]);
+	free(a->d_data.array);
+	a->d_data.array = NULL;
+}
+
+static boolean_t
+array_grow(data_t *a, int nsize)
+{
+	data_t **narray = zalloc(nsize * sizeof (data_t *));
+	if (narray == NULL) {
+		array_taint(a);
+		return (B_FALSE);
+	}
+	for (int i = 0; i < a->d_rsize; i++)
+		narray[i] = a->d_data.array[i];
+	free(a->d_data.array);
+	a->d_size = nsize;
+	a->d_data.array = narray;
+
+	return (B_TRUE);
+}
 
 void
 array_set(data_t *data, int index, data_t *value)
 {
-	if (data == NULL || data->d_data.array == NULL) {
+	if (data == NULL || array_tainted(data)) {
 		data_free(value);
 		return;
 	}
@@ -813,7 +840,7 @@
 int
 array_vset(data_t *data, int index, data_t *value)
 {
-	if (data == NULL || data->d_data.array == NULL) {
+	if (data == NULL || array_tainted(data)) {
 		data_free(value);
 		return (1);
 	}
@@ -822,21 +849,10 @@
 
 	if (index >= data->d_size) {
 		int nsize = data->d_size * 2;
-		data_t **narray = zalloc(nsize * sizeof (data_t *));
-		if (narray == NULL) {
-			for (int i = 0; i < data->d_size; i++)
-				data_free(data->d_data.array[i]);
-			free(data->d_data.array);
-			data->d_data.array = NULL;
+		if (!array_grow(data, nsize)) {
 			data_free(value);
 			return (1);
 		}
-		for (int i = 0; i < data->d_size; i++)
-			narray[i] = data->d_data.array[i];
-		free(data->d_data.array);
-
-		data->d_size = nsize;
-		data->d_data.array = narray;
 	}
 
 	array_set(data, index, value);
@@ -865,47 +881,56 @@
 }
 
 data_t *
-array_combine(data_t *a1, data_t *a2)
+array_addall(data_t *a1, data_t *a2)
 {
+	if (a1 == NULL || array_tainted(a1)) {
+		data_free(a2);
+		return (NULL);
+	}
+
+	if (a2 == NULL) {
+		/*
+		 * Unlike other array routines, all consumers overwrite
+		 * their references with the result, so we free instead
+		 * of taint.
+		 */
+		data_free(a1);
+		return (NULL);
+	}
+
 	assert(data_basetype(a1) == dt_array);
 	assert(data_basetype(a2) == dt_array);
 	assert(data_type(a1) == data_type(a2));
 
 	/*
-	 * array_combine is a destructive operation unless someone besides
-	 * the caller has references
+	 * We consume the caller's reference to a2.  If that's the only
+	 * reference, we get to co-opt the element references as well.
 	 */
-	boolean_t c1 = a1->d_refs > 1;
-	boolean_t c2 = a2->d_refs > 1;
-	data_t *result = data_new_array(a1->d_type, a1->d_rsize + a2->d_rsize);
-	result->d_rsize = a1->d_rsize + a2->d_rsize;
-
-	int i = 0;
-	for (int j = 0; j < a1->d_rsize; j++) {
-		if (c1)
-			(void) data_ref(a1->d_data.array[j]);
-		result->d_data.array[i++] = a1->d_data.array[j];
-	}
-	for (int j = 0; j < a2->d_rsize; j++) {
-		if (c2)
-			(void) data_ref(a2->d_data.array[j]);
-		result->d_data.array[i++] = a2->d_data.array[j];
+	boolean_t allmine = a2->d_refs == 1;
+	int nsize = a1->d_rsize + a2->d_rsize;
+	if (nsize > a1->d_size) {
+		if (!array_grow(a1, nsize)) {
+			data_free(a1);	/* See earlier comment */
+			data_free(a2);
+			return (NULL);
+		}
 	}
 
-	if (c1) {
-		data_free(a1);
-	} else {
-		free(a1->d_data.array);
-		free(a1);
+	int i = a1->d_rsize;
+	for (int j = 0; j < a2->d_rsize; j++) {
+		data_t *d = a1->d_data.array[i++] = a2->d_data.array[j];
+		if (allmine) {
+			/* Steal reference */
+			a2->d_data.array[j] = NULL;
+		} else {
+			/* We have to share */
+			data_ref(d);
+		}
 	}
-	if (c2) {
-		data_free(a2);
-	} else {
-		free(a2->d_data.array);
-		free(a2);
-	}
+	a1->d_rsize = i;
+	data_free(a2);
 
-	return (result);
+	return (a1);
 }
 
 static boolean_t
--- a/usr/src/lib/libadr/common/adr.h	Mon Nov 22 10:50:58 2010 -0500
+++ b/usr/src/lib/libadr/common/adr.h	Tue Nov 23 15:54:20 2010 -0800
@@ -253,7 +253,7 @@
 int array_vset(data_t *, int, data_t *);
 int array_add(data_t *, data_t *);
 void array_remove(data_t *, int);
-data_t *array_combine(data_t *, data_t *);
+data_t *array_addall(data_t *, data_t *);
 int array_search(data_t *, const char *, const char * const *);
 int array_nsearch(data_t *, const char *, int, const char * const *);
 
--- a/usr/src/lib/libadr/common/adr_name.c	Mon Nov 22 10:50:58 2010 -0500
+++ b/usr/src/lib/libadr/common/adr_name.c	Tue Nov 23 15:54:20 2010 -0800
@@ -46,8 +46,10 @@
 	int an_refs;
 	int an_count;
 	char *an_domain;
-	char **an_keys;
-	char **an_values;
+	char **an_keys;		/* Unsorted keys */
+	char **an_values;	/* Unsorted values */
+	char **an_skeys;	/* Sorted keys */
+	char **an_svalues;	/* Sorted values */
 };
 
 #define	zalloc(x)	calloc(1, x)
@@ -70,8 +72,10 @@
 	(void) pthread_mutex_init(&result->an_lock, NULL);
 	result->an_refs = 1;
 	result->an_count = n;
-	result->an_keys = zalloc(2 * n * sizeof (char *));
+	result->an_keys = zalloc(4 * n * sizeof (char *));
 	result->an_values = &result->an_keys[n];
+	result->an_skeys = &result->an_keys[2 * n];
+	result->an_svalues = &result->an_keys[3 * n];
 	if (result->an_keys == NULL) {
 		free(result);
 		return (NULL);
@@ -87,20 +91,25 @@
 adr_name_normalize(adr_name_t *name)
 {
 	int c = name->an_count;
+	for (int i = 0; i < c; i++) {
+		name->an_skeys[i] = name->an_keys[i];
+		name->an_svalues[i] = name->an_values[i];
+	}
+
 	for (int i = 0; i < c - 1; i++) {
 		for (int j = i + 1; j < c; j++) {
-			int res = strcmp(name->an_keys[i], name->an_keys[j]);
+			int res = strcmp(name->an_skeys[i], name->an_skeys[j]);
 			if (res == 0) {
 				adr_name_free(name);
 				return (NULL);
 			}
 			if (res > 0) {
-				char *ktmp = name->an_keys[i];
-				char *vtmp = name->an_values[i];
-				name->an_keys[i] = name->an_keys[j];
-				name->an_values[i] = name->an_values[j];
-				name->an_keys[j] = ktmp;
-				name->an_values[j] = vtmp;
+				char *ktmp = name->an_skeys[i];
+				char *vtmp = name->an_svalues[i];
+				name->an_skeys[i] = name->an_skeys[j];
+				name->an_svalues[i] = name->an_svalues[j];
+				name->an_skeys[j] = ktmp;
+				name->an_svalues[j] = vtmp;
 			}
 		}
 	}
@@ -482,12 +491,12 @@
 	if (n1->an_count > n2->an_count)
 		return (1);
 
-	/* Assumes order is either normalized or significant. */
+	/* Compare pre-sorted key/value pairs */
 	for (int i = 0; i < n1->an_count; i++) {
-		res = strcmp(n1->an_keys[i], n2->an_keys[i]);
+		res = strcmp(n1->an_skeys[i], n2->an_skeys[i]);
 		if (res != 0)
 			return (res);
-		res = strcmp(n1->an_values[i], n2->an_values[i]);
+		res = strcmp(n1->an_svalues[i], n2->an_svalues[i]);
 		if (res != 0)
 			return (res);
 	}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/pykstat/Makefile	Tue Nov 23 15:54:20 2010 -0800
@@ -0,0 +1,41 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+#
+
+include $(SRC)/Makefile.env
+
+PROTO_PYRAD = $(PROTO_PYTHON)/kstat
+PYSRCS = __init__.py rad.py
+PYOBJS = $(PYSRCS:%.py=%.pyc)
+PYFILES = $(PYSRCS) $(PYOBJS)
+INSTFILES = $(PYFILES:%=$(PROTO_PYRAD)/%)
+
+all: $(PYOBJS)
+
+install: all $(INSTFILES)
+
+$(PROTO_PYRAD)/%: %
+	$(INS.pyfile)
+
+include $(SRC)/Makefile.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/pykstat/__init__.py	Tue Nov 23 15:54:20 2010 -0800
@@ -0,0 +1,26 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+#
+
+# This file intentionally left blank.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/pykstat/rad.py	Tue Nov 23 15:54:20 2010 -0800
@@ -0,0 +1,115 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+#
+
+""" A thin wrapper around the kstats support provided by rad """
+
+from __future__ import absolute_import
+import rad.client as client
+import rad.adaptor as adaptor
+import rad.util as util
+import os
+
+_RAD_KSTAT_DOMAIN = "com.oracle.solaris.kstat"
+_RAD_KSTAT_MODULE = "/usr/lib/rad/module/kstat.so"
+
+# The rad interface doesn't support discriminated unions, so the kstat
+# module fakes one up with a traditional structure.  Snapshot and
+# StatAdaptor transform that structure to something more pythonic.
+
+class Snapshot:
+	def __init__(self, value):
+		self._timestamp = value.timestamp
+		self._stats = {}
+		for i in value.statistics:
+			if i.type == "char":
+				self._stats[i.name] = i.value.str
+			elif i.type == "int32":
+				self._stats[i.name] = i.value.i32
+			elif i.type == "uint32":
+				self._stats[i.name] = i.value.ui32
+			elif i.type == "int64":
+				self._stats[i.name] = i.value.i64
+			elif i.type == "uint64":
+				self._stats[i.name] = i.value.ui64
+			elif i.type == "float":
+				self._stats[i.name] = i.value.f
+			elif i.type == "double":
+				self._stats[i.name] = i.value.d
+			elif i.type == "string":
+				self._stats[i.name] = i.value.str
+
+	def timestamp(self):
+		return self._timestamp
+
+	def get(self, stat):
+		return self._stats[stat]
+
+class StatAdaptor(adaptor.Adaptor):
+	def __init__(self, statobj):
+		adaptor.Adaptor.__init__(self, statobj)
+		old_fresh = self.__dict__["get_fresh_value"]
+		self.__dict__["get_fresh_value"] = lambda: Snapshot(old_fresh())
+
+	def __getattr__(self, attr):
+		result = adaptor.Adaptor.__getattr__(self, attr)
+		if attr == "value":
+			return Snapshot(result)
+		return result
+
+class Kstats:
+	""" Represents the system's kstats """
+
+	ctlname = client.Name(_RAD_KSTAT_DOMAIN, [("type", "control")])
+
+	def __init__(self):
+		self._rc = util.connect_private([ _RAD_KSTAT_MODULE ])
+		self._ctl = adaptor.Adaptor(self._rc.get_object(Kstats.ctlname))
+
+	def lookup(self, module, name, instance):
+		return StatAdaptor(self._rc.get_object(
+		    client.Name(_RAD_KSTAT_DOMAIN, [("type", "kstat"),
+		    ("module", module), ("name", name),
+		    ("instance", "%d" % instance)])))
+
+	def lookup_many(self, module = None, name = None, instance = None,
+	    clazz = None):
+		keys = [("type", "kstat")]
+		if module:
+			keys.append(("module", module))
+		if name:
+			keys.append(("name", name))
+		if instance:
+			keys.append(("instance", "%d" % instance))
+		if clazz:
+			keys.append(("class", clazz))
+		o = self._rc.list_objects(client.Name(_RAD_KSTAT_DOMAIN, keys))
+
+		return [ StatAdaptor(self._rc.get_object(i)) for i in o ]
+
+	def update(self):
+		self._ctl.update_all()
+
+	def close(self):
+		self._rc.close()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkg/manifests/system-management-rad-kstat.mf	Tue Nov 23 15:54:20 2010 -0800
@@ -0,0 +1,44 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+#
+
+license cr_Oracle license=cr_Oracle
+set name=pkg.fmri value=pkg:/system/management/rad/kstat@$(PKGVERS)
+set name=pkg.summary value="rad kstat module"
+set name=pkg.description value="rad kstat module"
+set name=info.classification value="org.opensolaris.category.2008:Applications/Configuration and Preferences"
+set name=variant.arch value=$(ARCH)
+dir path=usr group=sys
+dir path=usr/lib
+dir path=usr/lib/python2.6
+dir path=usr/lib/python2.6/vendor-packages
+dir path=usr/lib/python2.6/vendor-packages/kstat
+file path=usr/lib/python2.6/vendor-packages/kstat/__init__.py
+file path=usr/lib/python2.6/vendor-packages/kstat/__init__.pyc
+file path=usr/lib/python2.6/vendor-packages/kstat/rad.py
+file path=usr/lib/python2.6/vendor-packages/kstat/rad.pyc
+dir path=usr/lib/rad
+dir path=usr/lib/rad/module
+file path=usr/lib/rad/module/kstat.so
+depend fmri=pkg:/system/management/rad@$(PKGVERS) type=require
--- a/usr/src/pkg/manifests_wos/system-management-visual-panels.content	Mon Nov 22 10:50:58 2010 -0500
+++ b/usr/src/pkg/manifests_wos/system-management-visual-panels.content	Tue Nov 23 15:54:20 2010 -0800
@@ -24,6 +24,7 @@
 #
 
 <include ../manifests/system-management-rad.mf>
+<include ../manifests/system-management-rad-kstat.mf>
 <include ../manifests/system-management-visual-panels.mf>
 <include ../manifests/system-management-visual-panels-sysmon.mf>
 <include ../manifests/system-management-visual-panels-svcs.mf>