changeset 12979:ab9ae749152f

PSARC/2009/617 Software Events Notification Parameters CLI PSARC/2009/618 snmp-notify: SNMP Notification Daemon for Software Events PSARC/2009/619 smtp-notify: Email Notification Daemon for Software Events PSARC/2010/225 fmd for non-global Solaris zones PSARC/2010/226 Solaris Instance UUID PSARC/2010/227 nvlist_nvflag(3NVPAIR) PSARC/2010/228 libfmevent additions PSARC/2010/257 sysevent_evc_setpropnvl and sysevent_evc_getpropnvl PSARC/2010/265 FMRI and FMA Event Stabilty, 'ireport' category 1 event class, and the 'sw' FMRI scheme PSARC/2010/278 FMA/SMF integration: instance state transitions PSARC/2010/279 Modelling panics within FMA PSARC/2010/290 logadm.conf upgrade 6392476 fmdump needs to pretty-print 6393375 userland ereport/ireport event generation interfaces 6445732 Add email notification agent for FMA and software events 6804168 RFE: Allow an efficient means to monitor SMF services status changes 6866661 scf_values_destroy(3SCF) will segfault if is passed NULL 6884709 Add snmp notification agent for FMA and software events 6884712 Add private interface to tap into libfmd_msg macro expansion capabilities 6897919 fmd to run in a non-global zone 6897937 fmd use of non-private doors is not safe 6900081 add a UUID to Solaris kernel image for use in crashdump identification 6914884 model panic events as a defect diagnosis in FMA 6944862 fmd_case_open_uuid, fmd_case_uuisresolved, fmd_nvl_create_defect 6944866 log legacy sysevents in fmd 6944867 enumerate svc scheme in topo 6944868 software-diagnosis and software-response fmd modules 6944870 model SMF maintenance state as a defect diagnosis in FMA 6944876 savecore runs in foreground for systems with zfs root and dedicated dump 6965796 Implement notification parameters for SMF state transitions and FMA events 6968287 SUN-FM-MIB.mib needs to be updated to reflect Oracle information 6972331 logadm.conf upgrade PSARC/2010/290
author Gavin Maltby <gavin.maltby@oracle.com>
date Fri, 30 Jul 2010 17:04:17 +1000
parents 19d842faf8e4
children 4de81c4d427e
files usr/src/Makefile.lint usr/src/cmd/cmd-inet/usr.lib/inetd/inetd.c usr/src/cmd/dumpadm/Makefile usr/src/cmd/dumpadm/dconf.c usr/src/cmd/dumpadm/dconf.h usr/src/cmd/dumpadm/dumpadm.xml usr/src/cmd/dumpadm/main.c usr/src/cmd/dumpadm/svc-dumpadm usr/src/cmd/fm/Makefile usr/src/cmd/fm/dicts/FMD.dict usr/src/cmd/fm/dicts/FMD.po usr/src/cmd/fm/dicts/FMNOTIFY.dict usr/src/cmd/fm/dicts/FMNOTIFY.po usr/src/cmd/fm/dicts/Makefile usr/src/cmd/fm/dicts/SMF.dict usr/src/cmd/fm/dicts/SMF.po usr/src/cmd/fm/dicts/SUNOS.dict usr/src/cmd/fm/dicts/SUNOS.po usr/src/cmd/fm/fmd/Makefile.fmd usr/src/cmd/fm/fmd/common/fmd.c usr/src/cmd/fm/fmd/common/fmd.h usr/src/cmd/fm/fmd/common/fmd.logadm.conf usr/src/cmd/fm/fmd/common/fmd.xml usr/src/cmd/fm/fmd/common/fmd_api.c usr/src/cmd/fm/fmd/common/fmd_api.h usr/src/cmd/fm/fmd/common/fmd_api.map usr/src/cmd/fm/fmd/common/fmd_builtin.c usr/src/cmd/fm/fmd/common/fmd_builtin.h usr/src/cmd/fm/fmd/common/fmd_case.c usr/src/cmd/fm/fmd/common/fmd_case.h usr/src/cmd/fm/fmd/common/fmd_log.h usr/src/cmd/fm/fmd/common/fmd_main.c usr/src/cmd/fm/fmd/common/fmd_mdb.c usr/src/cmd/fm/fmd/common/fmd_module.c usr/src/cmd/fm/fmd/common/fmd_module.h usr/src/cmd/fm/fmd/common/fmd_rpc.c usr/src/cmd/fm/fmd/common/fmd_rpc_adm.c usr/src/cmd/fm/fmd/common/fmd_self.c usr/src/cmd/fm/fmd/common/fmd_sysevent.c usr/src/cmd/fm/fmd/common/fmd_thread.c usr/src/cmd/fm/fmd/common/fmd_thread.h usr/src/cmd/fm/fmd/common/fmd_xprt.c usr/src/cmd/fm/fmdump/Makefile.com usr/src/cmd/fm/fmdump/common/asru.c usr/src/cmd/fm/fmdump/common/error.c usr/src/cmd/fm/fmdump/common/fault.c usr/src/cmd/fm/fmdump/common/fmdump.c usr/src/cmd/fm/fmdump/common/fmdump.h usr/src/cmd/fm/fmdump/common/info.c usr/src/cmd/fm/fmdump/common/nvlrender.c usr/src/cmd/fm/fmtopo/common/fmtopo.c usr/src/cmd/fm/modules/Makefile.plugin usr/src/cmd/fm/modules/common/Makefile usr/src/cmd/fm/modules/common/eversholt/eft.c usr/src/cmd/fm/modules/common/eversholt/fme.c usr/src/cmd/fm/modules/common/eversholt/platform.c usr/src/cmd/fm/modules/common/ext-event-transport/Makefile usr/src/cmd/fm/modules/common/ext-event-transport/ext-event-transport.conf usr/src/cmd/fm/modules/common/ext-event-transport/fmevt.h usr/src/cmd/fm/modules/common/ext-event-transport/fmevt_inbound.c usr/src/cmd/fm/modules/common/ext-event-transport/fmevt_inbound_default.c usr/src/cmd/fm/modules/common/ext-event-transport/fmevt_inbound_on.c usr/src/cmd/fm/modules/common/ext-event-transport/fmevt_inbound_smf.c usr/src/cmd/fm/modules/common/ext-event-transport/fmevt_inbound_sunos.c usr/src/cmd/fm/modules/common/ext-event-transport/fmevt_main.c usr/src/cmd/fm/modules/common/ext-event-transport/fmevt_outbound.c usr/src/cmd/fm/modules/common/sensor-transport/sensor_transport.c usr/src/cmd/fm/modules/common/snmp-trapgen/Makefile usr/src/cmd/fm/modules/common/snmp-trapgen/fmd-trapgen.conf usr/src/cmd/fm/modules/common/snmp-trapgen/snmp-trapgen.conf usr/src/cmd/fm/modules/common/snmp-trapgen/snmp.c usr/src/cmd/fm/modules/common/sw-diag-response/Makefile usr/src/cmd/fm/modules/common/sw-diag-response/Makefile.com usr/src/cmd/fm/modules/common/sw-diag-response/common/sw.h usr/src/cmd/fm/modules/common/sw-diag-response/common/sw_impl.h usr/src/cmd/fm/modules/common/sw-diag-response/common/sw_main_cmn.c usr/src/cmd/fm/modules/common/sw-diag-response/software-diagnosis/Makefile usr/src/cmd/fm/modules/common/sw-diag-response/software-diagnosis/software-diagnosis.conf usr/src/cmd/fm/modules/common/sw-diag-response/software-diagnosis/swde_case.c usr/src/cmd/fm/modules/common/sw-diag-response/software-diagnosis/swde_main.c usr/src/cmd/fm/modules/common/sw-diag-response/software-response/Makefile usr/src/cmd/fm/modules/common/sw-diag-response/software-response/software-response.conf usr/src/cmd/fm/modules/common/sw-diag-response/software-response/swrp_main.c usr/src/cmd/fm/modules/common/sw-diag-response/subsidiary/panic/panic.h usr/src/cmd/fm/modules/common/sw-diag-response/subsidiary/panic/panic_diag.c usr/src/cmd/fm/modules/common/sw-diag-response/subsidiary/smf/smf.h usr/src/cmd/fm/modules/common/sw-diag-response/subsidiary/smf/smf_diag.c usr/src/cmd/fm/modules/common/sw-diag-response/subsidiary/smf/smf_response.c usr/src/cmd/fm/modules/common/sw-diag-response/subsidiary/smf/smf_util.c usr/src/cmd/fm/modules/common/syslog-msgs/Makefile usr/src/cmd/fm/modules/common/syslog-msgs/syslog.c usr/src/cmd/fm/modules/sun4u/fps-transport/fps-transport.c usr/src/cmd/fm/notify/Makefile usr/src/cmd/fm/notify/Makefile.subdirs usr/src/cmd/fm/notify/notify-params.xml usr/src/cmd/fm/notify/smtp-notify/Makefile usr/src/cmd/fm/notify/smtp-notify/Makefile.com usr/src/cmd/fm/notify/smtp-notify/common/process_msg_template.sh usr/src/cmd/fm/notify/smtp-notify/common/smtp-notify.c usr/src/cmd/fm/notify/smtp-notify/common/smtp-notify.xml usr/src/cmd/fm/notify/smtp-notify/i386/Makefile usr/src/cmd/fm/notify/smtp-notify/sparc/Makefile usr/src/cmd/fm/notify/snmp-notify/Makefile usr/src/cmd/fm/notify/snmp-notify/Makefile.com usr/src/cmd/fm/notify/snmp-notify/common/snmp-notify.c usr/src/cmd/fm/notify/snmp-notify/common/snmp-notify.xml usr/src/cmd/fm/notify/snmp-notify/i386/Makefile usr/src/cmd/fm/notify/snmp-notify/sparc/Makefile usr/src/cmd/fm/schemes/Makefile usr/src/cmd/fm/schemes/sw/Makefile usr/src/cmd/fm/schemes/sw/Makefile.com usr/src/cmd/fm/schemes/sw/amd64/Makefile usr/src/cmd/fm/schemes/sw/i386/Makefile usr/src/cmd/fm/schemes/sw/scheme.c usr/src/cmd/fm/schemes/sw/sparc/Makefile usr/src/cmd/fm/schemes/sw/sparcv9/Makefile usr/src/cmd/fm/scripts/dictck.pl usr/src/cmd/logadm/Makefile usr/src/cmd/logadm/logadm-upgrade usr/src/cmd/logadm/logadm-upgrade.xml usr/src/cmd/logadm/logadm.conf usr/src/cmd/mdb/common/kmdb/kmdb_kvm.c usr/src/cmd/mdb/common/mdb/mdb_kvm.c usr/src/cmd/mdb/common/modules/svc.startd/startd.c usr/src/cmd/savecore/Makefile.com usr/src/cmd/savecore/amd64/Makefile usr/src/cmd/savecore/i386/Makefile usr/src/cmd/savecore/savecore.c usr/src/cmd/savecore/sparc/Makefile usr/src/cmd/savecore/sparcv9/Makefile usr/src/cmd/svc/common/notify_params.c usr/src/cmd/svc/common/notify_params.h usr/src/cmd/svc/dtd/service_bundle.dtd.1 usr/src/cmd/svc/milestone/fs-usr usr/src/cmd/svc/milestone/global.xml usr/src/cmd/svc/milestone/restarter.xml usr/src/cmd/svc/startd/Makefile usr/src/cmd/svc/startd/graph.c usr/src/cmd/svc/startd/libscf.c usr/src/cmd/svc/startd/method.c usr/src/cmd/svc/startd/protocol.c usr/src/cmd/svc/startd/protocol.h usr/src/cmd/svc/startd/restarter.c usr/src/cmd/svc/startd/startd.c usr/src/cmd/svc/startd/startd.h usr/src/cmd/svc/svccfg/Makefile usr/src/cmd/svc/svccfg/svccfg.h usr/src/cmd/svc/svccfg/svccfg.l usr/src/cmd/svc/svccfg/svccfg.y usr/src/cmd/svc/svccfg/svccfg_engine.c usr/src/cmd/svc/svccfg/svccfg_help.c usr/src/cmd/svc/svccfg/svccfg_libscf.c usr/src/cmd/svc/svccfg/svccfg_xml.c usr/src/cmd/svc/svcs/Makefile usr/src/cmd/svc/svcs/svcs.c usr/src/cmd/syseventd/sysevent.xml usr/src/common/nvpair/nvpair.c usr/src/lib/fm/Makefile usr/src/lib/fm/libfmd_msg/common/fmd_msg.c usr/src/lib/fm/libfmd_msg/common/fmd_msg.h usr/src/lib/fm/libfmd_msg/common/mapfile-vers usr/src/lib/fm/libfmd_snmp/Makefile usr/src/lib/fm/libfmd_snmp/common/fmd_snmp.h usr/src/lib/fm/libfmd_snmp/mibs/SUN-FM-MIB.mib usr/src/lib/fm/libfmd_snmp/mibs/SUN-IREPORT-MIB.mib usr/src/lib/fm/libfmevent/Makefile usr/src/lib/fm/libfmevent/Makefile.com usr/src/lib/fm/libfmevent/amd64/Makefile usr/src/lib/fm/libfmevent/common/fmev_channels.h usr/src/lib/fm/libfmevent/common/fmev_evaccess.c usr/src/lib/fm/libfmevent/common/fmev_impl.h usr/src/lib/fm/libfmevent/common/fmev_publish.c usr/src/lib/fm/libfmevent/common/fmev_subscribe.c usr/src/lib/fm/libfmevent/common/fmev_util.c usr/src/lib/fm/libfmevent/common/libfmevent.h usr/src/lib/fm/libfmevent/common/libfmevent_ruleset.h usr/src/lib/fm/libfmevent/common/mapfile-vers usr/src/lib/fm/libfmevent/i386/Makefile usr/src/lib/fm/libfmevent/sparc/Makefile usr/src/lib/fm/libfmevent/sparcv9/Makefile usr/src/lib/fm/libfmnotify/Makefile usr/src/lib/fm/libfmnotify/Makefile.com usr/src/lib/fm/libfmnotify/amd64/Makefile usr/src/lib/fm/libfmnotify/common/libfmnotify.c usr/src/lib/fm/libfmnotify/common/libfmnotify.h usr/src/lib/fm/libfmnotify/common/llib-lfmnotify usr/src/lib/fm/libfmnotify/common/mapfile-vers usr/src/lib/fm/libfmnotify/i386/Makefile usr/src/lib/fm/libfmnotify/sparc/Makefile usr/src/lib/fm/libfmnotify/sparcv9/Makefile usr/src/lib/fm/topo/libtopo/Makefile.com usr/src/lib/fm/topo/libtopo/common/dev.c usr/src/lib/fm/topo/libtopo/common/fmd.c usr/src/lib/fm/topo/libtopo/common/hc.c usr/src/lib/fm/topo/libtopo/common/mem.c usr/src/lib/fm/topo/libtopo/common/mod.c usr/src/lib/fm/topo/libtopo/common/pkg.c usr/src/lib/fm/topo/libtopo/common/svc.c usr/src/lib/fm/topo/libtopo/common/sw.c usr/src/lib/fm/topo/libtopo/common/sw.h usr/src/lib/fm/topo/libtopo/common/topo_builtin.c usr/src/lib/fm/topo/libtopo/common/topo_mod.c usr/src/lib/fm/topo/libtopo/common/topo_mod.h usr/src/lib/fm/topo/libtopo/common/topo_node.c usr/src/lib/fm/topo/libtopo/common/topo_snap.c usr/src/lib/fm/topo/libtopo/common/zfs.c usr/src/lib/libnvpair/libnvpair.c usr/src/lib/libnvpair/libnvpair.h usr/src/lib/libnvpair/mapfile-vers usr/src/lib/librestart/common/librestart.c usr/src/lib/librestart/common/librestart.h usr/src/lib/librestart/common/librestart_priv.h usr/src/lib/librestart/common/mapfile-vers usr/src/lib/libscf/Makefile.com usr/src/lib/libscf/common/libscf_impl.h usr/src/lib/libscf/common/lowlevel.c usr/src/lib/libscf/common/mapfile-vers usr/src/lib/libscf/common/midlevel.c usr/src/lib/libscf/common/notify_params.c usr/src/lib/libscf/common/scf_tmpl.c usr/src/lib/libscf/inc/libscf.h usr/src/lib/libscf/inc/libscf_priv.h usr/src/lib/libsecdb/auth_attr.txt usr/src/lib/libsecdb/prof_attr.txt usr/src/lib/libsysevent/libevchannel.c usr/src/lib/libsysevent/libsysevent.c usr/src/lib/libsysevent/libsysevent.h usr/src/lib/libsysevent/mapfile-vers usr/src/pkg/manifests/SUNWcs.mf usr/src/pkg/manifests/consolidation-osnet-osnet-message-files.mf usr/src/pkg/manifests/service-fault-management-smtp-notify.mf usr/src/pkg/manifests/service-fault-management-snmp-notify.mf usr/src/pkg/manifests/service-fault-management.mf usr/src/pkg/manifests/system-kernel.mf usr/src/uts/common/io/dump.c usr/src/uts/common/io/sysevent.c usr/src/uts/common/os/dumpsubr.c usr/src/uts/common/os/evchannels.c usr/src/uts/common/os/fm.c usr/src/uts/common/os/log_sysevent.c usr/src/uts/common/os/panic.c usr/src/uts/common/sys/dumpadm.h usr/src/uts/common/sys/dumphdr.h usr/src/uts/common/sys/fm/protocol.h usr/src/uts/common/sys/fm/util.h usr/src/uts/common/sys/nvpair.h usr/src/uts/common/sys/panic.h usr/src/uts/common/sys/sysevent.h usr/src/uts/common/sys/sysevent_impl.h usr/src/uts/intel/ia32/os/archdep.c usr/src/uts/sparc/os/archdep.c
diffstat 251 files changed, 21669 insertions(+), 1767 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/Makefile.lint	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/Makefile.lint	Fri Jul 30 17:04:17 2010 +1000
@@ -263,6 +263,7 @@
 	cmd/rpcgen \
 	cmd/rpcsvc/rpc.bootparamd \
 	cmd/runat \
+	cmd/savecore \
 	cmd/sbdadm \
 	cmd/sdpadm \
 	cmd/setpgrp \
@@ -425,6 +426,7 @@
 	lib/libsrpt \
 	lib/libstmf \
 	lib/libsun_ima \
+	lib/libsysevent \
 	lib/libthread \
 	lib/libtsnet \
 	lib/libtsol \
--- a/usr/src/cmd/cmd-inet/usr.lib/inetd/inetd.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/cmd-inet/usr.lib/inetd/inetd.c	Fri Jul 30 17:04:17 2010 +1000
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 /*
@@ -342,7 +341,7 @@
 	scf_instance_t		*scf_inst = NULL;
 	scf_error_t		sret;
 	int			ret;
-	char			*aux = "none";
+	restarter_str_t		aux = restarter_str_none;
 
 	/* update the repository/cached internal state */
 	inst->cur_istate = new_cur_state;
@@ -369,12 +368,12 @@
 		 */
 		if (new_cur_state == IIS_MAINTENANCE) {
 			if (restarter_inst_ractions_from_tty(scf_inst) == 0)
-				aux = "service_request";
+				aux = restarter_str_service_request;
 			else
-				aux = "administrative_request";
+				aux = restarter_str_administrative_request;
 		}
 
-		if (strcmp(aux, "service_request") == 0) {
+		if (aux == restarter_str_service_request) {
 			if (restarter_inst_validate_ractions_aux_fmri(
 			    scf_inst) == 0) {
 				if (restarter_inst_set_aux_fmri(scf_inst))
--- a/usr/src/cmd/dumpadm/Makefile	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/dumpadm/Makefile	Fri Jul 30 17:04:17 2010 +1000
@@ -19,8 +19,7 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
+# Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
 #
 
 PROG = dumpadm
@@ -41,7 +40,7 @@
 
 ROOTMANIFESTDIR = $(ROOTSVCSYSTEM)
 
-LDLIBS +=	-ldiskmgt -lzfs
+LDLIBS +=	-ldiskmgt -lzfs -luuid
 
 .KEEP_STATE:
 
--- a/usr/src/cmd/dumpadm/dconf.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/dumpadm/dconf.c	Fri Jul 30 17:04:17 2010 +1000
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #include <sys/types.h>
@@ -37,6 +36,7 @@
 #include <errno.h>
 #include <libdiskmgt.h>
 #include <libzfs.h>
+#include <uuid/uuid.h>
 
 #include "dconf.h"
 #include "minfree.h"
@@ -495,6 +495,24 @@
 	return (-1);
 }
 
+int
+dconf_write_uuid(dumpconf_t *dcp)
+{
+	char uuidstr[36 + 1];
+	uuid_t uu;
+	int err;
+
+	uuid_generate(uu);
+	uuid_unparse(uu, uuidstr);
+
+	err = ioctl(dcp->dc_dump_fd, DIOCSETUUID, uuidstr);
+
+	if (err)
+		warn(gettext("kernel image uuid write failed"));
+
+	return (err == 0);
+}
+
 void
 dconf_print(dumpconf_t *dcp, FILE *fp)
 {
@@ -533,7 +551,6 @@
 
 	(void) fprintf(fp, gettext("  Savecore enabled: %s\n"),
 	    (dcp->dc_enable == DC_OFF) ? gettext("no") : gettext("yes"));
-
 	(void) fprintf(fp, gettext("   Save compressed: %s\n"),
 	    (dcp->dc_csave == DC_UNCOMPRESSED) ? gettext("off") :
 	    gettext("on"));
--- a/usr/src/cmd/dumpadm/dconf.h	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/dumpadm/dconf.h	Fri Jul 30 17:04:17 2010 +1000
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #ifndef	_DCONF_H
@@ -71,6 +70,7 @@
 extern int dconf_write(dumpconf_t *);
 extern int dconf_update(dumpconf_t *, int);
 extern void dconf_print(dumpconf_t *, FILE *);
+extern int dconf_write_uuid(dumpconf_t *);
 
 extern int dconf_str2device(dumpconf_t *, char *);
 extern int dconf_str2savdir(dumpconf_t *, char *);
--- a/usr/src/cmd/dumpadm/dumpadm.xml	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/dumpadm/dumpadm.xml	Fri Jul 30 17:04:17 2010 +1000
@@ -1,15 +1,13 @@
 <?xml version="1.0"?>
 <!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
 <!--
- Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
- Use is subject to license terms.
+ Copyright (c) 2004, 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, Version 1.0 only
- (the "License").  You may not use this file except in compliance
- with the License.
+ 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.
@@ -24,8 +22,6 @@
 
  CDDL HEADER END
 
-	ident	"%Z%%M%	%I%	%E% SMI"
-
 	NOTE:  This service manifest is not editable; its contents will
 	be overwritten by package or patch operations, including
 	operating system upgrade.  Make customizations in a different
@@ -71,6 +67,18 @@
 		<service_fmri value='svc:/system/filesystem/autofs' />
 	</dependency>
 
+	<!--
+		The fmd service exists in all Solaris zones, but only
+		in the global zone (which dumpadm is defined in) should
+		it be dependent on dumpadm configuration.
+	-->
+	<dependent
+		name='dumpadm-fmd'
+		grouping='require_all'
+		restart_on='none'>
+		<service_fmri value='svc:/system/fmd' />
+	</dependent>
+
 	<exec_method
 		type='method'
 		name='start'
--- a/usr/src/cmd/dumpadm/main.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/dumpadm/main.c	Fri Jul 30 17:04:17 2010 +1000
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #include <sys/stat.h>
@@ -28,6 +27,7 @@
 #include <unistd.h>
 #include <stdlib.h>
 #include <stdio.h>
+#include <string.h>
 
 #include "dconf.h"
 #include "minfree.h"
@@ -37,7 +37,7 @@
 Usage: %s [-nuy] [-c kernel | curproc | all ] [-d dump-device | swap ]\n\
 	[-m min {k|m|%%} ] [-s savecore-dir] [-r root-dir] [-z on|off]\n";
 
-static const char OPTS[] = "nuyc:d:m:s:r:z:";
+static const char OPTS[] = "inuyc:d:m:s:r:z:";
 
 static const char PATH_DEVICE[] = "/dev/dump";
 static const char PATH_CONFIG[] = "/etc/dumpadm.conf";
@@ -55,6 +55,8 @@
 	int modified = 0;		/* have we modified the dump config? */
 	char *minfstr = NULL;		/* string value of -m argument */
 	dumpconf_t dc;			/* current configuration */
+	int chrooted = 0;
+	int douuid = 0;
 
 	(void) setlocale(LC_ALL, "");
 	(void) textdomain(TEXT_DOMAIN);
@@ -66,7 +68,7 @@
 	 */
 	while (optind < argc) {
 		while ((c = getopt(argc, argv, OPTS)) != (int)EOF) {
-			if (c == 'r' && chroot(optarg) == -1)
+			if (c == 'r' && ++chrooted && chroot(optarg) == -1)
 				die(gettext("failed to chroot to %s"), optarg);
 			else if (c == 'u')
 				dcmode = DC_OVERRIDE;
@@ -120,6 +122,16 @@
 				modified++;
 				break;
 
+			case 'i':
+				/* undocumented option */
+				if (chrooted) {
+					warn(gettext("-i and -r cannot be "
+					    "used together\n"));
+					return (E_USAGE);
+				}
+				douuid++;
+				break;
+
 			case 'm':
 				minfstr = optarg;
 				break;
@@ -156,6 +168,9 @@
 		}
 	}
 
+	if (douuid)
+		return (dconf_write_uuid(&dc) ? E_SUCCESS : E_ERROR);
+
 	if (minfstr != NULL) {
 		if (minfree_compute(dc.dc_savdir, minfstr, &minf) == -1)
 			return (E_USAGE);
--- a/usr/src/cmd/dumpadm/svc-dumpadm	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/dumpadm/svc-dumpadm	Fri Jul 30 17:04:17 2010 +1000
@@ -20,12 +20,11 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
+# Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
 #
-#ident	"%Z%%M%	%I%	%E% SMI"
 
 . /lib/svc/share/smf_include.sh
+. /lib/svc/share/fs_include.sh
 
 #
 # mksavedir
@@ -47,9 +46,33 @@
 # an alternate dump device, then the dump is in the primary swap partition,
 # which was configured as the dump device by the first swapadd early in boot.
 # Thus before we run dumpadm to configure the dump device, we first run
-# savecore to check the swap partition for a dump.
+# savecore to check the swap partition for a dump;  this is run in the
+# foreground to reduce the chances of overwriting the dump.
+#
+# This does not apply for zfs root systems that use a zvol for dump;
+# for such systems the dedicated dump device is appointed during startup
+# of the filesystem/usr:default instance before any swap is added.
+# Therefore we must check that the dump device is a swap device here -
+# if not then we'll run savecore here in the foreground and prevent
+# our dependent services coming online until we're done.
 #
-if [ -x /usr/bin/savecore ]; then
+
+rootiszfs=0
+alreadydedicated=0
+
+readmnttab / </etc/mnttab
+if [ "$fstype" = zfs ] ; then
+	rootiszfs=1
+	if [ -x /usr/sbin/dumpadm ]; then
+		if /usr/sbin/dumpadm 2>/dev/null | grep "Dump device:" | \
+		    grep '(dedicated)' > /dev/null 2>&1; then
+			alreadydedicated=1
+		fi
+	fi
+fi
+
+if [ -x /usr/bin/savecore -a \
+    \( ! $rootiszfs -eq 1 -o $alreadydedicated -eq 0 \) ]; then
 	[ -r /etc/dumpadm.conf ] && . /etc/dumpadm.conf
 
 	if [ "x$DUMPADM_ENABLE" != xno ] && mksavedir; then
@@ -57,8 +80,17 @@
 		shift $#
 		set -- `/usr/sbin/dumpadm 2>/dev/null | /usr/bin/grep 'device:'`
 		savedev=${3:-none}
+	else
+		#
+		# dumpadm -n is in effect, but we can still run savecore
+		# to raise an event with initial panic detail extracted
+		# from the dump header.
+		#
+		/usr/bin/savecore -c
 	fi
-else
+fi
+
+if [ ! -x /usr/bin/savecore ]; then
 	echo "WARNING: /usr/bin/savecore is missing or not executable" >& 2
 fi
 
@@ -82,20 +114,38 @@
 fi
 
 #
+# If the savecore executable is absent then we're done
+#
+if [ ! -x /usr/bin/savecore ]; then
+	exit $SMF_EXIT_ERR_CONFIG
+fi
+
+#
 # Now that dumpadm has reconfigured /dev/dump, we need to run savecore again
 # because the dump device may have changed.  If the earlier savecore had
 # saved the dump, savecore will just exit immediately.
 #
+
+isswap=0
+swapchanged=0
+if /usr/sbin/swap -l 2>/dev/null | grep "^${DUMPADM_DEVICE} " \
+	    >/dev/null 2>&1; then
+	isswap=1
+	if [ "x$savedev" != "x$DUMPADM_DEVICE" ]; then
+		swapchanged=1
+	fi
+fi
+
 if [ "x$DUMPADM_ENABLE" != xno ]; then
-	if /usr/sbin/swap -l 2>/dev/null | grep "^${DUMPADM_DEVICE} " \
-	    >/dev/null 2>&1; then
+	if [ $isswap -eq 1 ]; then
 		#
 		# If the dump device is part of swap, we only need to run
 		# savecore a second time if the device is different from the
 		# swap device on which we initially ran savecore.
 		#
-		[ "x$savedev" != "x$DUMPADM_DEVICE" ] && \
+		if [ $swapchanged -eq 1 ]; then
 		    mksavedir && /usr/bin/savecore $DUMPADM_SAVDIR &
+		fi
 	else
 		#
 		# The dump device couldn't have been dedicated before we
@@ -103,6 +153,16 @@
 		#
 		mksavedir && /usr/bin/savecore $DUMPADM_SAVDIR &
 	fi
+else
+		#
+		# savecore not enabled.  Check whether a valid dump is
+		# present on the device and raise an event to signal that,
+		# but avoid sending a duplicate event from the savecore -c
+		# earlier.
+		#
+		if [ $isswap -eq 0 -o $swapchanged -eq 1 ]; then
+			/usr/bin/savecore -c
+		fi
 fi
 
 exit $SMF_EXIT_OK
--- a/usr/src/cmd/fm/Makefile	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/fm/Makefile	Fri Jul 30 17:04:17 2010 +1000
@@ -18,11 +18,10 @@
 #
 # CDDL HEADER END
 #
+
 #
-# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
+# Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
 #
-#ident	"%Z%%M%	%I%	%E% SMI"
 
 SUBDIRS = \
 	fmd \
@@ -36,7 +35,8 @@
 	scripts \
 	modules \
 	dicts \
-	eversholt
+	eversholt \
+	notify
 
 include ./Makefile.subdirs
 
--- a/usr/src/cmd/fm/dicts/FMD.dict	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/fm/dicts/FMD.dict	Fri Jul 30 17:04:17 2010 +1000
@@ -1,4 +1,5 @@
 #
+# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
 #
 # CDDL HEADER START
 #
@@ -19,12 +20,11 @@
 #
 # CDDL HEADER END
 #
-# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
+
+# DO NOT EDIT -- this file is generated by the Event Registry.
 #
-#ident	"%Z%%M%	%I%	%E% SMI"
 
-FMDICT: name=FMD version=1 maxkey=1
+FMDICT: name=FMD version=1 maxkey=1 dictid=0x464d
 
 defect.sunos.fmd.nosub=0
 defect.sunos.fmd.nodiagcode=1
--- a/usr/src/cmd/fm/dicts/FMD.po	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/fm/dicts/FMD.po	Fri Jul 30 17:04:17 2010 +1000
@@ -1,6 +1,5 @@
 #
-# Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
+# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
 #
 # CDDL HEADER START
 #
@@ -21,6 +20,7 @@
 #
 # CDDL HEADER END
 #
+
 # DO NOT EDIT -- this file is generated by the Event Registry.
 #
 #
@@ -28,9 +28,6 @@
 #
 msgid "syslog-msgs-message-template"
 msgstr "SUNW-MSG-ID: %s, TYPE: %s, VER: 1, SEVERITY: %s\nEVENT-TIME: %s\nPLATFORM: %s, CSN: %s, HOSTNAME: %s\nSOURCE: %s, REV: %s\nEVENT-ID: %s\nDESC: %s\nAUTO-RESPONSE: %s\nIMPACT: %s\nREC-ACTION: %s\n"
-msgid "syslog-msgs-pointer"
-msgstr "\n...\nSee fmadm faulty -u <EVENT-ID> for full information.\n"
-
 #
 # Fault-related messages
 #
@@ -44,7 +41,7 @@
 msgid "FMD-8000-0W.severity"
 msgstr "Minor"
 msgid "FMD-8000-0W.description"
-msgstr "The Solaris Fault Manager received an event %<fault-list[0].nosub_class> to which no automated diagnosis software is currently subscribed.  Refer to %s for more information."
+msgstr "The Solaris Fault Manager received an event from a component to which no automated diagnosis software is currently subscribed.  Refer to %s for more information."
 msgid "FMD-8000-0W.response"
 msgstr "Error reports from the component will be logged for examination by Sun."
 msgid "FMD-8000-0W.impact"
@@ -110,9 +107,9 @@
 msgid "FMD-8000-4M.description"
 msgstr "All faults associated with an event id have been addressed.\n  Refer to %s for more information."
 msgid "FMD-8000-4M.response"
-msgstr "Some system components offlined because of the original fault may have been brought back online.\n"
+msgstr "Some system components offlined because of the original fault may have been brought back online."
 msgid "FMD-8000-4M.impact"
-msgstr "Performance degradation of the system due to the original fault may have been recovered.\n"
+msgstr "Performance degradation of the system due to the original fault may have been recovered."
 msgid "FMD-8000-4M.action"
 msgstr "Use fmdump -v -u <EVENT-ID> to identify the repaired components."
 #
@@ -126,11 +123,11 @@
 msgid "FMD-8000-58.description"
 msgstr "Some faults associated with an event id have been addressed.\n  Refer to %s for more information."
 msgid "FMD-8000-58.response"
-msgstr "Some system components offlined because of the original fault may have been brought back online.\n"
+msgstr "Some system components offlined because of the original fault may have been brought back online."
 msgid "FMD-8000-58.impact"
-msgstr "Performance degradation of the system due to the original fault may have been recovered.\n"
+msgstr "Performance degradation of the system due to the original fault may have been recovered."
 msgid "FMD-8000-58.action"
-msgstr "Use fmadm faulty to identify the repaired components, and any suspects that still need to be repaired.\n"
+msgstr "Use fmadm faulty to identify the repaired components, and any suspects that still need to be repaired."
 #
 # code: FMD-8000-6U
 # keys: list.resolved
@@ -142,8 +139,8 @@
 msgid "FMD-8000-6U.description"
 msgstr "All faults associated with an event id have been addressed.\n  Refer to %s for more information."
 msgid "FMD-8000-6U.response"
-msgstr "All system components offlined because of the original fault have been brought back online.\n"
+msgstr "All system components offlined because of the original fault have been brought back online."
 msgid "FMD-8000-6U.impact"
-msgstr "Performance degradation of the system due to the original fault has been recovered.\n"
+msgstr "Performance degradation of the system due to the original fault has been recovered."
 msgid "FMD-8000-6U.action"
-msgstr "Use fmdump -v -u <EVENT-ID> to identify the repaired components.\n"
+msgstr "Use fmdump -v -u <EVENT-ID> to identify the repaired components."
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fm/dicts/FMNOTIFY.dict	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,25 @@
+#
+#
+# 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.
+#
+
+FMDICT: name=FMNOTIFY version=1 maxkey=1
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fm/dicts/FMNOTIFY.po	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,63 @@
+#
+# 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
+#
+#
+# DO NOT EDIT -- this file is generated by the Event Registry.
+#
+
+#
+# default format for subject of emails generated by the smtp-notify agent
+#
+# params: hostname, code
+#
+msgid "smtp-notify-subject-template"
+msgstr "Solaris Event Notification:%s:%s"
+
+#
+# format for subject of emails generated by the smtp-notify agent
+# for SMF service state transition events
+#
+# params: hostname, fmri, from-state, to-state
+#
+msgid "smtp-notify-smf-subject-template"
+msgstr "%s: %s %s->%s"
+
+#
+# format for subject of emails generated by the smtp-notify agent
+# for FMA events
+#
+# params: hostname, code
+#
+msgid "smtp-notify-fm-subject-template"
+msgstr "Fault Management Event: %s:%s"
+
+#
+# Generic message template for raw software events
+#
+msgid "ireport-msg-template"
+msgstr "HOSTNAME: %s\nTIMESTAMP: %s\nCLASS: %s\n"
+
+#
+# default email message template for SMF service state transition events
+#
+msgid "ireport.os.smf-msg-template"
+msgstr "HOSTNAME: %s\nTIMESTAMP: %s\nFMRI: %s\nFROM-STATE: %s\nTO-STATE: %s\nDESCRIPTION: %s\nREASON: %s\n"
--- a/usr/src/cmd/fm/dicts/Makefile	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/fm/dicts/Makefile	Fri Jul 30 17:04:17 2010 +1000
@@ -27,7 +27,9 @@
 common_DCNAMES = \
 	DISK \
 	FMD \
+	FMNOTIFY \
 	NXGE \
+	SMF \
 	SUNOS \
 	PCI \
 	PCIEX \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fm/dicts/SMF.dict	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,60 @@
+#
+# 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
+#
+
+# DO NOT EDIT -- this file is generated by the Event Registry.
+#
+
+FMDICT: name=SMF version=1 maxkey=1 dictid=0x534d
+
+defect.sunos.smf.svc.disabled=0
+defect.sunos.smf.svc.temporarily_disabled=1
+defect.sunos.smf.svc.invalid_restarter=2
+defect.sunos.smf.svc.restarter_absent=3
+defect.sunos.smf.svc.uninitialized=4
+defect.sunos.smf.svc.restarter_dead=5
+defect.sunos.smf.svc.administrative_request=6
+defect.sunos.smf.svc.fault_threshold_reached=7
+defect.sunos.smf.svc.method_fail=8
+defect.sunos.smf.svc.none=9
+defect.sunos.smf.svc.unknown=10
+defect.sunos.smf.svc.starting=11
+defect.sunos.smf.svc.administrative_degraded=12
+defect.sunos.smf.svc.dependency_absent=13
+defect.sunos.smf.svc.dependency_running=14
+defect.sunos.smf.svc.dependency_other=15
+defect.sunos.smf.svc.dependency_cycle=16
+defect.sunos.smf.svc.invalid_dependency=17
+defect.sunos.smf.svc.start_method_fail=18
+defect.sunos.smf.svc.restarting_too_quickly=19
+defect.sunos.smf.db.verify=20
+defect.sunos.smf.svc.bad_repo_state=21
+defect.sunos.smf.svc.transitioning=22
+defect.sunos.smf.info.recovery=23
+defect.sunos.smf.svc.service_request=24
+ireport.os.smf.state-transition.offline=25
+ireport.os.smf.state-transition.online=26
+ireport.os.smf.state-transition.disabled=27
+ireport.os.smf.state-transition.degraded=28
+ireport.os.smf.state-transition.uninitialized=29
+ireport.os.smf.state-transition.maintenance=30
+defect.sunos.smf.svc.maintenance=31
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fm/dicts/SMF.po	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,537 @@
+#
+# 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
+#
+
+# DO NOT EDIT -- this file is generated by the Event Registry.
+#
+#
+# code: SMF-8000-05
+# keys: defect.sunos.smf.svc.disabled
+#
+msgid "SMF-8000-05.type"
+msgstr "XXX"
+msgid "SMF-8000-05.severity"
+msgstr "XXX"
+msgid "SMF-8000-05.description"
+msgstr "XXX  Refer to %s for more information."
+msgid "SMF-8000-05.response"
+msgstr "XXX"
+msgid "SMF-8000-05.impact"
+msgstr "XXX"
+msgid "SMF-8000-05.action"
+msgstr "XXX"
+#
+# code: SMF-8000-1S
+# keys: defect.sunos.smf.svc.temporarily_disabled
+#
+msgid "SMF-8000-1S.type"
+msgstr "XXX"
+msgid "SMF-8000-1S.severity"
+msgstr "XXX"
+msgid "SMF-8000-1S.description"
+msgstr "XXX  Refer to %s for more information."
+msgid "SMF-8000-1S.response"
+msgstr "XXX"
+msgid "SMF-8000-1S.impact"
+msgstr "XXX"
+msgid "SMF-8000-1S.action"
+msgstr "XXX"
+#
+# code: SMF-8000-2A
+# keys: defect.sunos.smf.svc.invalid_restarter
+#
+msgid "SMF-8000-2A.type"
+msgstr "XXX"
+msgid "SMF-8000-2A.severity"
+msgstr "XXX"
+msgid "SMF-8000-2A.description"
+msgstr "XXX  Refer to %s for more information."
+msgid "SMF-8000-2A.response"
+msgstr "XXX"
+msgid "SMF-8000-2A.impact"
+msgstr "XXX"
+msgid "SMF-8000-2A.action"
+msgstr "XXX"
+#
+# code: SMF-8000-3P
+# keys: defect.sunos.smf.svc.restarter_absent
+#
+msgid "SMF-8000-3P.type"
+msgstr "XXX"
+msgid "SMF-8000-3P.severity"
+msgstr "XXX"
+msgid "SMF-8000-3P.description"
+msgstr "XXX  Refer to %s for more information."
+msgid "SMF-8000-3P.response"
+msgstr "XXX"
+msgid "SMF-8000-3P.impact"
+msgstr "XXX"
+msgid "SMF-8000-3P.action"
+msgstr "XXX"
+#
+# code: SMF-8000-4D
+# keys: defect.sunos.smf.svc.uninitialized
+#
+msgid "SMF-8000-4D.type"
+msgstr "XXX"
+msgid "SMF-8000-4D.severity"
+msgstr "XXX"
+msgid "SMF-8000-4D.description"
+msgstr "XXX  Refer to %s for more information."
+msgid "SMF-8000-4D.response"
+msgstr "XXX"
+msgid "SMF-8000-4D.impact"
+msgstr "XXX"
+msgid "SMF-8000-4D.action"
+msgstr "XXX"
+#
+# code: SMF-8000-5H
+# keys: defect.sunos.smf.svc.restarter_dead
+#
+msgid "SMF-8000-5H.type"
+msgstr "XXX"
+msgid "SMF-8000-5H.severity"
+msgstr "XXX"
+msgid "SMF-8000-5H.description"
+msgstr "XXX  Refer to %s for more information."
+msgid "SMF-8000-5H.response"
+msgstr "XXX"
+msgid "SMF-8000-5H.impact"
+msgstr "XXX"
+msgid "SMF-8000-5H.action"
+msgstr "XXX"
+#
+# code: SMF-8000-63
+# keys: defect.sunos.smf.svc.administrative_request
+#
+msgid "SMF-8000-63.type"
+msgstr "XXX"
+msgid "SMF-8000-63.severity"
+msgstr "XXX"
+msgid "SMF-8000-63.description"
+msgstr "XXX  Refer to %s for more information."
+msgid "SMF-8000-63.response"
+msgstr "XXX"
+msgid "SMF-8000-63.impact"
+msgstr "XXX"
+msgid "SMF-8000-63.action"
+msgstr "XXX"
+#
+# code: SMF-8000-7Y
+# keys: defect.sunos.smf.svc.fault_threshold_reached
+#
+msgid "SMF-8000-7Y.type"
+msgstr "XXX"
+msgid "SMF-8000-7Y.severity"
+msgstr "XXX"
+msgid "SMF-8000-7Y.description"
+msgstr "XXX  Refer to %s for more information."
+msgid "SMF-8000-7Y.response"
+msgstr "XXX"
+msgid "SMF-8000-7Y.impact"
+msgstr "XXX"
+msgid "SMF-8000-7Y.action"
+msgstr "XXX"
+#
+# code: SMF-8000-8Q
+# keys: defect.sunos.smf.svc.method_fail
+#
+msgid "SMF-8000-8Q.type"
+msgstr "XXX"
+msgid "SMF-8000-8Q.severity"
+msgstr "XXX"
+msgid "SMF-8000-8Q.description"
+msgstr "XXX  Refer to %s for more information."
+msgid "SMF-8000-8Q.response"
+msgstr "XXX"
+msgid "SMF-8000-8Q.impact"
+msgstr "XXX"
+msgid "SMF-8000-8Q.action"
+msgstr "XXX"
+#
+# code: SMF-8000-9C
+# keys: defect.sunos.smf.svc.none
+#
+msgid "SMF-8000-9C.type"
+msgstr "XXX"
+msgid "SMF-8000-9C.severity"
+msgstr "XXX"
+msgid "SMF-8000-9C.description"
+msgstr "XXX  Refer to %s for more information."
+msgid "SMF-8000-9C.response"
+msgstr "XXX"
+msgid "SMF-8000-9C.impact"
+msgstr "XXX"
+msgid "SMF-8000-9C.action"
+msgstr "XXX"
+#
+# code: SMF-8000-AR
+# keys: defect.sunos.smf.svc.unknown
+#
+msgid "SMF-8000-AR.type"
+msgstr "XXX"
+msgid "SMF-8000-AR.severity"
+msgstr "XXX"
+msgid "SMF-8000-AR.description"
+msgstr "XXX  Refer to %s for more information."
+msgid "SMF-8000-AR.response"
+msgstr "XXX"
+msgid "SMF-8000-AR.impact"
+msgstr "XXX"
+msgid "SMF-8000-AR.action"
+msgstr "XXX"
+#
+# code: SMF-8000-C4
+# keys: defect.sunos.smf.svc.starting
+#
+msgid "SMF-8000-C4.type"
+msgstr "XXX"
+msgid "SMF-8000-C4.severity"
+msgstr "XXX"
+msgid "SMF-8000-C4.description"
+msgstr "XXX  Refer to %s for more information."
+msgid "SMF-8000-C4.response"
+msgstr "XXX"
+msgid "SMF-8000-C4.impact"
+msgstr "XXX"
+msgid "SMF-8000-C4.action"
+msgstr "XXX"
+#
+# code: SMF-8000-DX
+# keys: defect.sunos.smf.svc.administrative_degraded
+#
+msgid "SMF-8000-DX.type"
+msgstr "XXX"
+msgid "SMF-8000-DX.severity"
+msgstr "XXX"
+msgid "SMF-8000-DX.description"
+msgstr "XXX  Refer to %s for more information."
+msgid "SMF-8000-DX.response"
+msgstr "XXX"
+msgid "SMF-8000-DX.impact"
+msgstr "XXX"
+msgid "SMF-8000-DX.action"
+msgstr "XXX"
+#
+# code: SMF-8000-E2
+# keys: defect.sunos.smf.svc.dependency_absent
+#
+msgid "SMF-8000-E2.type"
+msgstr "XXX"
+msgid "SMF-8000-E2.severity"
+msgstr "XXX"
+msgid "SMF-8000-E2.description"
+msgstr "XXX  Refer to %s for more information."
+msgid "SMF-8000-E2.response"
+msgstr "XXX"
+msgid "SMF-8000-E2.impact"
+msgstr "XXX"
+msgid "SMF-8000-E2.action"
+msgstr "XXX"
+#
+# code: SMF-8000-FJ
+# keys: defect.sunos.smf.svc.dependency_running
+#
+msgid "SMF-8000-FJ.type"
+msgstr "XXX"
+msgid "SMF-8000-FJ.severity"
+msgstr "XXX"
+msgid "SMF-8000-FJ.description"
+msgstr "XXX  Refer to %s for more information."
+msgid "SMF-8000-FJ.response"
+msgstr "XXX"
+msgid "SMF-8000-FJ.impact"
+msgstr "XXX"
+msgid "SMF-8000-FJ.action"
+msgstr "XXX"
+#
+# code: SMF-8000-GE
+# keys: defect.sunos.smf.svc.dependency_other
+#
+msgid "SMF-8000-GE.type"
+msgstr "XXX"
+msgid "SMF-8000-GE.severity"
+msgstr "XXX"
+msgid "SMF-8000-GE.description"
+msgstr "XXX  Refer to %s for more information."
+msgid "SMF-8000-GE.response"
+msgstr "XXX"
+msgid "SMF-8000-GE.impact"
+msgstr "XXX"
+msgid "SMF-8000-GE.action"
+msgstr "XXX"
+#
+# code: SMF-8000-HP
+# keys: defect.sunos.smf.svc.dependency_cycle
+#
+msgid "SMF-8000-HP.type"
+msgstr "XXX"
+msgid "SMF-8000-HP.severity"
+msgstr "XXX"
+msgid "SMF-8000-HP.description"
+msgstr "XXX  Refer to %s for more information."
+msgid "SMF-8000-HP.response"
+msgstr "XXX"
+msgid "SMF-8000-HP.impact"
+msgstr "XXX"
+msgid "SMF-8000-HP.action"
+msgstr "XXX"
+#
+# code: SMF-8000-JA
+# keys: defect.sunos.smf.svc.invalid_dependency
+#
+msgid "SMF-8000-JA.type"
+msgstr "XXX"
+msgid "SMF-8000-JA.severity"
+msgstr "XXX"
+msgid "SMF-8000-JA.description"
+msgstr "XXX  Refer to %s for more information."
+msgid "SMF-8000-JA.response"
+msgstr "XXX"
+msgid "SMF-8000-JA.impact"
+msgstr "XXX"
+msgid "SMF-8000-JA.action"
+msgstr "XXX"
+#
+# code: SMF-8000-KS
+# keys: defect.sunos.smf.svc.start_method_fail
+#
+msgid "SMF-8000-KS.type"
+msgstr "XXX"
+msgid "SMF-8000-KS.severity"
+msgstr "XXX"
+msgid "SMF-8000-KS.description"
+msgstr "XXX  Refer to %s for more information."
+msgid "SMF-8000-KS.response"
+msgstr "XXX"
+msgid "SMF-8000-KS.impact"
+msgstr "XXX"
+msgid "SMF-8000-KS.action"
+msgstr "XXX"
+#
+# code: SMF-8000-L5
+# keys: defect.sunos.smf.svc.restarting_too_quickly
+#
+msgid "SMF-8000-L5.type"
+msgstr "XXX"
+msgid "SMF-8000-L5.severity"
+msgstr "XXX"
+msgid "SMF-8000-L5.description"
+msgstr "XXX  Refer to %s for more information."
+msgid "SMF-8000-L5.response"
+msgstr "XXX"
+msgid "SMF-8000-L5.impact"
+msgstr "XXX"
+msgid "SMF-8000-L5.action"
+msgstr "XXX"
+#
+# code: SMF-8000-MY
+# keys: defect.sunos.smf.db.verify
+#
+msgid "SMF-8000-MY.type"
+msgstr "XXX"
+msgid "SMF-8000-MY.severity"
+msgstr "XXX"
+msgid "SMF-8000-MY.description"
+msgstr "XXX  Refer to %s for more information."
+msgid "SMF-8000-MY.response"
+msgstr "XXX"
+msgid "SMF-8000-MY.impact"
+msgstr "XXX"
+msgid "SMF-8000-MY.action"
+msgstr "XXX"
+#
+# code: SMF-8000-N3
+# keys: defect.sunos.smf.svc.bad_repo_state
+#
+msgid "SMF-8000-N3.type"
+msgstr "XXX"
+msgid "SMF-8000-N3.severity"
+msgstr "XXX"
+msgid "SMF-8000-N3.description"
+msgstr "XXX  Refer to %s for more information."
+msgid "SMF-8000-N3.response"
+msgstr "XXX"
+msgid "SMF-8000-N3.impact"
+msgstr "XXX"
+msgid "SMF-8000-N3.action"
+msgstr "XXX"
+#
+# code: SMF-8000-PH
+# keys: defect.sunos.smf.svc.transitioning
+#
+msgid "SMF-8000-PH.type"
+msgstr "XXX"
+msgid "SMF-8000-PH.severity"
+msgstr "XXX"
+msgid "SMF-8000-PH.description"
+msgstr "XXX  Refer to %s for more information."
+msgid "SMF-8000-PH.response"
+msgstr "XXX"
+msgid "SMF-8000-PH.impact"
+msgstr "XXX"
+msgid "SMF-8000-PH.action"
+msgstr "XXX"
+#
+# code: SMF-8000-QD
+# keys: defect.sunos.smf.info.recovery
+#
+msgid "SMF-8000-QD.type"
+msgstr "XXX"
+msgid "SMF-8000-QD.severity"
+msgstr "XXX"
+msgid "SMF-8000-QD.description"
+msgstr "XXX  Refer to %s for more information."
+msgid "SMF-8000-QD.response"
+msgstr "XXX"
+msgid "SMF-8000-QD.impact"
+msgstr "XXX"
+msgid "SMF-8000-QD.action"
+msgstr "XXX"
+#
+# code: SMF-8000-R4
+# keys: defect.sunos.smf.svc.service_request
+#
+msgid "SMF-8000-R4.type"
+msgstr "XXX"
+msgid "SMF-8000-R4.severity"
+msgstr "XXX"
+msgid "SMF-8000-R4.description"
+msgstr "XXX  Refer to %s for more information."
+msgid "SMF-8000-R4.response"
+msgstr "XXX"
+msgid "SMF-8000-R4.impact"
+msgstr "XXX"
+msgid "SMF-8000-R4.action"
+msgstr "XXX"
+#
+# code: SMF-8000-SR
+# keys: ireport.os.smf.state-transition.offline
+#
+msgid "SMF-8000-SR.type"
+msgstr "XXX"
+msgid "SMF-8000-SR.severity"
+msgstr "minor"
+msgid "SMF-8000-SR.description"
+msgstr "The indicated service has transitioned to the offline state\n  Refer to %s for more information."
+msgid "SMF-8000-SR.response"
+msgstr "XXX"
+msgid "SMF-8000-SR.impact"
+msgstr "Functionality provided by the offline service is unavailable."
+msgid "SMF-8000-SR.action"
+msgstr "XXX"
+#
+# code: SMF-8000-TC
+# keys: ireport.os.smf.state-transition.online
+#
+msgid "SMF-8000-TC.type"
+msgstr "XXX"
+msgid "SMF-8000-TC.severity"
+msgstr "minor"
+msgid "SMF-8000-TC.description"
+msgstr "The indicated service has transitioned to the online state\n  Refer to %s for more information."
+msgid "SMF-8000-TC.response"
+msgstr "XXX"
+msgid "SMF-8000-TC.impact"
+msgstr "Functionality provided by the service is now available."
+msgid "SMF-8000-TC.action"
+msgstr "XXX"
+#
+# code: SMF-8000-UQ
+# keys: ireport.os.smf.state-transition.disabled
+#
+msgid "SMF-8000-UQ.type"
+msgstr "XXX"
+msgid "SMF-8000-UQ.severity"
+msgstr "minor"
+msgid "SMF-8000-UQ.description"
+msgstr "The indicated service has transitioned to the disabled state\n  Refer to %s for more information."
+msgid "SMF-8000-UQ.response"
+msgstr "XXX"
+msgid "SMF-8000-UQ.impact"
+msgstr "Functionality provided by the service is unavailable."
+msgid "SMF-8000-UQ.action"
+msgstr "XXX"
+#
+# code: SMF-8000-VE
+# keys: ireport.os.smf.state-transition.degraded
+#
+msgid "SMF-8000-VE.type"
+msgstr "XXX"
+msgid "SMF-8000-VE.severity"
+msgstr "minor"
+msgid "SMF-8000-VE.description"
+msgstr "The indicated service has transitioned to the degraded state\n  Refer to %s for more information."
+msgid "SMF-8000-VE.response"
+msgstr "XXX"
+msgid "SMF-8000-VE.impact"
+msgstr "Some functionality provided by the service may be unavailable."
+msgid "SMF-8000-VE.action"
+msgstr "XXX"
+#
+# code: SMF-8000-WJ
+# keys: ireport.os.smf.state-transition.uninitialized
+#
+msgid "SMF-8000-WJ.type"
+msgstr "XXX"
+msgid "SMF-8000-WJ.severity"
+msgstr "minor"
+msgid "SMF-8000-WJ.description"
+msgstr "The indicated service has transitioned to the uninitialized state\n  Refer to %s for more information."
+msgid "SMF-8000-WJ.response"
+msgstr "XXX"
+msgid "SMF-8000-WJ.impact"
+msgstr "Functionality provided by the service is unavailable."
+msgid "SMF-8000-WJ.action"
+msgstr "XXX"
+#
+# code: SMF-8000-X2
+# keys: ireport.os.smf.state-transition.maintenance
+#
+msgid "SMF-8000-X2.type"
+msgstr "XXX"
+msgid "SMF-8000-X2.severity"
+msgstr "major"
+msgid "SMF-8000-X2.description"
+msgstr "The indicated service has transitioned to the maintenance state\n  Refer to %s for more information."
+msgid "SMF-8000-X2.response"
+msgstr "XXX"
+msgid "SMF-8000-X2.impact"
+msgstr "Functionality provided by the service is unavailable."
+msgid "SMF-8000-X2.action"
+msgstr "XXX"
+#
+# code: SMF-8000-YX
+# keys: defect.sunos.smf.svc.maintenance
+#
+msgid "SMF-8000-YX.type"
+msgstr "defect"
+msgid "SMF-8000-YX.severity"
+msgstr "major"
+msgid "SMF-8000-YX.description"
+msgstr "A service failed - %<fault-list[0].reason-long>.\n  Refer to %s for more information."
+msgid "SMF-8000-YX.response"
+msgstr "The service has been placed into the maintenance state."
+msgid "SMF-8000-YX.impact"
+msgstr "%<fault-list[0].svc-string> is unavailable."
+msgid "SMF-8000-YX.action"
+msgstr "Run 'svcs -xv %<fault-list[0].svc-string>' to determine the generic reason why the service failed, the location of any logfiles, and a list of other services impacted."
--- a/usr/src/cmd/fm/dicts/SUNOS.dict	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/fm/dicts/SUNOS.dict	Fri Jul 30 17:04:17 2010 +1000
@@ -1,6 +1,5 @@
 #
-# Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
+# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
 #
 # CDDL HEADER START
 #
@@ -21,6 +20,7 @@
 #
 # CDDL HEADER END
 #
+
 # DO NOT EDIT -- this file is generated by the Event Registry.
 #
 
@@ -44,3 +44,4 @@
 defect.sunos.eft.undiag.limit=15
 defect.sunos.eft.undiag.unknown=16
 defect.sunos.eft.unexpected_telemetry fault.sunos.eft.unexpected_telemetry=17
+defect.sunos.kernel.panic=18
--- a/usr/src/cmd/fm/dicts/SUNOS.po	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/fm/dicts/SUNOS.po	Fri Jul 30 17:04:17 2010 +1000
@@ -1,6 +1,5 @@
 #
-# Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
+# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
 #
 # CDDL HEADER START
 #
@@ -21,6 +20,7 @@
 #
 # CDDL HEADER END
 #
+
 # DO NOT EDIT -- this file is generated by the Event Registry.
 #
 #
@@ -194,11 +194,11 @@
 msgid "SUNOS-8000-AK.description"
 msgstr "Solaris OS could not automatically adjust\n/boot/grub/menu.lst\nto the new boot architecture\n  Refer to %s for more information."
 msgid "SUNOS-8000-AK.response"
-msgstr "No automated response.\n"
+msgstr "No automated response."
 msgid "SUNOS-8000-AK.impact"
-msgstr "Future reboots could fail until this problem is corrected.\n"
+msgstr "Future reboots could fail until this problem is corrected."
 msgid "SUNOS-8000-AK.action"
-msgstr "Examine /boot/grub/menu.lst and revise as\ndescribed in Details.\n"
+msgstr "Examine /boot/grub/menu.lst and revise as\ndescribed in Details."
 #
 # code: SUNOS-8000-CF
 # keys: ereport.sunos.boot.upgrade.menu_lst_bfu
@@ -210,11 +210,11 @@
 msgid "SUNOS-8000-CF.description"
 msgstr "Solaris OS could not automatically adjust\n/boot/grub/menu.lst\nto the new boot architecture\n  Refer to %s for more information."
 msgid "SUNOS-8000-CF.response"
-msgstr "No automated response.\n"
+msgstr "No automated response."
 msgid "SUNOS-8000-CF.impact"
-msgstr "Future reboots could fail until this problem is corrected.\n"
+msgstr "Future reboots could fail until this problem is corrected."
 msgid "SUNOS-8000-CF.action"
-msgstr "Examine /boot/grub/menu.lst and revise as\ndescribed in Details.\n"
+msgstr "Examine /boot/grub/menu.lst and revise as\ndescribed in Details."
 #
 # code: SUNOS-8000-DM
 # keys: defect.sunos.smf.svc.maintenance
@@ -304,10 +304,26 @@
 msgid "SUNOS-8000-J0.severity"
 msgstr "Major"
 msgid "SUNOS-8000-J0.description"
-msgstr "The diagnosis engine encountered telemetry from the listed devices for which it was unable to perform a diagnosis - %<fault-list[0].reason>.\n  Refer to %s for more information."
+msgstr "The diagnosis engine encountered telemetry from the listed devices for which it was unable to perform a diagnosis - %.\n  Refer to %s for more information.  Refer to %s for more information."
 msgid "SUNOS-8000-J0.response"
 msgstr "Error reports have been logged for examination by Sun.\n"
 msgid "SUNOS-8000-J0.impact"
 msgstr "Automated diagnosis and response for these events will not occur.\n"
 msgid "SUNOS-8000-J0.action"
 msgstr "Ensure that the latest Solaris Kernel and Predictive Self-Healing (PSH) patches are installed.\n"
+#
+# code: SUNOS-8000-KL
+# keys: defect.sunos.kernel.panic
+#
+msgid "SUNOS-8000-KL.type"
+msgstr "Defect"
+msgid "SUNOS-8000-KL.severity"
+msgstr "Major"
+msgid "SUNOS-8000-KL.description"
+msgstr "The system has rebooted after a kernel panic.  Refer to %s for more information."
+msgid "SUNOS-8000-KL.response"
+msgstr "The failed system image was dumped to the dump device.  If savecore is enabled (see dumpadm(1M)) a copy of the dump will be written to the savecore directory %<fault-list[0].dump-dir>."
+msgid "SUNOS-8000-KL.impact"
+msgstr "There may be some performance impact while the panic is copied to the savecore directory.  Disk space usage by panics can be substantial."
+msgid "SUNOS-8000-KL.action"
+msgstr "If savecore is not enabled then please take steps to preserve the crash image.\nUse 'fmdump -Vp -u %<uuid>' to view more panic detail.  Please refer to the knowledge article for additional information."
--- a/usr/src/cmd/fm/fmd/Makefile.fmd	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/fm/fmd/Makefile.fmd	Fri Jul 30 17:04:17 2010 +1000
@@ -19,8 +19,7 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
+# Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
 #
 
 .KEEP_STATE:
@@ -77,6 +76,11 @@
 MAPFILE-DMOD = $(SRC)/cmd/mdb/common/modules/conf/mapfile-extern
 
 
+LOGADMFILE =	$(PROG).logadm.conf
+LOGADMDIR =	$(ROOT)/etc/logadm.d
+LOGADMENT =	$(LOGADMDIR)/$(LOGADMFILE)
+$(LOGADMENT) :=	FILEMODE = 444
+
 ROOTPDIR = $(ROOT)/usr/lib/fm/$(PROG)
 ROOTVDIR = $(ROOT)/var/fm/$(PROG)
 ROOTVSUB = $(ROOTVDIR)/ckpt $(ROOTVDIR)/rsrc $(ROOTVDIR)/xprt
@@ -139,6 +143,12 @@
 
 all: $(PROG) $(DMOD) install_h
 
+$(LOGADMDIR):
+	$(INS.dir)
+
+$(LOGADMDIR)/%.conf: ../common/%.conf
+	$(INS.file)
+
 $(PROG): $(OBJS)
 	$(LINK.c) $(OBJS) -o $@ $(LDLIBS)
 	$(CTFMERGE) -L VERSION -o $@ $(OBJS)
@@ -234,7 +244,7 @@
 
 install_h: $(ROOTHDIR) $(ROOTHDRS)
 
-install: all install_h $(ROOTPROG) $(ROOTDMOD) \
+install: all install_h $(ROOTPROG) $(ROOTDMOD) $(LOGADMDIR) $(LOGADMENT) \
 	$(ROOTCDIR) $(ROOTVDIR) $(ROOTVSUB) $(ROOTMANIFEST)
 
 check: $(CHKMANIFEST)
--- a/usr/src/cmd/fm/fmd/common/fmd.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/fm/fmd/common/fmd.c	Fri Jul 30 17:04:17 2010 +1000
@@ -20,8 +20,7 @@
  */
 
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #include <sys/types.h>
@@ -239,6 +238,7 @@
 { "client.buflim", &fmd_conf_size, "10m" },	/* client buffer space limit */
 { "client.dbout", &fmd_dbout_ops, NULL },	/* client debug output sinks */
 { "client.debug", &fmd_conf_bool, NULL },	/* client debug enable */
+{ "client.doorthrlim", &fmd_conf_uint32, "20" }, /* client door thread limit */
 { "client.error", &fmd_cerror_ops, "unload" },	/* client error policy */
 { "client.memlim", &fmd_conf_size, "10m" },	/* client allocation limit */
 { "client.evqlim", &fmd_conf_uint32, "256" },	/* client event queue limit */
@@ -265,6 +265,8 @@
 { "log.creator", &fmd_conf_string, "fmd" },	/* exacct log creator string */
 { "log.error", &fmd_conf_string, "var/fm/fmd/errlog" }, /* error log path */
 { "log.fault", &fmd_conf_string, "var/fm/fmd/fltlog" }, /* fault log path */
+{ "log.info", &fmd_conf_string, "var/fm/fmd/infolog" }, /* info log path */
+{ "log.info_hival", &fmd_conf_string, "var/fm/fmd/infolog_hival" }, /* hi pri */
 { "log.minfree", &fmd_conf_size, "2m" },	/* min log fsys free space */
 { "log.rsrc", &fmd_conf_string, "var/fm/fmd/rsrc" }, /* asru log dir path */
 { "log.tryrotate", &fmd_conf_uint32, "10" },	/* max log rotation attempts */
@@ -425,6 +427,8 @@
 	(void) pthread_mutex_init(&dp->d_stats_lock, NULL);
 	(void) pthread_mutex_init(&dp->d_topo_lock, NULL);
 	(void) pthread_rwlock_init(&dp->d_log_lock, NULL);
+	(void) pthread_rwlock_init(&dp->d_hvilog_lock, NULL);
+	(void) pthread_rwlock_init(&dp->d_ilog_lock, NULL);
 	(void) pthread_mutex_init(&dp->d_fmd_lock, NULL);
 	(void) pthread_cond_init(&dp->d_fmd_cv, NULL);
 
@@ -695,6 +699,14 @@
 		(void) pthread_rwlock_rdlock(&dp->d_log_lock);
 		fmd_log_update(dp->d_errlog);
 		(void) pthread_rwlock_unlock(&dp->d_log_lock);
+
+		(void) pthread_rwlock_rdlock(&dp->d_hvilog_lock);
+		fmd_log_update(dp->d_hvilog);
+		(void) pthread_rwlock_unlock(&dp->d_hvilog_lock);
+
+		(void) pthread_rwlock_rdlock(&dp->d_ilog_lock);
+		fmd_log_update(dp->d_ilog);
+		(void) pthread_rwlock_unlock(&dp->d_ilog_lock);
 	}
 
 	(void) fmd_conf_getprop(dp->d_conf, "gc_interval", &delta);
@@ -771,6 +783,9 @@
 /*
  * Custom door server create callback.  Any fmd services that use doors will
  * require those threads to have their fmd-specific TSD initialized, etc.
+ * Modules should use door_xcreate and derivatives such as
+ * sysevent_evc_xsubscribe in order to use private doors that
+ * avoid this global door server function (see fmd_api_module comments).
  */
 static void
 fmd_door(door_info_t *dip)
@@ -896,6 +911,12 @@
 	(void) fmd_conf_getprop(dp->d_conf, "log.fault", &name);
 	dp->d_fltlog = fmd_log_open(dp->d_rootdir, name, FMD_LOG_FAULT);
 
+	(void) fmd_conf_getprop(dp->d_conf, "log.info_hival", &name);
+	dp->d_hvilog = fmd_log_open(dp->d_rootdir, name, FMD_LOG_INFO);
+
+	(void) fmd_conf_getprop(dp->d_conf, "log.info", &name);
+	dp->d_ilog = fmd_log_open(dp->d_rootdir, name, FMD_LOG_INFO);
+
 	if (dp->d_asrus == NULL || dp->d_errlog == NULL || dp->d_fltlog == NULL)
 		fmd_error(EFMD_EXIT, "failed to initialize log files\n");
 
--- a/usr/src/cmd/fm/fmd/common/fmd.h	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/fm/fmd/common/fmd.h	Fri Jul 30 17:04:17 2010 +1000
@@ -20,15 +20,12 @@
  */
 
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #ifndef	_FMD_H
 #define	_FMD_H
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <libnvpair.h>
 #include <pthread.h>
 
@@ -135,6 +132,13 @@
 	pthread_rwlock_t d_log_lock;	/* log pointer lock (r=use, w=rotate) */
 	struct fmd_log *d_errlog;	/* log file for error events */
 	struct fmd_log *d_fltlog;	/* log file for fault events */
+
+	pthread_rwlock_t d_hvilog_lock;	/* log pointer lock (r=use, w=rotate) */
+	struct fmd_log *d_hvilog;	/* log file for hi value info events */
+
+	pthread_rwlock_t d_ilog_lock;	/* log pointer lock (r=use, w=rotate) */
+	struct fmd_log *d_ilog;		/* log file for info events */
+
 	pthread_cond_t d_fmd_cv;	/* sync startup with rpc */
 	pthread_mutex_t d_fmd_lock;	/* sync startup with rpc */
 } fmd_t;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fm/fmd/common/fmd.logadm.conf	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,27 @@
+#
+# 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.
+#
+# Entries to be added to /etc/logadm.conf by svc:/application/logadm-upgrade
+#
+/var/fm/fmd/infolog -N -A 2y -S 50m -s 10m -M '/usr/sbin/fmadm -q rotate infolog && mv /var/fm/fmd/infolog.0- $nfile'
+/var/fm/fmd/infolog_hival -N -A 2y -S 50m -s 10m -M '/usr/sbin/fmadm -q rotate infolog_hival && mv /var/fm/fmd/infolog_hival.0- $nfile'
+
--- a/usr/src/cmd/fm/fmd/common/fmd.xml	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/fm/fmd/common/fmd.xml	Fri Jul 30 17:04:17 2010 +1000
@@ -1,15 +1,13 @@
 <?xml version="1.0"?>
 <!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
 <!--
- Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
- Use is subject to license terms.
+ Copyright (c) 2004, 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, Version 1.0 only
- (the "License").  You may not use this file except in compliance
- with the License.
+ 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.
@@ -24,8 +22,6 @@
 
  CDDL HEADER END
 
-	ident	"%Z%%M%	%I%	%E% SMI"
-
 	NOTE:  This service manifest is not editable; its contents will
 	be overwritten by package or patch operations, including
 	operating system upgrade.  Make customizations in a different
@@ -61,10 +57,13 @@
 	<!--
 	  Fault Manager Dependencies, Part 2
 
-	    fmd should be started after /var is mounted, syseventd and rpcbind
-	    are running, and dumpadm has initialized.  It is resilient to the
-	    the failure and/or interruption of all, and therefore does not
-	    restart under any circumstance.
+	    fmd should be started after /var is mounted and rpcbind is
+	    running.  It is resilient to the the failure and/or
+	    interruption of rpcbind, and therefore does not restart under any
+	    circumstance.
+
+	    The dumpadm and sysevent services (global zone only) also list the
+	    fmd service as a dependent, with restart_on='none' for both.
 	-->
 	<dependency
 	    name='startup_req'
@@ -72,8 +71,6 @@
 	    restart_on='none'
 	    type='service'>
 		<service_fmri value='svc:/system/filesystem/minimal' />
-		<service_fmri value='svc:/system/dumpadm' />
-		<service_fmri value='svc:/system/sysevent' />
 	</dependency>
 
 	<dependency
--- a/usr/src/cmd/fm/fmd/common/fmd_api.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/fm/fmd/common/fmd_api.c	Fri Jul 30 17:04:17 2010 +1000
@@ -20,13 +20,13 @@
  */
 
 /*
- * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #include <sys/types.h>
 #include <sys/fm/protocol.h>
 #include <fm/topo_hc.h>
+#include <uuid/uuid.h>
 
 #include <unistd.h>
 #include <signal.h>
@@ -34,6 +34,7 @@
 #include <syslog.h>
 #include <alloca.h>
 #include <stddef.h>
+#include <door.h>
 
 #include <fmd_module.h>
 #include <fmd_api.h>
@@ -237,10 +238,17 @@
 	}
 
 	/*
-	 * If our TSD refers to the root module and is a door server thread,
-	 * then it was created asynchronously at the request of a module but
-	 * is using now the module API as an auxiliary module thread.  We reset
-	 * tp->thr_mod to the module handle so it can act as a module thread.
+	 * If our TSD refers to the root module and is a non-private
+	 * door server thread,  then it was created asynchronously at the
+	 * request of a module but is using now the module API as an
+	 * auxiliary module thread.  We reset tp->thr_mod to the module
+	 * handle so it can act as a module thread.
+	 *
+	 * If more than one module uses non-private doors then the
+	 * "client handle is not valid" check below can fail since
+	 * door server threads for such doors can service *any*
+	 * non-private door.  We use non-private door for legacy sysevent
+	 * alone.
 	 */
 	if (tp->thr_mod == fmd.d_rmod && tp->thr_func == &fmd_door_server)
 		tp->thr_mod = (fmd_module_t *)hdl;
@@ -368,7 +376,7 @@
 	 * the module thread to which we assigned this client handle.  The info
 	 * provided for the handle must be valid and have the minimal settings.
 	 */
-	if (version > FMD_API_VERSION_4)
+	if (version > FMD_API_VERSION_5)
 		return (fmd_hdl_register_error(mp, EFMD_VER_NEW));
 
 	if (version < FMD_API_VERSION_1)
@@ -536,6 +544,25 @@
 {
 	fmd_thread_t *tp = fmd_idspace_getspecific(ids, id);
 
+	/*
+	 * Door service threads are not cancellable (worse - if they're
+	 * waiting in door_return then that is interrupted, but they then spin
+	 * endlessly!).  Non-private door service threads are not tracked
+	 * in the module thread idspace so it's only private server threads
+	 * created via fmd_doorthr_create that we'll encounter.  In most
+	 * cases the module _fini should have tidied up (e.g., calling
+	 * sysevent_evc_unbind which will cleanup door threads if
+	 * sysevent_evc_xsubscribe was used).  One case that does not
+	 * clean up is sysev_fini which explicitly does not unbind the
+	 * channel, so we must skip any remaining door threads here.
+	 */
+	if (tp->thr_isdoor) {
+		fmd_dprintf(FMD_DBG_MOD, "not cancelling %s private door "
+		    "thread %u\n", mp->mod_name, tp->thr_tid);
+		fmd_thread_destroy(tp, FMD_THREAD_NOJOIN);
+		return;
+	}
+
 	fmd_dprintf(FMD_DBG_MOD, "cancelling %s auxiliary thread %u\n",
 	    mp->mod_name, tp->thr_tid);
 
@@ -1031,11 +1058,44 @@
 fmd_case_open(fmd_hdl_t *hdl, void *data)
 {
 	fmd_module_t *mp = fmd_api_module_lock(hdl);
-	fmd_case_t *cp = fmd_case_create(mp, data);
+	fmd_case_t *cp = fmd_case_create(mp, NULL, data);
 	fmd_module_unlock(mp);
 	return (cp);
 }
 
+fmd_case_t *
+fmd_case_open_uuid(fmd_hdl_t *hdl, const char *uuidstr, void *data)
+{
+	fmd_module_t *mp;
+	fmd_case_t *cp;
+	uint_t uuidlen;
+	uuid_t uuid;
+
+	mp = fmd_api_module_lock(hdl);
+
+	(void) fmd_conf_getprop(fmd.d_conf, "uuidlen", &uuidlen);
+
+	if (uuidstr == NULL) {
+		fmd_api_error(mp, EFMD_CASE_INVAL, "NULL uuid string\n");
+	} else if (strnlen(uuidstr, uuidlen + 1) != uuidlen) {
+		fmd_api_error(mp, EFMD_CASE_INVAL, "invalid uuid string: '%s' "
+		    "(expected length %d)\n", uuidstr, uuidlen);
+	} else if (uuid_parse((char *)uuidstr, uuid) == -1) {
+		fmd_api_error(mp, EFMD_CASE_INVAL, "cannot parse uuid string: "
+		    "'%s'\n", uuidstr);
+	}
+
+	if ((cp = fmd_case_hash_lookup(fmd.d_cases, uuidstr)) == NULL) {
+		cp = fmd_case_create(mp, uuidstr, data);
+	} else {
+		fmd_case_rele(cp);
+		cp = NULL;
+	}
+
+	fmd_module_unlock(mp);
+	return (cp);	/* May be NULL iff case already exists */
+}
+
 void
 fmd_case_reset(fmd_hdl_t *hdl, fmd_case_t *cp)
 {
@@ -1157,6 +1217,23 @@
 	fmd_module_unlock(mp);
 }
 
+int
+fmd_case_uuisresolved(fmd_hdl_t *hdl, const char *uuid)
+{
+	fmd_module_t *mp = fmd_api_module_lock(hdl);
+	fmd_case_t *cp = fmd_case_hash_lookup(fmd.d_cases, uuid);
+	fmd_case_impl_t *cip = (fmd_case_impl_t *)cp;
+	int rv = FMD_B_FALSE;
+
+	if (cip != NULL) {
+		rv = (cip->ci_state >= FMD_CASE_RESOLVED);
+		fmd_case_rele(cp);
+	}
+
+	fmd_module_unlock(mp);
+	return (rv);
+}
+
 static int
 fmd_case_instate(fmd_hdl_t *hdl, fmd_case_t *cp, uint_t state)
 {
@@ -1760,6 +1837,65 @@
 	fmd_module_unlock(mp);
 }
 
+/*ARGSUSED3*/
+int
+fmd_doorthr_create(door_info_t *dip, void *(*crf)(void *), void *crarg,
+    void *cookie)
+{
+	fmd_thread_t *old_tp, *new_tp;
+	fmd_module_t *mp;
+	pthread_t tid;
+
+	/*
+	 * We're called either during initial door_xcreate or during
+	 * a depletion callback.  In both cases the current thread
+	 * is already setup so we can retrieve the fmd_thread_t.
+	 * If not then we panic.  The new thread will be associated with
+	 * the same module as the old.
+	 *
+	 * If dip == NULL we're being called as part of the
+	 * sysevent_bind_subscriber hack - see comments there.
+	 */
+	if ((old_tp = pthread_getspecific(fmd.d_key)) == NULL)
+		fmd_panic("fmd_doorthr_create from unrecognized thread\n");
+
+	mp = old_tp->thr_mod;
+	(void) fmd_api_module_lock((fmd_hdl_t *)mp);
+
+	if (dip && mp->mod_stats->ms_doorthrtotal.fmds_value.ui32 >=
+	    mp->mod_stats->ms_doorthrlimit.fmds_value.ui32) {
+		fmd_module_unlock(mp);
+		(void) fmd_dprintf(FMD_DBG_XPRT, "door server %s for %p "
+		    "not attemped - at max\n",
+		    dip->di_attributes & DOOR_DEPLETION_CB ?
+		    "depletion callback" : "startup", (void *)dip);
+		return (0);
+	}
+
+	if ((new_tp = fmd_doorthread_create(mp, (fmd_thread_f *)crf,
+	    crarg)) != NULL) {
+		tid = new_tp->thr_tid;
+		mp->mod_stats->ms_doorthrtotal.fmds_value.ui32++;
+		(void) fmd_idspace_xalloc(mp->mod_threads, tid, new_tp);
+	}
+
+	fmd_module_unlock(mp);
+
+	if (dip) {
+		fmd_dprintf(FMD_DBG_XPRT, "door server startup for %p %s\n",
+		    (void *)dip, new_tp ? "successful" : "failed");
+	}
+
+	return (new_tp ? 1 : -1);
+}
+
+/*ARGSUSED*/
+void
+fmd_doorthr_setup(void *cookie)
+{
+	(void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
+}
+
 id_t
 fmd_timer_install(fmd_hdl_t *hdl, void *arg, fmd_event_t *ep, hrtime_t delta)
 {
@@ -1816,16 +1952,19 @@
 	fmd_eventq_cancel(mp->mod_queue, FMD_EVT_TIMEOUT, (void *)id);
 }
 
-nvlist_t *
-fmd_nvl_create_fault(fmd_hdl_t *hdl, const char *class,
-    uint8_t certainty, nvlist_t *asru, nvlist_t *fru, nvlist_t *rsrc)
+static nvlist_t *
+fmd_nvl_create_suspect(fmd_hdl_t *hdl, const char *class,
+    uint8_t certainty, nvlist_t *asru, nvlist_t *fru, nvlist_t *rsrc,
+    const char *pfx, boolean_t chkpfx)
 {
 	fmd_module_t *mp;
 	nvlist_t *nvl;
 
 	mp = fmd_api_module_lock(hdl);
-	if (class == NULL || class[0] == '\0')
-		fmd_api_error(mp, EFMD_NVL_INVAL, "invalid fault class\n");
+	if (class == NULL || class[0] == '\0' ||
+	    chkpfx == B_TRUE && strncmp(class, pfx, strlen(pfx)) != 0)
+		fmd_api_error(mp, EFMD_NVL_INVAL, "invalid %s class: '%s'\n",
+		    pfx, class ? class : "(empty)");
 
 	nvl = fmd_protocol_fault(class, certainty, asru, fru, rsrc, NULL);
 
@@ -1834,6 +1973,57 @@
 	return (nvl);
 }
 
+nvlist_t *
+fmd_nvl_create_fault(fmd_hdl_t *hdl, const char *class,
+    uint8_t certainty, nvlist_t *asru, nvlist_t *fru, nvlist_t *rsrc)
+{
+	/*
+	 * We can't enforce that callers only specifiy classes matching
+	 * fault.* since there are already a number of modules that
+	 * use fmd_nvl_create_fault to create a defect event.  Since
+	 * fmd_nvl_create_{fault,defect} are equivalent, for now anyway,
+	 * no harm is done.  So call fmd_nvl_create_suspect with last
+	 * argument B_FALSE.
+	 */
+	return (fmd_nvl_create_suspect(hdl, class, certainty, asru,
+	    fru, rsrc, FM_FAULT_CLASS ".", B_FALSE));
+}
+
+nvlist_t *
+fmd_nvl_create_defect(fmd_hdl_t *hdl, const char *class,
+    uint8_t certainty, nvlist_t *asru, nvlist_t *fru, nvlist_t *rsrc)
+{
+	return (fmd_nvl_create_suspect(hdl, class, certainty, asru,
+	    fru, rsrc, FM_DEFECT_CLASS ".", B_TRUE));
+}
+
+const nvlist_t *
+fmd_hdl_fmauth(fmd_hdl_t *hdl)
+{
+	fmd_module_t *mp = fmd_api_module_lock(hdl);
+	const nvlist_t *auth;
+
+	auth = (const nvlist_t *)fmd.d_rmod->mod_fmri;
+
+	fmd_module_unlock(mp);
+
+	return (auth);
+}
+
+const nvlist_t *
+fmd_hdl_modauth(fmd_hdl_t *hdl)
+{
+	fmd_module_t *mp = fmd_api_module_lock(hdl);
+	const nvlist_t *auth;
+
+	auth = (const nvlist_t *)mp->mod_fmri;
+
+	fmd_module_unlock(mp);
+
+	return (auth);
+}
+
+
 int
 fmd_nvl_class_match(fmd_hdl_t *hdl, nvlist_t *nvl, const char *pattern)
 {
--- a/usr/src/cmd/fm/fmd/common/fmd_api.h	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/fm/fmd/common/fmd_api.h	Fri Jul 30 17:04:17 2010 +1000
@@ -20,8 +20,7 @@
  */
 
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #ifndef	_FMD_API_H
@@ -30,6 +29,7 @@
 #include <sys/types.h>
 #include <libnvpair.h>
 #include <stdarg.h>
+#include <door.h>
 
 #ifdef	__cplusplus
 extern "C" {
@@ -49,8 +49,9 @@
 #define	FMD_API_VERSION_2	2
 #define	FMD_API_VERSION_3	3
 #define	FMD_API_VERSION_4	4
+#define	FMD_API_VERSION_5	5
 
-#define	FMD_API_VERSION		FMD_API_VERSION_4
+#define	FMD_API_VERSION		FMD_API_VERSION_5
 
 typedef struct fmd_hdl fmd_hdl_t;
 typedef struct fmd_event fmd_event_t;
@@ -168,6 +169,7 @@
 extern void fmd_stat_setstr(fmd_hdl_t *, fmd_stat_t *, const char *);
 
 extern fmd_case_t *fmd_case_open(fmd_hdl_t *, void *);
+extern fmd_case_t *fmd_case_open_uuid(fmd_hdl_t *, const char *, void *);
 extern void fmd_case_reset(fmd_hdl_t *, fmd_case_t *);
 extern void fmd_case_solve(fmd_hdl_t *, fmd_case_t *);
 extern void fmd_case_close(fmd_hdl_t *, fmd_case_t *);
@@ -176,6 +178,7 @@
 extern fmd_case_t *fmd_case_uulookup(fmd_hdl_t *, const char *);
 extern void fmd_case_uuclose(fmd_hdl_t *, const char *);
 extern int fmd_case_uuclosed(fmd_hdl_t *, const char *);
+extern int fmd_case_uuisresolved(fmd_hdl_t *, const char *);
 extern void fmd_case_uuresolved(fmd_hdl_t *, const char *);
 
 extern int fmd_case_solved(fmd_hdl_t *, fmd_case_t *);
@@ -215,12 +218,20 @@
 extern void fmd_thr_signal(fmd_hdl_t *, pthread_t);
 extern void fmd_thr_checkpoint(fmd_hdl_t *);
 
+extern door_xcreate_server_func_t fmd_doorthr_create;
+extern door_xcreate_thrsetup_func_t fmd_doorthr_setup;
+
 extern id_t fmd_timer_install(fmd_hdl_t *, void *, fmd_event_t *, hrtime_t);
 extern void fmd_timer_remove(fmd_hdl_t *, id_t);
 
+extern nvlist_t *fmd_nvl_create_defect(fmd_hdl_t *,
+    const char *, uint8_t, nvlist_t *, nvlist_t *, nvlist_t *);
 extern nvlist_t *fmd_nvl_create_fault(fmd_hdl_t *,
     const char *, uint8_t, nvlist_t *, nvlist_t *, nvlist_t *);
 
+extern const nvlist_t *fmd_hdl_fmauth(fmd_hdl_t *);
+extern const nvlist_t *fmd_hdl_modauth(fmd_hdl_t *);
+
 extern int fmd_nvl_class_match(fmd_hdl_t *, nvlist_t *, const char *);
 extern int fmd_nvl_fmri_expand(fmd_hdl_t *, nvlist_t *);
 extern int fmd_nvl_fmri_present(fmd_hdl_t *, nvlist_t *);
--- a/usr/src/cmd/fm/fmd/common/fmd_api.map	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/fm/fmd/common/fmd_api.map	Fri Jul 30 17:04:17 2010 +1000
@@ -39,6 +39,7 @@
 	fmd_case_getspecific		{ TYPE = function; FLAGS = extern };
 	fmd_case_next			{ TYPE = function; FLAGS = extern };
 	fmd_case_open			{ TYPE = function; FLAGS = extern };
+	fmd_case_open_uuid		{ TYPE = function; FLAGS = extern };
 	fmd_case_prev			{ TYPE = function; FLAGS = extern };
 	fmd_case_reset			{ TYPE = function; FLAGS = extern };
 	fmd_case_setprincipal		{ TYPE = function; FLAGS = extern };
@@ -48,9 +49,13 @@
 	fmd_case_uuclose		{ TYPE = function; FLAGS = extern };
 	fmd_case_uuclosed		{ TYPE = function; FLAGS = extern };
 	fmd_case_uuid			{ TYPE = function; FLAGS = extern };
+	fmd_case_uuisresolved		{ TYPE = function; FLAGS = extern };
 	fmd_case_uulookup		{ TYPE = function; FLAGS = extern };
 	fmd_case_uuresolved		{ TYPE = function; FLAGS = extern };
 
+	fmd_doorthr_create		{ TYPE = function; FLAGS = extern };
+	fmd_doorthr_setup		{ TYPE = function; FLAGS = extern };
+
 	fmd_event_local			{ TYPE = function; FLAGS = extern };
 	fmd_event_ena_create		{ TYPE = function; FLAGS = extern };
 
@@ -59,7 +64,9 @@
 	fmd_hdl_debug			{ TYPE = function; FLAGS = extern };
 	fmd_hdl_error			{ TYPE = function; FLAGS = extern };
 	fmd_hdl_free			{ TYPE = function; FLAGS = extern };
+	fmd_hdl_fmauth			{ TYPE = function; FLAGS = extern };
 	fmd_hdl_getspecific		{ TYPE = function; FLAGS = extern };
+	fmd_hdl_modauth			{ TYPE = function; FLAGS = extern };
 	fmd_hdl_opendict		{ TYPE = function; FLAGS = extern };
 	fmd_hdl_register		{ TYPE = function; FLAGS = extern };
 	fmd_hdl_setspecific		{ TYPE = function; FLAGS = extern };
@@ -77,6 +84,7 @@
 
 	fmd_nvl_alloc			{ TYPE = function; FLAGS = extern };
 	fmd_nvl_class_match		{ TYPE = function; FLAGS = extern };
+	fmd_nvl_create_defect		{ TYPE = function; FLAGS = extern };
 	fmd_nvl_create_fault		{ TYPE = function; FLAGS = extern };
 	fmd_nvl_dup			{ TYPE = function; FLAGS = extern };
 	fmd_nvl_fmri_expand		{ TYPE = function; FLAGS = extern };
--- a/usr/src/cmd/fm/fmd/common/fmd_builtin.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/fm/fmd/common/fmd_builtin.c	Fri Jul 30 17:04:17 2010 +1000
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * 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.
@@ -21,23 +20,22 @@
  */
 
 /*
- * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <fmd_module.h>
 #include <fmd_subr.h>
 #include <fmd_error.h>
 #include <fmd_string.h>
 #include <fmd_event.h>
 #include <fmd_builtin.h>
+#include <zone.h>
 
 static const struct fmd_builtin _fmd_builtins[] = {
-	{ "fmd-self-diagnosis", self_init, self_fini },
-	{ "sysevent-transport", sysev_init, sysev_fini },
-	{ NULL, NULL, NULL }
+	{ "fmd-self-diagnosis", self_init, self_fini, FMD_BUILTIN_ALLCTXT },
+	{ "sysevent-transport", sysev_init, sysev_fini,
+		FMD_BUILTIN_CTXT_GLOBALZONE },
+	{ NULL, NULL, NULL, 0 }
 };
 
 static int
@@ -106,9 +104,16 @@
 fmd_builtin_loadall(fmd_modhash_t *mhp)
 {
 	const fmd_builtin_t *bp;
+	uint32_t ctxt = 0;
 	int err = 0;
 
+	ctxt |= (getzoneid() == GLOBAL_ZONEID) ? FMD_BUILTIN_CTXT_GLOBALZONE :
+	    FMD_BUILTIN_CTXT_NONGLOBALZONE;
+
 	for (bp = _fmd_builtins; bp->bltin_name != NULL; bp++) {
+		if (!(ctxt & bp->bltin_ctxts))
+			continue;
+
 		if (fmd_modhash_load(mhp, bp->bltin_name,
 		    &fmd_bltin_ops) == NULL)
 			err = -1;
--- a/usr/src/cmd/fm/fmd/common/fmd_builtin.h	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/fm/fmd/common/fmd_builtin.h	Fri Jul 30 17:04:17 2010 +1000
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * 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.
@@ -21,15 +20,12 @@
  */
 
 /*
- * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #ifndef	_FMD_BUILTIN_H
 #define	_FMD_BUILTIN_H
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #ifdef	__cplusplus
 extern "C" {
 #endif
@@ -50,8 +46,15 @@
 	const char *bltin_name;
 	void (*bltin_init)(fmd_hdl_t *);
 	void (*bltin_fini)(fmd_hdl_t *);
+	int bltin_ctxts;
 } fmd_builtin_t;
 
+#define	FMD_BUILTIN_CTXT_GLOBALZONE	0x1
+#define	FMD_BUILTIN_CTXT_NONGLOBALZONE	0x2
+
+#define	FMD_BUILTIN_ALLCTXT \
+	(FMD_BUILTIN_CTXT_GLOBALZONE | FMD_BUILTIN_CTXT_NONGLOBALZONE)
+
 extern int fmd_builtin_loadall(fmd_modhash_t *);
 
 extern void self_init(fmd_hdl_t *);	/* see fmd_self.c */
--- a/usr/src/cmd/fm/fmd/common/fmd_case.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/fm/fmd/common/fmd_case.c	Fri Jul 30 17:04:17 2010 +1000
@@ -20,8 +20,7 @@
  */
 
 /*
- * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 /*
@@ -1247,7 +1246,7 @@
 }
 
 fmd_case_t *
-fmd_case_create(fmd_module_t *mp, void *data)
+fmd_case_create(fmd_module_t *mp, const char *uuidstr, void *data)
 {
 	fmd_case_impl_t *cip = fmd_zalloc(sizeof (fmd_case_impl_t), FMD_SLEEP);
 	fmd_case_impl_t *eip = NULL;
@@ -1272,17 +1271,35 @@
 	(void) fmd_conf_getprop(fmd.d_conf, "uuidlen", &cip->ci_uuidlen);
 	cip->ci_uuid = fmd_zalloc(cip->ci_uuidlen + 1, FMD_SLEEP);
 
-	/*
-	 * We expect this loop to execute only once, but code it defensively
-	 * against the possibility of libuuid bugs.  Keep generating uuids and
-	 * attempting to do a hash insert until we get a unique one.
-	 */
-	do {
-		if (eip != NULL)
-			fmd_case_rele((fmd_case_t *)eip);
-		uuid_generate(uuid);
-		uuid_unparse(uuid, cip->ci_uuid);
-	} while ((eip = fmd_case_hash_insert(fmd.d_cases, cip)) != cip);
+	if (uuidstr == NULL) {
+		/*
+		 * We expect this loop to execute only once, but code it
+		 * defensively against the possibility of libuuid bugs.
+		 * Keep generating uuids and attempting to do a hash insert
+		 * until we get a unique one.
+		 */
+		do {
+			if (eip != NULL)
+				fmd_case_rele((fmd_case_t *)eip);
+			uuid_generate(uuid);
+			uuid_unparse(uuid, cip->ci_uuid);
+		} while ((eip = fmd_case_hash_insert(fmd.d_cases, cip)) != cip);
+	} else {
+		/*
+		 * If a uuid was specified we must succeed with that uuid,
+		 * or return NULL indicating a case with that uuid already
+		 * exists.
+		 */
+		(void) strncpy(cip->ci_uuid, uuidstr, cip->ci_uuidlen + 1);
+		if (fmd_case_hash_insert(fmd.d_cases, cip) != cip) {
+			fmd_free(cip->ci_uuid, cip->ci_uuidlen + 1);
+			(void) fmd_buf_hash_destroy(&cip->ci_bufs);
+			fmd_module_rele(mp);
+			pthread_mutex_destroy(&cip->ci_lock);
+			fmd_free(cip, sizeof (*cip));
+			return (NULL);
+		}
+	}
 
 	ASSERT(fmd_module_locked(mp));
 	fmd_list_append(&mp->mod_cases, cip);
--- a/usr/src/cmd/fm/fmd/common/fmd_case.h	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/fm/fmd/common/fmd_case.h	Fri Jul 30 17:04:17 2010 +1000
@@ -20,8 +20,7 @@
  */
 
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #ifndef	_FMD_CASE_H
@@ -121,7 +120,7 @@
 extern void fmd_case_hash_apply(fmd_case_hash_t *,
     void (*)(fmd_case_t *, void *), void *);
 
-extern fmd_case_t *fmd_case_create(struct fmd_module *, void *);
+extern fmd_case_t *fmd_case_create(struct fmd_module *, const char *, void *);
 extern fmd_case_t *fmd_case_recreate(struct fmd_module *,
     struct fmd_xprt *, uint_t, const char *, const char *);
 extern void fmd_case_destroy(fmd_case_t *, int);
--- a/usr/src/cmd/fm/fmd/common/fmd_log.h	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/fm/fmd/common/fmd_log.h	Fri Jul 30 17:04:17 2010 +1000
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * 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.
@@ -20,15 +19,12 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #ifndef	_FMD_LOG_H
 #define	_FMD_LOG_H
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <pthread.h>
@@ -71,6 +67,7 @@
 #define	FMD_LOG_FAULT	"fault"		/* tag for fault log files */
 #define	FMD_LOG_ASRU	"asru"		/* tag for asru log files */
 #define	FMD_LOG_XPRT	"xprt"		/* tag for transport log files */
+#define	FMD_LOG_INFO	"info"		/* tag for info event log files */
 
 extern fmd_log_t *fmd_log_tryopen(const char *, const char *, const char *);
 extern fmd_log_t *fmd_log_open(const char *, const char *, const char *);
--- a/usr/src/cmd/fm/fmd/common/fmd_main.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/fm/fmd/common/fmd_main.c	Fri Jul 30 17:04:17 2010 +1000
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #include <sys/types.h>
@@ -37,6 +36,7 @@
 #include <strings.h>
 #include <stdlib.h>
 #include <stdio.h>
+#include <zone.h>
 
 #include <fmd_error.h>
 #include <fmd_string.h>
@@ -78,6 +78,7 @@
 static int
 daemonize_init(void)
 {
+	const char *gzp1, *gzp2, *gzp3, *gzp4, *gzp5;
 	int status, pfds[2];
 	sigset_t set, oset;
 	struct rlimit rlim;
@@ -111,14 +112,50 @@
 	 * Reset all of our privilege sets to the minimum set of required
 	 * privileges.  We continue to run as root so that files we create
 	 * such as logs and checkpoints are secured in the /var filesystem.
+	 *
+	 * In a non-global zone some of the privileges we retain in a
+	 * global zone are only optionally assigned to the zone, while others
+	 * are prohibited:
+	 *
+	 * PRIV_PROC_PRIOCNTL (optional in a non-global zone):
+	 *	There are no calls to priocntl(2) in fmd or plugins.
+	 *
+	 * PRIV_SYS_CONFIG (prohibited in a non-global zone):
+	 *	Required, I think, for sysevent_post_event and/or
+	 *	other legacy sysevent activity.  Legacy sysevent is not
+	 *	supported in a non-global zone.
+	 *
+	 * PRIV_SYS_DEVICES (prohibited in a non-global zone):
+	 *	Needed in the global zone for ioctls on various drivers
+	 *	such as memory-controller drivers.
+	 *
+	 * PRIV_SYS_RES_CONFIG (prohibited in a non-global zone):
+	 *	Require for p_online(2) calls to offline cpus.
+	 *
+	 * PRIV_SYS_NET_CONFIG (prohibited in a non-global zone):
+	 *	Required for ipsec in etm (which also requires
+	 *	PRIV_NET_PRIVADDR).
+	 *
+	 * We do without those privileges in a non-global zone.  It's
+	 * possible that there are other privs we could drop since
+	 * hardware-related plugins are not present.
 	 */
+	if (getzoneid() == GLOBAL_ZONEID) {
+		gzp1 = PRIV_PROC_PRIOCNTL;
+		gzp2 = PRIV_SYS_CONFIG;
+		gzp3 = PRIV_SYS_DEVICES;
+		gzp4 = PRIV_SYS_RES_CONFIG;
+		gzp5 = PRIV_SYS_NET_CONFIG;
+	} else {
+		gzp1 = gzp2 = gzp3 = gzp4 = gzp5 = NULL;
+	}
+
 	if (__init_daemon_priv(PU_RESETGROUPS | PU_LIMITPRIVS | PU_INHERITPRIVS,
 	    0, 0, /* run as uid 0 and gid 0 */
 	    PRIV_FILE_DAC_EXECUTE, PRIV_FILE_DAC_READ, PRIV_FILE_DAC_SEARCH,
 	    PRIV_FILE_DAC_WRITE, PRIV_FILE_OWNER, PRIV_PROC_OWNER,
-	    PRIV_PROC_PRIOCNTL, PRIV_SYS_ADMIN, PRIV_SYS_CONFIG,
-	    PRIV_SYS_DEVICES, PRIV_SYS_RES_CONFIG, PRIV_NET_PRIVADDR,
-	    PRIV_SYS_NET_CONFIG, NULL) != 0)
+	    PRIV_SYS_ADMIN, PRIV_NET_PRIVADDR,
+	    gzp1, gzp2, gzp3, gzp4, gzp5, NULL) != 0)
 		fmd_error(EFMD_EXIT, "additional privileges required to run\n");
 
 	/*
--- a/usr/src/cmd/fm/fmd/common/fmd_mdb.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/fm/fmd/common/fmd_mdb.c	Fri Jul 30 17:04:17 2010 +1000
@@ -483,6 +483,10 @@
 	    DCMD_ADDRSPEC | DCMD_LOOP, udp->argc, udp->argv);
 	(void) fmd_stat((uintptr_t)&mod_stats->ms_thrlimit,
 	    DCMD_ADDRSPEC | DCMD_LOOP, udp->argc, udp->argv);
+	(void) fmd_stat((uintptr_t)&mod_stats->ms_doorthrtotal,
+	    DCMD_ADDRSPEC | DCMD_LOOP, udp->argc, udp->argv);
+	(void) fmd_stat((uintptr_t)&mod_stats->ms_doorthrlimit,
+	    DCMD_ADDRSPEC | DCMD_LOOP, udp->argc, udp->argv);
 	(void) fmd_stat((uintptr_t)&mod_stats->ms_caseopen,
 	    DCMD_ADDRSPEC | DCMD_LOOP, udp->argc, udp->argv);
 	(void) fmd_stat((uintptr_t)&mod_stats->ms_casesolved,
@@ -877,6 +881,9 @@
 	case FMD_CASE_REPAIRED:
 		(void) strcpy(name, "RPAIR");
 		break;
+	case FMD_CASE_RESOLVED:
+		(void) strcpy(name, "RSLVD");
+		break;
 	default:
 		(void) mdb_snprintf(name, sizeof (name), "%u", ci.ci_state);
 	}
--- a/usr/src/cmd/fm/fmd/common/fmd_module.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/fm/fmd/common/fmd_module.c	Fri Jul 30 17:04:17 2010 +1000
@@ -79,6 +79,8 @@
 { "fmd.buflimit", FMD_TYPE_SIZE, "limit on total buffer space" },
 { "fmd.thrtotal", FMD_TYPE_UINT32, "total number of auxiliary threads" },
 { "fmd.thrlimit", FMD_TYPE_UINT32, "limit on number of auxiliary threads" },
+{ "fmd.doorthrtotal", FMD_TYPE_UINT32, "total number of door server threads" },
+{ "fmd.doorthrlimit", FMD_TYPE_UINT32, "limit on door server threads" },
 { "fmd.caseopen", FMD_TYPE_UINT64, "cases currently open by module" },
 { "fmd.casesolved", FMD_TYPE_UINT64, "total cases solved by module" },
 { "fmd.caseclosed", FMD_TYPE_UINT64, "total cases closed by module" },
@@ -247,6 +249,9 @@
 	(void) fmd_conf_getprop(fmd.d_conf, "client.thrlim",
 	    &mp->mod_stats->ms_thrlimit.fmds_value.ui32);
 
+	(void) fmd_conf_getprop(fmd.d_conf, "client.doorthrlim",
+	    &mp->mod_stats->ms_doorthrlimit.fmds_value.ui32);
+
 	(void) fmd_conf_getprop(fmd.d_conf, "client.xprtlim",
 	    &mp->mod_stats->ms_xprtlimit.fmds_value.ui32);
 
--- a/usr/src/cmd/fm/fmd/common/fmd_module.h	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/fm/fmd/common/fmd_module.h	Fri Jul 30 17:04:17 2010 +1000
@@ -20,15 +20,12 @@
  */
 
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #ifndef	_FMD_MODULE_H
 #define	_FMD_MODULE_H
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <sys/types.h>
 #include <fm/diagcode.h>
 #include <pthread.h>
@@ -84,6 +81,8 @@
 	fmd_stat_t ms_buflimit;		/* limit on space consumed by buffers */
 	fmd_stat_t ms_thrtotal;		/* total number of auxiliary threads */
 	fmd_stat_t ms_thrlimit;		/* limit on auxiliary threads */
+	fmd_stat_t ms_doorthrtotal;	/* total number of doorserver threads */
+	fmd_stat_t ms_doorthrlimit;	/* limit on doorserver threads */
 	fmd_stat_t ms_caseopen;		/* cases currently open */
 	fmd_stat_t ms_casesolved;	/* total cases solved by module */
 	fmd_stat_t ms_caseclosed;	/* total cases closed by module */
--- a/usr/src/cmd/fm/fmd/common/fmd_rpc.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/fm/fmd/common/fmd_rpc.c	Fri Jul 30 17:04:17 2010 +1000
@@ -19,12 +19,9 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <sys/types.h>
 #include <sys/fm/util.h>
 
@@ -236,7 +233,7 @@
 /*
  * Utillity function to fetch the XPRT's ucred and determine if we should deny
  * the request.  For now, we implement a simple policy of rejecting any caller
- * who does not have the PRIV_SYS_CONFIG bit in their Effective privilege set,
+ * who does not have the PRIV_SYS_ADMIN bit in their Effective privilege set,
  * unless the caller is loading a module, which requires all privileges.
  */
 int
@@ -265,5 +262,5 @@
 	if (rqp->rq_proc == FMD_ADM_MODLOAD)
 		return (!priv_isfullset(psp));
 #endif
-	return (!priv_ismember(psp, PRIV_SYS_CONFIG));
+	return (!priv_ismember(psp, PRIV_SYS_ADMIN));
 }
--- a/usr/src/cmd/fm/fmd/common/fmd_rpc_adm.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/fm/fmd/common/fmd_rpc_adm.c	Fri Jul 30 17:04:17 2010 +1000
@@ -20,8 +20,7 @@
  */
 
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #include <strings.h>
@@ -722,6 +721,7 @@
 {
 	fmd_log_t **lpp, *old, *new;
 	int try = 1, trylimit = 1;
+	pthread_rwlock_t *lockp;
 
 	hrtime_t nsec = 0;
 	timespec_t tv;
@@ -731,11 +731,19 @@
 		return (TRUE);
 	}
 
-	if (strcmp(name, "errlog") == 0)
+	if (strcmp(name, "errlog") == 0) {
 		lpp = &fmd.d_errlog;
-	else if (strcmp(name, "fltlog") == 0)
+		lockp = &fmd.d_log_lock;
+	} else if (strcmp(name, "fltlog") == 0) {
 		lpp = &fmd.d_fltlog;
-	else {
+		lockp = &fmd.d_log_lock;
+	} else if (strcmp(name, "infolog") == 0) {
+		lpp = &fmd.d_ilog;
+		lockp = &fmd.d_ilog_lock;
+	} else if (strcmp(name, "infolog_hival") == 0) {
+		lpp = &fmd.d_hvilog;
+		lockp = &fmd.d_hvilog_lock;
+	} else {
 		*rvp = FMD_ADM_ERR_ROTSRCH;
 		return (TRUE);
 	}
@@ -755,7 +763,7 @@
 		if (try > 1)
 			(void) nanosleep(&tv, NULL); /* wait for checkpoints */
 
-		(void) pthread_rwlock_wrlock(&fmd.d_log_lock);
+		(void) pthread_rwlock_wrlock(lockp);
 		old = *lpp;
 
 		if ((new = fmd_log_rotate(old)) != NULL) {
@@ -763,7 +771,7 @@
 			*lpp = new;
 		}
 
-		(void) pthread_rwlock_unlock(&fmd.d_log_lock);
+		(void) pthread_rwlock_unlock(lockp);
 
 	} while (new == NULL && errno == EFMD_LOG_ROTBUSY && try++ < trylimit);
 
--- a/usr/src/cmd/fm/fmd/common/fmd_self.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/fm/fmd/common/fmd_self.c	Fri Jul 30 17:04:17 2010 +1000
@@ -20,8 +20,7 @@
  */
 
 /*
- * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #include <sys/fm/protocol.h>
@@ -158,6 +157,10 @@
 	if (self_case_lookup(hdl, SC_CLASS, class) != NULL)
 		return; /* case is already open against this class */
 
+	if (strncmp(class, FM_IREPORT_CLASS ".",
+	    sizeof (FM_IREPORT_CLASS)) == 0)
+		return; /* no subscriber required for ireport.* */
+
 	cp = fmd_case_open(hdl, self_case_create(hdl, SC_CLASS, class));
 	fmd_case_add_ereport(hdl, cp, ep);
 	self_stats.nosub.fmds_value.ui64++;
--- a/usr/src/cmd/fm/fmd/common/fmd_sysevent.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/fm/fmd/common/fmd_sysevent.c	Fri Jul 30 17:04:17 2010 +1000
@@ -20,8 +20,7 @@
  */
 
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #include <sys/sysevent/eventdefs.h>
@@ -41,6 +40,7 @@
 #include <unistd.h>
 #include <fcntl.h>
 #include <errno.h>
+#include <zone.h>
 
 #undef MUTEX_HELD
 #undef RW_READ_HELD
@@ -87,6 +87,8 @@
 static int sysev_replay_wait = 1;
 static int sysev_exiting;
 
+static sysevent_subattr_t *subattr;
+
 /*
  * Entry point for legacy sysevents.  This function is responsible for two
  * things: passing off interesting events to the DR handler, and converting
@@ -100,7 +102,6 @@
 	char *fullclass;
 	size_t len;
 	nvlist_t *attr, *nvl;
-	fmd_event_t *e;
 	hrtime_t hrt;
 
 	/* notify the DR subsystem of the event */
@@ -128,15 +129,12 @@
 	(void) nvlist_add_uint8(nvl, FM_VERSION, FM_RSRC_VERSION);
 
 	/*
-	 * Dispatch the event.  Ideally, we'd like to use the same transport
-	 * interface as sysev_recv(), but because the legacy sysevent mechanism
-	 * puts in a thread outside fmd's control, using the module APIs is
-	 * impossible.
+	 * Dispatch the event.  Because we have used sysevent_bind_xhandle
+	 * the delivery thread is blessed as a proper fmd thread so
+	 * we may use regular fmd api calls.
 	 */
 	sysevent_get_time(sep, &hrt);
-	(void) nvlist_lookup_string(nvl, FM_CLASS, &fullclass);
-	e = fmd_event_create(FMD_EVT_PROTOCOL, hrt, nvl, fullclass);
-	fmd_dispq_dispatch(fmd.d_disp, e, fullclass);
+	fmd_xprt_post(sysev_hdl, sysev_xprt, nvl, hrt);
 }
 
 /*
@@ -444,6 +442,10 @@
 	uint_t flags;
 	const char *subclasses[] = { EC_SUB_ALL };
 
+	/* This builtin is for the global zone only */
+	if (getzoneid() != GLOBAL_ZONEID)
+		return;
+
 	if (fmd_hdl_register(hdl, FMD_API_VERSION, &sysev_info) != 0)
 		return; /* invalid property settings */
 
@@ -482,15 +484,22 @@
 	else
 		flags = EVCH_SUB_DUMP;
 
-	errno = sysevent_evc_subscribe(sysev_evc,
-	    sysev_sid, sysev_class, sysev_recv, sysev_xprt, flags);
+	if ((subattr = sysevent_subattr_alloc()) == NULL)
+		fmd_hdl_abort(hdl, "failed to allocate subscription "
+		    "attributes");
+
+	sysevent_subattr_thrcreate(subattr, fmd_doorthr_create, NULL);
+	sysevent_subattr_thrsetup(subattr, fmd_doorthr_setup, NULL);
+
+	errno = sysevent_evc_xsubscribe(sysev_evc,
+	    sysev_sid, sysev_class, sysev_recv, sysev_xprt, flags, subattr);
 
 	if (errno != 0) {
 		if (errno == EEXIST) {
 			fmd_hdl_abort(hdl, "another fault management daemon is "
 			    "active on transport channel %s\n", sysev_channel);
 		} else {
-			fmd_hdl_abort(hdl, "failed to subscribe to %s on "
+			fmd_hdl_abort(hdl, "failed to xsubscribe to %s on "
 			    "transport channel %s", sysev_class, sysev_channel);
 		}
 	}
@@ -512,7 +521,7 @@
 		return;
 
 	if ((fmd.d_sysev_hdl =
-	    sysevent_bind_handle(sysev_legacy)) == NULL)
+	    sysevent_bind_xhandle(sysev_legacy, subattr)) == NULL)
 		fmd_hdl_abort(hdl, "failed to bind to legacy sysevent channel");
 
 	if (sysevent_subscribe_event(fmd.d_sysev_hdl, EC_ALL,
@@ -540,6 +549,11 @@
 	if (fmd.d_sysev_hdl != NULL)
 		sysevent_unbind_handle(fmd.d_sysev_hdl);
 
+	if (subattr != NULL) {
+		sysevent_subattr_free(subattr);
+		subattr = NULL;
+	}
+
 	if (sysev_xprt != NULL) {
 		/*
 		 * Wait callback returns before destroy the transport.
--- a/usr/src/cmd/fm/fmd/common/fmd_thread.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/fm/fmd/common/fmd_thread.c	Fri Jul 30 17:04:17 2010 +1000
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * 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.
@@ -20,12 +19,9 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <signal.h>
 
 #include <fmd_alloc.h>
@@ -47,6 +43,7 @@
 	tp->thr_trdata = fmd_trace_create();
 	tp->thr_trfunc = (fmd_tracebuf_f *)fmd.d_thr_trace;
 	tp->thr_errdepth = 0;
+	tp->thr_isdoor = 0;
 
 	(void) pthread_mutex_lock(&fmd.d_thr_lock);
 	fmd_list_append(&fmd.d_thr_list, tp);
@@ -63,15 +60,18 @@
 	if (pthread_setspecific(fmd.d_key, tp) != 0)
 		fmd_panic("failed to initialize thread key to %p", arg);
 
-	(void) pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
-	(void) pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
+	if (!tp->thr_isdoor) {
+		(void) pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
+		(void) pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
+	}
 
 	tp->thr_func(tp->thr_arg);
 	return (NULL);
 }
 
-fmd_thread_t *
-fmd_thread_create(fmd_module_t *mp, fmd_thread_f *func, void *arg)
+static fmd_thread_t *
+fmd_thread_create_cmn(fmd_module_t *mp, fmd_thread_f *func, void *arg,
+    int isdoor)
 {
 	fmd_thread_t *tp = fmd_alloc(sizeof (fmd_thread_t), FMD_SLEEP);
 	sigset_t oset, nset;
@@ -83,10 +83,12 @@
 	tp->thr_trdata = fmd_trace_create();
 	tp->thr_trfunc = (fmd_tracebuf_f *)fmd.d_thr_trace;
 	tp->thr_errdepth = 0;
+	tp->thr_isdoor = isdoor;
 
 	(void) sigfillset(&nset);
 	(void) sigdelset(&nset, SIGABRT); /* always unblocked for fmd_panic() */
-	(void) sigdelset(&nset, fmd.d_thr_sig); /* for fmd_thr_signal() */
+	if (!isdoor)
+		(void) sigdelset(&nset, fmd.d_thr_sig); /* fmd_thr_signal() */
 
 	(void) pthread_sigmask(SIG_SETMASK, &nset, &oset);
 	err = pthread_create(&tp->thr_tid, NULL, fmd_thread_start, tp);
@@ -104,6 +106,18 @@
 	return (tp);
 }
 
+fmd_thread_t *
+fmd_thread_create(fmd_module_t *mp, fmd_thread_f *func, void *arg)
+{
+	return (fmd_thread_create_cmn(mp, func, arg, 0));
+}
+
+fmd_thread_t *
+fmd_doorthread_create(fmd_module_t *mp, fmd_thread_f *func, void *arg)
+{
+	return (fmd_thread_create_cmn(mp, func, arg, 1));
+}
+
 void
 fmd_thread_destroy(fmd_thread_t *tp, int flag)
 {
--- a/usr/src/cmd/fm/fmd/common/fmd_thread.h	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/fm/fmd/common/fmd_thread.h	Fri Jul 30 17:04:17 2010 +1000
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * 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.
@@ -20,15 +19,12 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #ifndef	_FMD_THREAD_H
 #define	_FMD_THREAD_H
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #ifdef	__cplusplus
 extern "C" {
 #endif
@@ -49,11 +45,14 @@
 	fmd_tracebuf_t *thr_trdata;	/* thread trace buffer */
 	fmd_tracebuf_f *thr_trfunc;	/* thread trace function */
 	uint_t thr_errdepth;		/* fmd_verror() nesting depth */
+	int thr_isdoor;			/* a private door server thread */
 } fmd_thread_t;
 
 extern fmd_thread_t *fmd_thread_xcreate(struct fmd_module *, pthread_t);
 extern fmd_thread_t *fmd_thread_create(struct fmd_module *,
     fmd_thread_f *, void *);
+extern fmd_thread_t *fmd_doorthread_create(struct fmd_module *,
+    fmd_thread_f *, void *);
 
 #define	FMD_THREAD_NOJOIN	0	/* do not attempt to join with thread */
 #define	FMD_THREAD_JOIN		1	/* wait for and join with thread */
--- a/usr/src/cmd/fm/fmd/common/fmd_xprt.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/fm/fmd/common/fmd_xprt.c	Fri Jul 30 17:04:17 2010 +1000
@@ -1099,7 +1099,7 @@
 	boolean_t injected;
 
 	fmd_module_lock(xip->xi_queue->eq_mod);
-	cp = fmd_case_create(xip->xi_queue->eq_mod, NULL);
+	cp = fmd_case_create(xip->xi_queue->eq_mod, NULL, NULL);
 	if (cp == NULL) {
 		fmd_module_unlock(xip->xi_queue->eq_mod);
 		return;
@@ -1457,7 +1457,7 @@
 
 	fmd_event_t *e;
 	char *class, *uuid;
-	boolean_t isproto, isereport;
+	boolean_t isproto, isereport, isireport, ishvireport, issysevent;
 
 	uint64_t *tod;
 	uint8_t ttl;
@@ -1505,13 +1505,30 @@
 	fmd_dprintf(FMD_DBG_XPRT, "xprt %u %s %s\n", xip->xi_id,
 	    ((logonly == FMD_B_TRUE) ? "logging" : "posting"), class);
 
-	isereport = (strncmp(class, FM_EREPORT_CLASS,
-	    sizeof (FM_EREPORT_CLASS - 1)) == 0) ? FMD_B_TRUE : FMD_B_FALSE;
+	isereport = (strncmp(class, FM_EREPORT_CLASS ".",
+	    sizeof (FM_EREPORT_CLASS)) == 0) ? FMD_B_TRUE : FMD_B_FALSE;
+
+	isireport = (strncmp(class, FM_IREPORT_CLASS ".",
+	    sizeof (FM_IREPORT_CLASS)) == 0) ?  FMD_B_TRUE : FMD_B_FALSE;
+
+	issysevent = (strncmp(class, SYSEVENT_RSRC_CLASS,
+	    sizeof (SYSEVENT_RSRC_CLASS) - 1)) == 0 ? FMD_B_TRUE : FMD_B_FALSE;
+
+	if (isireport) {
+		char *pri;
+
+		if (nvlist_lookup_string(nvl, FM_IREPORT_PRIORITY, &pri) == 0 &&
+		    strncmp(pri, "high", 5) == 0) {
+			ishvireport = 1;
+		} else {
+			ishvireport = 0;
+		}
+	}
 
 	/*
 	 * The logonly flag should only be set for ereports.
 	 */
-	if ((logonly == FMD_B_TRUE) && (isereport == FMD_B_FALSE)) {
+	if (logonly == FMD_B_TRUE && isereport == FMD_B_FALSE) {
 		fmd_error(EFMD_XPRT_INVAL, "discarding nvlist %p: "
 		    "logonly flag is not valid for class %s",
 		    (void *)nvl, class);
@@ -1605,13 +1622,30 @@
 	}
 
 	/*
-	 * Record the event in the errlog if it is an ereport.  This code will
+	 * Record ereports and ireports in the log.  This code will
 	 * be replaced later with a per-transport intent log instead.
 	 */
-	if (isereport == FMD_B_TRUE) {
-		(void) pthread_rwlock_rdlock(&dp->d_log_lock);
-		fmd_log_append(dp->d_errlog, e, NULL);
-		(void) pthread_rwlock_unlock(&dp->d_log_lock);
+	if (isereport == FMD_B_TRUE || isireport == FMD_B_TRUE ||
+	    issysevent == B_TRUE) {
+		pthread_rwlock_t *lockp;
+		fmd_log_t *lp;
+
+		if (isereport == FMD_B_TRUE) {
+			lp = fmd.d_errlog;
+			lockp = &fmd.d_log_lock;
+		} else {
+			if (ishvireport || issysevent) {
+				lp = fmd.d_hvilog;
+				lockp = &fmd.d_hvilog_lock;
+			} else {
+				lp = fmd.d_ilog;
+				lockp = &fmd.d_ilog_lock;
+			}
+		}
+
+		(void) pthread_rwlock_rdlock(lockp);
+		fmd_log_append(lp, e, NULL);
+		(void) pthread_rwlock_unlock(lockp);
 	}
 
 	/*
--- a/usr/src/cmd/fm/fmdump/Makefile.com	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/fm/fmdump/Makefile.com	Fri Jul 30 17:04:17 2010 +1000
@@ -19,14 +19,13 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
+# Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
 #
 
 .KEEP_STATE:
 .SUFFIXES:
 
-SRCS += fmdump.c asru.c error.c fault.c scheme.c
+SRCS += fmdump.c nvlrender.c asru.c error.c fault.c scheme.c info.c
 OBJS = $(SRCS:%.c=%.o)
 LINTFILES = $(SRCS:%.c=%.ln)
 
--- a/usr/src/cmd/fm/fmdump/common/asru.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/fm/fmdump/common/asru.c	Fri Jul 30 17:04:17 2010 +1000
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #include <fmdump.h>
@@ -71,7 +70,8 @@
 
 /*ARGSUSED*/
 static int
-asru_verb2(fmd_log_t *lp, const fmd_log_record_t *rp, FILE *fp)
+asru_verb23_cmn(fmd_log_t *lp, const fmd_log_record_t *rp, FILE *fp,
+    nvlist_prtctl_t pctl)
 {
 	char *uuid = "-";
 	boolean_t f = 0, u = 0;
@@ -95,12 +95,39 @@
 	fmdump_printf(fp, "%-20s.%9.9llu %-36s %s\n",
 	    fmdump_year(buf, sizeof (buf), rp), rp->rec_nsec, uuid, state + 1);
 
-	nvlist_print(fp, rp->rec_nvl);
+	if (pctl)
+		nvlist_prt(rp->rec_nvl, pctl);
+	else
+		nvlist_print(fp, rp->rec_nvl);
+
 	fmdump_printf(fp, "\n");
 
 	return (0);
 }
 
+static int
+asru_verb2(fmd_log_t *lp, const fmd_log_record_t *rp, FILE *fp)
+{
+	return (asru_verb23_cmn(lp, rp, fp, NULL));
+}
+
+static int
+asru_pretty(fmd_log_t *lp, const fmd_log_record_t *rp, FILE *fp)
+{
+	nvlist_prtctl_t pctl;
+	int rc;
+
+	if ((pctl = nvlist_prtctl_alloc()) != NULL) {
+		nvlist_prtctl_setdest(pctl, fp);
+		nvlist_prtctlop_nvlist(pctl, fmdump_render_nvlist, NULL);
+	}
+
+	rc = asru_verb23_cmn(lp, rp, fp, pctl);
+
+	nvlist_prtctl_free(pctl);
+	return (rc);
+}
+
 const fmdump_ops_t fmdump_asru_ops = {
 "asru", {
 {
@@ -113,6 +140,9 @@
 "TIME                           UUID                                 STATE",
 (fmd_log_rec_f *)asru_verb2
 }, {
+"TIME                           UUID                                 STATE",
+(fmd_log_rec_f *)asru_pretty
+}, {
 NULL, NULL
 } }
 };
--- a/usr/src/cmd/fm/fmdump/common/error.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/fm/fmdump/common/error.c	Fri Jul 30 17:04:17 2010 +1000
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #include <fmdump.h>
@@ -56,18 +55,46 @@
 
 /*ARGSUSED*/
 static int
-err_verb2(fmd_log_t *lp, const fmd_log_record_t *rp, FILE *fp)
+err_verb23_cmn(fmd_log_t *lp, const fmd_log_record_t *rp, FILE *fp,
+    nvlist_prtctl_t pctl)
 {
 	char buf[32];
 
 	fmdump_printf(fp, "%-20s.%9.9llu %s\n",
 	    fmdump_year(buf, sizeof (buf), rp), rp->rec_nsec, rp->rec_class);
 
-	nvlist_print(fp, rp->rec_nvl);
+	if (pctl)
+		nvlist_prt(rp->rec_nvl, pctl);
+	else
+		nvlist_print(fp, rp->rec_nvl);
+
 	fmdump_printf(fp, "\n");
 	return (0);
 }
 
+static int
+err_verb2(fmd_log_t *lp, const fmd_log_record_t *rp, FILE *fp)
+{
+	return (err_verb23_cmn(lp, rp, fp, NULL));
+}
+
+static int
+err_pretty(fmd_log_t *lp, const fmd_log_record_t *rp, FILE *fp)
+{
+	nvlist_prtctl_t pctl;
+	int rc;
+
+	if ((pctl = nvlist_prtctl_alloc()) != NULL) {
+		nvlist_prtctl_setdest(pctl, fp);
+		nvlist_prtctlop_nvlist(pctl, fmdump_render_nvlist, NULL);
+	}
+
+	rc = err_verb23_cmn(lp, rp, fp, pctl);
+
+	nvlist_prtctl_free(pctl);
+	return (rc);
+}
+
 const fmdump_ops_t fmdump_err_ops = {
 "error", {
 {
@@ -80,6 +107,9 @@
 "TIME                           CLASS",
 (fmd_log_rec_f *)err_verb2
 }, {
+"TIME                           CLASS",
+(fmd_log_rec_f *)err_pretty
+}, {
 NULL, NULL
 } }
 };
--- a/usr/src/cmd/fm/fmdump/common/fault.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/fm/fmdump/common/fault.c	Fri Jul 30 17:04:17 2010 +1000
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #include <fmdump.h>
@@ -34,22 +33,32 @@
 	char buf[32], str[32];
 	char *class = NULL, *uuid = "-", *code = "-";
 
+	static const struct {
+		const char *class;
+		const char *tag;
+	} tags[] = {
+		{ FM_LIST_SUSPECT_CLASS,	"Diagnosed" },
+		{ FM_LIST_REPAIRED_CLASS,	"Repaired" },
+		{ FM_LIST_RESOLVED_CLASS,	"Resolved" },
+		{ FM_LIST_UPDATED_CLASS,	"Updated" },
+		{ FM_LIST_ISOLATED_CLASS,	"Isolated" },
+	};
+
 	(void) nvlist_lookup_string(rp->rec_nvl, FM_SUSPECT_UUID, &uuid);
 	(void) nvlist_lookup_string(rp->rec_nvl, FM_SUSPECT_DIAG_CODE, &code);
 
 	(void) nvlist_lookup_string(rp->rec_nvl, FM_CLASS, &class);
-	if (class != NULL && strcmp(class, FM_LIST_REPAIRED_CLASS) == 0) {
-		(void) snprintf(str, sizeof (str), "%s %s", code, "Repaired");
-		code = str;
-	}
-	if (class != NULL && strcmp(class, FM_LIST_RESOLVED_CLASS) == 0) {
-		(void) snprintf(str, sizeof (str), "%s %s", code, "Resolved");
-		code = str;
-	}
+	if (class != NULL) {
+		int i;
 
-	if (class != NULL && strcmp(class, FM_LIST_UPDATED_CLASS) == 0) {
-		(void) snprintf(str, sizeof (str), "%s %s", code, "Updated");
-		code = str;
+		for (i = 0; i < sizeof (tags) / sizeof (tags[0]); i++) {
+			if (strcmp(class, tags[i].class) == 0) {
+				(void) snprintf(str, sizeof (str), "%s %s",
+				    code, tags[i].tag);
+				code = str;
+				break;
+			}
+		}
 	}
 
 	fmdump_printf(fp, "%-20s %-32s %s\n",
@@ -138,7 +147,8 @@
 }
 
 static int
-flt_verb2(fmd_log_t *lp, const fmd_log_record_t *rp, FILE *fp)
+flt_verb23_cmn(fmd_log_t *lp, const fmd_log_record_t *rp, FILE *fp,
+    nvlist_prtctl_t pctl)
 {
 	const struct fmdump_fmt *efp = &fmdump_err_ops.do_formats[FMDUMP_VERB1];
 	const struct fmdump_fmt *ffp = &fmdump_flt_ops.do_formats[FMDUMP_VERB2];
@@ -176,12 +186,39 @@
 	}
 
 	fmdump_printf(fp, "\n");
-	nvlist_print(fp, rp->rec_nvl);
+	if (pctl)
+		nvlist_prt(rp->rec_nvl, pctl);
+	else
+		nvlist_print(fp, rp->rec_nvl);
 	fmdump_printf(fp, "\n");
 
 	return (0);
 }
 
+static int
+flt_verb2(fmd_log_t *lp, const fmd_log_record_t *rp, FILE *fp)
+{
+	return (flt_verb23_cmn(lp, rp, fp, NULL));
+}
+
+
+static int
+flt_pretty(fmd_log_t *lp, const fmd_log_record_t *rp, FILE *fp)
+{
+	nvlist_prtctl_t pctl;
+	int rc;
+
+	if ((pctl = nvlist_prtctl_alloc()) != NULL) {
+		nvlist_prtctl_setdest(pctl, fp);
+		nvlist_prtctlop_nvlist(pctl, fmdump_render_nvlist, NULL);
+	}
+
+	rc = flt_verb23_cmn(lp, rp, fp, pctl);
+
+	nvlist_prtctl_free(pctl);
+	return (rc);
+}
+
 /*
  * There is a lack of uniformity in how the various entries in our diagnosis
  * are terminated.  Some end with one newline, others with two.  This makes the
@@ -230,16 +267,22 @@
 const fmdump_ops_t fmdump_flt_ops = {
 "fault", {
 {
-"TIME                 UUID                                 SUNW-MSG-ID",
+"TIME                 UUID                                 SUNW-MSG-ID "
+								"EVENT",
 (fmd_log_rec_f *)flt_short
 }, {
-"TIME                 UUID                                 SUNW-MSG-ID",
+"TIME                 UUID                                 SUNW-MSG-ID "
+								"EVENT",
 (fmd_log_rec_f *)flt_verb1
 }, {
 "TIME                           UUID"
 "                                 SUNW-MSG-ID",
 (fmd_log_rec_f *)flt_verb2
 }, {
+"TIME                           UUID"
+"                                 SUNW-MSG-ID",
+(fmd_log_rec_f *)flt_pretty
+}, {
 NULL,
 (fmd_log_rec_f *)flt_msg
 } }
--- a/usr/src/cmd/fm/fmdump/common/fmdump.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/fm/fmdump/common/fmdump.c	Fri Jul 30 17:04:17 2010 +1000
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #include <alloca.h>
@@ -35,6 +34,7 @@
 #include <ctype.h>
 #include <regex.h>
 #include <dirent.h>
+#include <pthread.h>
 
 #include <fmdump.h>
 
@@ -93,6 +93,46 @@
 	va_end(ap);
 }
 
+static void
+fmdump_exit(int err, int exitcode, const char *format, va_list ap)
+{
+	(void) fprintf(stderr, "%s: ", g_pname);
+
+	(void) vfprintf(stderr, format, ap);
+
+	if (strchr(format, '\n') == NULL)
+		(void) fprintf(stderr, ": %s\n", strerror(err));
+
+	exit(exitcode);
+}
+
+/*PRINTFLIKE1*/
+static void
+fmdump_fatal(const char *format, ...)
+{
+	int err = errno;
+
+	va_list ap;
+
+	va_start(ap, format);
+	fmdump_exit(err, FMDUMP_EXIT_FATAL, format, ap);
+	va_end(ap);
+}
+
+/*PRINTFLIKE1*/
+static void
+fmdump_usage(const char *format, ...)
+{
+
+	int err = errno;
+
+	va_list ap;
+
+	va_start(ap, format);
+	fmdump_exit(err, FMDUMP_EXIT_USAGE, format, ap);
+	va_end(ap);
+}
+
 char *
 fmdump_date(char *buf, size_t len, const fmd_log_record_t *rp)
 {
@@ -134,27 +174,36 @@
 	return (buf);
 }
 
+/* BEGIN CSTYLED */
+static const char *synopsis =
+"Usage: %s [[-e | -i | -I] | -A ] [-f] [-mvVp] [-c class] [-R root]\n"
+	"\t      [-t time ][-T time] [-u uuid] [-n name[.name]*[=value]] "
+							"[file]...\n    "
+    "Log selection: [-e | -i | -I] or one [file]; default is the fault log\n"
+	"\t-e  display error log content\n"
+	"\t-i  display infolog content\n"
+	"\t-I  display the high-value-infolog content\n"
+	"\t-R  set root directory for pathname expansions\n    "
+    "Command behaviour:\n"
+	"\t-A  Aggregate specified [file]s or, if no [file], all known logs\n"
+	"\t-f  follow growth of log file by waiting for additional data\n    "
+    "Output options:\n"
+	"\t-m  display human-readable messages (only for fault logs)\n"
+	"\t-v  set verbose mode: display additional event detail\n"
+	"\t-V  set very verbose mode: display complete event contents\n"
+	"\t-p  Used with -V: apply some output prettification\n    "
+    "Selection filters:\n"
+	"\t-c  select events that match the specified class\n"
+	"\t-t  select events that occurred after the specified time\n"
+	"\t-T  select events that occurred before the specified time\n"
+	"\t-u  select events that match the specified diagnosis uuid\n"
+	"\t-n  select events containing named nvpair (with matching value)\n";
+/* END CSTYLED */
+
 static int
 usage(FILE *fp)
 {
-	(void) fprintf(fp, "Usage: %s [-efmvV] [-c class] [-R root] [-t time] "
-	    "[-T time] [-u uuid]\n\t\t[-n name[.name]*[=value]] [file]\n",
-	    g_pname);
-
-	(void) fprintf(fp,
-	    "\t-c  select events that match the specified class\n"
-	    "\t-e  display error log content instead of fault log content\n"
-	    "\t-f  follow growth of log file by waiting for additional data\n"
-	    "\t-m  display human-readable messages for the fault log\n"
-	    "\t-R  set root directory for pathname expansions\n"
-	    "\t-t  select events that occurred after the specified time\n"
-	    "\t-T  select events that occurred before the specified time\n"
-	    "\t-u  select events that match the specified uuid\n"
-	    "\t-n  select events containing named nvpair "
-	    "(with matching value)\n"
-	    "\t-v  set verbose mode: display additional event detail\n"
-	    "\t-V  set very verbose mode: display complete event contents\n");
-
+	(void) fprintf(fp, synopsis, g_pname);
 	return (FMDUMP_EXIT_USAGE);
 }
 
@@ -204,17 +253,11 @@
 	struct tm tm;
 	char *p;
 
-	if (tvp == NULL) {
-		(void) fprintf(stderr, "%s: failed to allocate memory: %s\n",
-		    g_pname, strerror(errno));
-		exit(FMDUMP_EXIT_FATAL);
-	}
+	if (tvp == NULL)
+		fmdump_fatal("failed to allocate memory");
 
-	if (gettimeofday(&tod, NULL) != 0) {
-		(void) fprintf(stderr, "%s: failed to get tod: %s\n",
-		    g_pname, strerror(errno));
-		exit(FMDUMP_EXIT_FATAL);
-	}
+	if (gettimeofday(&tod, NULL) != 0)
+		fmdump_fatal("failed to get tod");
 
 	/*
 	 * First try a variety of strptime() calls.  If these all fail, we'll
@@ -257,11 +300,8 @@
 		errno = 0;
 		nsec = strtol(arg, (char **)&p, 10);
 
-		if (errno != 0 || nsec == 0 || p == arg || *p == '\0') {
-			(void) fprintf(stderr, "%s: illegal time "
-			    "format -- %s\n", g_pname, arg);
-			exit(FMDUMP_EXIT_USAGE);
-		}
+		if (errno != 0 || nsec == 0 || p == arg || *p == '\0')
+			fmdump_usage("illegal time format -- %s\n", arg);
 
 		for (i = 0; suffix[i].name != NULL; i++) {
 			if (strcasecmp(suffix[i].name, p) == 0) {
@@ -270,20 +310,15 @@
 			}
 		}
 
-		if (suffix[i].name == NULL) {
-			(void) fprintf(stderr, "%s: illegal time "
-			    "format -- %s\n", g_pname, arg);
-			exit(FMDUMP_EXIT_USAGE);
-		}
+		if (suffix[i].name == NULL)
+			fmdump_usage("illegal time format -- %s\n", arg);
 
 		tvp->tv_sec = nsec / NANOSEC;
 		tvp->tv_usec = (nsec % NANOSEC) / (NANOSEC / MICROSEC);
 
-		if (tvp->tv_sec > tod.tv_sec) {
-			(void) fprintf(stderr, "%s: time delta precedes "
-			    "UTC time origin -- %s\n", g_pname, arg);
-			exit(FMDUMP_EXIT_USAGE);
-		}
+		if (tvp->tv_sec > tod.tv_sec)
+			fmdump_usage("time delta precedes UTC time origin "
+			    "-- %s\n", arg);
 
 		tvp->tv_sec = tod.tv_sec - tvp->tv_sec;
 
@@ -316,11 +351,8 @@
 		tvp->tv_sec = mktime(&tm);
 		tvp->tv_usec = 0;
 
-		if (tvp->tv_sec == -1L && errno != 0) {
-			(void) fprintf(stderr, "%s: failed to compose "
-			    "time %s: %s\n", g_pname, arg, strerror(errno));
-			exit(FMDUMP_EXIT_ERROR);
-		}
+		if (tvp->tv_sec == -1L && errno != 0)
+			fmdump_fatal("failed to compose time %s", arg);
 
 		/*
 		 * If our mktime() set tm_isdst, adjust the result for DST by
@@ -335,17 +367,13 @@
 			tvp->tv_usec =
 			    (suseconds_t)(strtod(arg, &p) * (double)MICROSEC);
 
-			if (errno != 0 || p == arg || *p != '\0') {
-				(void) fprintf(stderr, "%s: illegal time "
-				    "suffix -- .%s\n", g_pname, arg);
-				exit(FMDUMP_EXIT_USAGE);
-			}
+			if (errno != 0 || p == arg || *p != '\0')
+				fmdump_usage("illegal time suffix -- .%s\n",
+				    arg);
 		}
 
 	} else {
-		(void) fprintf(stderr, "%s: unexpected suffix after "
-		    "time %s -- %s\n", g_pname, arg, p);
-		exit(FMDUMP_EXIT_USAGE);
+		fmdump_usage("unexpected suffix after time %s -- %s\n", arg, p);
 	}
 
 	return (tvp);
@@ -404,29 +432,23 @@
 		while (isspace(*value))
 			value++;
 
-		if ((value_regex = malloc(sizeof (regex_t))) == NULL) {
-			(void) fprintf(stderr, "%s: failed to allocate memory: "
-			    "%s\n", g_pname, strerror(errno));
-			exit(FMDUMP_EXIT_FATAL);
-		}
+		if ((value_regex = malloc(sizeof (regex_t))) == NULL)
+			fmdump_fatal("failed to allocate memory");
 
 		/* compile regular expression for possible string match */
 		if ((rv = regcomp(value_regex, value,
 		    REG_NOSUB|REG_NEWLINE)) != 0) {
 			(void) regerror(rv, value_regex, errstr,
 			    sizeof (errstr));
-			(void) fprintf(stderr, "unexpected regular expression "
-			    "in %s: %s\n", value, errstr);
 			free(value_regex);
-			exit(FMDUMP_EXIT_USAGE);
+			fmdump_usage("unexpected regular expression in "
+			    "%s: %s\n", value, errstr);
 		}
 	}
 
-	if ((argt = malloc(sizeof (fmd_log_filter_nvarg_t))) == NULL) {
-		(void) fprintf(stderr, "%s: failed to allocate memory: %s\n",
-		    g_pname, strerror(errno));
-		exit(FMDUMP_EXIT_FATAL);
-	}
+	if ((argt = malloc(sizeof (fmd_log_filter_nvarg_t))) == NULL)
+		fmdump_fatal("failed to allocate memory");
+
 	argt->nvarg_name = namevalue;		/* now just name */
 	argt->nvarg_value = value;
 	argt->nvarg_value_regex = value_regex;
@@ -441,14 +463,26 @@
 int
 log_filter_silent(fmd_log_t *lp, const fmd_log_record_t *rp, void *arg)
 {
+	int opt_A = (arg != NULL);
 	boolean_t msg;
+	char *class;
+
+	/*
+	 * If -A was used then apply this filter only to events of list class
+	 */
+	if (opt_A) {
+		if (nvlist_lookup_string(rp->rec_nvl, FM_CLASS, &class) != 0 ||
+		    strncmp(class, FM_LIST_EVENT ".",
+		    sizeof (FM_LIST_EVENT)) != 0)
+			return (1);
+	}
 
 	return (nvlist_lookup_boolean_value(rp->rec_nvl,
 	    FM_SUSPECT_MESSAGE, &msg) != 0 || msg != 0);
 }
 
 struct loglink {
-	char 		*path;
+	char		*path;
 	long		suffix;
 	struct loglink	*next;
 };
@@ -463,11 +497,8 @@
 	newp = malloc(sizeof (struct loglink));
 	len = strlen(dirname) + strlen(logname) + 2;
 	str = malloc(len);
-	if (newp == NULL || str == NULL) {
-		(void) fprintf(stderr, "%s: failed to allocate memory: %s\n",
-		    g_pname, strerror(errno));
-		exit(FMDUMP_EXIT_FATAL);
-	}
+	if (newp == NULL || str == NULL)
+		fmdump_fatal("failed to allocate memory");
 
 	(void) snprintf(str, len, "%s/%s", dirname, logname);
 	newp->path = str;
@@ -498,8 +529,8 @@
 	len = strlen(logname);
 
 	if ((dirp = opendir(dirname)) == NULL) {
-		(void) fprintf(stderr, "%s: failed to opendir `%s': %s\n",
-		    g_pname, dirname, strerror(errno));
+		fmdump_warn("failed to opendir `%s'", dirname);
+		g_errs++;
 		return (NULL);
 	}
 
@@ -533,13 +564,607 @@
 	return (head);
 }
 
+/*
+ * Aggregate log files.  If ifiles is not NULL then one or more files
+ * were listed on the command line, and we will merge just those files.
+ * Otherwise we will merge all known log file types, and include the
+ * rotated logs for each type (you can suppress the inclusion of
+ * some logtypes through use of FMDUMP_AGGREGATE_IGNORE in the process
+ * environment, setting it to a comma-separated list of log labels and/or
+ * log filenames to ignore).
+ *
+ * We will not attempt to perform a chronological sort across all log records
+ * of all files.  Indeed, we won't even sort individual log files -
+ * we will not re-order events differently to how they appeared in their
+ * original log file.  This is because log files are already inherently
+ * ordered by the order in which fmd receives and processes events.
+ * So we determine the output order by comparing the "next" record
+ * off the top of each log file.
+ *
+ * We will construct a number of log record source "pipelines".  As above,
+ * the next record to render in the overall output is that from the
+ * pipeline with the oldest event.
+ *
+ * For the case that input logfiles were listed on the command line, each
+ * pipeline will process exactly one of those logfiles.  Distinct pipelines
+ * may process logfiles of the same "type" - eg if two "error" logs and
+ * one "fault" logs are specified then there'll be two pipelines producing
+ * events from "error" logs.
+ *
+ * If we are merging all known log types then we will construct exactly
+ * one pipeline for each known log type - one for error, one for fault, etc.
+ * Each pipeline will process first the rotated logs of that type and then
+ * move on to the current log of that type.
+ *
+ * The output from all pipelines flows into a serializer which selects
+ * the next record once all pipelines have asserted their output state.
+ * The output state of a pipeline is one of:
+ *
+ *	- record available: the next record from this pipeline is available
+ *	  for comparison and consumption
+ *
+ *	- done: this pipeline will produce no more records
+ *
+ *	- polling: this pipeline is polling for new records and will
+ *	  make them available as output if/when any are observed
+ *
+ *	- processing: output state will be updated shortly
+ *
+ * A pipeline iterates over each file queued to it using fmd_log_xiter.
+ * We do this in a separate thread for each pipeline.  The callback on
+ * each iteration must update the serializer to let it know that
+ * a new record is available.  In the serializer thread we decide whether
+ * we have all records expected have arrived and it is time to choose
+ * the next output record.
+ */
+
+/*
+ * A pipeline descriptor.  The pl_cv condition variable is used together
+ * with pl_lock for initial synchronisation, and thereafter with the
+ * lock for the serializer for pausing and continuing this pipeline.
+ */
+struct fmdump_pipeline {
+	pthread_mutex_t pl_lock;	/* used only in pipeline startup */
+	int pl_started;			/* sync with main thread on startup */
+	pthread_t pl_thr;		/* our processing thread */
+	pthread_cond_t pl_cv;		/* see above */
+	struct loglink *pl_rotated;	/* rotated logs to process first */
+	char *pl_logpath;		/* target path to process */
+	char *pl_processing;		/* path currently being processed */
+	struct fmdump_srlzer *pl_srlzer;	/* link to serializer */
+	int pl_srlzeridx;		/* serializer index for this pipeline */
+	const fmdump_ops_t *pl_ops;	/* ops for the log type we're given */
+	int pl_fmt;			/* FMDUMP_{SHORT,VERB1,VERB2,PRETTY} */
+	boolean_t pl_follow;		/* go into poll mode at log end */
+	fmdump_arg_t pl_arg;		/* arguments */
+};
+
+enum fmdump_pipestate {
+	FMDUMP_PIPE_PROCESSING = 0x1000,
+	FMDUMP_PIPE_RECORDAVAIL,
+	FMDUMP_PIPE_POLLING,
+	FMDUMP_PIPE_DONE
+};
+
+/*
+ * Each pipeline has an associated output slot in the serializer.  This
+ * must be updated with the serializer locked.  After update evaluate
+ * whether there are enough slots decided that we should select a
+ * record to output.
+ */
+struct fmdump_srlzer_slot {
+	enum fmdump_pipestate ss_state;
+	uint64_t ss_sec;
+	uint64_t ss_nsec;
+};
+
+/*
+ * All pipelines are linked to a single serializer.  The serializer
+ * structure must be updated under the ds_lock; this mutex is also
+ * paired with the pl_cv of individual pipelines (one mutex, many condvars)
+ * in pausing and continuing individual pipelines.
+ */
+struct fmdump_srlzer {
+	struct fmdump_pipeline *ds_pipearr;	/* pipeline array */
+	pthread_mutex_t ds_lock;		/* see above */
+	uint32_t ds_pipecnt;			/* number of pipelines */
+	uint32_t ds_pollcnt;			/* pipelines in poll mode */
+	uint32_t ds_nrecordavail;		/* pipelines with a record */
+	uint32_t ds_ndone;			/* completed pipelines */
+	struct fmdump_srlzer_slot *ds_slot;	/* slot array */
+};
+
+/*
+ * All known log types.  When aggregation is requested an no file list
+ * is provided we will process the logs identified here (if lt_enabled
+ * is true and not over-ridden by environment settings).  We also
+ * use this in determining the appropriate ops structure for each distinct
+ * label.
+ */
+static struct fmdump_logtype {
+	const char *lt_label;		/* label from log header */
+	boolean_t lt_enabled;		/* include in merge? */
+	const char *lt_logname;		/* var/fm/fmd/%s */
+	const fmdump_ops_t *lt_ops;
+} logtypes[] = {
+	{
+		"error",
+		B_TRUE,
+		"errlog",
+		&fmdump_err_ops
+	},
+	{
+		"fault",
+		B_TRUE,
+		"fltlog",
+		&fmdump_flt_ops
+	},
+	{
+		"info",
+		B_TRUE,
+		"infolog",
+		&fmdump_info_ops
+	},
+	{
+		"info",
+		B_TRUE,
+		"infolog_hival",
+		&fmdump_info_ops
+	},
+	{
+		"asru",
+		B_FALSE,		/* not included unless in file list */
+		NULL,
+		&fmdump_asru_ops	/* but we need ops when it is */
+	}
+};
+
+/*
+ * Disable logtypes per environment setting.  Does not apply when a list
+ * of logs is provided on the command line.
+ */
+static void
+do_disables(void)
+{
+	char *env = getenv("FMDUMP_AGGREGATE_IGNORE");
+	char *dup, *start, *tofree;
+	int i;
+
+	if (env == NULL)
+		return;
+
+	tofree = dup = strdup(env);
+
+	while (dup != NULL) {
+		start = strsep(&dup, ",");
+		for (i = 0; i < sizeof (logtypes) / sizeof (logtypes[0]); i++) {
+			if (logtypes[i].lt_logname == NULL)
+				continue;
+
+			if (strcmp(start, logtypes[i].lt_label) == 0 ||
+			    strcmp(start, logtypes[i].lt_logname) == 0) {
+				logtypes[i].lt_enabled = B_FALSE;
+			}
+		}
+	}
+
+	free(tofree);
+}
+
+static void
+srlzer_enter(struct fmdump_pipeline *pl)
+{
+	struct fmdump_srlzer *srlzer = pl->pl_srlzer;
+
+	(void) pthread_mutex_lock(&srlzer->ds_lock);
+}
+
+static void
+srlzer_exit(struct fmdump_pipeline *pl)
+{
+	struct fmdump_srlzer *srlzer = pl->pl_srlzer;
+
+	ASSERT(MUTEX_HELD(&srlzer->ds_lock));
+	(void) pthread_mutex_unlock(&srlzer->ds_lock);
+}
+
+static struct fmdump_pipeline *
+srlzer_choose(struct fmdump_srlzer *srlzer)
+{
+	struct fmdump_srlzer_slot *slot, *oldest;
+	int oldestidx = -1;
+	int first = 1;
+	int i;
+
+	ASSERT(MUTEX_HELD(&srlzer->ds_lock));
+
+	for (i = 0, slot = &srlzer->ds_slot[0]; i < srlzer->ds_pipecnt;
+	    i++, slot++) {
+		if (slot->ss_state != FMDUMP_PIPE_RECORDAVAIL)
+			continue;
+
+		if (first) {
+			oldest = slot;
+			oldestidx = i;
+			first = 0;
+			continue;
+		}
+
+		if (slot->ss_sec < oldest->ss_sec ||
+		    slot->ss_sec == oldest->ss_sec &&
+		    slot->ss_nsec < oldest->ss_nsec) {
+			oldest = slot;
+			oldestidx = i;
+		}
+	}
+
+	return (oldestidx >= 0 ? &srlzer->ds_pipearr[oldestidx] : NULL);
+}
+
+static void
+pipeline_stall(struct fmdump_pipeline *pl)
+{
+	struct fmdump_srlzer *srlzer = pl->pl_srlzer;
+
+	ASSERT(MUTEX_HELD(&srlzer->ds_lock));
+	(void) pthread_cond_wait(&pl->pl_cv, &srlzer->ds_lock);
+}
+
+static void
+pipeline_continue(struct fmdump_pipeline *pl)
+{
+	struct fmdump_srlzer *srlzer = pl->pl_srlzer;
+
+	ASSERT(MUTEX_HELD(&srlzer->ds_lock));
+	(void) pthread_cond_signal(&srlzer->ds_pipearr[pl->pl_srlzeridx].pl_cv);
+}
+
+/*
+ * Called on each pipeline record iteration to make a new record
+ * available for input to the serializer.  Returns 0 to indicate that
+ * the caller must stall the pipeline, or 1 to indicate that the
+ * caller should go ahead and render their record.  If this record
+ * addition fills the serializer then choose a pipeline that must
+ * render output.
+ */
+static int
+pipeline_output(struct fmdump_pipeline *pl, const fmd_log_record_t *rp)
+{
+	struct fmdump_srlzer *srlzer = pl->pl_srlzer;
+	struct fmdump_srlzer_slot *slot;
+	struct fmdump_pipeline *wpl;
+	int thisidx = pl->pl_srlzeridx;
+
+	ASSERT(MUTEX_HELD(&srlzer->ds_lock));
+
+	slot = &srlzer->ds_slot[thisidx];
+	slot->ss_state = FMDUMP_PIPE_RECORDAVAIL;
+	slot->ss_sec = rp->rec_sec;
+	slot->ss_nsec = rp->rec_nsec;
+	srlzer->ds_nrecordavail++;
+
+	/*
+	 * Once all pipelines are polling we just render in arrival order.
+	 */
+	if (srlzer->ds_pollcnt == srlzer->ds_pipecnt)
+		return (1);
+
+	/*
+	 * If not all pipelines have asserted an output yet then the
+	 * caller must block.
+	 */
+	if (srlzer->ds_nrecordavail + srlzer->ds_ndone +
+	    srlzer->ds_pollcnt < srlzer->ds_pipecnt)
+		return (0);
+
+	/*
+	 * Right so it's time to turn the crank by choosing which of the
+	 * filled line of slots should produce output.  If it is the slot
+	 * for our caller then return their index to them, otherwise return
+	 * -1 to the caller to make them block and cv_signal the winner.
+	 */
+	wpl = srlzer_choose(srlzer);
+	ASSERT(wpl != NULL);
+
+	if (wpl == pl)
+		return (1);
+
+	/* Wake the oldest, and return 0 to put the caller to sleep */
+	pipeline_continue(wpl);
+
+	return (0);
+}
+
+static void
+pipeline_mark_consumed(struct fmdump_pipeline *pl)
+{
+	struct fmdump_srlzer *srlzer = pl->pl_srlzer;
+
+	ASSERT(MUTEX_HELD(&srlzer->ds_lock));
+	srlzer->ds_slot[pl->pl_srlzeridx].ss_state = FMDUMP_PIPE_PROCESSING;
+	srlzer->ds_nrecordavail--;
+}
+
+static void
+pipeline_done(struct fmdump_pipeline *pl)
+{
+	struct fmdump_srlzer *srlzer = pl->pl_srlzer;
+	struct fmdump_pipeline *wpl;
+
+	srlzer_enter(pl);
+
+	srlzer->ds_slot[pl->pl_srlzeridx].ss_state = FMDUMP_PIPE_DONE;
+	srlzer->ds_ndone++;
+	wpl = srlzer_choose(srlzer);
+	if (wpl != NULL)
+		pipeline_continue(wpl);
+
+	srlzer_exit(pl);
+}
+
+static void
+pipeline_pollmode(struct fmdump_pipeline *pl)
+{
+	struct fmdump_srlzer *srlzer = pl->pl_srlzer;
+	struct fmdump_pipeline *wpl;
+
+	if (srlzer->ds_slot[pl->pl_srlzeridx].ss_state == FMDUMP_PIPE_POLLING)
+		return;
+
+	srlzer_enter(pl);
+
+	srlzer->ds_slot[pl->pl_srlzeridx].ss_state = FMDUMP_PIPE_POLLING;
+	if (++srlzer->ds_pollcnt + srlzer->ds_nrecordavail ==
+	    srlzer->ds_pipecnt && (wpl = srlzer_choose(srlzer)) != NULL)
+		pipeline_continue(wpl);
+
+	srlzer_exit(pl);
+}
+
+static int
+pipeline_err(fmd_log_t *lp, void *arg)
+{
+	struct fmdump_pipeline *pl = (struct fmdump_pipeline *)arg;
+
+	fmdump_warn("skipping record in %s: %s\n", pl->pl_processing,
+	    fmd_log_errmsg(lp, fmd_log_errno(lp)));
+	g_errs++;
+
+	return (0);
+}
+
+static int
+pipeline_cb(fmd_log_t *lp, const fmd_log_record_t *rp, void *arg)
+{
+	struct fmdump_pipeline *pl = (struct fmdump_pipeline *)arg;
+	int rc;
+
+	fmd_log_rec_f *func = pl->pl_arg.da_fmt->do_func;
+
+	srlzer_enter(pl);
+
+	if (!pipeline_output(pl, rp))
+		pipeline_stall(pl);
+
+	rc = func(lp, rp, pl->pl_arg.da_fp);
+	pipeline_mark_consumed(pl);
+
+	srlzer_exit(pl);
+
+	return (rc);
+}
+
+static void
+pipeline_process(struct fmdump_pipeline *pl, char *logpath, boolean_t follow)
+{
+	fmd_log_header_t log;
+	fmd_log_t *lp;
+	int err;
+	int i;
+
+	pl->pl_processing = logpath;
+
+	if ((lp = fmd_log_open(FMD_LOG_VERSION, logpath, &err)) == NULL) {
+		fmdump_warn("failed to open %s: %s\n",
+		    logpath, fmd_log_errmsg(NULL, err));
+		g_errs++;
+		return;
+	}
+
+	fmd_log_header(lp, &log);
+	for (i = 0; i < sizeof (logtypes) / sizeof (logtypes[0]); i++) {
+		if (strcmp(log.log_label, logtypes[i].lt_label) == 0) {
+			pl->pl_ops = logtypes[i].lt_ops;
+			pl->pl_arg.da_fmt =
+			    &pl->pl_ops->do_formats[pl->pl_fmt];
+			break;
+		}
+	}
+
+	if (pl->pl_ops == NULL) {
+		fmdump_warn("unknown log type %s for %s\n",
+		    log.log_label, logpath);
+		g_errs++;
+		return;
+	}
+
+	do {
+		if (fmd_log_xiter(lp, FMD_LOG_XITER_REFS, pl->pl_arg.da_fc,
+		    pl->pl_arg.da_fv, pipeline_cb, pipeline_err, (void *)pl,
+		    NULL) != 0) {
+			fmdump_warn("failed to dump %s: %s\n",
+			    logpath, fmd_log_errmsg(lp, fmd_log_errno(lp)));
+			g_errs++;
+			fmd_log_close(lp);
+			return;
+		}
+
+		if (follow) {
+			pipeline_pollmode(pl);
+			(void) sleep(1);
+		}
+
+	} while (follow);
+
+	fmd_log_close(lp);
+}
+
+static void *
+pipeline_thr(void *arg)
+{
+	struct fmdump_pipeline *pl = (struct fmdump_pipeline *)arg;
+	struct loglink *ll;
+
+	(void) pthread_mutex_lock(&pl->pl_lock);
+	pl->pl_started = 1;
+	(void) pthread_mutex_unlock(&pl->pl_lock);
+	(void) pthread_cond_signal(&pl->pl_cv);
+
+	for (ll = pl->pl_rotated; ll != NULL; ll = ll->next)
+		pipeline_process(pl, ll->path, B_FALSE);
+
+	pipeline_process(pl, pl->pl_logpath, pl->pl_follow);
+	pipeline_done(pl);
+
+	return (NULL);
+}
+
+
+static int
+aggregate(char **ifiles, int n_ifiles, int opt_f,
+    fmd_log_filter_t *fv, uint_t fc,
+    int opt_v, int opt_V, int opt_p)
+{
+	struct fmdump_pipeline *pipeline, *pl;
+	struct fmdump_srlzer srlzer;
+	uint32_t npipe;
+	int fmt;
+	int i;
+
+	if (ifiles != NULL) {
+		npipe = n_ifiles;
+		pipeline = calloc(npipe, sizeof (struct fmdump_pipeline));
+		if (!pipeline)
+			fmdump_fatal("failed to allocate memory");
+
+		for (i = 0; i < n_ifiles; i++)
+			pipeline[i].pl_logpath = ifiles[i];
+	} else {
+		pipeline = calloc(sizeof (logtypes) / sizeof (logtypes[0]),
+		    sizeof (struct fmdump_pipeline));
+		if (!pipeline)
+			fmdump_fatal("failed to allocate memory");
+
+		do_disables();
+
+		npipe = 0;
+		for (i = 0; i < sizeof (logtypes) / sizeof (logtypes[0]); i++) {
+			struct fmdump_logtype *ltp = &logtypes[i];
+			char *logpath;
+
+			if (ltp->lt_enabled == B_FALSE)
+				continue;
+
+			if ((logpath = malloc(PATH_MAX)) == NULL)
+				fmdump_fatal("failed to allocate memory");
+
+			(void) snprintf(logpath, PATH_MAX,
+			    "%s/var/fm/fmd/%s",
+			    g_root ? g_root : "", ltp->lt_logname);
+
+			pipeline[npipe].pl_rotated =
+			    get_rotated_logs(logpath);
+
+			pipeline[npipe++].pl_logpath = logpath;
+		}
+	}
+
+	if (opt_V)
+		fmt = opt_p ? FMDUMP_PRETTY : FMDUMP_VERB2;
+	else if (opt_v)
+		fmt = FMDUMP_VERB1;
+	else
+		fmt = FMDUMP_SHORT;
+
+	bzero(&srlzer, sizeof (srlzer));
+	srlzer.ds_pipearr = pipeline;
+	srlzer.ds_pipecnt = npipe;
+	srlzer.ds_slot = calloc(npipe, sizeof (struct fmdump_srlzer_slot));
+	if (!srlzer.ds_slot)
+		fmdump_fatal("failed to allocate memory");
+	(void) pthread_mutex_init(&srlzer.ds_lock, NULL);
+
+	for (i = 0, pl = &pipeline[0]; i < npipe; i++, pl++) {
+		(void) pthread_mutex_init(&pl->pl_lock, NULL);
+		(void) pthread_cond_init(&pl->pl_cv, NULL);
+		srlzer.ds_slot[i].ss_state = FMDUMP_PIPE_PROCESSING;
+		pl->pl_srlzer = &srlzer;
+		pl->pl_srlzeridx = i;
+		pl->pl_follow = opt_f ? B_TRUE : B_FALSE;
+		pl->pl_fmt = fmt;
+		pl->pl_arg.da_fv = fv;
+		pl->pl_arg.da_fc = fc;
+		pl->pl_arg.da_fp = stdout;
+
+		(void) pthread_mutex_lock(&pl->pl_lock);
+
+		if (pthread_create(&pl->pl_thr, NULL,
+		    pipeline_thr, (void *)pl) != 0)
+			fmdump_fatal("pthread_create for pipeline %d failed",
+			    i);
+	}
+
+	for (i = 0, pl = &pipeline[0]; i < npipe; i++, pl++) {
+		while (!pl->pl_started)
+			(void) pthread_cond_wait(&pl->pl_cv, &pl->pl_lock);
+
+		(void) pthread_mutex_unlock(&pl->pl_lock);
+	}
+
+	for (i = 0, pl = &pipeline[0]; i < npipe; i++, pl++)
+		(void) pthread_join(pl->pl_thr, NULL);
+
+	if (ifiles == NULL) {
+		for (i = 0; i < npipe; i++)
+			free(pipeline[i].pl_logpath);
+	}
+
+	free(srlzer.ds_slot);
+
+	free(pipeline);
+
+	return (FMDUMP_EXIT_SUCCESS);
+}
+
+static void
+cleanup(char **ifiles, int n_ifiles)
+{
+	int i;
+
+	if (ifiles == NULL)
+		return;
+
+	for (i = 0; i < n_ifiles; i++) {
+		if (ifiles[i] != NULL) {
+			free(ifiles[i]);
+			ifiles[i] = NULL;
+		}
+	}
+
+	free(ifiles);
+}
+
 int
 main(int argc, char *argv[])
 {
-	int opt_a = 0, opt_e = 0, opt_f = 0, opt_H = 0, opt_m = 0;
+	int opt_a = 0, opt_e = 0, opt_f = 0, opt_H = 0, opt_m = 0, opt_p = 0;
 	int opt_u = 0, opt_v = 0, opt_V = 0;
-
-	char ifile[PATH_MAX] = "";
+	int opt_i = 0, opt_I = 0;
+	int opt_A = 0;
+	char **ifiles = NULL;
+	char *ifile = NULL;
+	int n_ifiles;
+	int ifileidx = 0;
 	int iflags = 0;
 
 	fmdump_arg_t arg;
@@ -568,8 +1193,11 @@
 
 	while (optind < argc) {
 		while ((c =
-		    getopt(argc, argv, "ac:efHmn:O:R:t:T:u:vV")) != EOF) {
+		    getopt(argc, argv, "Aac:efHiImn:O:pR:t:T:u:vV")) != EOF) {
 			switch (c) {
+			case 'A':
+				opt_A++;
+				break;
 			case 'a':
 				opt_a++;
 				break;
@@ -579,6 +1207,8 @@
 				allfv[allfc++] = errfv[errfc++];
 				break;
 			case 'e':
+				if (opt_i)
+					return (usage(stderr));
 				opt_e++;
 				break;
 			case 'f':
@@ -587,6 +1217,16 @@
 			case 'H':
 				opt_H++;
 				break;
+			case 'i':
+				if (opt_e || opt_I)
+					return (usage(stderr));
+				opt_i++;
+				break;
+			case 'I':
+				if (opt_e || opt_i)
+					return (usage(stderr));
+				opt_I++;
+				break;
 			case 'm':
 				opt_m++;
 				break;
@@ -594,6 +1234,9 @@
 				off = strtoull(optarg, NULL, 16);
 				iflags |= FMD_LOG_XITER_OFFS;
 				break;
+			case 'p':
+				opt_p++;
+				break;
 			case 'R':
 				g_root = optarg;
 				break;
@@ -631,21 +1274,79 @@
 			}
 		}
 
+		if (opt_A && (opt_e || opt_i || opt_I || opt_m || opt_u))
+			fmdump_usage("-A excludes all of "
+			    "-e, -i, -I, -m and -u\n");
+
 		if (optind < argc) {
-			if (*ifile != '\0') {
-				(void) fprintf(stderr, "%s: illegal "
-				    "argument -- %s\n", g_pname, argv[optind]);
-				return (FMDUMP_EXIT_USAGE);
-			} else {
-				(void) strlcpy(ifile,
-				    argv[optind++], sizeof (ifile));
+			char *dest;
+
+			if (ifiles == NULL) {
+				n_ifiles = argc - optind;
+				ifiles = calloc(n_ifiles, sizeof (char *));
+				if (ifiles == NULL) {
+					fmdump_fatal(
+					    "failed to allocate memory for "
+					    "%d input file%s", n_ifiles,
+					    n_ifiles > 1 ? "s" : "");
+				}
 			}
+
+			if (ifileidx > 0 && !opt_A)
+				fmdump_usage("illegal argument -- %s\n",
+				    argv[optind]);
+
+			if ((dest = malloc(PATH_MAX)) == NULL)
+				fmdump_fatal("failed to allocate memory");
+
+			(void) strlcpy(dest, argv[optind++], PATH_MAX);
+			ifiles[ifileidx++] = dest;
 		}
 	}
 
+	if (opt_A) {
+		int rc;
+
+		if (!opt_a) {
+			fltfv[fltfc].filt_func = log_filter_silent;
+			fltfv[fltfc].filt_arg = (void *)1;
+			allfv[allfc++] = fltfv[fltfc++];
+		}
+
+		rc = aggregate(ifiles, n_ifiles, opt_f,
+		    allfv, allfc,
+		    opt_v, opt_V, opt_p);
+
+		cleanup(ifiles, n_ifiles);
+		return (rc);
+	} else {
+		if (ifiles == NULL) {
+			if ((ifile = calloc(1, PATH_MAX)) == NULL)
+				fmdump_fatal("failed to allocate memory");
+		} else {
+			ifile = ifiles[0];
+		}
+	}
+
+
 	if (*ifile == '\0') {
-		(void) snprintf(ifile, sizeof (ifile), "%s/var/fm/fmd/%slog",
-		    g_root ? g_root : "", opt_e && !opt_u ? "err" : "flt");
+		const char *pfx, *sfx;
+
+		if (opt_u || (!opt_e && !opt_i && !opt_I)) {
+			pfx = "flt";
+			sfx = "";
+		} else {
+			if (opt_e) {
+				pfx = "err";
+				sfx = "";
+			} else {
+				pfx = "info";
+				sfx = opt_I ? "_hival" : "";
+			}
+		}
+
+		(void) snprintf(ifile, PATH_MAX, "%s/var/fm/fmd/%slog%s",
+		    g_root ? g_root : "", pfx, sfx);
 		/*
 		 * logadm may rotate the logs.  When no input file is specified,
 		 * we try to dump all the rotated logs as well in the right
@@ -654,21 +1355,16 @@
 		if (!opt_H && off == 0)
 			rotated_logs = get_rotated_logs(ifile);
 	} else if (g_root != NULL) {
-		(void) fprintf(stderr, "%s: -R option is not appropriate "
-		    "when file operand is present\n", g_pname);
-		return (FMDUMP_EXIT_USAGE);
+		fmdump_usage("-R option is not appropriate "
+		    "when file operand is present\n");
 	}
 
-	if ((g_msg = fmd_msg_init(g_root, FMD_MSG_VERSION)) == NULL) {
-		(void) fprintf(stderr, "%s: failed to initialize "
-		    "libfmd_msg: %s\n", g_pname, strerror(errno));
-		return (FMDUMP_EXIT_FATAL);
-	}
+	if ((g_msg = fmd_msg_init(g_root, FMD_MSG_VERSION)) == NULL)
+		fmdump_fatal("failed to initialize libfmd_msg");
 
 	if ((lp = fmd_log_open(FMD_LOG_VERSION, ifile, &err)) == NULL) {
-		(void) fprintf(stderr, "%s: failed to open %s: %s\n",
-		    g_pname, ifile, fmd_log_errmsg(NULL, err));
-		return (FMDUMP_EXIT_FATAL);
+		fmdump_fatal("failed to open %s: %s\n", ifile,
+		    fmd_log_errmsg(NULL, err));
 	}
 
 	if (opt_H) {
@@ -687,9 +1383,8 @@
 	}
 
 	if (off != 0 && fmd_log_seek(lp, off) != 0) {
-		(void) fprintf(stderr, "%s: failed to seek %s: %s\n",
-		    g_pname, ifile, fmd_log_errmsg(lp, fmd_log_errno(lp)));
-		return (FMDUMP_EXIT_FATAL);
+		fmdump_fatal("failed to seek %s: %s\n", ifile,
+		    fmd_log_errmsg(lp, fmd_log_errno(lp)));
 	}
 
 	if (opt_e && opt_u)
@@ -698,6 +1393,8 @@
 		ops = &fmdump_flt_ops;
 	else if (strcmp(fmd_log_label(lp), fmdump_asru_ops.do_label) == 0)
 		ops = &fmdump_asru_ops;
+	else if (strcmp(fmd_log_label(lp), fmdump_info_ops.do_label) == 0)
+		ops = &fmdump_info_ops;
 	else
 		ops = &fmdump_err_ops;
 
@@ -708,7 +1405,8 @@
 	}
 
 	if (opt_V) {
-		arg.da_fmt = &ops->do_formats[FMDUMP_VERB2];
+		arg.da_fmt =
+		    &ops->do_formats[opt_p ? FMDUMP_PRETTY : FMDUMP_VERB2];
 		iflags |= FMD_LOG_XITER_REFS;
 	} else if (opt_v) {
 		arg.da_fmt = &ops->do_formats[FMDUMP_VERB1];
@@ -718,9 +1416,8 @@
 		arg.da_fmt = &ops->do_formats[FMDUMP_SHORT];
 
 	if (opt_m && arg.da_fmt->do_func == NULL) {
-		(void) fprintf(stderr, "%s: -m mode is not supported for "
-		    "log of type %s: %s\n", g_pname, fmd_log_label(lp), ifile);
-		return (FMDUMP_EXIT_USAGE);
+		fmdump_usage("-m mode is not supported for "
+		    "log of type %s: %s\n", fmd_log_label(lp), ifile);
 	}
 
 	arg.da_fv = errfv;
@@ -759,8 +1456,8 @@
 
 		if ((rlp = fmd_log_open(FMD_LOG_VERSION, llp->path, &err))
 		    == NULL) {
-			(void) fprintf(stderr, "%s: failed to open %s: %s\n",
-			    g_pname, llp->path, fmd_log_errmsg(NULL, err));
+			fmdump_warn("failed to open %s: %s\n",
+			    llp->path, fmd_log_errmsg(NULL, err));
 			g_errs++;
 			continue;
 		}
@@ -768,8 +1465,7 @@
 		recs = 0;
 		if (fmd_log_xiter(rlp, iflags, filtc, filtv,
 		    func, error, farg, &recs) != 0) {
-			(void) fprintf(stderr,
-			    "%s: failed to dump %s: %s\n", g_pname, llp->path,
+			fmdump_warn("failed to dump %s: %s\n", llp->path,
 			    fmd_log_errmsg(rlp, fmd_log_errno(rlp)));
 			g_errs++;
 		}
@@ -782,8 +1478,7 @@
 		recs = 0;
 		if (fmd_log_xiter(lp, iflags, filtc, filtv,
 		    func, error, farg, &recs) != 0) {
-			(void) fprintf(stderr,
-			    "%s: failed to dump %s: %s\n", g_pname, ifile,
+			fmdump_warn("failed to dump %s: %s\n", ifile,
 			    fmd_log_errmsg(lp, fmd_log_errno(lp)));
 			g_errs++;
 		}
@@ -795,7 +1490,7 @@
 	} while (opt_f);
 
 	if (!opt_f && g_recs == 0 && isatty(STDOUT_FILENO))
-		(void) fprintf(stderr, "%s: %s is empty\n", g_pname, ifile);
+		fmdump_warn("%s is empty\n", ifile);
 
 	if (g_thp != NULL)
 		topo_close(g_thp);
@@ -803,5 +1498,10 @@
 	fmd_log_close(lp);
 	fmd_msg_fini(g_msg);
 
+	if (ifiles == NULL)
+		free(ifile);
+	else
+		cleanup(ifiles, n_ifiles);
+
 	return (g_errs ? FMDUMP_EXIT_ERROR : FMDUMP_EXIT_SUCCESS);
 }
--- a/usr/src/cmd/fm/fmdump/common/fmdump.h	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/fm/fmdump/common/fmdump.h	Fri Jul 30 17:04:17 2010 +1000
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #ifndef	_FMDUMP_H
@@ -30,8 +29,10 @@
 extern "C" {
 #endif
 
+#include <assert.h>
 #include <stdarg.h>
 #include <stdio.h>
+#include <synch.h>
 
 #include <sys/types.h>
 #include <sys/fm/protocol.h>
@@ -40,10 +41,17 @@
 #include <fm/fmd_msg.h>
 #include <fm/libtopo.h>
 
+#ifdef DEBUG
+#define	ASSERT(x) (assert(x))
+#else
+#define	ASSERT(x)
+#endif
+
 enum {
 	FMDUMP_SHORT,
 	FMDUMP_VERB1,
 	FMDUMP_VERB2,
+	FMDUMP_PRETTY,
 	FMDUMP_MSG,
 	FMDUMP_NFMTS
 };
@@ -72,6 +80,7 @@
 extern const fmdump_ops_t fmdump_err_ops;
 extern const fmdump_ops_t fmdump_flt_ops;
 extern const fmdump_ops_t fmdump_asru_ops;
+extern const fmdump_ops_t fmdump_info_ops;
 
 extern const char *g_pname;
 extern ulong_t g_errs;
@@ -89,6 +98,9 @@
 extern char *fmdump_year(char *, size_t, const fmd_log_record_t *);
 extern char *fmdump_nvl2str(nvlist_t *nvl);
 
+extern int fmdump_render_nvlist(nvlist_prtctl_t, void *, nvlist_t *,
+    const char *, nvlist_t *);
+
 #ifdef	__cplusplus
 }
 #endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fm/fmdump/common/info.c	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,118 @@
+/*
+ * 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 <fmdump.h>
+#include <stdio.h>
+#include <time.h>
+
+/*ARGSUSED*/
+static int
+info_short(fmd_log_t *lp, const fmd_log_record_t *rp, FILE *fp)
+{
+	char buf[32];
+
+	fmdump_printf(fp, "%-20s %-32s\n",
+	    fmdump_date(buf, sizeof (buf), rp), rp->rec_class);
+
+	return (0);
+}
+
+/*ARGSUSED*/
+static int
+info_verb1(fmd_log_t *lp, const fmd_log_record_t *rp, FILE *fp)
+{
+	char *uuid = "(absent)";
+	char buf[32];
+
+	(void) nvlist_lookup_string(rp->rec_nvl, FM_IREPORT_UUID, &uuid);
+
+	fmdump_printf(fp, "%-20s %-36s %s\n",
+	    fmdump_date(buf, sizeof (buf), rp), uuid, rp->rec_class);
+
+	return (0);
+}
+
+/*ARGSUSED*/
+static int
+info_verb23_cmn(fmd_log_t *lp, const fmd_log_record_t *rp, FILE *fp,
+    nvlist_prtctl_t pctl)
+{
+	char buf[32];
+	char *uuid = "(absent)";
+
+	(void) nvlist_lookup_string(rp->rec_nvl, FM_IREPORT_UUID, &uuid);
+
+	fmdump_printf(fp, "%-20s.%9.9llu %s\n",
+	    fmdump_year(buf, sizeof (buf), rp), rp->rec_nsec, uuid);
+
+	if (pctl)
+		nvlist_prt(rp->rec_nvl, pctl);
+	else
+		nvlist_print(fp, rp->rec_nvl);
+
+	fmdump_printf(fp, "\n");
+	return (0);
+}
+
+static int
+info_verb2(fmd_log_t *lp, const fmd_log_record_t *rp, FILE *fp)
+{
+	return (info_verb23_cmn(lp, rp, fp, NULL));
+}
+
+static int
+info_pretty(fmd_log_t *lp, const fmd_log_record_t *rp, FILE *fp)
+{
+	nvlist_prtctl_t pctl;
+	int rc;
+
+	if ((pctl = nvlist_prtctl_alloc()) != NULL) {
+		nvlist_prtctl_setdest(pctl, fp);
+		nvlist_prtctlop_nvlist(pctl, fmdump_render_nvlist, NULL);
+	}
+
+	rc = info_verb23_cmn(lp, rp, fp, pctl);
+
+	nvlist_prtctl_free(pctl);
+	return (rc);
+}
+
+const fmdump_ops_t fmdump_info_ops = {
+"info", {
+{
+"TIME                 CLASS",
+(fmd_log_rec_f *)info_short
+}, {
+"TIME                 UUID                                 CLASS",
+(fmd_log_rec_f *)info_verb1
+}, {
+"TIME                           UUID",
+(fmd_log_rec_f *)info_verb2
+}, {
+"TIME                           UUID",
+(fmd_log_rec_f *)info_pretty
+}, {
+NULL, NULL
+} }
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fm/fmdump/common/nvlrender.c	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+
+/*
+ * Rendering functions for nvlist_prt that are of use to all types
+ * of log.
+ */
+
+#include <fmdump.h>
+#include <stdio.h>
+#include <strings.h>
+
+extern topo_hdl_t *fmd_fmri_topo_hold(int);
+
+/*
+ * Can be appointed to be called for dumping all nvlist members of
+ * an nvlist we ask to print with nvlist_prt.  Return 0 if the
+ * nvlist is not recognized as an fmri, and default formatting
+ * will be applied; otherwise format as an fmri string and return 1.
+ */
+
+/*ARGSUSED*/
+int
+fmdump_render_nvlist(nvlist_prtctl_t pctl, void *private, nvlist_t *nvl,
+    const char *name, nvlist_t *fmri)
+{
+	topo_hdl_t *thp = fmd_fmri_topo_hold(TOPO_VERSION);
+	FILE *fp = nvlist_prtctl_getdest(pctl);
+	char *class, *fmristr = NULL;
+	uint8_t version;
+	int err;
+
+	if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &class) != 0 ||
+	    nvlist_lookup_uint8(fmri, FM_VERSION, &version) != 0)
+		return (0);
+
+	/*
+	 * Instead of hardcoding known FMRI classes here we'll try
+	 * topo_fmri_nvl2str which should fail gracefully for invalid
+	 * schemes (ie an nvlist that just happens to have the expected
+	 * class and version members but that isn't an FMRI).
+	 */
+	if (topo_fmri_nvl2str(thp, fmri, &fmristr, &err) != 0 ||
+	    fmristr == NULL)
+		return (0);
+
+	nvlist_prtctl_doindent(pctl, 1);
+	nvlist_prtctl_dofmt(pctl, NVLIST_FMT_MEMBER_NAME, name);
+	(void) fprintf(fp, "%s", fmristr);
+	topo_hdl_strfree(thp, fmristr);
+
+	return (1);
+}
--- a/usr/src/cmd/fm/fmtopo/common/fmtopo.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/fm/fmtopo/common/fmtopo.c	Fri Jul 30 17:04:17 2010 +1000
@@ -20,8 +20,7 @@
  */
 
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 
@@ -34,6 +33,7 @@
 #include <stdio.h>
 #include <errno.h>
 #include <umem.h>
+#include <zone.h>
 #include <sys/param.h>
 
 #define	FMTOPO_EXIT_SUCCESS	0
@@ -960,6 +960,11 @@
 	topo_walk_t *twp;
 	int flag;
 
+	if (getzoneid() != GLOBAL_ZONEID &&
+	    strcmp(opt_s, FM_FMRI_SCHEME_HC) == 0) {
+		return (0);
+	}
+
 	if ((twp = topo_walk_init(thp, opt_s, walk_node, NULL, &err))
 	    == NULL) {
 		(void) fprintf(stderr, "%s: failed to walk %s topology:"
@@ -1268,8 +1273,11 @@
 		    g_pname, topo_strerror(err));
 		return (fmtopo_exit(thp, uuid, FMTOPO_EXIT_ERROR));
 	} else if (err != 0) {
-		(void) fprintf(stderr, "%s: topology snapshot incomplete\n",
-		    g_pname);
+		(void) fprintf(stderr, "%s: topology snapshot incomplete%s\n",
+		    g_pname, getzoneid() != GLOBAL_ZONEID &&
+		    strcmp(opt_s, FM_FMRI_SCHEME_HC) == 0 ?
+		    " (" FM_FMRI_SCHEME_HC " scheme does not enumerate "
+		    "in a non-global zone)": "");
 	}
 
 	if (opt_x) {
--- a/usr/src/cmd/fm/modules/Makefile.plugin	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/fm/modules/Makefile.plugin	Fri Jul 30 17:04:17 2010 +1000
@@ -19,15 +19,13 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
+# Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
 #
-#ident	"%Z%%M%	%I%	%E% SMI"
 
 .KEEP_STATE:
 .SUFFIXES:
 
-include ../../../../Makefile.cmd
+include $(SRC)/cmd/Makefile.cmd
 MODCLASS = plugins
 
 #
@@ -40,6 +38,13 @@
 CONF = $(MODULE:%=%.conf)
 
 #
+# We may have sources from directories other than the current, but
+# we build all objects in the current directory (not necessarily in the
+# directory of its source).
+SEDCMDLINE = echo $(OBJS) | sed 's!\.\./[a-z_/]*/!!g'
+LINKOBJS = $(SEDCMDLINE:sh)
+
+#
 # A module may set DMOD and DMOD_SRCS if it has a mdb proc module.
 # DMOD, if set, must match PROG above (for mdb autoloading) so it will
 # be built in a subdirectory.
@@ -68,8 +73,8 @@
 DMODLINTTGT = $(DMOD:%=lint_dmod)
 DMODLINTFILES = $(DMOD_SRCS:%.c=%.ln)
 
-APIMAP = ../../../fmd/common/fmd_api.map
-FMRIMAP = ../../../fmd/common/fmd_fmri.map
+APIMAP = $(SRC)/cmd/fm/fmd/common/fmd_api.map
+FMRIMAP = $(SRC)/cmd/fm/fmd/common/fmd_fmri.map
 
 CFLAGS += $(CTF_FLAGS) $(CCVERBOSE) $(XSTRCONST) $(CC_PICFLAGS)
 CFLAGS += -G $(XREGSFLAG) 
@@ -89,8 +94,8 @@
 .PARALLEL: $(OBJS) $(LINTFILES) $(DMOD_OBJS) $(DMODLINTFILES)
 
 $(PROG): $(OBJS) $(APIMAP)
-	$(LINK.c) $(OBJS) -o $@ $(LDLIBS)
-	$(CTFMERGE) -L VERSION -o $@ $(OBJS)
+	$(LINK.c) $(LINKOBJS) -o $@ $(LDLIBS)
+	$(CTFMERGE) -L VERSION -o $@ $(LINKOBJS)
 	$(POST_PROCESS_SO)
 
 $(DMODPROG): $(DMOD_OBJS) $(MAPFILE-DMOD)
@@ -99,17 +104,18 @@
 	$(POST_PROCESS)
 
 %.o: %.c
-	$(COMPILE.c) $<
-	$(CTFCONVERT_O)
+	$(COMPILE.c) $< -o $(@F)
+	$(CTFCONVERT) $(CTFCVTFLAGS) $(@F)
 
 clean:
-	$(RM) $(OBJS) $(DMOD_OBJS) $(LINTFILES) $(DMODLINTFILES) $(CLEANFILES)
+	$(RM) $(OBJS) $(DMOD_OBJS) $(LINTFILES) $(DMODLINTFILES) $(CLEANFILES) \
+	    $(LINKOBJS)
 
 clobber: clean
 	$(RM) $(PROG) $(DMODPROG)
 
 %.ln: %.c
-	$(LINT.c) -c $<
+	$(LINT.c) -dirout=$(@D) -c $<
 
 lint_prog: $(LINTFILES)
 	$(LINT) $(LINTFLAGS) $(LINTFILES) $(LDLIBS)
@@ -132,4 +138,4 @@
 
 install: $(ROOTPROG) $(ROOTCONF) $(ROOTDMOD)
 
-include ../../Makefile.rootdirs
+include $(SRC)/cmd/fm/modules/Makefile.rootdirs
--- a/usr/src/cmd/fm/modules/common/Makefile	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/fm/modules/common/Makefile	Fri Jul 30 17:04:17 2010 +1000
@@ -19,9 +19,9 @@
 # CDDL HEADER END
 #
 #
+# Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
 #
-#Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
-#
+
 SUBDIRS = cpumem-retire \
 	disk-monitor	\
 	disk-transport	\
@@ -33,7 +33,7 @@
 	ip-transport	\
 	sensor-transport \
 	ses-log-transport \
-	snmp-trapgen	\
+	sw-diag-response \
 	sp-monitor	\
 	syslog-msgs	\
 	zfs-diagnosis	\
--- a/usr/src/cmd/fm/modules/common/eversholt/eft.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/fm/modules/common/eversholt/eft.c	Fri Jul 30 17:04:17 2010 +1000
@@ -20,10 +20,10 @@
  */
 
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
+#include <unistd.h>
 #include <stdio.h>
 #include <string.h>
 #include <fm/fmd_api.h>
@@ -213,7 +213,7 @@
 {
 	char *ename = (char *)lhs;
 
-	out(O_DEBUG, "allow silent discard_if_config_unknown: \"%s\"", ename);
+	out(O_VERB, "allow silent discard_if_config_unknown: \"%s\"", ename);
 }
 
 extern struct stats *Filecount;
@@ -248,7 +248,7 @@
 static void
 doopendict(const char *lhs, void *rhs, void *arg)
 {
-	out(O_DEBUG, "opendict: \"%s\"", lhs);
+	out(O_VERB, "opendict: \"%s\"", lhs);
 	fmd_hdl_opendict(Hdl, lhs);
 }
 
@@ -340,7 +340,7 @@
 	fme_istat_load(hdl);
 	fme_serd_load(hdl);
 
-	out(O_DEBUG, "reconstituting any existing fmes");
+	out(O_VERB, "reconstituting any existing fmes");
 	while ((casep = fmd_case_next(hdl, casep)) != NULL) {
 		fme_restart(hdl, casep);
 	}
--- a/usr/src/cmd/fm/modules/common/eversholt/fme.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/fm/modules/common/eversholt/fme.c	Fri Jul 30 17:04:17 2010 +1000
@@ -2779,14 +2779,14 @@
 			ipath_print(O_DEBUG|O_NONL,
 			    rp->suspect->enode->u.event.ename->u.name.s,
 			    rp->suspect->ipp);
-			out(O_DEBUG, " has no FITrate (using 1)");
+			out(O_VERB, " has no FITrate (using 1)");
 			fr = 1;
 		} else if (fr == 0) {
 			out(O_DEBUG|O_NONL, "event ");
 			ipath_print(O_DEBUG|O_NONL,
 			    rp->suspect->enode->u.event.ename->u.name.s,
 			    rp->suspect->ipp);
-			out(O_DEBUG, " has zero FITrate (using 1)");
+			out(O_VERB, " has zero FITrate (using 1)");
 			fr = 1;
 		}
 
--- a/usr/src/cmd/fm/modules/common/eversholt/platform.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/fm/modules/common/eversholt/platform.c	Fri Jul 30 17:04:17 2010 +1000
@@ -867,7 +867,7 @@
 	fnlen = strlen(fnstr);
 
 	for (i = 0; dirname[i] != NULL; i++) {
-		out(O_DEBUG, "Looking for %s files in %s", fnstr, dirname[i]);
+		out(O_VERB, "Looking for %s files in %s", fnstr, dirname[i]);
 		if ((dirp = opendir(dirname[i])) == NULL) {
 			out(O_DEBUG|O_SYS,
 			    "platform_get_files: opendir failed for %s",
@@ -886,7 +886,7 @@
 					if (lut_lookup(foundnames,
 					    (void *)snm,
 					    NULL) != NULL) {
-						out(O_DEBUG,
+						out(O_VERB,
 						    "platform_get_files: "
 						    "skipping repeated name "
 						    "%s/%s",
@@ -910,7 +910,7 @@
 				totlen = strlen(dirname[i]) + 1;
 				totlen += strlen(dp->d_name) + 1;
 				files[nfiles] = MALLOC(totlen);
-				out(O_DEBUG, "File %d: \"%s/%s\"", nfiles,
+				out(O_VERB, "File %d: \"%s/%s\"", nfiles,
 				    dirname[i], dp->d_name);
 				(void) snprintf(files[nfiles++], totlen,
 				    "%s/%s", dirname[i], dp->d_name);
--- a/usr/src/cmd/fm/modules/common/ext-event-transport/Makefile	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/fm/modules/common/ext-event-transport/Makefile	Fri Jul 30 17:04:17 2010 +1000
@@ -19,16 +19,22 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
+# Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
 #
 
 MODULE = ext-event-transport
 CLASS = common
-SRCS = fmevt_main.c fmevt_outbound.c 
+SRCS = fmevt_main.c \
+	fmevt_outbound.c \
+	fmevt_inbound.c \
+	fmevt_inbound_default.c \
+	fmevt_inbound_on.c \
+	fmevt_inbound_smf.c \
+	fmevt_inbound_sunos.c
 
 include ../../Makefile.plugin
 
 CFLAGS += $(INCS)
 LINTFLAGS += $(INCS)
-LDLIBS += -lsysevent
+LDLIBS += -L$(ROOTLIB)/fm -lsysevent -lfmevent -ltopo -luuid -lscf -lc
+LDFLAGS += -R/usr/lib/fm
--- a/usr/src/cmd/fm/modules/common/ext-event-transport/ext-event-transport.conf	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/fm/modules/common/ext-event-transport/ext-event-transport.conf	Fri Jul 30 17:04:17 2010 +1000
@@ -19,8 +19,7 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
+# Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
 #
 
 #
@@ -48,5 +47,4 @@
 # Changing this list may lead to breakage and/or excessive event forwarding.
 #
 subscribe	list.*
-subscribe	swevent.*
-
+subscribe	ireport.*
--- a/usr/src/cmd/fm/modules/common/ext-event-transport/fmevt.h	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/fm/modules/common/ext-event-transport/fmevt.h	Fri Jul 30 17:04:17 2010 +1000
@@ -20,8 +20,7 @@
  */
 
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #ifndef _FMEVT_H
@@ -35,10 +34,10 @@
 extern "C" {
 #endif
 
-#include <sys/fm/protocol.h>
+#include <libnvpair.h>
 #include <fm/fmd_api.h>
 #include <fm/libfmevent.h>
-#include <libnvpair.h>
+#include <sys/fm/protocol.h>
 
 #include "../../../../../lib/fm/libfmevent/common/fmev_channels.h"
 
@@ -48,8 +47,90 @@
 extern void fmevt_init_outbound(fmd_hdl_t *);
 extern void fmevt_fini_outbound(fmd_hdl_t *);
 
+extern void fmevt_init_inbound(fmd_hdl_t *);
+extern void fmevt_fini_inbound(fmd_hdl_t *);
+
 extern void fmevt_recv(fmd_hdl_t *, fmd_event_t *, nvlist_t *, const char *);
 
+
+/*
+ * Post-processing
+ */
+
+/*
+ * Structure passed to a post-processing functions with details of the
+ * raw event.
+ */
+struct fmevt_ppargs {
+	const char *pp_rawclass;	/* class from event publication */
+	const char *pp_rawsubclass;	/* subclass from event publication */
+	hrtime_t pp_hrt;		/* hrtime of event publication */
+	int pp_user;			/* userland or kernel source? */
+	int pp_priv;			/* privileged? */
+	fmev_pri_t pp_pri;		/* published priority */
+	char pp_uuidstr[36 + 1];	/* uuid we'll use for first event */
+};
+
+/*
+ * The maximum length that a protocol event class name generated
+ * in post-processing can be.
+ */
+#define	FMEVT_MAX_CLASS		64
+
+/*
+ * A post-processing function may derive up to this number of separate
+ * protocol events for each raw event.
+ */
+#define	FMEVT_FANOUT_MAX	5
+
+/*
+ * Post-processing function type.  The function receives raw event
+ * details in the struct fmevt_ppargs.  It must prepare up to
+ * FMEVT_FANOUT_MAX protocol events (usually just one event)
+ * based on the raw event, and return the number of events
+ * to be posted.  The array of class pointers must have that
+ * number of non-NULL entries.  You may return 0 to ditch an event;
+ * in this case the caller will not perform an frees so you must
+ * tidy up.
+ *
+ * The array of string pointers has the first member pointed to
+ * some storage of size FMEV_MAX_CLASS into which the post-processing
+ * function must render the protocol event classname.  If fanning
+ * out into more than one event then the post-processing function
+ * must allocate additional buffers (using fmd_hdl_alloc) and return
+ * pointers to these in the array of string pointers (but do not change
+ * the first element); buffers allocated and returned in this way will
+ * be freed by the caller as it iterates over the protocol events to
+ * post them.  Similarly the function must prepare an attributes
+ * nvlist for each event; it can return the raw attributes or it
+ * can fmd_nvl_alloc or fmd_nvl_dup and return those (to be freed
+ * by the caller).
+ *
+ * Events will be generated based on the results as follows:
+ *
+ * event[i] =
+ *
+ *	timestamp = as supplied by incoming event and in pp_hrt
+ *	class = class_array[i];  entry 0 is allocated, fmd_hdl_alloc others
+ *	detector = generated detector as passed to function
+ *	uuid = generated UUID, or that supplied by raw event
+ *	attr = nvlist_array[i], can be absent; may return raw attributes
+ *
+ */
+typedef uint_t fmevt_pp_func_t(
+    char *[FMEVT_FANOUT_MAX],		/* event class(es) */
+    nvlist_t *[FMEVT_FANOUT_MAX],	/* event attributes */
+    const char *,			/* ruleset */
+    const nvlist_t *,			/* detector */
+    nvlist_t *,				/* raw attributes */
+    const struct fmevt_ppargs *);	/* more raw event info */
+
+extern fmevt_pp_func_t fmevt_pp_on_ereport;
+extern fmevt_pp_func_t fmevt_pp_smf;
+extern fmevt_pp_func_t fmevt_pp_on_sunos;
+extern fmevt_pp_func_t fmevt_pp_on_private;
+extern fmevt_pp_func_t fmevt_pp_unregistered;
+
 #ifdef __cplusplus
 }
 #endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fm/modules/common/ext-event-transport/fmevt_inbound.c	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,658 @@
+/*
+ * 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.
+ */
+
+/*
+ * Receive (on GPEC channels) raw events published by a few select producers
+ * using the private libfmevent publication interfaces, and massage those
+ * raw events into full protocol events.  Each raw event selects a "ruleset"
+ * by which to perform the transformation into a protocol event.
+ *
+ * Only publication from userland running privileged is supported; two
+ * channels are used - one for high-value and one for low-value events.
+ * There is some planning in the implementation below for kernel hi and low
+ * value channels, and for non-privileged userland low and hi value channels.
+ */
+
+#include <fm/fmd_api.h>
+#include <fm/libfmevent.h>
+#include <uuid/uuid.h>
+#include <libsysevent.h>
+#include <pthread.h>
+#include <libnvpair.h>
+#include <strings.h>
+#include <zone.h>
+
+#include "fmevt.h"
+
+static struct fmevt_inbound_stats {
+	fmd_stat_t raw_callbacks;
+	fmd_stat_t raw_noattrlist;
+	fmd_stat_t raw_nodetector;
+	fmd_stat_t pp_bad_ruleset;
+	fmd_stat_t pp_explicitdrop;
+	fmd_stat_t pp_fallthrurule;
+	fmd_stat_t pp_fanoutmax;
+	fmd_stat_t pp_intldrop;
+	fmd_stat_t pp_badclass;
+	fmd_stat_t pp_nvlallocfail;
+	fmd_stat_t pp_nvlbuildfail;
+	fmd_stat_t pp_badreturn;
+	fmd_stat_t xprt_posted;
+} inbound_stats = {
+	{ "raw_callbacks", FMD_TYPE_UINT64,
+	    "total raw event callbacks from producers" },
+	{ "raw_noattrlist", FMD_TYPE_UINT64,
+	    "missing attribute list" },
+	{ "raw_nodetector", FMD_TYPE_UINT64,
+	    "unable to add detector" },
+	{ "pp_bad_ruleset", FMD_TYPE_UINT64,
+	    "post-process bad ruleset" },
+	{ "pp_explicitdrop", FMD_TYPE_UINT64,
+	    "ruleset drops event with NULL func" },
+	{ "pp_fanoutmax", FMD_TYPE_UINT64,
+	    "post-processing produced too many events" },
+	{ "pp_intldrop", FMD_TYPE_UINT64,
+	    "post-processing requested event drop" },
+	{ "pp_badclass", FMD_TYPE_UINT64,
+	    "post-processing produced invalid event class" },
+	{ "pp_nvlallocfail", FMD_TYPE_UINT64,
+	    "fmd_nvl_alloc failed" },
+	{ "pp_nvlbuildfail", FMD_TYPE_UINT64,
+	    "nvlist_add_foo failed in building event" },
+	{ "pp_badreturn", FMD_TYPE_UINT64,
+	    "inconsistent number of events returned" },
+	{ "xprt_posted", FMD_TYPE_UINT64,
+	    "protocol events posted with fmd_xprt_post" },
+};
+
+static int isglobalzone;
+static char zonename[ZONENAME_MAX];
+
+#define	BUMPSTAT(stat)	inbound_stats.stat.fmds_value.ui64++
+
+#define	CBF_USER	0x1U
+#define	CBF_PRIV	0x2U
+#define	CBF_LV		0x4U
+#define	CBF_HV		0x8U
+#define	CBF_ALL		(CBF_USER | CBF_PRIV | CBF_LV | CBF_HV)
+
+static struct fmevt_chaninfo {
+	const char *ci_propname;	/* property to get channel name */
+	evchan_t *ci_binding;		/* GPEC binding for this channel */
+	char ci_sid[MAX_SUBID_LEN];	/* subscriber id */
+	uint32_t ci_cbarg;		/* callback cookie */
+	uint32_t ci_sflags;		/* subscription flags to use */
+} chaninfo[] = {
+	{ "user_priv_highval_channel", NULL, { 0 },
+		CBF_USER | CBF_PRIV | CBF_HV, EVCH_SUB_KEEP },
+	{ "user_priv_lowval_channel", NULL, { 0 },
+		CBF_USER | CBF_PRIV | CBF_LV, EVCH_SUB_KEEP },
+};
+
+static pthread_cond_t fmevt_cv = PTHREAD_COND_INITIALIZER;
+static pthread_mutex_t fmevt_lock = PTHREAD_MUTEX_INITIALIZER;
+static int fmevt_exiting;
+
+static fmd_xprt_t *fmevt_xprt;
+static uint32_t fmevt_xprt_refcnt;
+static sysevent_subattr_t *subattr;
+
+/*
+ * Rulesets we recognize and who handles them.  Additions and changes
+ * must follow the Portfolio Review process.  At ths time only
+ * the FMEV_RULESET_ON_SUNOS and FMEVT_RULESET_SMF rulesets are
+ * formally recognized by that process - the others here are experimental.
+ */
+static struct fmevt_rs {
+	char *rs_pat;
+	fmevt_pp_func_t *rs_ppfunc;
+	char *rs_namespace;
+	char *rs_subsys;
+} rulelist[] = {
+	{ FMEV_RULESET_SMF, fmevt_pp_smf },
+	{ FMEV_RULESET_ON_EREPORT, fmevt_pp_on_ereport },
+	{ FMEV_RULESET_ON_SUNOS, fmevt_pp_on_sunos },
+	{ FMEV_RULESET_ON_PRIVATE, fmevt_pp_on_private },
+	{ FMEV_RULESET_UNREGISTERED, fmevt_pp_unregistered }
+};
+
+/*
+ * Take a ruleset specification string and separate it into namespace
+ * and subsystem components.
+ */
+static int
+fmevt_rs_burst(fmd_hdl_t *hdl, char *ruleset, char **nsp, char **subsysp,
+    boolean_t alloc)
+{
+	char *ns, *s;
+	size_t len;
+
+	if (ruleset == NULL || *ruleset == '\0' ||
+	    strnlen(ruleset, FMEV_MAX_RULESET_LEN) == FMEV_MAX_RULESET_LEN)
+		return (0);
+
+	if (alloc == B_FALSE) {
+		s = ruleset;
+		ns = strsep(&s, FMEV_RS_SEPARATOR);
+
+		if (s == NULL || s == ns + 1)
+			return (0);
+	} else {
+		if ((s = strstr(ruleset, FMEV_RS_SEPARATOR)) == NULL ||
+		    s == ruleset + strlen(ruleset) - 1)
+			return (0);
+
+		len = s - ruleset;
+
+		ns = fmd_hdl_alloc(hdl, len + 1, FMD_SLEEP);
+		(void) strncpy(ns, ruleset, len);
+		ns[len] = '\0';
+
+		s++;
+	}
+
+	if (nsp)
+		*nsp = ns;	/* caller must free if alloc == B_TRUE */
+
+	if (subsysp)
+		*subsysp = s;	/* always within original ruleset string */
+
+	return (1);
+}
+
+static int
+fmevt_rs_init(fmd_hdl_t *hdl)
+{
+	int i;
+
+	for (i = 0; i < sizeof (rulelist) / sizeof (rulelist[0]); i++) {
+		struct fmevt_rs *rsp = &rulelist[i];
+
+		if (!fmevt_rs_burst(hdl, rsp->rs_pat, &rsp->rs_namespace,
+		    &rsp->rs_subsys, B_TRUE))
+			return (0);
+	}
+
+	return (1);
+}
+
+/*
+ * Construct a "sw" scheme detector FMRI.
+ *
+ * We make no use of priv or pri.
+ */
+/*ARGSUSED3*/
+static nvlist_t *
+fmevt_detector(nvlist_t *attr, char *ruleset, int user, int priv,
+    fmev_pri_t pri)
+{
+	char buf[FMEV_MAX_RULESET_LEN + 1];
+	char *ns, *subsys;
+	nvlist_t *obj, *dtcr, *site, *ctxt;
+	char *execname = NULL;
+	int32_t i32;
+	int64_t i64;
+	int err = 0;
+	char *str;
+
+	(void) strncpy(buf, ruleset, sizeof (buf));
+	if (!fmevt_rs_burst(NULL, buf, &ns, &subsys, B_FALSE))
+		return (NULL);
+
+	obj = fmd_nvl_alloc(fmevt_hdl, FMD_SLEEP);
+	dtcr = fmd_nvl_alloc(fmevt_hdl, FMD_SLEEP);
+	site = fmd_nvl_alloc(fmevt_hdl, FMD_SLEEP);
+	ctxt = fmd_nvl_alloc(fmevt_hdl, FMD_SLEEP);
+
+	if (obj == NULL || dtcr == NULL || site == NULL || ctxt == NULL) {
+		err++;
+		goto done;
+	}
+
+	/*
+	 * Build up 'object' nvlist.
+	 */
+	if (nvlist_lookup_string(attr, "__fmev_execname", &execname) == 0)
+		err += nvlist_add_string(obj, FM_FMRI_SW_OBJ_PATH, execname);
+
+	/*
+	 * Build up 'site' nvlist.  We should have source file and line
+	 * number and, if the producer was compiled with C99, function name.
+	 */
+	if (nvlist_lookup_string(attr, "__fmev_file", &str) == 0) {
+		err += nvlist_add_string(site, FM_FMRI_SW_SITE_FILE, str);
+		(void) nvlist_remove(attr, "__fmev_file", DATA_TYPE_STRING);
+	}
+
+	if (nvlist_lookup_string(attr, "__fmev_func", &str) == 0) {
+		err += nvlist_add_string(site, FM_FMRI_SW_SITE_FUNC, str);
+		(void) nvlist_remove(attr, "__fmev_func", DATA_TYPE_STRING);
+	}
+
+	if (nvlist_lookup_int64(attr, "__fmev_line", &i64) == 0) {
+		err += nvlist_add_int64(site, FM_FMRI_SW_SITE_LINE, i64);
+		(void) nvlist_remove(attr, "__fmev_line", DATA_TYPE_INT64);
+	}
+
+	/*
+	 * Build up 'context' nvlist.  We do not include contract id at
+	 * this time.
+	 */
+
+	err += nvlist_add_string(ctxt, FM_FMRI_SW_CTXT_ORIGIN,
+	    user ? "userland" : "kernel");
+
+	if (execname) {
+		err += nvlist_add_string(ctxt, FM_FMRI_SW_CTXT_EXECNAME,
+		    execname);
+		(void) nvlist_remove(attr, "__fmev_execname", DATA_TYPE_STRING);
+	}
+
+	if (nvlist_lookup_int32(attr, "__fmev_pid", &i32) == 0) {
+		err += nvlist_add_int32(ctxt, FM_FMRI_SW_CTXT_PID, i32);
+		(void) nvlist_remove(attr, "__fmev_pid", DATA_TYPE_INT32);
+	}
+
+	if (!isglobalzone)
+		err += nvlist_add_string(ctxt, FM_FMRI_SW_CTXT_ZONE, zonename);
+
+	/* Put it all together */
+
+	err += nvlist_add_uint8(dtcr, FM_VERSION, SW_SCHEME_VERSION0);
+	err += nvlist_add_string(dtcr, FM_FMRI_SCHEME, FM_FMRI_SCHEME_SW);
+	err += nvlist_add_nvlist(dtcr, FM_FMRI_SW_OBJ, obj);
+	err += nvlist_add_nvlist(dtcr, FM_FMRI_SW_SITE, site);
+	err += nvlist_add_nvlist(dtcr, FM_FMRI_SW_CTXT, ctxt);
+
+done:
+	if (obj != NULL)
+		nvlist_free(obj);
+	if (site != NULL)
+		nvlist_free(site);
+	if (ctxt != NULL)
+		nvlist_free(ctxt);
+
+	if (err == 0) {
+		return (dtcr);
+	} else {
+		nvlist_free(dtcr);
+		return (NULL);
+	}
+}
+
+static int
+class_ok(char *class)
+{
+	static const char *approved[] = {
+		FM_IREPORT_CLASS ".",
+		FM_EREPORT_CLASS "."
+	};
+
+	int i;
+
+	for (i = 0; i < sizeof (approved) / sizeof (approved[0]); i++) {
+		if (strncmp(class, approved[i], strlen(approved[i])) == 0)
+			return (1);
+	}
+
+	return (0);
+}
+
+static void
+fmevt_postprocess(char *ruleset, nvlist_t *dtcr, nvlist_t *rawattr,
+    struct fmevt_ppargs *eap)
+{
+	uint_t expected = 0, processed = 0;
+	char rs2burst[FMEV_MAX_RULESET_LEN + 1];
+	char *class[FMEVT_FANOUT_MAX];
+	nvlist_t *attr[FMEVT_FANOUT_MAX];
+	fmevt_pp_func_t *dispf = NULL;
+	char buf[FMEV_MAX_CLASS];
+	char *ns, *subsys;
+	int i, found = 0;
+	uuid_t uu;
+
+	(void) strncpy(rs2burst, ruleset, sizeof (rs2burst));
+	if (!fmevt_rs_burst(NULL, rs2burst, &ns, &subsys, B_FALSE)) {
+		BUMPSTAT(pp_bad_ruleset);
+		return;
+	}
+
+	/*
+	 * Lookup a matching rule in our table.
+	 */
+	for (i = 0; i < sizeof (rulelist) / sizeof (rulelist[0]); i++) {
+		struct fmevt_rs *rsp = &rulelist[i];
+
+		if (*ns != '*' && *rsp->rs_namespace != '*' &&
+		    strcmp(ns, rsp->rs_namespace) != 0)
+			continue;
+
+		if (*subsys != '*' && *rsp->rs_subsys != '*' &&
+		    strcmp(subsys, rsp->rs_subsys) != 0)
+			continue;
+
+		dispf = rsp->rs_ppfunc;
+		found = 1;
+		break;
+
+	}
+
+	/*
+	 * If a ruleset matches but specifies a NULL function then
+	 * it's electing to drop the event.  If no rule was matched
+	 * then default to unregistered processing.
+	 */
+	if (dispf == NULL) {
+		if (found) {
+			BUMPSTAT(pp_explicitdrop);
+			return;
+		} else {
+			BUMPSTAT(pp_fallthrurule);
+			dispf = fmevt_pp_unregistered;
+		}
+	}
+
+	/*
+	 * Clear the arrays in which class strings and attribute
+	 * nvlists can be returned.  Pass a pointer to our stack buffer
+	 * that the callee can use for the first event class (for others
+	 * it must fmd_hdl_alloc and we'll free below).  We will free
+	 * and nvlists that are returned.
+	 */
+	bzero(class, sizeof (class));
+	bzero(attr, sizeof (attr));
+	class[0] = buf;
+
+	/*
+	 * Generate an event UUID which will be used for the first
+	 * event generated by post-processing; if post-processing
+	 * fans out into more than one event the additional events
+	 * can reference this uuid (but we don't generate their
+	 * UUIDs until later).
+	 */
+	uuid_generate(uu);
+	uuid_unparse(uu, eap->pp_uuidstr);
+
+	/*
+	 * Call selected post-processing function.  See block comment
+	 * in fmevt.h for a description of this process.
+	 */
+	expected = (*dispf)(class, attr, ruleset,
+	    (const nvlist_t *)dtcr, rawattr,
+	    (const struct fmevt_ppargs *)eap);
+
+	if (expected > FMEVT_FANOUT_MAX) {
+		BUMPSTAT(pp_fanoutmax);
+		return;	/* without freeing class and nvl - could leak */
+	} else if (expected == 0) {
+		BUMPSTAT(pp_intldrop);
+		return;
+	}
+
+	/*
+	 * Post as many events as the callback completed.
+	 */
+	for (i = 0; i < FMEVT_FANOUT_MAX; i++) {
+		char uuidstr[36 + 1];
+		char *uuidstrp;
+		nvlist_t *nvl;
+		int err = 0;
+
+		if (class[i] == NULL)
+			continue;
+
+		if (!class_ok(class[i])) {
+			BUMPSTAT(pp_badclass);
+			continue;
+		}
+
+		if (processed++ == 0) {
+			uuidstrp = eap->pp_uuidstr;
+		} else {
+			uuid_generate(uu);
+			uuid_unparse(uu, uuidstr);
+			uuidstrp = uuidstr;
+		}
+
+		if ((nvl = fmd_nvl_alloc(fmevt_hdl, FMD_SLEEP)) == NULL) {
+			BUMPSTAT(pp_nvlallocfail);
+			continue;
+		}
+
+		err += nvlist_add_uint8(nvl, FM_VERSION, 0);
+		err += nvlist_add_string(nvl, FM_CLASS, (const char *)class[i]);
+		err += nvlist_add_string(nvl, FM_IREPORT_UUID, uuidstrp);
+		err += nvlist_add_nvlist(nvl, FM_IREPORT_DETECTOR, dtcr);
+		err += nvlist_add_string(nvl, FM_IREPORT_PRIORITY,
+		    fmev_pri_string(eap->pp_pri) ?
+		    fmev_pri_string(eap->pp_pri) : "?");
+
+		if (attr[i] != NULL)
+			err += nvlist_add_nvlist(nvl, FM_IREPORT_ATTRIBUTES,
+			    attr[i]);
+
+		/*
+		 * If we post the event into fmd_xport_post then the
+		 * transport code is responsible for freeing the nvl we
+		 * posted.
+		 */
+		if (err == 0) {
+			fmd_xprt_post(fmevt_hdl, fmevt_xprt, nvl,
+			    eap->pp_hrt);
+		} else {
+			BUMPSTAT(pp_nvlbuildfail);
+			nvlist_free(nvl);
+		}
+	}
+
+	if (processed != expected)
+		BUMPSTAT(pp_badreturn);
+
+	for (i = 0; i < FMEVT_FANOUT_MAX; i++) {
+		/*
+		 * We provided storage for class[0] but any
+		 * additional events have allocated a string.
+		 */
+		if (i > 0 && class[i] != NULL)
+			fmd_hdl_strfree(fmevt_hdl, class[i]);
+
+		/*
+		 * Free all attribute lists passed in if they are not
+		 * just a pointer to the raw attributes
+		 */
+		if (attr[i] != NULL && attr[i] != rawattr)
+			nvlist_free(attr[i]);
+	}
+}
+
+static int
+fmevt_cb(sysevent_t *sep, void *arg)
+{
+	char *ruleset = NULL, *rawclass, *rawsubclass;
+	uint32_t cbarg = (uint32_t)arg;
+	nvlist_t *rawattr = NULL;
+	struct fmevt_ppargs ea;
+	nvlist_t *dtcr;
+	int user, priv;
+	fmev_pri_t pri;
+
+	BUMPSTAT(raw_callbacks);
+
+	if (cbarg & ~CBF_ALL)
+		fmd_hdl_abort(fmevt_hdl, "event receipt callback with "
+		    "invalid flags\n");
+
+	user = (cbarg & CBF_USER) != 0;
+	priv = (cbarg & CBF_PRIV) != 0;
+	pri = (cbarg & CBF_HV ? FMEV_HIPRI : FMEV_LOPRI);
+
+	(void) pthread_mutex_lock(&fmevt_lock);
+
+	if (fmevt_exiting) {
+		while (fmevt_xprt_refcnt > 0)
+			(void) pthread_cond_wait(&fmevt_cv, &fmevt_lock);
+		(void) pthread_mutex_unlock(&fmevt_lock);
+		return (0);	/* discard event */
+	}
+
+	fmevt_xprt_refcnt++;
+	(void) pthread_mutex_unlock(&fmevt_lock);
+
+	ruleset = sysevent_get_vendor_name(sep);	/* must free */
+	rawclass = sysevent_get_class_name(sep);	/* valid with sep */
+	rawsubclass = sysevent_get_subclass_name(sep);	/* valid with sep */
+
+	if (sysevent_get_attr_list(sep, &rawattr) != 0) {
+		BUMPSTAT(raw_noattrlist);
+		goto done;
+	}
+
+	if ((dtcr = fmevt_detector(rawattr, ruleset, user, priv,
+	    pri)) == NULL) {
+		BUMPSTAT(raw_nodetector);
+		goto done;
+	}
+
+	ea.pp_rawclass = rawclass;
+	ea.pp_rawsubclass = rawsubclass;
+	sysevent_get_time(sep, &ea.pp_hrt);
+	ea.pp_user = user;
+	ea.pp_priv = priv;
+	ea.pp_pri = pri;
+
+	fmevt_postprocess(ruleset, dtcr, rawattr, &ea);
+	nvlist_free(dtcr);
+done:
+	(void) pthread_mutex_lock(&fmevt_lock);
+
+	if (--fmevt_xprt_refcnt == 0 && fmevt_exiting)
+		(void) pthread_cond_broadcast(&fmevt_cv);
+
+	(void) pthread_mutex_unlock(&fmevt_lock);
+
+	if (ruleset)
+		free(ruleset);
+
+	if (rawattr)
+		nvlist_free(rawattr);
+
+	return (0);	/* in all cases consider the event delivered */
+}
+
+void
+fmevt_init_inbound(fmd_hdl_t *hdl)
+{
+	char *sidpfx;
+	zoneid_t zoneid;
+	int i;
+
+	if (!fmevt_rs_init(hdl))
+		fmd_hdl_abort(hdl, "error in fmevt_rs_init\n");
+
+	(void) fmd_stat_create(hdl, FMD_STAT_NOALLOC, sizeof (inbound_stats) /
+	    sizeof (fmd_stat_t), (fmd_stat_t *)&inbound_stats);
+
+	zoneid = getzoneid();
+	isglobalzone = (zoneid == GLOBAL_ZONEID);
+	if (getzonenamebyid(zoneid, zonename, sizeof (zonename)) == -1)
+		fmd_hdl_abort(hdl, "getzonenamebyid failed");
+
+	if ((subattr = sysevent_subattr_alloc()) == NULL)
+		fmd_hdl_abort(hdl, "failed to allocate subscription "
+		    "attributes: %s");
+
+	sysevent_subattr_thrcreate(subattr, fmd_doorthr_create, NULL);
+	sysevent_subattr_thrsetup(subattr, fmd_doorthr_setup, NULL);
+
+	sidpfx = fmd_prop_get_string(hdl, "sidprefix");
+	fmevt_xprt = fmd_xprt_open(hdl, FMD_XPRT_RDONLY, NULL, NULL);
+
+	for (i = 0; i < sizeof (chaninfo) / sizeof (chaninfo[0]); i++) {
+		struct fmevt_chaninfo *cip = &chaninfo[i];
+		char *channel = fmd_prop_get_string(hdl, cip->ci_propname);
+		int err;
+
+		if (sysevent_evc_bind(channel, &cip->ci_binding,
+		    EVCH_CREAT | EVCH_HOLD_PEND_INDEF) != 0)
+			fmd_hdl_abort(hdl, "failed to bind GPEC channel for "
+			    "channel %s", channel);
+
+		(void) snprintf(cip->ci_sid, sizeof (cip->ci_sid),
+		    "%s_%c%c%c", sidpfx,
+		    cip->ci_cbarg & CBF_USER ? 'u' : 'k',
+		    cip->ci_cbarg & CBF_PRIV ? 'p' : 'n',
+		    cip->ci_cbarg & CBF_HV ? 'h' : 'l');
+
+		err = sysevent_evc_xsubscribe(cip->ci_binding, cip->ci_sid,
+		    EC_ALL, fmevt_cb, (void *)cip->ci_cbarg,
+		    cip->ci_sflags, subattr);
+
+		if (err == EEXIST)
+			fmd_hdl_abort(hdl, "another fmd is active on "
+			    "channel %s\n", channel);
+		else if (err != 0)
+			fmd_hdl_abort(hdl, "failed to subscribe to channel %s",
+			    channel);
+
+		fmd_prop_free_string(hdl, channel);
+	}
+
+	fmd_prop_free_string(hdl, sidpfx);
+}
+
+void
+fmevt_fini_inbound(fmd_hdl_t *hdl)
+{
+	int i;
+
+	for (i = 0; i < sizeof (chaninfo) / sizeof (chaninfo[0]); i++) {
+		struct fmevt_chaninfo *cip = &chaninfo[i];
+
+		if (cip->ci_binding) {
+			(void) sysevent_evc_unsubscribe(cip->ci_binding,
+			    cip->ci_sid);
+			(void) sysevent_evc_unbind(cip->ci_binding);
+			cip->ci_binding = NULL;
+		}
+	}
+
+	if (subattr) {
+		sysevent_subattr_free(subattr);
+		subattr = NULL;
+	}
+
+	if (fmevt_xprt) {
+		/* drain before destruction */
+		(void) pthread_mutex_lock(&fmevt_lock);
+		fmevt_exiting = 1;
+		while (fmevt_xprt_refcnt > 0)
+			(void) pthread_cond_wait(&fmevt_cv, &fmevt_lock);
+		(void) pthread_mutex_unlock(&fmevt_lock);
+
+		fmd_xprt_close(hdl, fmevt_xprt);
+	}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fm/modules/common/ext-event-transport/fmevt_inbound_default.c	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,52 @@
+/*
+ * 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 "fmevt.h"
+
+/*
+ * Post-processing according to the FMEV_RULESET_UNREGISTERED ruleset.
+ *
+ * Generate a class of ireport.unregistered.<raw-class>.<raw-subclass>
+ * with attributes just those of the raw event.  If either the raw
+ * class or subclass is NULL or empty then drop the event.
+ */
+/*ARGSUSED*/
+uint_t
+fmevt_pp_unregistered(char *classes[FMEVT_FANOUT_MAX],
+    nvlist_t *attr[FMEVT_FANOUT_MAX], const char *ruleset,
+    const nvlist_t *detector, nvlist_t *rawattr,
+    const struct fmevt_ppargs *eap)
+{
+	if (eap->pp_rawclass == NULL || eap->pp_rawsubclass == NULL)
+		return (0);
+
+	if (snprintf(classes[0], FMEVT_MAX_CLASS, "%s.%s.%s.%s",
+	    FM_IREPORT_CLASS, "unregistered",
+	    eap->pp_rawclass, eap->pp_rawsubclass) >= FMEVT_MAX_CLASS - 1)
+		return (0);
+
+	attr[0] = rawattr;
+	return (1);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fm/modules/common/ext-event-transport/fmevt_inbound_on.c	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,76 @@
+/*
+ * 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 "fmevt.h"
+
+/*
+ * Post-processing according to the FMEV_RULESET_ON_EREPORT ruleset.
+ * Produce a single event of class ereport.<raw-class>.<raw_subclass>.
+ * If either the raw class or subclass is NULL, drop the event.
+ * The event will have the raw attributes.
+ */
+/*ARGSUSED*/
+uint_t
+fmevt_pp_on_ereport(char *classes[FMEVT_FANOUT_MAX],
+    nvlist_t *attr[FMEVT_FANOUT_MAX], const char *ruleset,
+    const nvlist_t *detector, nvlist_t *rawattr,
+    const struct fmevt_ppargs *eap)
+{
+	if (eap->pp_rawclass == NULL || eap->pp_rawsubclass == NULL)
+		return (0);
+
+	if (snprintf(classes[0], FMEVT_MAX_CLASS, "%s.%s.%s", FM_EREPORT_CLASS,
+	    eap->pp_rawclass, eap->pp_rawsubclass) >= FMEVT_MAX_CLASS - 1)
+		return (0);
+
+	attr[0] = rawattr;
+	return (1);
+}
+
+/*
+ * Post-processing according to the FMEV_RULESET_ON_PRIVATE ruleset.
+ * Produce a single event of class
+ * ireport.private.solaris-osnet.<raw-class>.<raw_subclass>.
+ * If either the raw class or subclass is NULL, drop the event.
+ * The event will have the raw attributes.
+ */
+/*ARGSUSED*/
+uint_t
+fmevt_pp_on_private(char *classes[FMEVT_FANOUT_MAX],
+    nvlist_t *attr[FMEVT_FANOUT_MAX], const char *ruleset,
+    const nvlist_t *detector, nvlist_t *rawattr,
+    const struct fmevt_ppargs *eap)
+{
+	if (eap->pp_rawclass == NULL || eap->pp_rawsubclass == NULL)
+		return (0);
+
+	if (snprintf(classes[0], FMEVT_MAX_CLASS, "%s.%s.%s.%s",
+	    FM_IREPORT_CLASS, "private.solaris-osnet",
+	    eap->pp_rawclass, eap->pp_rawsubclass) >= FMEVT_MAX_CLASS - 1)
+		return (0);
+
+	attr[0] = rawattr;
+	return (1);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fm/modules/common/ext-event-transport/fmevt_inbound_smf.c	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,194 @@
+/*
+ * 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 <strings.h>
+#include <libscf.h>
+#include <fm/fmd_api.h>
+#include <fm/libtopo.h>
+#include <fm/libfmevent.h>
+
+#include "fmevt.h"
+
+/*
+ * Post-processing according to the FMEV_RULESET_SMF ruleset.
+ *
+ * Raw event we expect:
+ *
+ * ==========================================================================
+ * Class: "state-transition"
+ * Subclasses: The new state, one of SCF_STATE_STRING_* from libscf.h
+ * Attr:
+ * Name		DATA_TYPE_*	Description
+ * ------------ --------------- ---------------------------------------------
+ * fmri		STRING		svc:/... (svc scheme shorthand version)
+ * transition	INT32		(old_state << 16) | new_state
+ * reason-version UINT32	reason-short namespace version
+ * reason-short	STRING		Short/keyword reason for transition
+ * reason-long	STRING		Long-winded reason for the transition
+ * ==========================================================================
+ *
+ * Protocol event components we return:
+ *
+ * ==========================================================================
+ * Class: ireport.os.smf.state-transition.<new-state>
+ * Attr:
+ * Name		DATA_TYPE_*	Description
+ * ------------ --------------- ----------------------------------------
+ * svc		NVLIST		"svc" scheme FMRI of affected service instance
+ * svc-string	STRING		SMF FMRI in short string form svc:/foo/bar
+ * from-state	STRING		Previous state; SCF_STATE_STRING_*
+ * to-state	STRING		New state; SCF_STATE_STRING_*
+ * reason-version UINT32	reason-short namespace version
+ * reason-short	STRING		Short/keyword reason for transition
+ * reason-long	STRING		Long-winded reason for the transition
+ * ==========================================================================
+ */
+
+/*
+ * svc.startd generates events using the FMRI shorthand (svc:/foo/bar)
+ * instead of the standard form (svc:///foo/bar).  This function converts to
+ * the standard representation.  The caller must free the allocated string.
+ */
+static char *
+shortfmri_to_fmristr(fmd_hdl_t *hdl, const char *shortfmristr)
+{
+	size_t len;
+	char *fmristr;
+
+	if (strncmp(shortfmristr, "svc:/", 5) != 0)
+		return (NULL);
+
+	len = strlen(shortfmristr) + 3;
+	fmristr = fmd_hdl_alloc(hdl, len, FMD_SLEEP);
+	(void) snprintf(fmristr, len, "svc:///%s", shortfmristr + 5);
+
+	return (fmristr);
+}
+
+/*
+ * Convert a shorthand svc FMRI into a full svc FMRI nvlist
+ */
+static nvlist_t *
+shortfmri_to_fmri(fmd_hdl_t *hdl, const char *shortfmristr)
+{
+	nvlist_t *ret, *fmri;
+	topo_hdl_t *thp;
+	char *fmristr;
+	int err;
+
+	if ((fmristr = shortfmri_to_fmristr(hdl, shortfmristr)) == NULL)
+		return (NULL);
+
+	thp = fmd_hdl_topo_hold(hdl, TOPO_VERSION);
+
+	if (topo_fmri_str2nvl(thp, fmristr, &fmri, &err) != 0) {
+		fmd_hdl_error(hdl, "failed to convert '%s' to nvlist\n",
+		    fmristr);
+		fmd_hdl_strfree(hdl, fmristr);
+		fmd_hdl_topo_rele(hdl, thp);
+		return (NULL);
+	}
+
+	fmd_hdl_strfree(hdl, fmristr);
+
+	if ((ret = fmd_nvl_dup(hdl, fmri, FMD_SLEEP)) == NULL) {
+		fmd_hdl_error(hdl, "failed to dup fmri\n");
+		nvlist_free(fmri);
+		fmd_hdl_topo_rele(hdl, thp);
+		return (NULL);
+	}
+
+	nvlist_free(fmri);
+	fmd_hdl_topo_rele(hdl, thp);
+
+	return (ret);
+}
+
+/*ARGSUSED*/
+uint_t
+fmevt_pp_smf(char *classes[FMEVT_FANOUT_MAX],
+    nvlist_t *attr[FMEVT_FANOUT_MAX], const char *ruleset,
+    const nvlist_t *detector, nvlist_t *rawattr,
+    const struct fmevt_ppargs *eap)
+{
+	int32_t transition, from, to;
+	const char *fromstr, *tostr;
+	char *svcname, *rsn, *rsnl;
+	nvlist_t *myattr;
+	nvlist_t *fmri;
+	uint32_t ver;
+
+	if (!fmd_prop_get_int32(fmevt_hdl, "inbound_postprocess_smf"))
+		return (0);
+
+	if (rawattr == NULL ||
+	    strcmp(eap->pp_rawclass, "state-transition") != 0 ||
+	    nvlist_lookup_string(rawattr, "fmri", &svcname) != 0 ||
+	    nvlist_lookup_int32(rawattr, "transition", &transition) != 0 ||
+	    nvlist_lookup_string(rawattr, "reason-short", &rsn) != 0 ||
+	    nvlist_lookup_string(rawattr, "reason-long", &rsnl) != 0 ||
+	    nvlist_lookup_uint32(rawattr, "reason-version", &ver) != 0)
+		return (0);
+
+	from = transition >> 16;
+	to = transition & 0xffff;
+
+	fromstr = smf_state_to_string(from);
+	tostr = smf_state_to_string(to);
+
+	if (fromstr == NULL || tostr == NULL)
+		return (0);
+
+	if (strcmp(eap->pp_rawsubclass, tostr) != 0)
+		return (0);
+
+	if ((fmri = shortfmri_to_fmri(fmevt_hdl, svcname)) == NULL)
+		return (0);
+
+	if (snprintf(classes[0], FMEVT_MAX_CLASS, "%s.%s.%s.%s",
+	    FM_IREPORT_CLASS, "os.smf", eap->pp_rawclass,
+	    eap->pp_rawsubclass) >= FMEVT_MAX_CLASS - 1)
+		return (0);
+
+	if ((myattr = fmd_nvl_alloc(fmevt_hdl, FMD_SLEEP)) == NULL)
+		return (0);
+
+	if (nvlist_add_nvlist(myattr, "svc", fmri) != 0 ||
+	    nvlist_add_string(myattr, "svc-string", svcname) != 0 ||
+	    nvlist_add_string(myattr, "from-state", fromstr) != 0 ||
+	    nvlist_add_string(myattr, "to-state", tostr) != 0 ||
+	    nvlist_add_uint32(myattr, "reason-version", ver) != 0 ||
+	    nvlist_add_string(myattr, "reason-short", rsn) != 0 ||
+	    nvlist_add_string(myattr, "reason-long", rsnl) != 0) {
+		nvlist_free(fmri);
+		nvlist_free(myattr);
+		return (0);
+	}
+
+	attr[0] = myattr;
+	nvlist_free(fmri);
+
+	return (1);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fm/modules/common/ext-event-transport/fmevt_inbound_sunos.c	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,88 @@
+/*
+ * 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 <string.h>
+#include "fmevt.h"
+
+/*
+ * Support for the FMEV_RULESET_ON_SUNOS ruleset.
+ */
+
+/*
+ * Panic events.
+ */
+
+/*ARGSUSED*/
+static int
+pp_sunos_panic(char *classes[FMEVT_FANOUT_MAX],
+    nvlist_t *attr[FMEVT_FANOUT_MAX], const char *ruleset,
+    const nvlist_t *detector, nvlist_t *rawattr,
+    const struct fmevt_ppargs *eap)
+
+{
+	nvlist_t *myattr;
+	time_t panictime32;
+	int64_t panictime;
+	char buf[128];
+	struct tm ts;
+
+	if (strcmp(eap->pp_rawsubclass, "dump_pending_on_device") != 0 &&
+	    strcmp(eap->pp_rawsubclass, "savecore_failure") != 0 &&
+	    strcmp(eap->pp_rawsubclass, "dump_available") != 0)
+		return (0);
+
+	if (snprintf(classes[0], FMEVT_MAX_CLASS, "%s.%s.%s", FM_IREPORT_CLASS,
+	    "os.sunos.panic", eap->pp_rawsubclass) >= FMEVT_MAX_CLASS - 1)
+		return (0);
+
+	if (nvlist_lookup_int64(rawattr, "crashtime", &panictime) != 0)
+		return (0);
+
+	panictime32 = (time_t)panictime;
+
+	myattr = fmd_nvl_dup(fmevt_hdl, rawattr, FMD_SLEEP);
+
+	if (localtime_r(&panictime32, &ts) != NULL &&
+	    strftime(buf, sizeof (buf), "%c %Z", &ts) != 0)
+		(void) nvlist_add_string(myattr, "panic-time", buf);
+
+	attr[0] = myattr;
+	return (1);
+}
+
+
+/*ARGSUSED*/
+uint_t
+fmevt_pp_on_sunos(char *classes[FMEVT_FANOUT_MAX],
+    nvlist_t *attr[FMEVT_FANOUT_MAX], const char *ruleset,
+    const nvlist_t *detector, nvlist_t *rawattr,
+    const struct fmevt_ppargs *eap)
+{
+	if (strcmp(eap->pp_rawclass, "panic") == 0)
+		return (pp_sunos_panic(classes, attr, ruleset,
+		    detector, rawattr, eap));
+
+	return (0);
+}
--- a/usr/src/cmd/fm/modules/common/ext-event-transport/fmevt_main.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/fm/modules/common/ext-event-transport/fmevt_main.c	Fri Jul 30 17:04:17 2010 +1000
@@ -20,8 +20,7 @@
  */
 
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #include <sys/types.h>
@@ -32,6 +31,12 @@
 	{ "protocol_forward_disable", FMD_TYPE_BOOL, "false" },
 	{ "outbound_channel", FMD_TYPE_STRING, FMD_SNOOP_CHANNEL },
 	{ "outbound_channel_depth", FMD_TYPE_INT32, "256" },
+	{ "user_priv_highval_channel", FMD_TYPE_STRING,
+	    FMEV_CHAN_USER_PRIV_HV },
+	{ "user_priv_lowval_channel", FMD_TYPE_STRING,
+	    FMEV_CHAN_USER_PRIV_LV },
+	{ "sidprefix", FMD_TYPE_STRING, "fmd" },
+	{ "inbound_postprocess_smf", FMD_TYPE_BOOL, "true" },
 	{ NULL, 0, NULL },
 };
 
@@ -46,9 +51,11 @@
 };
 
 static const fmd_hdl_info_t fmd_info = {
-	"External FM event transport", "0.1", &fmd_ops, fmevt_props
+	"External FM event transport", "0.2", &fmd_ops, fmevt_props
 };
 
+fmd_hdl_t *fmevt_hdl;
+
 void
 _fmd_init(fmd_hdl_t *hdl)
 {
@@ -60,11 +67,15 @@
 	if (fmd_hdl_register(hdl, FMD_API_VERSION, &fmd_info) != 0)
 		return;
 
+	fmevt_hdl = hdl;
+
 	fmevt_init_outbound(hdl);
+	fmevt_init_inbound(hdl);
 }
 
 void
 _fmd_fini(fmd_hdl_t *hdl)
 {
 	fmevt_fini_outbound(hdl);
+	fmevt_fini_inbound(hdl);
 }
--- a/usr/src/cmd/fm/modules/common/ext-event-transport/fmevt_outbound.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/fm/modules/common/ext-event-transport/fmevt_outbound.c	Fri Jul 30 17:04:17 2010 +1000
@@ -20,8 +20,7 @@
  */
 
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #include <sys/types.h>
@@ -38,7 +37,7 @@
 static struct fmevt_outbound_stats {
 	fmd_stat_t recv_calls;
 	fmd_stat_t recv_list;
-	fmd_stat_t recv_swevent;
+	fmd_stat_t recv_ireport;
 	fmd_stat_t recv_other;
 	fmd_stat_t fwd_success;
 	fmd_stat_t fwd_failure;
@@ -47,8 +46,8 @@
 	    "total events received for forwarding" },
 	{ "outbound_cat1class_list", FMD_TYPE_UINT64,
 	    "events received matching list.*" },
-	{ "outbound_cat1class_swevent", FMD_TYPE_UINT64,
-	    "events received matching swevent.*" },
+	{ "outbound_cat1class_ireport", FMD_TYPE_UINT64,
+	    "events received matching ireport.*" },
 	{ "outbound_cat1class_other", FMD_TYPE_UINT64,
 	    "events of other classes" },
 	{ "outbound_fwd_success", FMD_TYPE_UINT64,
@@ -60,7 +59,7 @@
 #define	BUMPSTAT(stat)	outbound_stats.stat.fmds_value.ui64++
 
 /*
- * In the .conf file we subscribe to list.* and swevent.* event classes.
+ * In the .conf file we subscribe to list.* and ireport.* event classes.
  * Any additions to that set could cause some unexpected behaviour.
  * For example adding fault.foo won't work (since we don't publish
  * faults directly but only within a list.suspect) but we will get
@@ -74,8 +73,8 @@
 
 	if (strncmp(class, "list.", 5) == 0)
 		BUMPSTAT(recv_list);
-	else if (strncmp(class, "swevent.", 8) == 0)
-		BUMPSTAT(recv_swevent);
+	else if (strncmp(class, "ireport.", 8) == 0)
+		BUMPSTAT(recv_ireport);
 	else
 		BUMPSTAT(recv_other);
 
@@ -93,6 +92,7 @@
 {
 	int32_t channel_depth;
 	char *channel_name;
+	nvlist_t *nvl;
 
 	if (fmd_prop_get_int32(hdl, "protocol_forward_disable") == B_TRUE) {
 		fmd_hdl_debug(hdl, "protocol forwarding disabled "
@@ -122,8 +122,14 @@
 		fmd_hdl_abort(hdl, "Unable to set depth of channel %s to %d",
 		    channel_name, channel_depth);
 	}
+	fmd_prop_free_string(hdl, channel_name);
 
-	fmd_prop_free_string(hdl, channel_name);
+	nvl = fmd_nvl_alloc(hdl, FMD_SLEEP);
+	(void) nvlist_add_nvlist(nvl, "fmdauth",
+	    (nvlist_t *)fmd_hdl_fmauth(hdl));
+	(void) sysevent_evc_setpropnvl(fmevt_outbound_chan, nvl);
+	nvlist_free(nvl);
+
 }
 
 /*ARGSUSED*/
--- a/usr/src/cmd/fm/modules/common/sensor-transport/sensor_transport.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/fm/modules/common/sensor-transport/sensor_transport.c	Fri Jul 30 17:04:17 2010 +1000
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #include <fm/fmd_api.h>
@@ -71,6 +70,9 @@
 	{ "repairs", FMD_TYPE_UINT64, "auto repairs" }
 };
 
+static int st_check_component_complaints;
+static int have_complained;
+
 static int
 st_check_component(topo_hdl_t *thp, tnode_t *node, void *arg)
 {
@@ -117,9 +119,12 @@
 	if (topo_method_invoke(node, TOPO_METH_SENSOR_FAILURE,
 	    TOPO_METH_SENSOR_FAILURE_VERSION, NULL, &nvl, &err) != 0) {
 		if (err == ETOPO_METHOD_NOTSUP) {
-			fmd_hdl_debug(hdl, "Method %s not supported on %s=%d",
-			    TOPO_METH_SENSOR_FAILURE, name,
-			    topo_node_instance(node));
+			st_check_component_complaints++;
+			if (!have_complained) {
+				fmd_hdl_debug(hdl, "Method %s not supported "
+				    "on %s=%d", TOPO_METH_SENSOR_FAILURE, name,
+				    topo_node_instance(node));
+			}
 			nvlist_free(rsrc);
 			return (0);
 		}
@@ -271,6 +276,8 @@
 	return (0);
 }
 
+int st_timeout_verbose = 0;
+
 /*ARGSUSED*/
 static void
 st_timeout(fmd_hdl_t *hdl, id_t id, void *data)
@@ -281,7 +288,8 @@
 	topo_walk_t *twp;
 	int err;
 
-	fmd_hdl_debug(hdl, "timeout: checking topology");
+	if (st_timeout_verbose)
+		fmd_hdl_debug(hdl, "timeout: checking topology");
 
 	stp = fmd_hdl_getspecific(hdl);
 	thp = fmd_hdl_topo_hold(hdl, TOPO_VERSION);
@@ -295,6 +303,9 @@
 		return;
 	}
 
+	if (st_check_component_complaints)
+		have_complained++;
+
 	/*
 	 * Initialize values in our internal FRU list for this iteration of
 	 * sensor reads.  Keep track of whether the FRU was faulted in the
--- a/usr/src/cmd/fm/modules/common/snmp-trapgen/Makefile	Thu Jul 29 22:45:58 2010 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,50 +0,0 @@
-#
-# 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 2009 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
-#
-#
-
-MODULE = snmp-trapgen
-CLASS = common
-SRCS = snmp.c
-NETSNMPCONFS = fmd-trapgen.conf
-
-ROOTNETSNMPDIR = $(ROOT)/etc/net-snmp/snmp
-ROOTNETSNMPCONFS = $(NETSNMPCONFS:%=$(ROOTNETSNMPDIR)/%)
-
-include ../../Makefile.plugin
-
-SNMPLIBS = -lnetsnmp -lnetsnmpagent -lfmd_msg
-lint := SNMPLIBS =
-
-LDFLAGS += -L$(ROOT)/usr/lib/fm -R/usr/lib/fm
-LINTFLAGS += -L$(ROOT)/usr/lib/fm
-LDLIBS += $(SNMPLIBS) -lfmd_msg
-
-$(ROOTNETSNMPCONFS) := FILEMODE = 0600
-
-$(ROOTNETSNMPDIR)/%: %
-	$(INS.file)
-
-install: $(ROOTNETSNMPCONFS)
--- a/usr/src/cmd/fm/modules/common/snmp-trapgen/fmd-trapgen.conf	Thu Jul 29 22:45:58 2010 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,33 +0,0 @@
-#
-# 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 2009 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
-#
-#
-
-#
-# Supplemental configuration for the snmp-trapgen FMA module.  Normal
-# configuration of this module should be made via trap sink directives
-# in /etc/net-snmp/snmp/snmpd.conf, not in this file.  See snmpd(4) and
-# snmp_config(4).
-#
--- a/usr/src/cmd/fm/modules/common/snmp-trapgen/snmp-trapgen.conf	Thu Jul 29 22:45:58 2010 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,31 +0,0 @@
-#
-# 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 2006 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
-#
-# ident	"%Z%%M%	%I%	%E% SMI"
-#
-
-#
-# snmp-trapgen agent properties:
-#
--- a/usr/src/cmd/fm/modules/common/snmp-trapgen/snmp.c	Thu Jul 29 22:45:58 2010 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,286 +0,0 @@
-/*
- * 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 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
- */
-
-#include <sys/fm/protocol.h>
-#include <fm/fmd_api.h>
-#include <fm/fmd_snmp.h>
-#include <fm/fmd_msg.h>
-#include <net-snmp/net-snmp-config.h>
-#include <net-snmp/net-snmp-includes.h>
-#include <net-snmp/agent/net-snmp-agent-includes.h>
-#include <locale.h>
-#include <strings.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <limits.h>
-#include <alloca.h>
-
-static struct stats {
-	fmd_stat_t bad_vers;
-	fmd_stat_t bad_code;
-	fmd_stat_t bad_uuid;
-	fmd_stat_t no_trap;
-} snmp_stats = {
-	{ "bad_vers", FMD_TYPE_UINT64, "event version is missing or invalid" },
-	{ "bad_code", FMD_TYPE_UINT64, "failed to compute url for code" },
-	{ "bad_uuid", FMD_TYPE_UINT64, "event uuid is too long to send" },
-	{ "no_trap", FMD_TYPE_UINT64, "trap generation suppressed" }
-};
-
-static fmd_msg_hdl_t *snmp_msghdl;	/* handle for libfmd_msg */
-static int snmp_trapall;		/* set to trap on all faults */
-
-static const char SNMP_SUPPCONF[] = "fmd-trapgen";
-
-/*ARGSUSED*/
-static void
-send_trap(fmd_hdl_t *hdl, const char *uuid, const char *code, const char *url)
-{
-	static const oid sunFmProblemTrap_oid[] = { SUNFMPROBLEMTRAP_OID };
-	const size_t sunFmProblemTrap_len = OID_LENGTH(sunFmProblemTrap_oid);
-
-	static const oid sunFmProblemUUID_oid[] =
-	    { SUNFMPROBLEMTABLE_OID, 1, SUNFMPROBLEM_COL_UUID };
-	static const oid sunFmProblemCode_oid[] =
-	    { SUNFMPROBLEMTABLE_OID, 1, SUNFMPROBLEM_COL_CODE };
-	static const oid sunFmProblemURL_oid[] =
-	    { SUNFMPROBLEMTABLE_OID, 1, SUNFMPROBLEM_COL_URL };
-
-	const size_t sunFmProblem_base_len = OID_LENGTH(sunFmProblemUUID_oid);
-
-	size_t uuid_len = strlen(uuid);
-	size_t var_len = sunFmProblem_base_len + 1 + uuid_len;
-	oid var_name[MAX_OID_LEN];
-	int i;
-
-	netsnmp_variable_list *notification_vars = NULL;
-
-	/*
-	 * The format of our trap varbinds' oids is as follows:
-	 *
-	 * +-----------------------+---+--------+----------+------+
-	 * | SUNFMPROBLEMTABLE_OID | 1 | column | uuid_len | uuid |
-	 * +-----------------------+---+--------+----------+------+
-	 *					 \---- index ----/
-	 *
-	 * A common mistake here is to send the trap with varbinds that
-	 * do not contain the index.  All the indices are the same, and
-	 * all the oids are the same length, so the only thing we need to
-	 * do for each varbind is set the table and column parts of the
-	 * variable name.
-	 */
-
-	if (var_len > MAX_OID_LEN) {
-		snmp_stats.bad_uuid.fmds_value.ui64++;
-		return;
-	}
-
-	var_name[sunFmProblem_base_len] = (oid)uuid_len;
-	for (i = 0; i < uuid_len; i++)
-		var_name[i + sunFmProblem_base_len + 1] = (oid)uuid[i];
-
-	/*
-	 * Ordinarily, we would need to add the OID of the trap itself
-	 * to the head of the variable list; this is required by SNMP v2.
-	 * However, send_enterprise_trap_vars does this for us as a part
-	 * of converting between v1 and v2 traps, so we skip directly to
-	 * the objects we're sending.
-	 */
-
-	(void) memcpy(var_name, sunFmProblemUUID_oid,
-	    sunFmProblem_base_len * sizeof (oid));
-	(void) snmp_varlist_add_variable(&notification_vars, var_name, var_len,
-	    ASN_OCTET_STR, (uchar_t *)uuid, strlen(uuid));
-	(void) memcpy(var_name, sunFmProblemCode_oid,
-	    sunFmProblem_base_len * sizeof (oid));
-	(void) snmp_varlist_add_variable(&notification_vars, var_name, var_len,
-	    ASN_OCTET_STR, (uchar_t *)code, strlen(code));
-	(void) memcpy(var_name, sunFmProblemURL_oid,
-	    sunFmProblem_base_len * sizeof (oid));
-	(void) snmp_varlist_add_variable(&notification_vars, var_name, var_len,
-	    ASN_OCTET_STR, (uchar_t *)url, strlen(url));
-
-	/*
-	 * This function is capable of sending both v1 and v2/v3 traps.
-	 * Which is sent to a specific destination is determined by the
-	 * configuration file(s).
-	 */
-	send_enterprise_trap_vars(SNMP_TRAP_ENTERPRISESPECIFIC,
-	    sunFmProblemTrap_oid[sunFmProblemTrap_len - 1],
-	    (oid *)sunFmProblemTrap_oid, sunFmProblemTrap_len - 2,
-	    notification_vars);
-
-	snmp_free_varbind(notification_vars);
-}
-
-/*ARGSUSED*/
-static void
-snmp_recv(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl, const char *class)
-{
-	char *uuid, *code, *url;
-	boolean_t domsg;
-	uint8_t version;
-
-	if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 ||
-	    version > FM_SUSPECT_VERSION) {
-		fmd_hdl_debug(hdl, "invalid event version: %u\n", version);
-		snmp_stats.bad_vers.fmds_value.ui64++;
-		return;
-	}
-
-	if (!snmp_trapall && nvlist_lookup_boolean_value(nvl,
-	    FM_SUSPECT_MESSAGE, &domsg) == 0 && !domsg) {
-		fmd_hdl_debug(hdl, "%s requested no trap\n", class);
-		snmp_stats.no_trap.fmds_value.ui64++;
-		return;
-	}
-
-	(void) nvlist_lookup_string(nvl, FM_SUSPECT_UUID, &uuid);
-	(void) nvlist_lookup_string(nvl, FM_SUSPECT_DIAG_CODE, &code);
-
-	url = fmd_msg_getitem_nv(snmp_msghdl, NULL, nvl, FMD_MSG_ITEM_URL);
-
-	if (url != NULL) {
-		send_trap(hdl, uuid, code, url);
-		free(url);
-	} else {
-		fmd_hdl_debug(hdl, "failed to format url for %s", uuid);
-		snmp_stats.bad_code.fmds_value.ui64++;
-	}
-}
-
-static int
-init_sma(void)
-{
-	int err;
-
-	/*
-	 * The only place we could possibly log is syslog, but the
-	 * full agent doesn't normally log there.  It would be confusing
-	 * if this agent did so; therefore we disable logging entirely.
-	 */
-	snmp_disable_log();
-
-	/*
-	 * Net-SNMP has a provision for reading an arbitrary number of
-	 * configuration files.  A configuration file is read if it has
-	 * had any handlers registered for it, or if it's the value in
-	 * of NETSNMP_DS_LIB_APPTYPE.  Our objective here is to read
-	 * both snmpd.conf and fmd-trapgen.conf.
-	 */
-	if ((err = netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID,
-	    NETSNMP_DS_AGENT_ROLE, 0 /* MASTER_AGENT */)) != SNMPERR_SUCCESS)
-		return (err);
-
-	init_agent_read_config("snmpd");
-	if ((err = netsnmp_ds_set_string(NETSNMP_DS_LIBRARY_ID,
-	    NETSNMP_DS_LIB_APPTYPE, SNMP_SUPPCONF)) != SNMPERR_SUCCESS)
-		return (err);
-	if (register_app_config_handler("trapsink", snmpd_parse_config_trapsink,
-	    snmpd_free_trapsinks, "host [community] [port]") == NULL)
-		return (SNMPERR_MALLOC);
-	if (register_app_config_handler("trap2sink",
-	    snmpd_parse_config_trap2sink, NULL, "host [community] [port]") ==
-	    NULL)
-		return (SNMPERR_MALLOC);
-	if (register_app_config_handler("trapsess", snmpd_parse_config_trapsess,
-	    NULL, "[snmpcmdargs] host") == NULL)
-		return (SNMPERR_MALLOC);
-
-	init_traps();
-	init_snmp(SNMP_SUPPCONF);
-
-	return (SNMPERR_SUCCESS);
-}
-
-static const fmd_prop_t fmd_props[] = {
-	{ "url", FMD_TYPE_STRING, "http://sun.com/msg/" },
-	{ "trap_all", FMD_TYPE_BOOL, "false" },
-	{ NULL, 0, NULL }
-};
-
-static const fmd_hdl_ops_t fmd_ops = {
-	snmp_recv,	/* fmdo_recv */
-	NULL,		/* fmdo_timeout */
-	NULL,		/* fmdo_close */
-	NULL,		/* fmdo_stats */
-	NULL,		/* fmdo_gc */
-};
-
-static const fmd_hdl_info_t fmd_info = {
-	"SNMP Trap Generation Agent", "1.0", &fmd_ops, fmd_props
-};
-
-void
-_fmd_init(fmd_hdl_t *hdl)
-{
-	char *rootdir, *urlbase;
-
-	if (fmd_hdl_register(hdl, FMD_API_VERSION, &fmd_info) != 0)
-		return; /* invalid data in configuration file */
-
-	(void) fmd_stat_create(hdl, FMD_STAT_NOALLOC, sizeof (snmp_stats) /
-	    sizeof (fmd_stat_t), (fmd_stat_t *)&snmp_stats);
-
-	if (init_sma() != SNMPERR_SUCCESS)
-		fmd_hdl_abort(hdl, "snmp-trapgen agent initialization failed");
-
-	rootdir = fmd_prop_get_string(hdl, "fmd.rootdir");
-	snmp_msghdl = fmd_msg_init(rootdir, FMD_MSG_VERSION);
-	fmd_prop_free_string(hdl, rootdir);
-
-	if (snmp_msghdl == NULL)
-		fmd_hdl_abort(hdl, "failed to initialize libfmd_msg");
-
-	urlbase = fmd_prop_get_string(hdl, "url");
-	(void) fmd_msg_url_set(snmp_msghdl, urlbase);
-	fmd_prop_free_string(hdl, urlbase);
-
-	snmp_trapall = fmd_prop_get_int32(hdl, "trap_all");
-	fmd_hdl_subscribe(hdl, FM_LIST_SUSPECT_CLASS);
-	fmd_hdl_subscribe(hdl, FM_LIST_REPAIRED_CLASS);
-	fmd_hdl_subscribe(hdl, FM_LIST_RESOLVED_CLASS);
-}
-
-/*ARGSUSED*/
-void
-_fmd_fini(fmd_hdl_t *hdl)
-{
-	fmd_msg_fini(snmp_msghdl);
-
-	/*
-	 * snmp_shutdown, which we would normally use here, calls free_slots,
-	 * a callback that is supposed to tear down the pkcs11 state; however,
-	 * it abuses C_Finalize, causing fmd to drop core on shutdown.  Avoid
-	 * this by shutting down the library piecemeal.
-	 */
-	snmp_store(SNMP_SUPPCONF);
-	snmp_alarm_unregister_all();
-	(void) snmp_close_sessions();
-	shutdown_mib();
-	unregister_all_config_handlers();
-	netsnmp_ds_shutdown();
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fm/modules/common/sw-diag-response/Makefile	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,27 @@
+#
+# 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.
+#
+
+SUBDIRS = software-diagnosis software-response
+
+include ../../../Makefile.subdirs
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fm/modules/common/sw-diag-response/Makefile.com	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,31 @@
+#
+# 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.
+#
+
+CMN_SRCS = common/sw_main_cmn.c
+
+SMF_CMN_SRCS = subsidiary/smf/smf_util.c
+SMF_DE_SRCS = subsidiary/smf/smf_diag.c $(SMF_CMN_SRCS)
+SMF_RP_SRCS = subsidiary/smf/smf_response.c $(SMF_CMN_SRCS)
+
+PANIC_DE_SRCS = subsidiary/panic/panic_diag.c 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fm/modules/common/sw-diag-response/common/sw.h	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,255 @@
+/*
+ * 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.
+ */
+
+#ifndef	_SW_H
+#define	_SW_H
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+#include <sys/fm/protocol.h>
+#include <fm/fmd_api.h>
+#include <libnvpair.h>
+#include <pthread.h>
+#include <libuutil.h>
+
+/*
+ * We have two real fmd modules - software-diagnosis and software-response.
+ * Each hosts a number of subsidiary diagnosis engines and response agents,
+ * although these are not fmd modules as such (the intention is to avoid
+ * a proliferation of small C diagnosis and response modules).
+ *
+ * Subsidiary "modules" are not loaded as normal fmd modules are.  Instead
+ * each of the real modules software-diagnosis and software-response includes
+ * an array listing the subsidiaries it hosts, and when the real module
+ * is loaded by fmd it iterates over this list to "load" subsidiaries by
+ * calling their nominated init function.
+ */
+
+/* Maximum number of subsidiary "modules" */
+#define	SW_SUB_MAX	10
+
+/* Maximum number of supported timers across all subsidiaries */
+#define	SW_TIMER_MAX	20
+
+/*
+ * A subsidiary must perform fmd_hdl_subscribe calls for all events of
+ * interest to it.  These are typically performed during its init
+ * function.  All subscription callbacks funnel through the shared
+ * fmdo_recv entry point; that function walks through the dispatch list
+ * for each subsidiary and performs a callback for the first matching entry of
+ * each subsidiary.  The init entry point for each subsidiary
+ * returns a pointer to an array of struct sw_disp applicable for that
+ * entity.
+ *
+ * Note that the framework does *not* perform any fmd_hdl_subscribe calls
+ * on behalf of the subsidiary - the swd_classpat member below is used
+ * in routing events, not in establishing subscriptions for them.  A
+ * subsidiary can subscribe to say "ireport.foo.a" and "ireport.foo.b"
+ * but could elect to nominate a common handler for those via a single
+ * struct sw_disp with swd_classpat of "ireport.foo.*".
+ */
+typedef void sw_dispfunc_t(fmd_hdl_t *, fmd_event_t *, nvlist_t *,
+    const char *, void *);
+
+struct sw_disp {
+	const char *swd_classpat;	/* event classes to callback for */
+	sw_dispfunc_t *swd_func;	/* callback function */
+	void *swd_arg;			/* opaque argument to callback */
+};
+
+/*
+ * A diagnosis or response subsidiary must provide a struct sw_subinfo with
+ * all its pertinent information;  a pointer to this structure must be
+ * included in the array of struct sw_subinfo pointers in each of
+ * software-diagnosis and software-response.
+ *
+ * swsub_name
+ *	This should be chosen to be unique to this subsidiary;
+ *	by convention it should also be the name prefix used in any fmd
+ *	buffers	the subsidiary creates.
+ *
+ * swsub_casetype
+ *	A diagnosis subsidiary solves cases using swde_case_* below, and it
+ *	must specify in swsub_casetype the type of case it solves.  A response
+ *	subsidiary must specify SW_CASE_NONE here.  A subsidiary may only solve
+ *	at most one type of case, and no two subsidiaries must solve the same
+ *	case type.  We use the case type to associate a subsidiary owner of
+ *	the fmd case that is really owned by the host module.
+ *
+ * swsub_init
+ *	The initialization function for this subsidiary, akin to the
+ *	_fmd_init in a traditional fmd module.  This must not be NULL.
+ *
+ *	 When the host diagnosis/response module initializes the _fmd_init
+ *	 entry point will call the swsub_init function for each subsidiary
+ *	 in turn.  The fmd handle has already been registered and timers are
+ *	 available for installation (see below);  the swsub_init function must
+ *	 return a pointer to a NULL-terminated array of struct sw_disp
+ *	 describing the event dispatch preferences for that module, and fill
+ *	 an integer we pass with the number of entries in that array (including
+ *	 the terminating NULL entry).  The swsub_init function also receives
+ *	 a subsidiary-unique id_t assigned by the framework that it should
+ *	 keep a note of for use in timer installation (see below);  this id
+ *	 should not be persisted to checkpoint data.
+ *
+ * swsub_fini
+ *	When the host module _fmd_fini is called it will call this function
+ *	for each subsidiary.  A subsidiary can specify NULL here.
+ *
+ * swsub_timeout
+ *	This is the timeout function to call for expired timers installed by
+ *	this subsidiary.  See sw_timer_{install,remove} below.  May be
+ *	NULL if no timers are used by this subsidiary.
+ *
+ * swsub_case_close
+ *	This function is called when a case "owned" by a subsidiary
+ *	is the subject of an fmdo_close callback.  Can be NULL, and
+ *	must be NULL for a subsidiary with case type SW_CASE_NONE (such
+ *	as a response subsidiary).
+ *
+ * swsub_case_verify
+ *	This is called during _fmd_init of the host module.  The host module
+ *	iterates over all cases that it owns and calls the verify function
+ *	for the real owner which may choose to close cases if they no longer
+ *	apply.  Can be NULL, and must be NULL for a subsidiary with case
+ *	type SW_CASE_NONE.
+ */
+
+/*
+ * sw_casetype values are persisted to checkpoints - do not change values.
+ */
+enum sw_casetype {
+	SW_CASE_NONE = 0x0ca5e000,
+	SW_CASE_SMF,
+	SW_CASE_PANIC
+};
+
+/*
+ * Returns for swsub_init.  The swsub_fini entry point will only be
+ * called for subsidiaries that returned SW_SUB_INIT_SUCCESS on init.
+ */
+#define	SW_SUB_INIT_SUCCESS		0
+#define	SW_SUB_INIT_FAIL_VOLUNTARY	1	/* chose not to init */
+#define	SW_SUB_INIT_FAIL_ERROR		2	/* error prevented init */
+
+typedef void swsub_case_close_func_t(fmd_hdl_t *, fmd_case_t *);
+typedef int sw_case_vrfy_func_t(fmd_hdl_t *, fmd_case_t *);
+
+struct sw_subinfo {
+	const char *swsub_name;
+	enum sw_casetype swsub_casetype;
+	int (*swsub_init)(fmd_hdl_t *, id_t, const struct sw_disp **, int *);
+	void (*swsub_fini)(fmd_hdl_t *);
+	void (*swsub_timeout)(fmd_hdl_t *, id_t, void *);
+	swsub_case_close_func_t *swsub_case_close;
+	sw_case_vrfy_func_t *swsub_case_verify;
+};
+
+/*
+ * List sw_subinfo for each subsidiary diagnosis and response "module" here
+ */
+extern const struct sw_subinfo smf_diag_info;
+extern const struct sw_subinfo smf_response_info;
+extern const struct sw_subinfo panic_diag_info;
+
+/*
+ * Timers - as per the fmd module API but with an additional id_t argument
+ * specifying the unique id of the subsidiary installing the timer (provided
+ * to the subsidiary in its swsub_init call).
+ */
+extern id_t sw_timer_install(fmd_hdl_t *, id_t, void *, fmd_event_t *,
+    hrtime_t);
+extern void sw_timer_remove(fmd_hdl_t *, id_t, id_t);
+
+/*
+ * The software-diagnosis subsidiaries can open and solve cases; to do so
+ * they must use the following wrappers to the usual fmd module API case
+ * management functions.  We need this so that a subsidiary can iterate
+ * over *its* cases (fmd_case_next would iterate over those of other
+ * subsidiaries), receive in the subsidiary a callback when a case it opened
+ * is closed, etc.  The subsidiary can use other fmd module API members
+ * for case management, such as fmd_case_add_ereport.
+ *
+ * Each subsidiary opens cases of its own unique type, identified by
+ * the sw_casetype enumeration.  The values used in this enumeration
+ * must never change - they are written to checkpoint state.
+ *
+ * swde_case_open
+ *	Opens a new case of the correct subsidiary type for the given
+ *	subsidiary id.  If a uuid string is provided then open a case
+ *	with that uuid using fmd_case_open_uuid, allowing case uuid
+ *	to match some relevant uuid that was received in one of the
+ *	events that has led us to open this case.
+ *
+ *	If the subsidiarywishes to associate some persistent
+ *	case data with the new case thenit can fmd_hdl_alloc and complete a
+ *	suitably-packed serialization structure and include a pointer to it
+ *	in the call to sw_case_open together with the structure size and
+ *	structure version.  The	framework will create a new fmd buffer (named
+ *	for you, based on the case type) and write the structure out to disk;
+ *	when the module or fmd is restarted this structure is restored from
+ *	disk for you and reassociated with the case - use swde_case_data to
+ *	retrieve a pointer to it.
+ *
+ * swde_case_first, swde_case_next
+ *	A subsidiary DE can iterate over its cases using swde_case_first and
+ *	swde_case_next.  For swde_case_first quote the subsidiary id;
+ *	for swde_case_next quote the last case returned.
+ *
+ * swde_case_data
+ *	Returns a pointer to the previously-serialized case data, and fills
+ *	a uint32_t with the version of that serialized data.
+ *
+ * swde_case_data_write
+ *	Whenever a subsidiary modifies its persistent data structure
+ *	it must call swde_case_data_write to indicate that the associated
+ *	fmd buffer is dirty and needs to be rewritten.
+ *
+ * swde_case_data_upgrade
+ *	If the subsidiary ever revs its persistent structure it needs to call
+ *	swde_case_data_upgrade to register the new version and structure size,
+ *	and write the structure out to a reallocated fmd buffer;  the old
+ *	case data structure (if any) will be freed.  A subsidiary may use
+ *	this interface to migrate old persistence structures restored from
+ *	checkpoint - swde_case_data will return a version number below the
+ *	current.
+ */
+
+extern fmd_case_t *swde_case_open(fmd_hdl_t *, id_t, char *, uint32_t,
+    void *, size_t);
+extern fmd_case_t *swde_case_first(fmd_hdl_t *, id_t);
+extern fmd_case_t *swde_case_next(fmd_hdl_t *, fmd_case_t *);
+extern void *swde_case_data(fmd_hdl_t *, fmd_case_t *, uint32_t *);
+extern void swde_case_data_write(fmd_hdl_t *, fmd_case_t *);
+extern void swde_case_data_upgrade(fmd_hdl_t *, fmd_case_t *, uint32_t,
+    void *, size_t);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _SW_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fm/modules/common/sw-diag-response/common/sw_impl.h	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,101 @@
+/*
+ * 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.
+ */
+
+#ifndef	_SW_IMPL_H
+#define	_SW_IMPL_H
+
+#include "sw.h"
+
+/*
+ * The common code between software-response and software-diagnosis
+ * needs somewhere to track the subsidaries that have "registered",
+ * their dispatch tables etc.  In the _fmd_init of each module we
+ * call the shared sw_fmd_init code, and there we allocate a
+ * struct sw_modspecific and assign this as the fmd fodule-specific
+ * data with fmd_hdl_setspecific.
+ */
+struct sw_modspecific {
+	int swms_dispcnt;
+	const struct sw_subinfo *(*swms_subinfo)[SW_SUB_MAX];
+	const struct sw_disp *(*swms_disptbl)[SW_SUB_MAX];
+	pthread_mutex_t swms_timerlock;
+	struct {
+		int swt_state;		/* slot in use? */
+		id_t swt_timerid;	/* fmd_timer_install result */
+		id_t swt_ownerid;	/* subsidiary owner id */
+	} swms_timers[SW_TIMER_MAX];
+};
+
+#define	SW_TMR_INUSE		1
+#define	SW_TMR_RMVD		0
+#define	SW_TMR_UNTOUCHED	-1
+
+extern swsub_case_close_func_t *sw_sub_case_close_func(fmd_hdl_t *,
+    enum sw_casetype);
+extern sw_case_vrfy_func_t *sw_sub_case_vrfy_func(fmd_hdl_t *,
+    enum sw_casetype);
+
+/*
+ * Software DE fmdo_close entry point.
+ */
+extern void swde_close(fmd_hdl_t *, fmd_case_t *);
+
+/*
+ * Shared functions for software-diagnosis and software-response fmd
+ * module implementation using shared code.  Subsidiaries do not need
+ * to call these functions.
+ *
+ * sw_fmd_init is called from _fmd_init of the two modules, to do most of
+ * the real work of initializing the subsidiaries etc.
+ *
+ * sw_fmd_fini is called from _fmd_fini and calls the swsub_fini
+ * function of each subsidiary after uninstalling all timers.
+ *
+ * sw_recv is the fmdo_recv entry point; it checks the event against
+ * the dispatch table of each subsidiary and dispatches the first
+ * match for each module.
+ *
+ * sw_timeout is the fmdo_timeout entry point; it looks up the unique id_t
+ * of the subsidiary that installed the timer (via sw_timer_install in which
+ * the id is quoted) and calls the swsub_timeout function for that subsidiary.
+ *
+ * swde_case_init and swde_case_fini initialize and finalize the
+ * software-diagnosis case-tracking infrastructure;  swde_case_init
+ * is responsible for unserializing case state.
+ *
+ * sw_id_to_casetype take a subsidiary id and returns the case type it
+ * registered with.
+ */
+extern int sw_fmd_init(fmd_hdl_t *, const fmd_hdl_info_t *,
+    const struct sw_subinfo *(*)[SW_SUB_MAX]);
+extern void sw_fmd_fini(fmd_hdl_t *);
+extern void sw_recv(fmd_hdl_t *, fmd_event_t *, nvlist_t *, const char *);
+extern void sw_timeout(fmd_hdl_t *, id_t, void *);
+extern void swde_case_init(fmd_hdl_t *);
+extern void swde_case_fini(fmd_hdl_t *);
+
+enum sw_casetype sw_id_to_casetype(fmd_hdl_t *, id_t);
+
+#endif	/* _SW_IMPL_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fm/modules/common/sw-diag-response/common/sw_main_cmn.c	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,474 @@
+/*
+ * 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.
+ */
+
+/*
+ * Code shared by software-diagnosis and software-response modules.
+ * The fmd module linkage info for the two modules lives in swde_main.c
+ * (for software-diagnosis) and swrp_main.c (for software-response).
+ */
+
+#include "../common/sw_impl.h"
+
+/*
+ * Each subsidiary that is hosted is assigned a unique subsidiary id.  These
+ * macros convert between the id of a subsidiary and the index used in keeping
+ * track of subsidiaries.  Outside of this file these ids should remain
+ * opaque.
+ */
+#define	ID2IDX(id)	((int)((id) & 0xff0000) >> 16)
+#define	IDX2ID(i)	((id_t)((i) << 16) | 0x1d000000)
+
+#define	SUBIDVALID(msinfo, id)  (((int)(id) & 0xff00ffff) == 0x1d000000 && \
+    ID2IDX(id) < (msinfo)->swms_dispcnt)
+
+static struct {
+	fmd_stat_t sw_recv_total;
+	fmd_stat_t sw_recv_match;
+	fmd_stat_t sw_recv_callback;
+} sw_stats = {
+	{ "sw_recv_total", FMD_TYPE_UINT64,
+	    "total events received" },
+	{ "sw_recv_match", FMD_TYPE_UINT64,
+	    "events matching some subsidiary" },
+	{ "sw_recv_callback", FMD_TYPE_UINT64,
+	    "callbacks to all subsidiaries" },
+};
+
+#define	BUMPSTAT(stat)		sw_stats.stat.fmds_value.ui64++
+#define	BUMPSTATN(stat, n)	sw_stats.stat.fmds_value.ui64 += (n)
+
+/*
+ * ========================== Event Receipt =================================
+ *
+ * The fmdo_recv entry point.  See which sub de/response agents have a
+ * matching subscription and callback for the first match from each.
+ * The sub de/response agents should dispatch *all* their subscriptions
+ * via their registered dispatch table, including things like list.repaired.
+ */
+void
+sw_recv(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl, const char *class)
+{
+	struct sw_modspecific *msinfo;
+	int calls = 0;
+	int mod;
+
+	BUMPSTAT(sw_recv_total);
+
+	msinfo = (struct sw_modspecific *)fmd_hdl_getspecific(hdl);
+
+	/*
+	 * For each sub module that has a matching class pattern call the
+	 * registered callback for that sub DE.  Only one match per sub module
+	 * is allowed (the first match in its table, others are not checked).
+	 */
+	for (mod = 0; mod < msinfo->swms_dispcnt; mod++) {
+		const struct sw_disp *dp;
+		sw_dispfunc_t *dispf = NULL;
+
+		for (dp = (*msinfo->swms_disptbl)[mod];
+		    dp != NULL && dp->swd_classpat != NULL; dp++) {
+			if (fmd_nvl_class_match(hdl, nvl, dp->swd_classpat)) {
+				dispf = dp->swd_func;
+				break;
+			}
+		}
+		if (dispf != NULL) {
+			calls++;
+			(*dispf)(hdl, ep, nvl, class, dp->swd_arg);
+		}
+	}
+
+	BUMPSTAT(sw_recv_match);
+	if (calls)
+		BUMPSTATN(sw_recv_callback, calls);
+}
+
+/*
+ * ========================== Timers ========================================
+ *
+ * A subsidiary can install a timer; it must pass an additional argument
+ * identifying itself so that we can hand off to the appropriate
+ * swsub_timeout function in the fmdo_timeout entry point when the timer fires.
+ */
+id_t
+sw_timer_install(fmd_hdl_t *hdl, id_t who, void *arg, fmd_event_t *ep,
+    hrtime_t hrt)
+{
+	struct sw_modspecific *msinfo;
+	const struct sw_subinfo **subinfo;
+	const struct sw_subinfo *sip;
+	int slot, chosen = -1;
+	id_t timerid;
+
+	msinfo = (struct sw_modspecific *)fmd_hdl_getspecific(hdl);
+	if (!SUBIDVALID(msinfo, who))
+		fmd_hdl_abort(hdl, "sw_timer_install: invalid subid %d\n", who);
+
+	subinfo = *msinfo->swms_subinfo;
+	sip = subinfo[ID2IDX(who)];
+
+	if (sip-> swsub_timeout == NULL)
+		fmd_hdl_abort(hdl, "sw_timer_install: no swsub_timeout\n");
+
+	/*
+	 * Look for a slot.  Module entry points are single-threaded
+	 * in nature, but if someone installs a timer from a door
+	 * service function we're contended.
+	 */
+	(void) pthread_mutex_lock(&msinfo->swms_timerlock);
+	for (slot = 0; slot < SW_TIMER_MAX; slot++) {
+		if (msinfo->swms_timers[slot].swt_state != SW_TMR_INUSE) {
+			chosen = slot;
+			break;
+		}
+	}
+
+	if (chosen == -1)
+		fmd_hdl_abort(hdl, "timer slots exhausted\n");
+
+	msinfo->swms_timers[chosen].swt_state = SW_TMR_INUSE;
+	msinfo->swms_timers[chosen].swt_ownerid = who;
+	msinfo->swms_timers[chosen].swt_timerid = timerid =
+	    fmd_timer_install(hdl, arg, ep, hrt);
+
+	(void) pthread_mutex_unlock(&msinfo->swms_timerlock);
+
+	return (timerid);
+}
+
+/*
+ * Look for a timer installed by a given subsidiary matching timerid.
+ */
+static int
+subtimer_find(struct sw_modspecific *msinfo, id_t who, id_t timerid)
+{
+	int slot;
+
+	for (slot = 0; slot < SW_TIMER_MAX; slot++) {
+		if (msinfo->swms_timers[slot].swt_state == SW_TMR_INUSE &&
+		    (who == -1 ||
+		    msinfo->swms_timers[slot].swt_ownerid == who) &&
+		    msinfo->swms_timers[slot].swt_timerid == timerid)
+			return (slot);
+	}
+
+	return (-1);
+}
+
+void
+sw_timer_remove(fmd_hdl_t *hdl, id_t who, id_t timerid)
+{
+	struct sw_modspecific *msinfo;
+	const struct sw_subinfo **subinfo;
+	const struct sw_subinfo *sip;
+	int slot;
+
+	msinfo = (struct sw_modspecific *)fmd_hdl_getspecific(hdl);
+	if (!SUBIDVALID(msinfo, who))
+		fmd_hdl_abort(hdl, "sw_timer_remove: invalid subid\n");
+
+	subinfo = *msinfo->swms_subinfo;
+	sip = subinfo[ID2IDX(who)];
+
+	(void) pthread_mutex_lock(&msinfo->swms_timerlock);
+	if ((slot = subtimer_find(msinfo, who, timerid)) == -1)
+		fmd_hdl_abort(hdl, "sw_timer_remove: timerid %d not found "
+		    "for %s\n", timerid, sip->swsub_name);
+	fmd_timer_remove(hdl, timerid);
+	msinfo->swms_timers[slot].swt_state = SW_TMR_RMVD;
+	(void) pthread_mutex_unlock(&msinfo->swms_timerlock);
+}
+
+/*
+ * The fmdo_timeout entry point.
+ */
+void
+sw_timeout(fmd_hdl_t *hdl, id_t timerid, void *arg)
+{
+	struct sw_modspecific *msinfo;
+	const struct sw_subinfo **subinfo;
+	const struct sw_subinfo *sip;
+	id_t owner;
+	int slot;
+
+	msinfo = (struct sw_modspecific *)fmd_hdl_getspecific(hdl);
+
+	(void) pthread_mutex_lock(&msinfo->swms_timerlock);
+	if ((slot = subtimer_find(msinfo, -1, timerid)) == -1)
+		fmd_hdl_abort(hdl, "sw_timeout: timerid %d not found\n");
+	(void) pthread_mutex_unlock(&msinfo->swms_timerlock);
+
+	owner = msinfo->swms_timers[slot].swt_ownerid;
+	if (!SUBIDVALID(msinfo, owner))
+		fmd_hdl_abort(hdl, "sw_timeout: invalid subid\n");
+
+	subinfo = *msinfo->swms_subinfo;
+	sip = subinfo[ID2IDX(owner)];
+
+	sip->swsub_timeout(hdl, timerid, arg);
+}
+
+/*
+ * ========================== sw_subinfo access =============================
+ */
+
+enum sw_casetype
+sw_id_to_casetype(fmd_hdl_t *hdl, id_t who)
+{
+	struct sw_modspecific *msinfo;
+	const struct sw_subinfo **subinfo;
+	const struct sw_subinfo *sip;
+
+	msinfo = (struct sw_modspecific *)fmd_hdl_getspecific(hdl);
+	if (!SUBIDVALID(msinfo, who))
+		fmd_hdl_abort(hdl, "sw_id_to_casetype: invalid subid %d\n",
+		    who);
+
+	subinfo = *msinfo->swms_subinfo;
+	sip = subinfo[ID2IDX(who)];
+
+	if ((sip->swsub_casetype & SW_CASE_NONE) != SW_CASE_NONE)
+		fmd_hdl_abort(hdl, "sw_id_to_casetype: bad case type %d "
+		    "for %s\n", sip->swsub_casetype, sip->swsub_name);
+
+	return (sip->swsub_casetype);
+}
+
+/*
+ * Given a case type lookup the struct sw_subinfo for the subsidiary
+ * that opens cases of that type.
+ */
+static const struct sw_subinfo *
+sw_subinfo_bycase(fmd_hdl_t *hdl, enum sw_casetype type)
+{
+	struct sw_modspecific *msinfo;
+	const struct sw_subinfo **subinfo;
+	const struct sw_subinfo *sip;
+	int i;
+
+	msinfo = (struct sw_modspecific *)fmd_hdl_getspecific(hdl);
+
+	subinfo = *msinfo->swms_subinfo;
+	for (i = 0; i < SW_SUB_MAX; i++) {
+		sip = subinfo[i];
+		if (sip->swsub_casetype == type)
+			return (sip);
+	}
+
+	return (NULL);
+}
+
+/*
+ * Find the case close function for the given case type; can be NULL.
+ */
+swsub_case_close_func_t *
+sw_sub_case_close_func(fmd_hdl_t *hdl, enum sw_casetype type)
+{
+	const struct sw_subinfo *sip;
+
+	if ((sip = sw_subinfo_bycase(hdl, type)) == NULL)
+		fmd_hdl_abort(hdl, "sw_sub_case_close_func: case type "
+		    "%d not found\n", type);
+
+	return (sip->swsub_case_close);
+}
+
+/*
+ * Find the case verify function for the given case type; can be NULL.
+ */
+sw_case_vrfy_func_t *
+sw_sub_case_vrfy_func(fmd_hdl_t *hdl, enum sw_casetype type)
+{
+	const struct sw_subinfo *sip;
+
+	if ((sip = sw_subinfo_bycase(hdl, type)) == NULL)
+		fmd_hdl_abort(hdl, "sw_sub_case_vrfy_func: case type "
+		    "%d not found\n", type);
+
+	return (sip->swsub_case_verify);
+}
+
+/*
+ * ========================== Initialization ================================
+ *
+ * The two modules - software-diagnosis and software-response - call
+ * sw_fmd_init from their _fmd_init entry points.
+ */
+
+static void
+sw_add_callbacks(fmd_hdl_t *hdl, const char *who,
+    const struct sw_disp *dp, int nelem, struct sw_modspecific *msinfo)
+{
+	int i;
+
+	(*msinfo->swms_disptbl)[msinfo->swms_dispcnt++] = dp;
+
+	if (dp == NULL)
+		return;		/* subsidiary failed init */
+
+	/* check that the nelem'th entry is the NULL termination */
+	if (dp[nelem - 1].swd_classpat != NULL ||
+	    dp[nelem - 1].swd_func != NULL || dp[nelem - 1].swd_arg != NULL)
+		fmd_hdl_abort(hdl, "subsidiary %s dispatch table not NULL-"
+		    "terminated\n", who);
+
+	/* now validate the entries; we allow NULL handlers */
+	for (i = 0; i < nelem - 1; i++) {
+		if (dp[i].swd_classpat == NULL)
+			fmd_hdl_abort(hdl, "subsidiary %s dispatch table entry "
+			    "%d has a NULL pattern or function\n", who, i);
+	}
+
+}
+
+int
+sw_fmd_init(fmd_hdl_t *hdl, const fmd_hdl_info_t *hdlinfo,
+    const struct sw_subinfo *(*subsid)[SW_SUB_MAX])
+{
+	struct sw_modspecific *msinfo;
+	int i;
+
+	if (fmd_hdl_register(hdl, FMD_API_VERSION, hdlinfo) != 0)
+		return (0);
+
+	if (fmd_prop_get_int32(hdl, "enable") != B_TRUE) {
+		fmd_hdl_debug(hdl, "%s disabled though .conf file setting\n",
+		    hdlinfo->fmdi_desc);
+		fmd_hdl_unregister(hdl);
+		return (0);
+	}
+
+	msinfo = fmd_hdl_zalloc(hdl, sizeof (*msinfo), FMD_SLEEP);
+
+	msinfo->swms_subinfo = subsid;
+	msinfo->swms_disptbl = fmd_hdl_zalloc(hdl,
+	    SW_SUB_MAX * sizeof (struct sw_disp *), FMD_SLEEP);
+
+	(void) pthread_mutex_init(&msinfo->swms_timerlock, NULL);
+
+	for (i = 0; i < SW_TIMER_MAX; i++)
+		msinfo->swms_timers[i].swt_state = SW_TMR_UNTOUCHED;
+
+	fmd_hdl_setspecific(hdl, (void *)msinfo);
+
+	(void) fmd_stat_create(hdl, FMD_STAT_NOALLOC, sizeof (sw_stats) /
+	    sizeof (fmd_stat_t), (fmd_stat_t *)&sw_stats);
+
+	/*
+	 * Initialize subsidiaries.  Each must make any subscription
+	 * requests it needs and return a pointer to a NULL-terminated
+	 * callback dispatch table and an indication of the number of
+	 * entries in that table including the NULL termination entry.
+	 */
+	for (i = 0; i < SW_SUB_MAX; i++) {
+		const struct sw_subinfo *sip = (*subsid)[i];
+		const struct sw_disp *dp;
+		char dbgbuf[80];
+		int nelem = -1;
+		int initrslt;
+
+		if (!sip || sip->swsub_name == NULL)
+			break;
+
+		initrslt = (*sip->swsub_init)(hdl, IDX2ID(i), &dp, &nelem);
+
+		(void) snprintf(dbgbuf, sizeof (dbgbuf),
+		    "subsidiary %d (id 0x%lx) '%s'",
+		    i, IDX2ID(i), sip->swsub_name);
+
+		switch (initrslt) {
+		case SW_SUB_INIT_SUCCESS:
+			if (dp == NULL || nelem < 1)
+				fmd_hdl_abort(hdl, "%s returned dispatch "
+				    "table 0x%p and nelem %d\n",
+				    dbgbuf, dp, nelem);
+
+			fmd_hdl_debug(hdl, "%s initialized\n", dbgbuf);
+			sw_add_callbacks(hdl, sip->swsub_name, dp, nelem,
+			    msinfo);
+			break;
+
+		case SW_SUB_INIT_FAIL_VOLUNTARY:
+			fmd_hdl_debug(hdl, "%s chose not to initialize\n",
+			    dbgbuf);
+			sw_add_callbacks(hdl, sip->swsub_name, NULL, -1,
+			    msinfo);
+			break;
+
+		case SW_SUB_INIT_FAIL_ERROR:
+			fmd_hdl_debug(hdl, "%s failed to initialize "
+			    "because of an error\n", dbgbuf);
+			sw_add_callbacks(hdl, sip->swsub_name, NULL, -1,
+			    msinfo);
+			break;
+
+		default:
+			fmd_hdl_abort(hdl, "%s returned out-of-range result "
+			    "%d\n", dbgbuf, initrslt);
+			break;
+		}
+	}
+
+	return (1);
+}
+
+void
+sw_fmd_fini(fmd_hdl_t *hdl)
+{
+	const struct sw_subinfo **subinfo;
+	struct sw_modspecific *msinfo;
+	int i;
+
+	msinfo = (struct sw_modspecific *)fmd_hdl_getspecific(hdl);
+	subinfo = *msinfo->swms_subinfo;
+
+	(void) pthread_mutex_lock(&msinfo->swms_timerlock);
+	for (i = 0; i < SW_TIMER_MAX; i++) {
+		if (msinfo->swms_timers[i].swt_state != SW_TMR_INUSE)
+			continue;
+
+		fmd_timer_remove(hdl, msinfo->swms_timers[i].swt_timerid);
+		msinfo->swms_timers[i].swt_state = SW_TMR_RMVD;
+	}
+	(void) pthread_mutex_unlock(&msinfo->swms_timerlock);
+
+	(void) pthread_mutex_destroy(&msinfo->swms_timerlock);
+
+	for (i = 0; i < msinfo->swms_dispcnt; i++) {
+		const struct sw_subinfo *sip = subinfo[i];
+
+		if ((*msinfo->swms_disptbl)[i] == NULL)
+			continue;	/* swsub_init did not succeed */
+
+		if (sip->swsub_fini != NULL)
+			(*sip->swsub_fini)(hdl);
+	}
+
+	fmd_hdl_free(hdl, msinfo->swms_disptbl,
+	    SW_SUB_MAX * sizeof (struct sw_disp *));
+
+	fmd_hdl_setspecific(hdl, NULL);
+	fmd_hdl_free(hdl, msinfo, sizeof (*msinfo));
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fm/modules/common/sw-diag-response/software-diagnosis/Makefile	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,51 @@
+#
+# 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.
+#
+
+MODULE = software-diagnosis
+CLASS = common
+
+include ../Makefile.com
+
+#
+# Sources for the primary software-diagnosis module
+#
+SWDE_SRCS = swde_main.c swde_case.c
+
+#
+# Sources for subsidiary diagnosis "modules" that we host.  These should
+# be listed in ../Makefile.com
+#
+SUBDE_SRCS = $(SMF_DE_SRCS) $(PANIC_DE_SRCS)
+
+#
+# All sources for softtware-diagnosis
+#
+SRCS = $(SWDE_SRCS) $(CMN_SRCS:%=../%) $(SUBDE_SRCS:%=../%)
+
+include ../../../Makefile.plugin
+
+CFLAGS += $(INCS)
+LINTFLAGS += $(INCS)
+LDLIBS += -L$(ROOTLIB)/fm -ltopo -luutil -luuid -lkstat
+LDFLAGS += -R/usr/lib/fm
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fm/modules/common/sw-diag-response/software-diagnosis/software-diagnosis.conf	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,36 @@
+#
+# 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.
+#
+
+#
+# Configuration for the software-diagnosis diagnosis engine.
+#
+
+#
+# Dictionaries in use by software-diagnosis.  The SMF dictionary *must*
+# be listed before the SUNOS dictionary so that the smf maintenance
+# defect is found in SMF instead of SUNOS.
+#
+
+dictionary SMF
+dictionary SUNOS
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fm/modules/common/sw-diag-response/software-diagnosis/swde_case.c	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,368 @@
+/*
+ * 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 <stddef.h>
+#include <strings.h>
+
+#include "../common/sw_impl.h"
+
+/*
+ * We maintain a single list of all active cases across all
+ * subsidary diagnosis "modules".  We also offer some serialization
+ * services to them.
+ *
+ * To open a new case a subsidiary engine should use swde_case_open
+ * indicating the subsidiary id (from which we lookup the enum sw_casetype)
+ * and, optionally, a pointer to a structure for serialization and its size.
+ *
+ * For each case opened with swde_case_open we maintain an swde_case_t
+ * structure in-core.  Embedded in this is the swde_case_data_t with
+ * information we need to keep track of and manage this case - it's
+ * case type, buffer name used for the sub-de-private data (if any)
+ * and the size of the sub-de-private structure.  It is this
+ * embedded structure which is serialized as the "casedata" buffer,
+ * while the subsidiary-private structure is serialized into another buffer
+ * "casedata_<casetype-in-hex>".
+ *
+ * The subsidiary-private data structure, if any, is required to start
+ * with a uint32_t recording the data structure version.  This
+ * version is also specified as an argument to swde_case_open, and
+ * we note it in the "casedata" buffer we write out and require
+ * a match on restore.
+ *
+ * When we unserialize we restore our management structure as well as
+ * the sub-de-private structure.
+ *
+ * Here's how serialization works:
+ *
+ * In swde_case_open we create a case data buffer for the case named
+ * SW_CASE_DATA_BUFNAME.  We write the buffer out after filling in the
+ * structure version and recording the type of case this is, and if there
+ * is data for the subsidiary then we call swde_subdata to note the
+ * size and version of that data in the case data buf and then to create
+ * and write the subdata in a buffer named SW_CASE_DATA_BUFNAME_<casetype>.
+ *
+ * If the subsidiary updates its case data it is required to call
+ * swde_case_data_write.  This just calls fmd_buf_write for the subsidiary
+ * buffer name.
+ *
+ * A subsidiary can retrieve its private data buffer for a case using
+ * swde_case_data.  This also fills a uint32_t with the version of the
+ * buffer that we have for this subsidiary;  if that is an old version
+ * the subsidiary can cast appropriately and/or upgrade the buffer as
+ * below.
+ *
+ * When the host module is reloaded it calls swde_case_init to iterate
+ * through all cases we own.  For each we call swde_case_unserialize
+ * which restores our case tracking data and any subsidiary-private
+ * data that our case data notes.  We then call swde_case_verify which
+ * calls any registered verify function in the subsidiary owner, and if this
+ * returns 0 the case is closed.
+ *
+ * After initial write, we don't usually have to update the
+ * SW_CASE_DATA_BUFNAME buffer unless the subsidiary changes the size or
+ * version of its private buffer.  To do that the subsidiary must call
+ * swde_case_data_upgrade.  In that function we destroy the old subsidiary
+ * buffer and, if there is still a subsidiary data structure, create a
+ * new buffer appropriately sized and call swde_subdata to write it out
+ * after updating our case structure with new size etc.  Finally we write
+ * out our updated case data structure.
+ */
+
+#define	SW_CASE_DATA_BUFNAME		"casedata"
+
+#define	SW_CASE_DATA_VERSION_INITIAL		1
+#define	SW_CASE_DATA_BUFNAMELEN	18	/* 8 + 1 + 8 + 1 */
+typedef struct swde_case_data {
+	uint32_t sc_version;		/* buffer structure version */
+	int32_t sc_type;		/* enum sw_casetype */
+	uint32_t sc_sub_bufvers;	/* version expected in subsidiary */
+	char sc_sub_bufname[SW_CASE_DATA_BUFNAMELEN];	/* subsidiary bufname */
+	int32_t sc_sub_bufsz;		/* subsidiary structure size */
+} swde_case_data_t;
+
+#define	SW_CASE_DATA_VERSION		SW_CASE_DATA_VERSION_INITIAL
+
+/*
+ * In-core case structure.
+ */
+typedef struct swde_case {
+	fmd_case_t *swc_fmdcase;	/* fmd case handle */
+	swde_case_data_t swc_data;	/* case data for serialization */
+	void *swc_subdata;		/* subsidiary data for serialization */
+} swde_case_t;
+
+static void
+swde_case_associate(fmd_hdl_t *hdl, fmd_case_t *cp, swde_case_t *scp,
+    void *subdata)
+{
+	scp->swc_fmdcase = cp;
+	scp->swc_subdata = subdata;
+	fmd_case_setspecific(hdl, cp, scp);
+}
+
+static void
+swde_case_unserialize(fmd_hdl_t *hdl, fmd_case_t *cp)
+{
+	swde_case_t *scp;
+	swde_case_data_t *datap;
+	void *subdata;
+	size_t sz;
+
+	scp = fmd_hdl_zalloc(hdl, sizeof (*scp), FMD_SLEEP);
+	datap = &scp->swc_data;
+
+	fmd_buf_read(hdl, cp, SW_CASE_DATA_BUFNAME, datap, sizeof (*datap));
+
+	if (datap->sc_version > SW_CASE_DATA_VERSION_INITIAL) {
+		fmd_hdl_free(hdl, scp, sizeof (*scp));
+		return;
+	}
+
+	if ((sz = datap->sc_sub_bufsz) != 0) {
+		subdata = fmd_hdl_alloc(hdl, sz, FMD_SLEEP);
+		fmd_buf_read(hdl, cp, datap->sc_sub_bufname, subdata, sz);
+
+		if (*((uint32_t *)subdata) != datap->sc_sub_bufvers) {
+			fmd_hdl_abort(hdl, "unserialize: expected subdata "
+			    "version %u but received %u\n",
+			    datap->sc_sub_bufvers, *((uint32_t *)subdata));
+		}
+	}
+
+	swde_case_associate(hdl, cp, scp, subdata);
+}
+
+static void
+swde_subdata(fmd_hdl_t *hdl, fmd_case_t *cp, enum sw_casetype type,
+    swde_case_t *scp, uint32_t subdata_vers, void *subdata, size_t subdata_sz)
+{
+	swde_case_data_t *datap = &scp->swc_data;
+
+	if (*((uint32_t *)subdata) != subdata_vers)
+		fmd_hdl_abort(hdl, "swde_subdata: subdata version "
+		    "does not match argument\n");
+
+	(void) snprintf(datap->sc_sub_bufname, sizeof (datap->sc_sub_bufname),
+	    "%s_%08x", SW_CASE_DATA_BUFNAME, type);
+
+	datap->sc_sub_bufsz = subdata_sz;
+	datap->sc_sub_bufvers = subdata_vers;
+	fmd_buf_create(hdl, cp, datap->sc_sub_bufname, subdata_sz);
+	fmd_buf_write(hdl, cp, datap->sc_sub_bufname, subdata, subdata_sz);
+}
+
+fmd_case_t *
+swde_case_open(fmd_hdl_t *hdl, id_t who, char *req_uuid,
+    uint32_t subdata_vers, void *subdata, size_t subdata_sz)
+{
+	enum sw_casetype ct = sw_id_to_casetype(hdl, who);
+	swde_case_data_t *datap;
+	swde_case_t *scp;
+	fmd_case_t *cp;
+
+	if (ct == SW_CASE_NONE)
+		fmd_hdl_abort(hdl, "swde_case_open for type SW_CASE_NONE\n");
+
+	if (subdata != NULL && subdata_sz <= sizeof (uint32_t) ||
+	    subdata_sz != 0 && subdata == NULL)
+		fmd_hdl_abort(hdl, "swde_case_open: bad subdata\n", ct);
+
+	scp = fmd_hdl_zalloc(hdl, sizeof (*scp), FMD_SLEEP);
+	datap = &scp->swc_data;
+
+	if (req_uuid == NULL) {
+		cp = fmd_case_open(hdl, (void *)scp);
+	} else {
+		cp = fmd_case_open_uuid(hdl, req_uuid, (void *)scp);
+		if (cp == NULL) {
+			fmd_hdl_free(hdl, scp, sizeof (*scp));
+			return (NULL);
+		}
+	}
+
+	fmd_buf_create(hdl, cp, SW_CASE_DATA_BUFNAME, sizeof (*datap));
+	datap->sc_version = SW_CASE_DATA_VERSION_INITIAL;
+	datap->sc_type = ct;
+
+	if (subdata)
+		swde_subdata(hdl, cp, ct, scp, subdata_vers, subdata,
+		    subdata_sz);
+
+	fmd_buf_write(hdl, cp, SW_CASE_DATA_BUFNAME, datap, sizeof (*datap));
+	swde_case_associate(hdl, cp, scp, subdata);
+
+	return (cp);
+}
+
+/*
+ * fmdo_close entry point for software-diagnosis
+ */
+void
+swde_close(fmd_hdl_t *hdl, fmd_case_t *cp)
+{
+	swde_case_t *scp = fmd_case_getspecific(hdl, cp);
+	swde_case_data_t *datap = &scp->swc_data;
+	swsub_case_close_func_t *closefunc;
+
+	if ((closefunc = sw_sub_case_close_func(hdl, datap->sc_type)) != NULL)
+		closefunc(hdl, cp);
+
+	/*
+	 * Now that the sub-de has had a chance to clean up, do some ourselves.
+	 * Note that we free the sub-de-private subdata structure.
+	 */
+
+	if (scp->swc_subdata) {
+		fmd_hdl_free(hdl, scp->swc_subdata, datap->sc_sub_bufsz);
+		fmd_buf_destroy(hdl, cp, datap->sc_sub_bufname);
+	}
+
+	fmd_buf_destroy(hdl, cp, SW_CASE_DATA_BUFNAME);
+
+	fmd_hdl_free(hdl, scp, sizeof (*scp));
+}
+
+fmd_case_t *
+swde_case_first(fmd_hdl_t *hdl, id_t who)
+{
+	enum sw_casetype ct = sw_id_to_casetype(hdl, who);
+	swde_case_t *scp;
+	fmd_case_t *cp;
+
+	if (ct == SW_CASE_NONE)
+		fmd_hdl_abort(hdl, "swde_case_first for type SW_CASE_NONE\n");
+
+	for (cp = fmd_case_next(hdl, NULL); cp; cp = fmd_case_next(hdl, cp)) {
+		scp = fmd_case_getspecific(hdl, cp);
+		if (scp->swc_data.sc_type == ct)
+			break;
+	}
+
+	return (cp);
+}
+
+fmd_case_t *
+swde_case_next(fmd_hdl_t *hdl, fmd_case_t *lastcp)
+{
+	swde_case_t *scp;
+	fmd_case_t *cp;
+	int ct;
+
+	if (lastcp == NULL)
+		fmd_hdl_abort(hdl, "swde_case_next called for NULL lastcp\n");
+
+	scp = fmd_case_getspecific(hdl, lastcp);
+	ct = scp->swc_data.sc_type;
+
+	cp = lastcp;
+	while ((cp = fmd_case_next(hdl, cp)) != NULL) {
+		scp = fmd_case_getspecific(hdl, cp);
+		if (scp->swc_data.sc_type == ct)
+			break;
+	}
+
+	return (cp);
+}
+
+void *
+swde_case_data(fmd_hdl_t *hdl, fmd_case_t *cp, uint32_t *svp)
+{
+	swde_case_t *scp = fmd_case_getspecific(hdl, cp);
+	swde_case_data_t *datap = &scp->swc_data;
+
+	if (svp != NULL && scp->swc_subdata)
+		*svp = datap->sc_sub_bufvers;
+
+	return (scp->swc_subdata);
+}
+
+void
+swde_case_data_write(fmd_hdl_t *hdl, fmd_case_t *cp)
+{
+	swde_case_t *scp = fmd_case_getspecific(hdl, cp);
+	swde_case_data_t *datap = &scp->swc_data;
+
+	if (scp->swc_subdata == NULL)
+		return;
+
+	fmd_buf_write(hdl, cp, scp->swc_data.sc_sub_bufname,
+	    scp->swc_subdata, datap->sc_sub_bufsz);
+}
+
+void
+swde_case_data_upgrade(fmd_hdl_t *hdl, fmd_case_t *cp, uint32_t subdata_vers,
+    void *subdata, size_t subdata_sz)
+{
+	swde_case_t *scp = fmd_case_getspecific(hdl, cp);
+	swde_case_data_t *datap = &scp->swc_data;
+
+	if (scp->swc_subdata) {
+		fmd_buf_destroy(hdl, cp, datap->sc_sub_bufname);
+		fmd_hdl_free(hdl, scp->swc_subdata, datap->sc_sub_bufsz);
+		scp->swc_subdata = NULL;
+		datap->sc_sub_bufsz = 0;
+		datap->sc_sub_bufname[0] = '\0';
+	}
+
+	if (subdata != NULL) {
+		scp->swc_subdata = subdata;
+		swde_subdata(hdl, cp, datap->sc_type, scp, subdata_vers,
+		    subdata, subdata_sz);
+	}
+
+	fmd_buf_write(hdl, scp->swc_fmdcase, SW_CASE_DATA_BUFNAME,
+	    datap, sizeof (*datap));
+}
+
+static void
+swde_case_verify(fmd_hdl_t *hdl, fmd_case_t *cp)
+{
+	swde_case_t *scp = fmd_case_getspecific(hdl, cp);
+	swde_case_data_t *datap = &scp->swc_data;
+	sw_case_vrfy_func_t *vrfy_func;
+
+	if ((vrfy_func = sw_sub_case_vrfy_func(hdl, datap->sc_type)) != NULL) {
+		if (vrfy_func(hdl, cp) == 0)
+			fmd_case_close(hdl, cp);
+	}
+}
+
+void
+swde_case_init(fmd_hdl_t *hdl)
+{
+	fmd_case_t *cp;
+
+	for (cp = fmd_case_next(hdl, NULL); cp; cp = fmd_case_next(hdl, cp)) {
+		swde_case_unserialize(hdl, cp);
+		swde_case_verify(hdl, cp);
+	}
+}
+
+/*ARGSUSED*/
+void
+swde_case_fini(fmd_hdl_t *hdl)
+{
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fm/modules/common/sw-diag-response/software-diagnosis/swde_main.c	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,67 @@
+/*
+ * 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 "../common/sw_impl.h"
+
+static const fmd_prop_t swde_props[] = {
+	{ "enable", FMD_TYPE_BOOL, "true" },
+	{ NULL, 0, NULL }
+};
+
+static const fmd_hdl_ops_t swde_ops = {
+	sw_recv,	/* fmdo_recv - provided by common code */
+	sw_timeout,	/* fmdo_timeout - provided by common code */
+	swde_close,	/* fmdo_close */
+	NULL,		/* fmdo_stats */
+	NULL,		/* fmdo_gc */
+	NULL,		/* fmdo_send */
+	NULL		/* fmdo_topo */
+};
+
+const fmd_hdl_info_t swde_info = {
+	"Software Diagnosis engine", "0.1", &swde_ops, swde_props
+};
+
+/*
+ * Subsidiary diagnosis "modules" that we host.
+ */
+static const struct sw_subinfo *subde[SW_SUB_MAX] = {
+	&smf_diag_info,
+	&panic_diag_info
+};
+
+void
+_fmd_init(fmd_hdl_t *hdl)
+{
+	if (sw_fmd_init(hdl, &swde_info, &subde))
+		swde_case_init(hdl);
+}
+
+void
+_fmd_fini(fmd_hdl_t *hdl)
+{
+	swde_case_fini(hdl);
+	sw_fmd_fini(hdl);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fm/modules/common/sw-diag-response/software-response/Makefile	Fri Jul 30 17:04:17 2010 +1000
@@ -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.
+#
+
+MODULE = software-response
+CLASS = common
+
+include ../Makefile.com
+
+SWRP_SRCS = swrp_main.c
+
+SUBRP_SRCS = $(SMF_RP_SRCS)
+
+SRCS = $(SWRP_SRCS) $(CMN_SRCS:%=../%) $(SUBRP_SRCS:%=../%)
+
+include ../../../Makefile.plugin
+
+CFLAGS += $(INCS)
+LINTFLAGS += $(INCS)
+LDLIBS += -L$(ROOTLIB)/fm -ltopo -lscf
+LDFLAGS += -R/usr/lib/fm
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fm/modules/common/sw-diag-response/software-response/software-response.conf	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,29 @@
+#
+# 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.
+#
+
+#
+# Configuration for the software-diagnosis diagnosis engine.
+#
+
+subscribe list.repaired
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fm/modules/common/sw-diag-response/software-response/swrp_main.c	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,64 @@
+/*
+ * 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 "../common/sw_impl.h"
+
+static const fmd_prop_t swrp_props[] = {
+	{ "enable", FMD_TYPE_BOOL, "true" },
+	{ NULL, 0, NULL }
+};
+
+static const fmd_hdl_ops_t swrp_ops = {
+	sw_recv,	/* fmdo_recv - provided by common code */
+	sw_timeout,	/* fmdo_timeout */
+	NULL,		/* fmdo_close */
+	NULL,		/* fmdo_stats */
+	NULL,		/* fmdo_gc */
+	NULL,		/* fmdo_send */
+	NULL		/* fmdo_topo */
+};
+
+const fmd_hdl_info_t swrp_info = {
+	"Software Response Agent", "0.1", &swrp_ops, swrp_props
+};
+
+/*
+ * Subsidiary response "modules" that we host.
+ */
+static const struct sw_subinfo *subrp[SW_SUB_MAX] = {
+	&smf_response_info,
+};
+
+void
+_fmd_init(fmd_hdl_t *hdl)
+{
+	(void) sw_fmd_init(hdl, &swrp_info, &subrp);
+}
+
+void
+_fmd_fini(fmd_hdl_t *hdl)
+{
+	sw_fmd_fini(hdl);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fm/modules/common/sw-diag-response/subsidiary/panic/panic.h	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+
+#ifndef _PANIC_H
+#define	_PANIC_H
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+#define	SW_SUNOS_PANIC_DETECTED	"ireport.os.sunos.panic.dump_pending_on_device"
+#define	SW_SUNOS_PANIC_FAILURE	"ireport.os.sunos.panic.savecore_failure"
+#define	SW_SUNOS_PANIC_AVAIL	"ireport.os.sunos.panic.dump_available"
+
+#define	SW_SUNOS_PANIC_DEFECT	"defect.sunos.kernel.panic"
+
+extern char *sw_panic_fmri2str(fmd_hdl_t *, nvlist_t *);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _PANIC_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fm/modules/common/sw-diag-response/subsidiary/panic/panic_diag.c	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,591 @@
+/*
+ * 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.
+ */
+
+/*
+ * Panic software-diagnosis subsidiary
+ *
+ * We model a system panic as a defect diagnosis in FMA. When a system
+ * panicks, savecore publishes events which we subscribe to here.
+ *
+ * Our driving events are all raised by savecore, run either from
+ * startup of the dumpadm service or interactively at the command line.
+ * The following describes the logic for the handling of these events.
+ *
+ * On reboot after panic we will run savecore as part of the dumpadm
+ * service startup; we run savecore even if savecore is otherwise
+ * disabled (ie dumpadm -n in effect) - we run savecore -c to check for
+ * a valid dump and raise the initial event.
+ *
+ * If savecore (or savecore -c) observes a valid dump pending on the
+ * device, it raises a "dump_pending_on_device" event provided this
+ * was not an FMA-initiated panic (for those we will replay ereports
+ * from the dump device as usual and make a diagnosis from those; we do
+ * not need to open a case for the panic).  We subscribe to the
+ * "dump_pending_on_device" event and use that to open a case;  we
+ * open a case requesting the same case uuid as the panic dump image
+ * has for the OS instance uuid - if that fails because of a duplicate
+ * uuid then we have already opened a case for this panic so no need
+ * to open another.
+ *
+ * Included in the "dump_pending_on_device" event is an indication of
+ * whether or not dumpadm is enabled.  If not (dumpadm -n in effect)
+ * then we do not expect any further events regarding this panic
+ * until such time as the admin runs savecore manually (if ever).
+ * So in this case we solve the case immediately after open.  If/when
+ * subsequent events arrive when savecore is run manually, we will toss
+ * them.
+ *
+ * If dumpadm is enabled then savecore, run from dumpadm service startup,
+ * will attempt to process the dump - either to copy it off the dump
+ * device (if saving compressed) or to uncompress it off the dump device.
+ * If this succeeds savecore raises a "dump_available" event which
+ * includes information on the directory it was saved in, the instance
+ * number, image uuid, compressed form or not, and whether the dump
+ * was complete (as per the dumphdr).  If the savecore fails for
+ * some reason then it exits and raises a "savecore_failure" event.
+ * These two events are raised even for FMA-initiated panics.
+ *
+ * We subscribe to both the "dump_available" and "savecore_failed" events,
+ * and in the handling thereof we will close the case opened earlier (if
+ * this is not an FMA-initiated panic).  On receipt of the initial
+ * "dump_available" event we also arm a timer for +10 minutes if
+ * dumpadm is enabled - if no "dump_available" or "savecore_failed" arrives
+ * in that time we will solve the case on timeout.
+ *
+ * When the timer fires we check whether the initial event for each panic
+ * case was received more than 30 minutes ago; if it was we solve the case
+ * with what we have.  If we're still within the waiting period we rearm
+ * for a further 10 minutes.  The timer is shared by all cases that we
+ * create, which is why the fire interval is shorter than the maximum time
+ * we are prepared to wait.
+ */
+
+#include <strings.h>
+#include <sys/panic.h>
+#include <alloca.h>
+#include <zone.h>
+
+#include "../../common/sw.h"
+#include "panic.h"
+
+#define	MAX_STRING_LEN 160
+
+static id_t myid;
+
+static id_t mytimerid;
+
+/*
+ * Our serialization structure type.
+ */
+#define	SWDE_PANIC_CASEDATA_VERS	1
+
+typedef struct swde_panic_casedata {
+	uint32_t scd_vers;		/* must be first member */
+	uint64_t scd_receive_time;	/* when we first knew of this panic */
+	size_t scd_nvlbufsz;		/* size of following buffer */
+					/* packed attr nvlist follows */
+} swde_panic_casedata_t;
+
+static struct {
+	fmd_stat_t swde_panic_diagnosed;
+	fmd_stat_t swde_panic_badclass;
+	fmd_stat_t swde_panic_noattr;
+	fmd_stat_t swde_panic_unexpected_fm_panic;
+	fmd_stat_t swde_panic_badattr;
+	fmd_stat_t swde_panic_badfmri;
+	fmd_stat_t swde_panic_noinstance;
+	fmd_stat_t swde_panic_nouuid;
+	fmd_stat_t swde_panic_dupuuid;
+	fmd_stat_t swde_panic_nocase;
+	fmd_stat_t swde_panic_notime;
+	fmd_stat_t swde_panic_nopanicstr;
+	fmd_stat_t swde_panic_nodumpdir;
+	fmd_stat_t swde_panic_nostack;
+	fmd_stat_t swde_panic_incomplete;
+	fmd_stat_t swde_panic_failed;
+	fmd_stat_t swde_panic_basecasedata;
+	fmd_stat_t swde_panic_failsrlz;
+} swde_panic_stats = {
+	{ "swde_panic_diagnosed", FMD_TYPE_UINT64,
+	    "panic defects published" },
+	{ "swde_panic_badclass", FMD_TYPE_UINT64,
+	    "incorrect event class received" },
+	{ "swde_panic_noattr", FMD_TYPE_UINT64,
+	    "malformed event - missing attr nvlist" },
+	{ "swde_panic_unexpected_fm_panic", FMD_TYPE_UINT64,
+	    "dump available for an fm_panic()" },
+	{ "swde_panic_badattr", FMD_TYPE_UINT64,
+	    "malformed event - invalid attr list" },
+	{ "swde_panic_badfmri", FMD_TYPE_UINT64,
+	    "malformed event - fmri2str fails" },
+	{ "swde_panic_noinstance", FMD_TYPE_UINT64,
+	    "malformed event - no instance number" },
+	{ "swde_panic_nouuid", FMD_TYPE_UINT64,
+	    "malformed event - missing uuid" },
+	{ "swde_panic_dupuuid", FMD_TYPE_UINT64,
+	    "duplicate events received" },
+	{ "swde_panic_nocase", FMD_TYPE_UINT64,
+	    "case missing for uuid" },
+	{ "swde_panic_notime", FMD_TYPE_UINT64,
+	    "missing crash dump time" },
+	{ "swde_panic_nopanicstr", FMD_TYPE_UINT64,
+	    "missing panic string" },
+	{ "swde_panic_nodumpdir", FMD_TYPE_UINT64,
+	    "missing crashdump save directory" },
+	{ "swde_panic_nostack", FMD_TYPE_UINT64,
+	    "missing panic stack" },
+	{ "swde_panic_incomplete", FMD_TYPE_UINT64,
+	    "missing panic incomplete" },
+	{ "swde_panic_failed", FMD_TYPE_UINT64,
+	    "missing panic failed" },
+	{ "swde_panic_badcasedata", FMD_TYPE_UINT64,
+	    "bad case data during timeout" },
+	{ "swde_panic_failsrlz", FMD_TYPE_UINT64,
+	    "failures to serialize case data" },
+};
+
+#define	BUMPSTAT(stat)		swde_panic_stats.stat.fmds_value.ui64++
+
+static nvlist_t *
+panic_sw_fmri(fmd_hdl_t *hdl, char *object)
+{
+	nvlist_t *fmri;
+	nvlist_t *sw_obj;
+	int err = 0;
+
+	fmri = fmd_nvl_alloc(hdl, FMD_SLEEP);
+	err |= nvlist_add_uint8(fmri, FM_VERSION, FM_SW_SCHEME_VERSION);
+	err |= nvlist_add_string(fmri, FM_FMRI_SCHEME, FM_FMRI_SCHEME_SW);
+
+	sw_obj = fmd_nvl_alloc(hdl, FMD_SLEEP);
+	err |= nvlist_add_string(sw_obj, FM_FMRI_SW_OBJ_PATH, object);
+	err |= nvlist_add_nvlist(fmri, FM_FMRI_SW_OBJ, sw_obj);
+	if (sw_obj)
+		nvlist_free(sw_obj);
+	if (!err)
+		return (fmri);
+	else
+		return (0);
+}
+
+static const char *dumpfiles[2] = { "unix.%lld", "vmcore.%lld" };
+static const char *dumpfiles_comp[2] = { "vmdump.%lld", NULL};
+
+static void
+swde_panic_solve(fmd_hdl_t *hdl, fmd_case_t *cp,
+    nvlist_t *attr, fmd_event_t *ep, boolean_t savecore_success)
+{
+	char *dumpdir, *path, *uuid;
+	nvlist_t *defect, *rsrc;
+	nvpair_t *nvp;
+	int i;
+
+	/*
+	 * Attribute members to include in event-specific defect
+	 * payload.  Some attributes will not be present for some
+	 * cases - e.g., if we timed out and solved the case without
+	 * a "dump_available" report.
+	 */
+	const char *toadd[] = {
+		"os-instance-uuid",	/* same as case uuid */
+		"panicstr",		/* for initial classification work */
+		"panicstack",		/* for initial classification work */
+		"crashtime",		/* in epoch time */
+		"panic-time",		/* Formatted crash time */
+	};
+
+	if (ep != NULL)
+		fmd_case_add_ereport(hdl, cp, ep);
+	/*
+	 * As a temporary solution we create and fmri in the sw scheme
+	 * in panic_sw_fmri. This should become a generic fmri constructor
+	 *
+	 * We need to user a resource FMRI which will have a sufficiently
+	 * unique string representation such that fmd will not see
+	 * repeated panic diagnoses (all using the same defect class)
+	 * as duplicates and discard later cases.  We can't actually diagnose
+	 * the panic to anything specific (e.g., a path to a module and
+	 * function/line etc therein).  We could pick on a generic
+	 * representative such as /kernel/genunix but that could lead
+	 * to misunderstanding.  So we choose a path based on <dumpdir>
+	 * and the OS instance UUID - "<dumpdir>/.<os-instance-uuid>".
+	 * There's no file at that path (*) but no matter.  We can't use
+	 * <dumpdir>/vmdump.N or similar because if savecore is disabled
+	 * or failed we don't have any file or instance number.
+	 *
+	 * (*) Some day it would seem tidier to keep all files to do
+	 * with a single crash (unix/vmcore/vmdump, analysis output etc)
+	 * in a distinct directory, and <dumpdir>/.<uuid> seems like a good
+	 * choice.  For compatability we'd symlink into it.  So that is
+	 * another reason for this choice - some day it may exist!
+	 */
+	(void) nvlist_lookup_string(attr, "dumpdir", &dumpdir);
+	(void) nvlist_lookup_string(attr, "os-instance-uuid", &uuid);
+	path = alloca(strlen(dumpdir) + 1 + 1 + 36 + 1);
+	/* LINTED: E_SEC_SPRINTF_UNBOUNDED_COPY */
+	(void) sprintf(path, "%s/.%s", dumpdir, uuid);
+	rsrc = panic_sw_fmri(hdl, path);
+
+	defect = fmd_nvl_create_defect(hdl, SW_SUNOS_PANIC_DEFECT,
+	    100, rsrc, NULL, rsrc);
+	nvlist_free(rsrc);
+
+	(void) nvlist_add_boolean_value(defect, "savecore-succcess",
+	    savecore_success);
+
+	if (savecore_success) {
+		boolean_t compressed;
+		int64_t instance;
+		const char **pathfmts;
+		char buf[2][32];
+		int files = 0;
+		char *arr[2];
+		int i;
+
+		(void) nvlist_lookup_int64(attr, "instance", &instance);
+		(void) nvlist_lookup_boolean_value(attr, "compressed",
+		    &compressed);
+
+		pathfmts = compressed ? &dumpfiles_comp[0] : &dumpfiles[0];
+
+		for (i = 0; i < 2; i++) {
+			if (pathfmts[i] == NULL) {
+				arr[i] = NULL;
+				continue;
+			}
+
+			(void) snprintf(buf[i], 32, pathfmts[i], instance);
+			arr[i] = buf[i];
+			files++;
+		}
+
+		(void) nvlist_add_string(defect, "dump-dir", dumpdir);
+		(void) nvlist_add_string_array(defect, "dump-files", arr,
+		    files);
+	} else {
+		char *rsn;
+
+		if (nvlist_lookup_string(attr, "failure-reason", &rsn) == 0)
+			(void) nvlist_add_string(defect, "failure-reason", rsn);
+	}
+
+	/*
+	 * Not all attributes will necessarily be available - eg if
+	 * dumpadm was not enabled there'll be no instance and dumpdir.
+	 */
+	for (i = 0; i < sizeof (toadd) / sizeof (toadd[0]); i++) {
+		if (nvlist_lookup_nvpair(attr, toadd[i], &nvp) == 0)
+			(void) nvlist_add_nvpair(defect, nvp);
+	}
+
+	fmd_case_add_suspect(hdl, cp, defect);
+	fmd_case_solve(hdl, cp);
+
+	/*
+	 * Close the case.  Do no free casedata - framework does that for us
+	 * on closure callback.
+	 */
+	fmd_case_close(hdl, cp);
+	BUMPSTAT(swde_panic_diagnosed);
+}
+
+/*ARGSUSED*/
+static void
+swde_panic_timeout(fmd_hdl_t *hdl, id_t timerid, void *data)
+{
+	fmd_case_t *cp = swde_case_first(hdl, myid);
+	swde_panic_casedata_t *cdp;
+	time_t now = time(NULL);
+	nvlist_t *attr;
+	int remain = 0;
+	uint32_t vers;
+
+	while (cp != NULL) {
+		cdp = swde_case_data(hdl, cp, &vers);
+		if (vers != SWDE_PANIC_CASEDATA_VERS)
+			fmd_hdl_abort(hdl, "case data version confused\n");
+
+		if (now > cdp->scd_receive_time + 30 * 60) {
+			if (nvlist_unpack((char *)cdp + sizeof (*cdp),
+			    cdp->scd_nvlbufsz, &attr, 0) == 0) {
+				swde_panic_solve(hdl, cp, attr, NULL, B_FALSE);
+				nvlist_free(attr);
+			} else {
+				BUMPSTAT(swde_panic_basecasedata);
+				fmd_case_close(hdl, cp);
+			}
+		} else {
+			remain++;
+		}
+
+
+		cp = swde_case_next(hdl, cp);
+	}
+
+	if (remain) {
+		mytimerid = sw_timer_install(hdl, myid, NULL, NULL,
+		    10ULL * NANOSEC * 60);
+	}
+}
+
+/*
+ * Our verify entry point is called for each of our open cases during
+ * module load.  We must return 0 for the case to be closed by our caller,
+ * or 1 to keep it (or if we have already closed it during this call).
+ */
+static int
+swde_panic_vrfy(fmd_hdl_t *hdl, fmd_case_t *cp)
+{
+	swde_panic_casedata_t *cdp;
+	time_t now = time(NULL);
+	nvlist_t *attr;
+	uint32_t vers;
+
+	cdp = swde_case_data(hdl, cp, &vers);
+
+	if (vers != SWDE_PANIC_CASEDATA_VERS)
+		return (0);	/* case will be closed */
+
+	if (now > cdp->scd_receive_time + 30 * 60) {
+		if (nvlist_unpack((char *)cdp + sizeof (*cdp),
+		    cdp->scd_nvlbufsz, &attr, 0) == 0) {
+			swde_panic_solve(hdl, cp, attr, NULL, B_FALSE);
+			nvlist_free(attr);
+			return (1);	/* case already closed */
+		} else {
+			return (0);	/* close case */
+		}
+	}
+
+	if (mytimerid != 0)
+		mytimerid = sw_timer_install(hdl, myid,
+		    NULL, NULL, 10ULL * NANOSEC * 60);
+
+	return (1);	/* retain case */
+}
+
+/*
+ * Handler for ireport.os.sunos.panic.dump_pending_on_device.
+ *
+ * A future RFE should try adding a means of avoiding diagnosing repeated
+ * defects on panic loops, which would just add to the mayhem and potentially
+ * log lots of calls through ASR.  Panics with similar enough panic
+ * strings and/or stacks should not diagnose to new defects with some
+ * period of time, for example.
+ */
+
+/*ARGSUSED*/
+void
+swde_panic_detected(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl,
+    const char *class, void *arg)
+{
+	boolean_t fm_panic, expect_savecore;
+	swde_panic_casedata_t *cdp;
+	nvlist_t *attr;
+	fmd_case_t *cp;
+	char *fmribuf;
+	char *uuid;
+	size_t sz;
+
+	fmd_hdl_debug(hdl, "swde_panic_detected\n");
+
+	if (nvlist_lookup_nvlist(nvl, FM_IREPORT_ATTRIBUTES, &attr) != 0) {
+		BUMPSTAT(swde_panic_noattr);
+		return;
+	}
+
+	if (nvlist_lookup_string(attr, "os-instance-uuid", &uuid) != 0) {
+		BUMPSTAT(swde_panic_nouuid);
+		return;
+	}
+
+	fmd_hdl_debug(hdl, "swde_panic_detected: OS instance %s\n", uuid);
+
+	if (nvlist_lookup_boolean_value(attr, "fm-panic", &fm_panic) != 0 ||
+	    fm_panic == B_TRUE) {
+		BUMPSTAT(swde_panic_unexpected_fm_panic);
+		return;
+	}
+
+	/*
+	 * Prepare serialization data to be associated with a new
+	 * case.  Our serialization data consists of a swde_panic_casedata_t
+	 * structure followed by a packed nvlist of the attributes of
+	 * the initial event.
+	 */
+	if (nvlist_size(attr, &sz, NV_ENCODE_NATIVE) != 0) {
+		BUMPSTAT(swde_panic_failsrlz);
+		return;
+	}
+
+	cdp = fmd_hdl_zalloc(hdl, sizeof (*cdp) + sz, FMD_SLEEP);
+	fmribuf = (char *)cdp + sizeof (*cdp);
+	cdp->scd_vers = SWDE_PANIC_CASEDATA_VERS;
+	cdp->scd_receive_time = time(NULL);
+	cdp->scd_nvlbufsz = sz;
+
+	/*
+	 * Open a case with UUID matching the the panicking kernel, add this
+	 * event to the case.
+	 */
+	if ((cp = swde_case_open(hdl, myid, uuid, SWDE_PANIC_CASEDATA_VERS,
+	    cdp, sizeof (*cdp) + sz)) == NULL) {
+		BUMPSTAT(swde_panic_dupuuid);
+		fmd_hdl_debug(hdl, "swde_case_open returned NULL - dup?\n");
+		fmd_hdl_free(hdl, cdp, sizeof (*cdp) + sz);
+		return;
+	}
+
+	fmd_case_setprincipal(hdl, cp, ep);
+
+	if (nvlist_lookup_boolean_value(attr, "will-attempt-savecore",
+	    &expect_savecore) != 0 || expect_savecore == B_FALSE) {
+		fmd_hdl_debug(hdl, "savecore not being attempted - "
+		    "solve now\n");
+		swde_panic_solve(hdl, cp, attr, ep, B_FALSE);
+		return;
+	}
+
+	/*
+	 * We expect to see either a "dump_available" or a "savecore_failed"
+	 * event before too long.  In case that never shows up, for whatever
+	 * reason, we want to be able to solve the case anyway.
+	 */
+	fmd_case_add_ereport(hdl, cp, ep);
+	(void) nvlist_pack(attr, &fmribuf, &sz, NV_ENCODE_NATIVE, 0);
+	swde_case_data_write(hdl, cp);
+
+	if (mytimerid == 0) {
+		mytimerid = sw_timer_install(hdl, myid, NULL, ep,
+		    10ULL * NANOSEC * 60);
+		fmd_hdl_debug(hdl, "armed timer\n");
+	} else {
+		fmd_hdl_debug(hdl, "timer already armed\n");
+	}
+}
+
+/*
+ * savecore has now run and saved a crash dump to the filesystem. It is
+ * either a compressed dump (vmdump.n) or uncompressed {unix.n, vmcore.n}
+ * Savecore has raised an ireport to say the dump is there.
+ */
+
+/*ARGSUSED*/
+void
+swde_panic_savecore_done(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl,
+    const char *class, void *arg)
+{
+	boolean_t savecore_success = (arg != NULL);
+	boolean_t fm_panic;
+	nvlist_t *attr;
+	fmd_case_t *cp;
+	char *uuid;
+
+	fmd_hdl_debug(hdl, "savecore_done (%s)\n", savecore_success ?
+	    "success" : "fail");
+
+	if (nvlist_lookup_nvlist(nvl, FM_IREPORT_ATTRIBUTES, &attr) != 0) {
+		BUMPSTAT(swde_panic_noattr);
+		return;
+	}
+
+	if (nvlist_lookup_boolean_value(attr, "fm-panic", &fm_panic) != 0 ||
+	    fm_panic == B_TRUE) {
+		return;		/* not expected, but just in case */
+	}
+
+	if (nvlist_lookup_string(attr, "os-instance-uuid", &uuid) != 0) {
+		BUMPSTAT(swde_panic_nouuid);
+		return;
+	}
+
+	/*
+	 * Find the case related to the panicking kernel; our cases have
+	 * the same uuid as the crashed OS image.
+	 */
+	cp = fmd_case_uulookup(hdl, uuid);
+	if (!cp) {
+		/* Unable to find the case. */
+		fmd_hdl_debug(hdl, "savecore_done: can't find case for "
+		    "image %s\n", uuid);
+		BUMPSTAT(swde_panic_nocase);
+		return;
+	}
+
+	fmd_hdl_debug(hdl, "savecore_done: solving case %s\n", uuid);
+	swde_panic_solve(hdl, cp, attr, ep, savecore_success);
+}
+
+const struct sw_disp swde_panic_disp[] = {
+	{ SW_SUNOS_PANIC_DETECTED, swde_panic_detected, NULL },
+	{ SW_SUNOS_PANIC_AVAIL, swde_panic_savecore_done, (void *)1 },
+	{ SW_SUNOS_PANIC_FAILURE, swde_panic_savecore_done, NULL },
+	/*
+	 * Something has to subscribe to every fault
+	 * or defect diagnosed in fmd.  We do that here, but throw it away.
+	 */
+	{ SW_SUNOS_PANIC_DEFECT, NULL, NULL },
+	{ NULL, NULL, NULL }
+};
+
+/*ARGSUSED*/
+int
+swde_panic_init(fmd_hdl_t *hdl, id_t id, const struct sw_disp **dpp,
+    int *nelemp)
+{
+	myid = id;
+
+	if (getzoneid() != GLOBAL_ZONEID)
+		return (SW_SUB_INIT_FAIL_VOLUNTARY);
+
+	(void) fmd_stat_create(hdl, FMD_STAT_NOALLOC,
+	    sizeof (swde_panic_stats) / sizeof (fmd_stat_t),
+	    (fmd_stat_t *)&swde_panic_stats);
+
+	fmd_hdl_subscribe(hdl, SW_SUNOS_PANIC_DETECTED);
+	fmd_hdl_subscribe(hdl, SW_SUNOS_PANIC_FAILURE);
+	fmd_hdl_subscribe(hdl, SW_SUNOS_PANIC_AVAIL);
+
+	*dpp = &swde_panic_disp[0];
+	*nelemp = sizeof (swde_panic_disp) / sizeof (swde_panic_disp[0]);
+	return (SW_SUB_INIT_SUCCESS);
+}
+
+void
+swde_panic_fini(fmd_hdl_t *hdl)
+{
+	if (mytimerid)
+		sw_timer_remove(hdl, myid, mytimerid);
+}
+
+const struct sw_subinfo panic_diag_info = {
+	"panic diagnosis",		/* swsub_name */
+	SW_CASE_PANIC,			/* swsub_casetype */
+	swde_panic_init,		/* swsub_init */
+	swde_panic_fini,		/* swsub_fini */
+	swde_panic_timeout,		/* swsub_timeout */
+	NULL,				/* swsub_case_close */
+	swde_panic_vrfy,		/* swsub_case_vrfy */
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fm/modules/common/sw-diag-response/subsidiary/smf/smf.h	Fri Jul 30 17:04:17 2010 +1000
@@ -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.
+ */
+
+#ifndef _SMF_H
+#define	_SMF_H
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+#define	TRANCLASS(leaf)		"ireport.os.smf.state-transition." leaf
+
+#define	SW_SMF_MAINT_DEFECT	"defect.sunos.smf.svc.maintenance"
+
+extern char *sw_smf_svcfmri2str(fmd_hdl_t *, nvlist_t *);
+extern char *sw_smf_svcfmri2shortstr(fmd_hdl_t *, nvlist_t *);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _SMF_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fm/modules/common/sw-diag-response/subsidiary/smf/smf_diag.c	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,305 @@
+/*
+ * 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.
+ */
+
+/*
+ * SMF software-diagnosis subsidiary
+ *
+ * We model service instances in maintenance state as a defect diagnosis
+ * in FMA.  When an instance transitions to maintenance state the SMF
+ * graph engine publishes an event which we subscribe to here, and diagnose
+ * a corresponding defect.
+ *
+ * We always solve a case immediately after opening it.  But we leave the
+ * case close action to the response agent which needs to cache case UUIDs.
+ * So in the normal case, where software-response is loaded and operational,
+ * our cases will transition to CLOSED state moments after we solve them.
+ * But if fmd restarts in the interim or if software-response is not loaded
+ * then our cases may hang around in SOLVED state for a while, which means
+ * we could iterate over them on receipt of new events.  But we don't -
+ * we blindly solve a new case for every new maintenance event received,
+ * and leave it to the fmd duplicate detection and history-based diagnosis
+ * logic to do the right thing.
+ *
+ * Our sibling SMF response subsidiary propogates fmadm-initiated repairs
+ * into SMF, and svcadm-initiated clears back into FMA.  In both cases
+ * the case is moved on to the RESOLVED state, even if fmd is unable to
+ * verify that the service is out of maintenance state (i.e., no longer
+ * isolated).  If the service immediately re-enters maintenance state then
+ * we diagnose a fresh case.  The history-based diagnosis changes in fmd
+ * "do the right thing" and avoid throwing away new cases as duplicates
+ * of old ones hanging around in the "resolved but not all usable again"
+ * state.
+ */
+
+#include <strings.h>
+#include <fm/libtopo.h>
+#include <fm/fmd_fmri.h>
+
+#include "../../common/sw.h"
+#include "smf.h"
+
+static id_t myid;
+
+static struct {
+	fmd_stat_t swde_smf_diagnosed;
+	fmd_stat_t swde_smf_bad_class;
+	fmd_stat_t swde_smf_no_attr;
+	fmd_stat_t swde_smf_bad_attr;
+	fmd_stat_t swde_smf_bad_fmri;
+	fmd_stat_t swde_smf_no_uuid;
+	fmd_stat_t swde_smf_no_reason_short;
+	fmd_stat_t swde_smf_no_reason_long;
+	fmd_stat_t swde_smf_no_svcname;
+	fmd_stat_t swde_smf_admin_maint_drop;
+	fmd_stat_t swde_smf_bad_nvlist_pack;
+	fmd_stat_t swde_smf_dupuuid;
+} swde_smf_stats = {
+	{ "swde_smf_diagnosed", FMD_TYPE_UINT64,
+	    "maintenance state defects published" },
+	{ "swde_smf_bad_class", FMD_TYPE_UINT64,
+	    "incorrect event class received" },
+	{ "swde_smf_no_attr", FMD_TYPE_UINT64,
+	    "malformed event - missing attr nvlist" },
+	{ "swde_smf_bad_attr", FMD_TYPE_UINT64,
+	    "malformed event - invalid attr list" },
+	{ "swde_smf_bad_fmri", FMD_TYPE_UINT64,
+	    "malformed event - fmri2str fails" },
+	{ "swde_smf_no_uuid", FMD_TYPE_UINT64,
+	    "malformed event - missing uuid" },
+	{ "swde_smf_no_reason_short", FMD_TYPE_UINT64,
+	    "SMF transition event had no reason-short" },
+	{ "swde_smf_no_reason_long", FMD_TYPE_UINT64,
+	    "SMF transition event had no reason-long" },
+	{ "swde_smf_no_svcname", FMD_TYPE_UINT64,
+	    "SMF transition event had no svc-string" },
+	{ "swde_smf_admin_maint_drop", FMD_TYPE_UINT64,
+	    "maintenance transitions requested by admin - no diagnosis" },
+	{ "swde_smf_bad_nvlist_pack", FMD_TYPE_UINT64,
+	    "failed nvlist_size or nvlist_pack" },
+	{ "swde_smf_dupuuid", FMD_TYPE_UINT64,
+	    "duplicate events received" },
+};
+
+#define	SWDE_SMF_CASEDATA_VERS		1
+
+typedef struct swde_smf_casedata {
+	uint32_t scd_vers;		/* must be first member */
+	size_t scd_nvlbufsz;		/* size of following buffer */
+					/* packed fmri nvlist follows */
+} swde_smf_casedata_t;
+
+#define	BUMPSTAT(stat)		swde_smf_stats.stat.fmds_value.ui64++
+
+/*ARGSUSED*/
+void
+swde_smf_recv(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl,
+    const char *class, void *arg)
+{
+	char *rsn = NULL, *rsnl = NULL, *svcname = NULL;
+	nvlist_t *attr, *svcfmri, *defect;
+	swde_smf_casedata_t *cdp;
+	fmd_case_t *cp;
+	char *fmribuf;
+	char *uuid;
+	size_t sz;
+
+	if (!fmd_nvl_class_match(hdl, nvl, TRANCLASS("maintenance"))) {
+		BUMPSTAT(swde_smf_bad_class);
+		return;
+	}
+
+	if (nvlist_lookup_nvlist(nvl, FM_IREPORT_ATTRIBUTES, &attr) != 0) {
+		BUMPSTAT(swde_smf_no_attr);
+		return;
+	}
+
+	if (nvlist_lookup_string(nvl, FM_IREPORT_UUID, &uuid) != 0) {
+		BUMPSTAT(swde_smf_no_uuid);
+		return;
+	}
+
+	if (nvlist_lookup_nvlist(attr, "svc", &svcfmri) != 0) {
+		BUMPSTAT(swde_smf_bad_attr);
+		return;
+	}
+
+	if (nvlist_lookup_string(attr, "reason-short", &rsn) != 0) {
+		BUMPSTAT(swde_smf_no_reason_short);
+		return;
+	}
+
+	if (nvlist_lookup_string(attr, "reason-long", &rsnl) != 0) {
+		BUMPSTAT(swde_smf_no_reason_long);
+		return;
+	}
+
+	if (nvlist_lookup_string(attr, "svc-string", &svcname) != 0) {
+		BUMPSTAT(swde_smf_no_svcname);
+		return;
+	}
+
+	if (strcmp(rsn, "administrative_request") == 0) {
+		BUMPSTAT(swde_smf_admin_maint_drop);
+		return;
+	}
+
+	/*
+	 * Our case checkpoint data, version 1.
+	 */
+	if (nvlist_size(svcfmri, &sz, NV_ENCODE_NATIVE) != 0) {
+		BUMPSTAT(swde_smf_bad_nvlist_pack);
+		return;
+	}
+	cdp = fmd_hdl_zalloc(hdl, sizeof (*cdp) + sz, FMD_SLEEP);
+	cdp->scd_vers = SWDE_SMF_CASEDATA_VERS;
+	fmribuf = (char *)cdp + sizeof (*cdp);
+	cdp->scd_nvlbufsz = sz;
+	(void) nvlist_pack(svcfmri, &fmribuf, &sz, NV_ENCODE_NATIVE, 0);
+
+	/*
+	 * Open a case with UUID matching the originating event, and no
+	 * associated serialization data.  Create a defect and add it to
+	 * the case, and link the originating event to the case.  This
+	 * call will return NULL if a case with the requested UUID already
+	 * exists, which would mean we are processing an event twice so
+	 * we can discard.
+	 */
+	if ((cp = swde_case_open(hdl, myid, uuid, SWDE_SMF_CASEDATA_VERS,
+	    (void *)cdp, sizeof (*cdp) + sz)) == NULL) {
+		BUMPSTAT(swde_smf_dupuuid);
+		fmd_hdl_free(hdl, cdp, sizeof (*cdp) + sz);
+		return;
+	}
+
+	defect = fmd_nvl_create_defect(hdl, SW_SMF_MAINT_DEFECT,
+	    100, svcfmri, NULL, svcfmri);
+	if (rsn != NULL)
+		(void) nvlist_add_string(defect, "reason-short", rsn);
+	if (rsnl != NULL)
+		(void) nvlist_add_string(defect, "reason-long", rsnl);
+	if (svcname != NULL)
+		(void) nvlist_add_string(defect, "svc-string", svcname);
+	fmd_case_add_suspect(hdl, cp, defect);
+	fmd_case_add_ereport(hdl, cp, ep);
+
+	/*
+	 * Now solve the case, and immediately close it.  Although the
+	 * resource is already isolated (SMF put it in maintenance state)
+	 * we do not immediately close the case here - our sibling response
+	 * logic will do that after caching the case UUID.
+	 */
+	fmd_case_solve(hdl, cp);
+	BUMPSTAT(swde_smf_diagnosed);
+}
+
+/*
+ * In the normal course of events we keep in sync with SMF through the
+ * maintenance enter/clear events it raises.  Even if a maintenance
+ * state is cleared using svcadm while fmd is not running, the event
+ * will pend and be consumed when fmd does start and we'll close the
+ * case (in the response agent).
+ *
+ * But is is possible for discontinuities to produce some confusion:
+ *
+ *	- if an instance is in maintenance state (and so shown in svcs -x
+ *	  and fmadm faulty output) at the time we clone a new boot
+ *	  environment then when we boot the new BE we can be out of
+ *	  sync if the instance is cleared when we boot there
+ *
+ *	- meddling with /var/fm state - eg manual clear of files there,
+ *	  or restore of old state
+ *
+ * So as an extra guard we have a case verify function which is called
+ * at fmd restart (module load for software-diagnosis).  We must
+ * return 0 to close the case, non-zero to retain it.
+ */
+int
+swde_smf_vrfy(fmd_hdl_t *hdl, fmd_case_t *cp)
+{
+	swde_smf_casedata_t *cdp;
+	nvlist_t *svcfmri;
+	uint32_t v;
+	int rv;
+
+	cdp = swde_case_data(hdl, cp, &v);
+
+	if (cdp == NULL || v != 1)
+		return (0);	/* bad or damaged - just close */
+
+	if (nvlist_unpack((char *)cdp + sizeof (*cdp),
+	    cdp->scd_nvlbufsz, &svcfmri, 0) != 0)
+		return (0);	/* ditto */
+
+	switch (fmd_nvl_fmri_service_state(hdl, svcfmri)) {
+	case FMD_SERVICE_STATE_UNUSABLE:
+		/*
+		 * Keep case iff in maintenance state
+		 */
+		rv = 1;
+		break;
+
+	default:
+		/*
+		 * Discard the case for all other states - cleared,
+		 * service no longer exists, ... whatever.
+		 */
+		rv = 0;
+		break;
+	}
+
+	nvlist_free(svcfmri);
+	return (rv);
+}
+
+const struct sw_disp swde_smf_disp[] = {
+	{ TRANCLASS("maintenance"), swde_smf_recv, NULL },
+	{ NULL, NULL, NULL }
+};
+
+/*ARGSUSED*/
+int
+swde_smf_init(fmd_hdl_t *hdl, id_t id, const struct sw_disp **dpp, int *nelemp)
+{
+	myid = id;
+
+	(void) fmd_stat_create(hdl, FMD_STAT_NOALLOC, sizeof (swde_smf_stats) /
+	    sizeof (fmd_stat_t), (fmd_stat_t *)&swde_smf_stats);
+
+	fmd_hdl_subscribe(hdl, TRANCLASS("maintenance"));
+
+	*dpp = &swde_smf_disp[0];
+	*nelemp = sizeof (swde_smf_disp) / sizeof (swde_smf_disp[0]);
+	return (SW_SUB_INIT_SUCCESS);
+}
+
+const struct sw_subinfo smf_diag_info = {
+	"smf diagnosis",		/* swsub_name */
+	SW_CASE_SMF,			/* swsub_casetype */
+	swde_smf_init,			/* swsub_init */
+	NULL,				/* swsub_fini */
+	NULL,				/* swsub_timeout */
+	NULL,				/* swsub_case_close */
+	swde_smf_vrfy,			/* swsub_case_vrfy */
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fm/modules/common/sw-diag-response/subsidiary/smf/smf_response.c	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,543 @@
+/*
+ * 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.
+ */
+
+/*
+ * SMF software-response subsidiary
+ */
+
+#include <strings.h>
+#include <fm/libtopo.h>
+#include <libscf.h>
+#include <sys/fm/protocol.h>
+#include <fm/fmd_fmri.h>
+
+#include "../../common/sw.h"
+#include "smf.h"
+
+static struct {
+	fmd_stat_t swrp_smf_repairs;
+	fmd_stat_t swrp_smf_clears;
+	fmd_stat_t swrp_smf_closed;
+	fmd_stat_t swrp_smf_wrongclass;
+	fmd_stat_t swrp_smf_badlist;
+	fmd_stat_t swrp_smf_badresource;
+	fmd_stat_t swrp_smf_badclrevent;
+	fmd_stat_t swrp_smf_noloop;
+	fmd_stat_t swrp_smf_suppressed;
+	fmd_stat_t swrp_smf_cachefull;
+} swrp_smf_stats = {
+	{ "swrp_smf_repairs", FMD_TYPE_UINT64,
+	    "repair events received for propogation to SMF" },
+	{ "swrp_smf_clears", FMD_TYPE_UINT64,
+	    "notifications from SMF of exiting maint state" },
+	{ "swrp_smf_closed", FMD_TYPE_UINT64,
+	    "cases closed" },
+	{ "swrp_smf_wrongclass", FMD_TYPE_UINT64,
+	    "unexpected event class received" },
+	{ "swrp_smf_badlist", FMD_TYPE_UINT64,
+	    "list event with invalid structure" },
+	{ "swrp_smf_badresource", FMD_TYPE_UINT64,
+	    "list.repaired with smf fault but bad svc fmri" },
+	{ "swrp_smf_badclrevent", FMD_TYPE_UINT64,
+	    "maint clear event from SMF malformed" },
+	{ "swrp_smf_noloop", FMD_TYPE_UINT64,
+	    "avoidance of smf->fmd->smf repairs propogations" },
+	{ "swrp_smf_suppressed", FMD_TYPE_UINT64,
+	    "not propogated to smf because no longer in maint" },
+	{ "swrp_smf_cachefull", FMD_TYPE_UINT64,
+	    "uuid cache full" },
+};
+
+#define	BUMPSTAT(stat)		swrp_smf_stats.stat.fmds_value.ui64++
+
+#define	CACHE_NENT_INC		16
+#define	CACHE_NENT_MAX		128
+
+struct smf_uuid_cache_ent {
+	char uuid[37];
+	char fmristr[90];
+	uint8_t mark;
+};
+
+#define	CACHE_VERSION		1
+
+struct smf_uuid_cache {
+	uint32_t version;			/* Version */
+	uint32_t nentries;			/* Real size of array below */
+	struct smf_uuid_cache_ent entry[1];	/* Cache entries */
+};
+
+static struct smf_uuid_cache *uuid_cache;
+
+#define	UUID_CACHE_BUFNAME	"uuid_cache"
+
+static void
+uuid_cache_grow(fmd_hdl_t *hdl)
+{
+	struct smf_uuid_cache *newcache;
+	size_t newsz;
+	uint32_t n;
+
+	n = (uuid_cache == NULL ? 0 : uuid_cache->nentries) + CACHE_NENT_INC;
+	newsz = sizeof (struct smf_uuid_cache) + (n - 1) *
+	    sizeof (struct smf_uuid_cache_ent);
+
+	newcache = fmd_hdl_zalloc(hdl, newsz, FMD_SLEEP);
+	newcache->version = CACHE_VERSION;
+	newcache->nentries = n;
+
+	if (uuid_cache != NULL) {
+		uint32_t oldn = uuid_cache->nentries;
+		size_t oldsz = sizeof (struct smf_uuid_cache) +
+		    (oldn - 1) * sizeof (struct smf_uuid_cache_ent);
+
+		bcopy(&uuid_cache->entry[0], &newcache->entry[0], oldsz);
+		fmd_hdl_free(hdl, uuid_cache, oldsz);
+		fmd_buf_destroy(hdl, NULL, UUID_CACHE_BUFNAME);
+	}
+
+	uuid_cache = newcache;
+	fmd_buf_create(hdl, NULL, UUID_CACHE_BUFNAME, newsz);
+}
+
+static void
+uuid_cache_persist(fmd_hdl_t *hdl)
+{
+	size_t sz = sizeof (struct smf_uuid_cache) +
+	    (uuid_cache->nentries - 1) * sizeof (struct smf_uuid_cache_ent);
+
+	fmd_buf_write(hdl, NULL, UUID_CACHE_BUFNAME, uuid_cache, sz);
+}
+
+/*
+ * Garbage-collect the uuid cache.  Any cases that are already resolved
+ * we do not need an entry for.  If a case is not resolved but the
+ * service involved in that case is no longer in maintenance state
+ * then we've lost sync somehow, so repair the asru (which will
+ * also resolve the case).
+ */
+static void
+uuid_cache_gc(fmd_hdl_t *hdl)
+{
+	struct smf_uuid_cache_ent *entp;
+	topo_hdl_t *thp = NULL;
+	nvlist_t *svcfmri;
+	char *svcname;
+	int err, i;
+
+	for (i = 0; i < uuid_cache->nentries; i++) {
+		entp = &uuid_cache->entry[i];
+
+		if (entp->uuid[0] == '\0')
+			continue;
+
+		if (fmd_case_uuisresolved(hdl, entp->uuid)) {
+			bzero(entp->uuid, sizeof (entp->uuid));
+			bzero(entp->fmristr, sizeof (entp->fmristr));
+			entp->mark = 0;
+		} else {
+			if (thp == NULL)
+				thp = fmd_hdl_topo_hold(hdl, TOPO_VERSION);
+
+			if (topo_fmri_str2nvl(thp, entp->fmristr, &svcfmri,
+			    &err) != 0) {
+				fmd_hdl_error(hdl, "str2nvl failed for %s\n",
+				    entp->fmristr);
+				continue;
+			}
+
+			if (fmd_nvl_fmri_service_state(hdl, svcfmri) !=
+			    FMD_SERVICE_STATE_UNUSABLE) {
+				svcname = sw_smf_svcfmri2shortstr(hdl, svcfmri);
+				(void) fmd_repair_asru(hdl, entp->fmristr);
+				fmd_hdl_strfree(hdl, svcname);
+			}
+
+			nvlist_free(svcfmri);
+		}
+	}
+
+	if (thp)
+		fmd_hdl_topo_rele(hdl, thp);
+
+	uuid_cache_persist(hdl);
+}
+
+static void
+uuid_cache_restore(fmd_hdl_t *hdl)
+{
+	size_t sz = fmd_buf_size(hdl, NULL, UUID_CACHE_BUFNAME);
+
+	if (sz == 0)
+		return;
+
+	uuid_cache = fmd_hdl_alloc(hdl, sz, FMD_SLEEP);
+	fmd_buf_read(hdl, NULL, UUID_CACHE_BUFNAME, uuid_cache, sz);
+
+	/*
+	 * Garbage collect now, not just for tidiness but also to help
+	 * fmd and smf state stay in sync at module startup.
+	 */
+	uuid_cache_gc(hdl);
+}
+
+/*
+ * Add the UUID of an SMF maintenance defect case to our cache and
+ * record the associated full svc FMRI string for the case.
+ */
+static void
+swrp_smf_cache_add(fmd_hdl_t *hdl, char *uuid, char *fmristr)
+{
+	struct smf_uuid_cache_ent *entp = NULL;
+	int gced = 0;
+	int i;
+
+	if (uuid_cache == NULL)
+		uuid_cache_grow(hdl);
+
+	/*
+	 * If we somehow already have an entry for this uuid then
+	 * return leaving it undisturbed.
+	 */
+	for (i = 0; i < uuid_cache->nentries; i++) {
+		if (strcmp(uuid, uuid_cache->entry[i].uuid) == 0)
+			return;
+	}
+
+scan:
+	for (i = 0; i < uuid_cache->nentries; i++) {
+		if (uuid_cache->entry[i].uuid[0] == '\0') {
+			entp = &uuid_cache->entry[i];
+			break;
+		}
+	}
+
+	if (entp == NULL) {
+		uint32_t oldn = uuid_cache->nentries;
+
+		/*
+		 * Before growing the cache we try again after first
+		 * garbage-collecting the existing cache for any cases
+		 * that are confirmed as resolved.
+		 */
+		if (!gced) {
+			uuid_cache_gc(hdl);
+			gced = 1;
+			goto scan;
+		}
+
+		if (oldn < CACHE_NENT_MAX) {
+			uuid_cache_grow(hdl);
+			entp = &uuid_cache->entry[oldn];
+		} else {
+			BUMPSTAT(swrp_smf_cachefull);
+			return;
+		}
+	}
+
+	(void) strncpy(entp->uuid, uuid, sizeof (entp->uuid));
+	(void) strncpy(entp->fmristr, fmristr, sizeof (entp->fmristr));
+	uuid_cache_persist(hdl);
+}
+
+/*
+ * Mark cache entry/entries as resolved - if they match in either uuid
+ * (if not NULL) or fmristr (if not NULL) mark as resolved.  Return 1 iff
+ * an entry that matched on uuid was already marked, otherwise (entry
+ * matched on either, matched on uuid but not marked, not found).
+ */
+static int
+swrp_smf_cache_mark(fmd_hdl_t *hdl, char *uuid, char *fmristr)
+{
+	int dirty = 0;
+	int rv = 0;
+	int i;
+
+	if (uuid_cache == NULL)
+		return (0);
+
+	for (i = 0; i < uuid_cache->nentries; i++) {
+		struct smf_uuid_cache_ent *entp = &uuid_cache->entry[i];
+
+		if (entp->uuid[0] == '\0')
+			continue;
+
+		if (uuid && strcmp(uuid, entp->uuid) == 0) {
+			if (entp->mark)
+				rv = 1;
+			entp->mark = 1;
+			dirty++;
+		} else if (fmristr && strcmp(fmristr, entp->fmristr) == 0) {
+			entp->mark = 1;
+			dirty++;
+		}
+	}
+
+	if (dirty)
+		uuid_cache_persist(hdl);
+
+	return (rv);
+}
+
+/*
+ * We will receive list events for cases we are not interested in.  Test
+ * that this list has exactly one suspect and that it matches the maintenance
+ * defect.  Return the defect to the caller in the second argument,
+ * and the defect resource element in the third arg.
+ */
+static int
+suspect_is_maint_defect(fmd_hdl_t *hdl, nvlist_t *nvl,
+    nvlist_t **defectnvl, nvlist_t **rsrcnvl)
+{
+	nvlist_t **faults;
+	uint_t nfaults;
+
+	if (nvlist_lookup_nvlist_array(nvl, FM_SUSPECT_FAULT_LIST,
+	    &faults, &nfaults) != 0) {
+		BUMPSTAT(swrp_smf_badlist);
+		return (0);
+	}
+
+	if (nfaults != 1 ||
+	    !fmd_nvl_class_match(hdl, faults[0], SW_SMF_MAINT_DEFECT))
+		return (0);
+
+	if (nvlist_lookup_nvlist(faults[0], FM_FAULT_RESOURCE, rsrcnvl) != 0) {
+		BUMPSTAT(swrp_smf_badlist);
+		return (0);
+	}
+
+	*defectnvl = faults[0];
+
+	return (1);
+}
+
+/*
+ * Received newly-diagnosed list.suspect events that are for the
+ * maintenane defect we diagnose.  Close the case (the resource was already
+ * isolated by SMF) after cachng the case UUID.
+ */
+/*ARGSUSED*/
+static void
+swrp_smf_cacheuuid(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl,
+    const char *class, void *arg)
+{
+	nvlist_t *defect, *rsrc;
+	char *fmristr, *uuid;
+
+	if (nvlist_lookup_string(nvl, FM_SUSPECT_UUID, &uuid) != 0) {
+		BUMPSTAT(swrp_smf_badlist);
+		return;
+	}
+
+	if (!suspect_is_maint_defect(hdl, nvl, &defect, &rsrc))
+		return;
+
+	if ((fmristr = sw_smf_svcfmri2str(hdl, rsrc)) == NULL) {
+		BUMPSTAT(swrp_smf_badlist);
+		return;
+	}
+
+	swrp_smf_cache_add(hdl, uuid, fmristr);
+	fmd_hdl_strfree(hdl, fmristr);
+
+	if (!fmd_case_uuclosed(hdl, uuid)) {
+		fmd_case_uuclose(hdl, uuid);
+		BUMPSTAT(swrp_smf_closed);
+	}
+}
+
+/*ARGSUSED*/
+static void
+swrp_smf2fmd(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl,
+    const char *class, void *arg)
+{
+	nvlist_t *attr, *fmri;
+	char *fromstate;
+	char *fmristr;
+
+	if (!fmd_nvl_class_match(hdl, nvl, TRANCLASS("*"))) {
+		BUMPSTAT(swrp_smf_wrongclass);
+		return;
+	}
+
+	if (nvlist_lookup_nvlist(nvl, FM_IREPORT_ATTRIBUTES, &attr) != 0 ||
+	    nvlist_lookup_string(attr, "from-state", &fromstate) != 0) {
+		BUMPSTAT(swrp_smf_badclrevent);
+		return;
+	}
+
+	/*
+	 * Filter those not describing a transition out of maintenance.
+	 */
+	if (strcmp(fromstate, "maintenance") != 0)
+		return;
+
+	if (nvlist_lookup_nvlist(attr, "svc", &fmri) != 0) {
+		BUMPSTAT(swrp_smf_badclrevent);
+		return;
+	}
+
+	if ((fmristr = sw_smf_svcfmri2str(hdl, fmri)) == NULL) {
+		BUMPSTAT(swrp_smf_badclrevent);
+		return;
+	}
+
+	/*
+	 * Mark any UUID for a case against this service as resolved
+	 * in our cache.  When we fmd_repair_asru below fmd will emit
+	 * a list.repaired as a result, and our handling of that event
+	 * must not propogate the repair towards SMF (since the repair
+	 * was initiated via SMF itself and not via fmadm).
+	 */
+	(void) swrp_smf_cache_mark(hdl, NULL, fmristr);
+
+	(void) fmd_repair_asru(hdl, fmristr);
+	fmd_hdl_strfree(hdl, fmristr);
+	BUMPSTAT(swrp_smf_clears);
+}
+
+/*ARGSUSED*/
+static void
+swrp_fmd2smf(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl,
+    const char *class, void *arg)
+{
+	char *fmristr, *shrtfmristr;
+	nvlist_t *defect, *rsrc;
+	char *uuid;
+	int already;
+
+	if (strcmp(class, FM_LIST_REPAIRED_CLASS) != 0) {
+		BUMPSTAT(swrp_smf_wrongclass);
+		return;
+	}
+
+	if (nvlist_lookup_string(nvl, FM_SUSPECT_UUID, &uuid) != 0) {
+		BUMPSTAT(swrp_smf_badlist);
+		return;
+	}
+
+	if (!suspect_is_maint_defect(hdl, nvl, &defect, &rsrc))
+		return;
+
+	if ((fmristr = sw_smf_svcfmri2str(hdl, rsrc)) == NULL) {
+		BUMPSTAT(swrp_smf_badresource);
+		return;
+	}
+
+	already = swrp_smf_cache_mark(hdl, uuid, fmristr);
+	fmd_hdl_strfree(hdl, fmristr);
+
+	/*
+	 * If the cache already had a marked entry for this UUID then
+	 * this is a list.repaired arising from a SMF-initiated maintenance
+	 * clear (propogated with fmd_repair_asru above which then results
+	 * in a list.repaired) and so we should not propogate the repair
+	 * back towards SMF.  But do still force the case to RESOLVED state in
+	 * case fmd is unable to confirm the service no longer in maintenance
+	 * state (it may have failed again) so that a new case can be opened.
+	 */
+	fmd_case_uuresolved(hdl, uuid);
+	if (already) {
+		BUMPSTAT(swrp_smf_noloop);
+		return;
+	}
+
+	/*
+	 * Only propogate to SMF if we can see that service still
+	 * in maintenance state.  We're not synchronized with SMF
+	 * and this state could change at any time, but if we can
+	 * see it's not in maintenance state then things are obviously
+	 * moving (e.g., external svcadm active) so we don't poke
+	 * at SMF otherwise we confuse things or duplicate operations.
+	 */
+
+	if (fmd_nvl_fmri_service_state(hdl, rsrc) ==
+	    FMD_SERVICE_STATE_UNUSABLE) {
+		shrtfmristr = sw_smf_svcfmri2shortstr(hdl, rsrc);
+
+		if (shrtfmristr != NULL) {
+			(void) smf_restore_instance(shrtfmristr);
+			fmd_hdl_strfree(hdl, shrtfmristr);
+			BUMPSTAT(swrp_smf_repairs);
+		} else {
+			BUMPSTAT(swrp_smf_badresource);
+		}
+	} else {
+		BUMPSTAT(swrp_smf_suppressed);
+	}
+}
+
+const struct sw_disp swrp_smf_disp[] = {
+	{ TRANCLASS("*"), swrp_smf2fmd, NULL },
+	{ FM_LIST_SUSPECT_CLASS, swrp_smf_cacheuuid, NULL },
+	{ FM_LIST_REPAIRED_CLASS, swrp_fmd2smf, NULL },
+	{ NULL, NULL, NULL }
+};
+
+/*ARGSUSED*/
+int
+swrp_smf_init(fmd_hdl_t *hdl, id_t id, const struct sw_disp **dpp, int *nelemp)
+{
+	(void) fmd_stat_create(hdl, FMD_STAT_NOALLOC, sizeof (swrp_smf_stats) /
+	    sizeof (fmd_stat_t), (fmd_stat_t *)&swrp_smf_stats);
+
+	uuid_cache_restore(hdl);
+
+	/*
+	 * We need to subscribe to all SMF transition class events because
+	 * we need to look inside the payload to see which events indicate
+	 * a transition out of maintenance state.
+	 */
+	fmd_hdl_subscribe(hdl, TRANCLASS("*"));
+
+	/*
+	 * Subscribe to the defect class diagnosed for maintenance events.
+	 * The module will then receive list.suspect events including
+	 * these defects, and in our dispatch table above we list routing
+	 * for list.suspect.
+	 */
+	fmd_hdl_subscribe(hdl, SW_SMF_MAINT_DEFECT);
+
+	*dpp = &swrp_smf_disp[0];
+	*nelemp = sizeof (swrp_smf_disp) / sizeof (swrp_smf_disp[0]);
+	return (SW_SUB_INIT_SUCCESS);
+}
+
+/*ARGSUSED*/
+void
+swrp_smf_fini(fmd_hdl_t *hdl)
+{
+}
+
+const struct sw_subinfo smf_response_info = {
+	"smf repair",			/* swsub_name */
+	SW_CASE_NONE,			/* swsub_casetype */
+	swrp_smf_init,			/* swsub_init */
+	swrp_smf_fini,			/* swsub_fini */
+	NULL,				/* swsub_timeout */
+	NULL,				/* swsub_case_close */
+	NULL,				/* swsub_case_vrfy */
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fm/modules/common/sw-diag-response/subsidiary/smf/smf_util.c	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,80 @@
+/*
+ * 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.
+ */
+
+/*
+ * SMF software diagnosis engine components.
+ */
+
+#include <fm/libtopo.h>
+#include <strings.h>
+
+#include "../../common/sw.h"
+#include "smf.h"
+
+/*
+ * Given a "svc' scheme FMRI in nvlist form, produce a string form
+ * of the FMRI (with no short-hand).
+ */
+char *
+sw_smf_svcfmri2str(fmd_hdl_t *hdl, nvlist_t *fmri)
+{
+	char *fmristr = NULL;
+	topo_hdl_t *thp;
+	char *topostr;
+	int err;
+
+	thp = fmd_hdl_topo_hold(hdl, TOPO_VERSION);
+	if (topo_fmri_nvl2str(thp, fmri, &topostr, &err) == 0) {
+		fmristr = fmd_hdl_strdup(hdl, (const char *)topostr, FMD_SLEEP);
+		topo_hdl_strfree(thp, topostr);
+	}
+	fmd_hdl_topo_rele(hdl, thp);
+
+	return (fmristr);	/* caller must fmd_hdl_strfree */
+}
+
+/*
+ * Given a "svc" scheme FMRI in nvlist form, produce a short-hand form
+ * string FMRI "svc:/..." as generally used in SMF cmdline output.
+ */
+char *
+sw_smf_svcfmri2shortstr(fmd_hdl_t *hdl, nvlist_t *fmri)
+{
+	char *name, *inst, *bufp, *fullname;
+	size_t len;
+
+	if (nvlist_lookup_string(fmri, FM_FMRI_SVC_NAME, &name) != 0 ||
+	    nvlist_lookup_string(fmri, FM_FMRI_SVC_INSTANCE, &inst) != 0)
+		return (NULL);
+
+	len = strlen(name) + strlen(inst) + 8;
+	bufp = fmd_hdl_alloc(hdl, len, FMD_SLEEP);
+	(void) snprintf(bufp, len, "svc:/%s:%s", name, inst);
+
+	fullname = fmd_hdl_strdup(hdl, bufp, FMD_SLEEP);
+	fmd_hdl_free(hdl, bufp, len);
+
+	return (fullname);	/* caller must fmd_hdl_strfree */
+}
--- a/usr/src/cmd/fm/modules/common/syslog-msgs/Makefile	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/fm/modules/common/syslog-msgs/Makefile	Fri Jul 30 17:04:17 2010 +1000
@@ -19,10 +19,8 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
+# Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
 #
-#ident	"%Z%%M%	%I%	%E% SMI"
 
 MODULE = syslog-msgs
 CLASS = common
@@ -32,4 +30,4 @@
 
 LDFLAGS += -L $(ROOT)/usr/lib/fm  -R/usr/lib/fm
 LINTFLAGS += -L$(ROOT)/usr/lib/fm
-LDLIBS += -lfmd_msg
+LDLIBS += -lfmd_msg -lscf
--- a/usr/src/cmd/fm/modules/common/syslog-msgs/syslog.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/fm/modules/common/syslog-msgs/syslog.c	Fri Jul 30 17:04:17 2010 +1000
@@ -20,13 +20,13 @@
  */
 
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #include <sys/fm/protocol.h>
 #include <sys/strlog.h>
 #include <sys/log.h>
+#include <libscf.h>
 
 #include <fm/fmd_api.h>
 #include <fm/fmd_msg.h>
@@ -175,13 +175,84 @@
 	}
 }
 
+static void
+free_notify_prefs(fmd_hdl_t *hdl, nvlist_t **prefs, uint_t nprefs)
+{
+	int i;
+
+	for (i = 0; i < nprefs; i++) {
+		if (prefs[i])
+			nvlist_free(prefs[i]);
+	}
+
+	fmd_hdl_free(hdl, prefs, sizeof (nvlist_t *) * nprefs);
+}
+
+static int
+get_notify_prefs(fmd_hdl_t *hdl, nvlist_t *ev_nvl, nvlist_t ***pref_nvl,
+    uint_t *nprefs)
+{
+	nvlist_t *top_nvl, **np_nvlarr, *mech_nvl;
+	nvlist_t **tmparr;
+	int ret, i;
+	uint_t nelem, nslelem;
+
+	if ((ret = smf_notify_get_params(&top_nvl, ev_nvl)) != SCF_SUCCESS) {
+		ret = scf_error();
+		if (ret != SCF_ERROR_NOT_FOUND) {
+			fmd_hdl_debug(hdl, "Error looking up notification "
+			    "preferences (%s)", scf_strerror(ret));
+			return (ret);
+		}
+		return (ret);
+	}
+
+	if (nvlist_lookup_nvlist_array(top_nvl, SCF_NOTIFY_PARAMS, &np_nvlarr,
+	    &nelem) != 0) {
+		fmd_hdl_debug(hdl, "Malformed preference nvlist\n");
+		ret = SCF_ERROR_INVALID_ARGUMENT;
+		goto pref_done;
+	}
+
+	tmparr = fmd_hdl_alloc(hdl, nelem * sizeof (nvlist_t *), FMD_SLEEP);
+	nslelem = 0;
+
+	for (i = 0; i < nelem; i++) {
+		if (nvlist_lookup_nvlist(np_nvlarr[i], "syslog", &mech_nvl)
+		    == 0)
+			tmparr[nslelem++] = fmd_nvl_dup(hdl, mech_nvl,
+			    FMD_SLEEP);
+	}
+
+	if (nslelem != 0) {
+		size_t sz = nslelem * sizeof (nvlist_t *);
+
+		*pref_nvl = fmd_hdl_zalloc(hdl, sz, FMD_SLEEP);
+		*nprefs = nslelem;
+		bcopy(tmparr, *pref_nvl, sz);
+		ret = 0;
+	} else {
+		*pref_nvl = NULL;
+		*nprefs = 0;
+		ret = SCF_ERROR_NOT_FOUND;
+	}
+
+	fmd_hdl_free(hdl, tmparr, nelem * sizeof (nvlist_t *));
+pref_done:
+	nvlist_free(top_nvl);
+	return (ret);
+}
+
 /*ARGSUSED*/
 static void
 syslog_recv(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl, const char *class)
 {
 	uint8_t version;
-	boolean_t domsg;
+	boolean_t domsg, *active;
 	char *msg;
+	nvlist_t **prefs;
+	uint_t nprefs, nelems;
+	int ret;
 
 	if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 ||
 	    version > FM_SUSPECT_VERSION) {
@@ -197,6 +268,32 @@
 		return; /* event is not to be messaged */
 	}
 
+	ret = get_notify_prefs(hdl, nvl, &prefs, &nprefs);
+	if (ret == SCF_ERROR_NOT_FOUND) {
+		/*
+		 * No syslog notification preferences specified for this type of
+		 * event, so we're done
+		 */
+		fmd_hdl_debug(hdl, "No syslog notification preferences "
+		    "configured for class %s\n", class);
+		syslog_stats.no_msg.fmds_value.ui64++;
+		return;
+	} else if (ret != 0 || nvlist_lookup_boolean_array(prefs[0], "active",
+	    &active, &nelems)) {
+		fmd_hdl_debug(hdl, "Failed to retrieve notification "
+		    "preferences for class %s\n", class);
+		if (ret == 0)
+			free_notify_prefs(hdl, prefs, nprefs);
+		return;
+	} else if (!active[0]) {
+		fmd_hdl_debug(hdl, "Syslog notifications disabled for "
+		    "class %s\n", class);
+		syslog_stats.no_msg.fmds_value.ui64++;
+		free_notify_prefs(hdl, prefs, nprefs);
+		return;
+	}
+	free_notify_prefs(hdl, prefs, nprefs);
+
 	if ((msg = fmd_msg_gettext_nv(syslog_msghdl, NULL, nvl)) == NULL) {
 		fmd_hdl_debug(hdl, "failed to format message");
 		syslog_stats.bad_code.fmds_value.ui64++;
@@ -204,8 +301,10 @@
 	}
 
 	syslog_ctl.pri &= LOG_FACMASK;
-	if (strcmp(class, FM_LIST_RESOLVED_CLASS) == 0 ||
-	    strcmp(class, FM_LIST_REPAIRED_CLASS) == 0)
+	if (strcmp(class, FM_LIST_ISOLATED_CLASS) == 0 ||
+	    strcmp(class, FM_LIST_RESOLVED_CLASS) == 0 ||
+	    strcmp(class, FM_LIST_REPAIRED_CLASS) == 0 ||
+	    strcmp(class, FM_LIST_UPDATED_CLASS) == 0)
 		syslog_ctl.pri |= LOG_NOTICE;
 	else
 		syslog_ctl.pri |= LOG_ERR;
@@ -233,7 +332,7 @@
 };
 
 static const fmd_hdl_info_t fmd_info = {
-	"Syslog Messaging Agent", "1.0", &fmd_ops, fmd_props
+	"Syslog Messaging Agent", "1.1", &fmd_ops, fmd_props
 };
 
 void
@@ -303,9 +402,16 @@
 	(void) fmd_msg_url_set(syslog_msghdl, urlbase);
 	fmd_prop_free_string(hdl, urlbase);
 
+	/*
+	 * We subscribe to all FM events and then consult the notification
+	 * preferences in the serice configuration repo to determine whether
+	 * or not to emit a console message.
+	 */
 	fmd_hdl_subscribe(hdl, FM_LIST_SUSPECT_CLASS);
 	fmd_hdl_subscribe(hdl, FM_LIST_REPAIRED_CLASS);
 	fmd_hdl_subscribe(hdl, FM_LIST_RESOLVED_CLASS);
+	fmd_hdl_subscribe(hdl, FM_LIST_ISOLATED_CLASS);
+	fmd_hdl_subscribe(hdl, FM_LIST_UPDATED_CLASS);
 }
 
 /*ARGSUSED*/
--- a/usr/src/cmd/fm/modules/sun4u/fps-transport/fps-transport.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/fm/modules/sun4u/fps-transport/fps-transport.c	Fri Jul 30 17:04:17 2010 +1000
@@ -20,8 +20,7 @@
  */
 
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #include <stdlib.h>
@@ -69,6 +68,8 @@
 	{ "eagain", FMD_TYPE_UINT64, "events retried due to low memory" },
 };
 
+static sysevent_subattr_t *subattr;
+
 /*
  * event_transfer(sysevent_t *ev, void *arg)
  * takes a sysevent ev, extracts the nvlist of
@@ -122,6 +123,8 @@
 	if (h_event != NULL) {
 		(void) sysevent_evc_unsubscribe(h_event, SUBSCRIBE_ID);
 		(void) sysevent_evc_unbind(h_event);
+		if (subattr != NULL)
+			sysevent_subattr_free(subattr);
 	}
 
 	if (h_fmd != NULL && h_xprt != NULL)
@@ -154,8 +157,15 @@
 		fmd_hdl_unregister(hdl);
 	}
 
-	ret = sysevent_evc_subscribe(h_event, SUBSCRIBE_ID,
-	    SUBSCRIBE_FLAGS, event_transfer, NULL, 0);
+	if ((subattr = sysevent_subattr_alloc()) == NULL)
+		fmd_hdl_abort(hdl, "failed to allocate subscription "
+		    "attributes: %s");
+
+	sysevent_subattr_thrcreate(subattr, fmd_doorthr_create, NULL);
+	sysevent_subattr_thrsetup(subattr, fmd_doorthr_setup, NULL);
+
+	ret = sysevent_evc_xsubscribe(h_event, SUBSCRIBE_ID,
+	    SUBSCRIBE_FLAGS, event_transfer, NULL, 0, subattr);
 	if (ret != 0) {
 		if (ret == EEXIST) {
 			fmd_hdl_unregister(hdl);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fm/notify/Makefile	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,31 @@
+#
+# 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.
+#
+
+SUBDIRS = \
+	smtp-notify \
+	snmp-notify
+
+include ./Makefile.subdirs
+include ../../Makefile.cmd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fm/notify/Makefile.subdirs	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,40 @@
+#
+# 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.
+#
+
+.KEEP_STATE:
+
+all := TARGET += all
+clean := TARGET += clean
+clobber := TARGET += clobber
+install := TARGET += install
+install_h := TARGET += install_h
+lint := TARGET += lint
+
+all clean clobber install install_h lint: $(SUBDIRS)
+
+$(SUBDIRS): FRC
+	@cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fm/notify/notify-params.xml	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,103 @@
+<?xml version="1.0"?>
+<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
+<!--
+ 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
+
+	NOTE:  This service manifest is not editable; its contents will
+	be overwritten by package or patch operations, including
+	operating system upgrade.  Make customizations in a different
+	file.
+-->
+
+<service_bundle type='manifest' name='SUNWckr:notify-params'>
+
+<service
+	name='system/fm/notify-params'
+	type='service'
+	version='1'>
+
+	<!--
+	  Fault Manager notification parameters
+	-->
+
+	<exec_method
+	    type='method'
+	    name='start'
+	    exec=':true'
+	    timeout_seconds='0' />
+
+	<exec_method
+	    type='method'
+	    name='stop'
+	    exec=':true'
+	    timeout_seconds='0' />
+
+	<!--
+	    SMF property groups match the regex
+	    ([A-Za-z][ A-Za-z0-9.-]*,)?[A-Za-z][ A-Za-z0-9-]*
+	    swfma was added at the end to allow the class dotted names
+	    The macro SCF_NOTIFY_PG_POSTFIX in libscf.h translates to "swfma"
+	-->
+
+	<instance name='default' enabled='false'>
+		<!--
+		This sets the default notification preferences for FMA events
+		which is to send an email to root and emit a console message via
+		syslog for list.suspect events and to send an SNMP trap for
+		list.suspect, list.repaired and list.resolved events.
+		-->
+
+		<notification_parameters>
+			<event value='problem-diagnosed' />
+			<type name='smtp' active='true'>
+				<paramval name='to' value='root@localhost' />
+				<paramval name='reply-to'
+				    value='root@localhost' />
+			</type>
+			<type name='snmp' active='true' />
+			<type name='syslog' active='true' />
+		</notification_parameters>
+		<notification_parameters>
+			<event value='problem-repaired,problem-resolved' />
+			<type name='snmp' active='true' />
+		</notification_parameters>
+	</instance>
+
+	<stability value='Evolving' />
+
+	<template>
+	    	<common_name>
+		    	<loctext xml:lang='C'>
+				Solaris Fault Manager notification parameters
+			</loctext>
+		</common_name>
+
+		<documentation>
+		    	<manpage
+			    title='smf'
+			    section='5'
+			    manpath='/usr/share/man' />
+		</documentation>
+	</template>
+</service>
+
+</service_bundle>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fm/notify/smtp-notify/Makefile	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,28 @@
+#
+# 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.
+#
+
+SUBDIRS = $(MACH)
+
+include ../Makefile.subdirs
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fm/notify/smtp-notify/Makefile.com	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,109 @@
+#
+# 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.
+#
+
+.KEEP_STATE:
+.SUFFIXES:
+
+SRCS += smtp-notify.c
+OBJS = $(SRCS:%.c=%.o)
+LINTFILES = $(SRCS:%.c=%.ln)
+
+PROG = smtp-notify
+HELPER = process_msg_template.sh
+ROOTLIBFM = $(ROOT)/usr/lib/fm
+ROOTLIBNOTIFY = $(ROOT)/usr/lib/fm/notify
+ROOTPROG = $(ROOTLIBNOTIFY)/$(PROG)
+ROOTHELPER = $(ROOTLIBNOTIFY)/$(HELPER)
+
+ROOTMANIFESTDIR = $(ROOTSVCSYSTEM)/fm
+ROOTMANIFEST = $(ROOTMANIFESTDIR)/$(PROG).xml
+ROOTNOTIFYPARAMS = $(ROOTMANIFESTDIR)/notify-params.xml
+$(ROOTMANIFEST) := FILEMODE = 0444
+$(ROOTNOTIFYPARAMS) := FILEMODE = 0444
+
+$(NOT_RELEASE_BUILD)CPPFLAGS += -DDEBUG
+CPPFLAGS += -I. -I../common -I../../../../../lib/fm/libfmnotify/common
+C99MODE	= $(C99_ENABLE)
+CFLAGS += $(CTF_FLAGS) $(CCVERBOSE) $(XSTRCONST)
+LDLIBS += -L$(ROOT)/usr/lib/fm -lnvpair -lfmevent -lfmd_msg -lfmnotify \
+-lumem
+LDFLAGS += -R/usr/lib/fm
+LINTFLAGS += -mnu
+
+.NO_PARALLEL:
+.PARALLEL: $(OBJS) $(LINTFILES)
+
+all: $(PROG) $(HELPER)
+
+$(PROG): $(OBJS)
+	$(LINK.c) $(OBJS) -o $@ $(LDLIBS)
+	$(CTFMERGE) -L VERSION -o $@ $(OBJS)
+	$(POST_PROCESS)
+
+$(HELPER): ../common/$(HELPER)
+	$(CP) ../common/$(HELPER) .
+
+%.o: ../common/%.c
+	$(COMPILE.c) $<
+	$(CTFCONVERT_O)
+
+%.o: %.c
+	$(COMPILE.c) $<
+	$(CTFCONVERT_O)
+
+clean:
+	$(RM) $(OBJS) $(HELPER) $(LINTFILES)
+
+clobber: clean
+	$(RM) $(PROG)
+
+%.ln: ../common/%.c
+	$(LINT.c) -c $<
+
+%.ln: %.c
+	$(LINT.c) -c $<
+
+lint: $(LINTFILES)
+	$(LINT) $(LINTFLAGS) $(LINTFILES)
+
+$(ROOTLIBNOTIFY):
+	$(INS.dir)
+
+$(ROOTLIBNOTIFY)/%: %
+	$(INS.file)
+
+$(ROOTMANIFESTDIR):
+	$(INS.dir)
+
+$(ROOTMANIFESTDIR)/%.xml: ../common/%.xml
+	$(INS.file)
+
+$(ROOTMANIFESTDIR)/notify-params.xml: ../../notify-params.xml
+	$(INS.file) ../../notify-params.xml
+
+install_h:
+
+install: all $(ROOTLIBNOTIFY) $(ROOTPROG) $(ROOTHELPER) $(ROOTMANIFESTDIR) \
+$(ROOTMANIFEST) $(ROOTNOTIFYPARAMS)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fm/notify/smtp-notify/common/process_msg_template.sh	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,68 @@
+#!/bin/sh
+
+#
+# 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 is a simple helper script for smtp-notify which looks for certain
+# expansion macros, which we've committed and converts them to valid
+# libfmd_msg macros which directly reference event payload members.
+#
+# This allows us to change event payload names or alter the libfmd_msg
+# expansion macro syntax without breaking user-supplied message body
+# templates.
+#
+# We use all-caps for the committed macro names to avoid colliding
+# with an actual event payload member name.
+#
+# Usage: process_msg_template.sh <infile> <outfile> <code> <severity>
+#
+
+#
+# Verify template exists, is readable and is an ascii text file
+#
+if [ ! -e $1 ] || [ ! -r $1 ]; then
+	exit 1
+fi
+
+/usr/bin/file $1 | grep "ascii text" > /dev/null
+if [ $? != 0 ]; then
+	exit 1
+fi
+
+tmpfile1=$2;
+tmpfile2=`/usr/bin/mktemp -p /var/tmp`
+
+cat $1 | sed s/\%\<CODE\>/$3/g > $tmpfile1
+cat $tmpfile1 | sed s/\%\<UUID\>/\%\<uuid\>/g > $tmpfile2
+cat $tmpfile2 | sed s/\%\<CLASS\>/\%\<class\>/g > $tmpfile1
+cat $tmpfile1 | sed s/\%\<SEVERITY\>/$4/g > $tmpfile2
+cat $tmpfile2 | sed s/\%\<FMRI\>/svc\:\\/\%\<attr.svc.svc-name\>\:\%\<attr.svc.svc-instance\>/g > $tmpfile1
+cat $tmpfile1 | sed s/\%\<FROM-STATE\>/\%\<attr.from-state\>/g > $tmpfile2
+cat $tmpfile2 | sed s/\%\<TO-STATE\>/\%\<attr.to-state\>/g > $tmpfile1
+cat $tmpfile1 | sed s/\%\<HOSTNAME\>/\%h/g > $tmpfile2
+cat $tmpfile2 | sed s/\%\<URL\>/\%s/g > $tmpfile1
+rm -f $tmpfile2
+exit 0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fm/notify/smtp-notify/common/smtp-notify.c	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,906 @@
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <alloca.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libscf.h>
+#include <priv_utils.h>
+#include <netdb.h>
+#include <signal.h>
+#include <strings.h>
+#include <time.h>
+#include <unistd.h>
+#include <zone.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fm/fmd_msg.h>
+#include <fm/libfmevent.h>
+#include "libfmnotify.h"
+
+#define	SENDMAIL	"/usr/sbin/sendmail"
+#define	SVCNAME		"system/fm/smtp-notify"
+
+#define	XHDR_HOSTNAME		"X-FMEV-HOSTNAME"
+#define	XHDR_CLASS		"X-FMEV-CLASS"
+#define	XHDR_UUID		"X-FMEV-UUID"
+#define	XHDR_MSGID		"X-FMEV-CODE"
+#define	XHDR_SEVERITY		"X-FMEV-SEVERITY"
+#define	XHDR_FMRI		"X-FMEV-FMRI"
+#define	XHDR_FROM_STATE		"X-FMEV-FROM-STATE"
+#define	XHDR_TO_STATE		"X-FMEV-TO-STATE"
+
+/*
+ * Debug messages can be enabled by setting the debug property to true
+ *
+ * # svccfg -s svc:/system/fm/smtp-notify setprop config/debug=true
+ *
+ * Debug messages will be spooled to the service log at:
+ * <root>/var/svc/log/system-fm-smtp-notify:default.log
+ */
+#define	PP_SCRIPT "usr/lib/fm/notify/process_msg_template.sh"
+
+typedef struct email_pref
+{
+	int ep_num_recips;
+	char **ep_recips;
+	char *ep_reply_to;
+	char *ep_template_path;
+	char *ep_template;
+} email_pref_t;
+
+static nd_hdl_t *nhdl;
+static char hostname[MAXHOSTNAMELEN + 1];
+static const char optstr[] = "dfR:";
+static const char DEF_SUBJ_TEMPLATE[] = "smtp-notify-subject-template";
+static const char SMF_SUBJ_TEMPLATE[] = "smtp-notify-smf-subject-template";
+static const char FM_SUBJ_TEMPLATE[] = "smtp-notify-fm-subject-template";
+static const char IREPORT_MSG_TEMPLATE[] = "ireport-msg-template";
+static const char SMF_MSG_TEMPLATE[] = "ireport.os.smf-msg-template";
+
+static int
+usage(const char *pname)
+{
+	(void) fprintf(stderr, "Usage: %s [-df] [-R <altroot>]\n", pname);
+
+	(void) fprintf(stderr,
+	    "\t-d  enable debug mode\n"
+	    "\t-f  stay in foreground\n"
+	    "\t-R  specify alternate root\n");
+
+	return (1);
+}
+
+/*
+ * This function simply reads the file specified by "template" into a buffer
+ * and returns a pointer to that buffer (or NULL on failure).  The caller is
+ * responsible for free'ing the returned buffer.
+ */
+static char *
+read_template(const char *template)
+{
+	int fd;
+	struct stat statb;
+	char *buf;
+
+	if (stat(template, &statb) != 0) {
+		nd_error(nhdl, "Failed to stat %s (%s)", template,
+		    strerror(errno));
+		return (NULL);
+	}
+	if ((fd = open(template, O_RDONLY)) < 0) {
+		nd_error(nhdl, "Failed to open %s (%s)", template,
+		    strerror(errno));
+		return (NULL);
+	}
+	if ((buf = malloc(statb.st_size + 1)) == NULL) {
+		nd_error(nhdl, "Failed to allocate %d bytes", statb.st_size);
+		(void) close(fd);
+		return (NULL);
+	}
+	if (read(fd, buf, statb.st_size) < 0) {
+		nd_error(nhdl, "Failed to read in template (%s)",
+		    strerror(errno));
+		free(buf);
+		(void) close(fd);
+		return (NULL);
+	}
+	buf[statb.st_size] = '\0';
+	(void) close(fd);
+	return (buf);
+}
+
+/*
+ * This function runs a user-supplied message body template through a script
+ * which replaces the "committed" expansion macros with actual libfmd_msg
+ * expansion macros.
+ */
+static int
+process_template(nd_ev_info_t *ev_info, email_pref_t *eprefs)
+{
+	char pp_script[PATH_MAX], tmpfile[PATH_MAX], pp_cli[PATH_MAX];
+	int ret = -1;
+
+	(void) snprintf(pp_script, sizeof (pp_script), "%s%s",
+	    nhdl->nh_rootdir, PP_SCRIPT);
+	(void) snprintf(tmpfile, sizeof (tmpfile), "%s%s",
+	    nhdl->nh_rootdir, tmpnam(NULL));
+
+	/*
+	 * If it's an SMF event, then the diagcode and severity won't be part
+	 * of the event payload and so libfmd_msg won't be able to expand them.
+	 * Therefore we pass the code and severity into the script and let the
+	 * script do the expansion.
+	 */
+	/* LINTED: E_SEC_SPRINTF_UNBOUNDED_COPY */
+	(void) sprintf(pp_cli, "%s %s %s %s %s", pp_script,
+	    eprefs->ep_template_path, tmpfile, ev_info->ei_diagcode,
+	    ev_info->ei_severity);
+
+	nd_debug(nhdl, "Executing %s", pp_cli);
+	if (system(pp_cli) != -1)
+		if ((eprefs->ep_template = read_template(tmpfile)) != NULL)
+			ret = 0;
+
+	(void) unlink(tmpfile);
+	return (ret);
+}
+
+/*
+ * If someone does an "svcadm refresh" on us, then this function gets called,
+ * which rereads our service configuration.
+ */
+static void
+get_svc_config()
+{
+	int s = 0;
+	uint8_t val;
+
+	s = nd_get_boolean_prop(nhdl, SVCNAME, "config", "debug", &val);
+	nhdl->nh_debug = val;
+
+	s += nd_get_astring_prop(nhdl, SVCNAME, "config", "rootdir",
+	    &(nhdl->nh_rootdir));
+
+	if (s != 0)
+		nd_error(nhdl, "Failed to read retrieve service "
+		    "properties\n");
+}
+
+static void
+nd_sighandler(int sig)
+{
+	if (sig == SIGHUP)
+		get_svc_config();
+	else
+		nd_cleanup(nhdl);
+}
+
+/*
+ * This function constructs all the email headers and puts them into the
+ * "headers" buffer handle.  The caller is responsible for free'ing this
+ * buffer.
+ */
+static int
+build_headers(nd_hdl_t *nhdl, nd_ev_info_t *ev_info, email_pref_t *eprefs,
+    char **headers)
+{
+	const char *subj_key;
+	char *subj_fmt, *subj = NULL;
+	size_t len;
+	boolean_t is_smf_event = B_FALSE, is_fm_event = B_FALSE;
+
+	/*
+	 * Fetch and format the email subject.
+	 */
+	if (strncmp(ev_info->ei_class, "list.", 5) == 0) {
+		is_fm_event = B_TRUE;
+		subj_key = FM_SUBJ_TEMPLATE;
+	} else if (strncmp(ev_info->ei_class, "ireport.os.smf", 14) == 0) {
+		is_smf_event = B_TRUE;
+		subj_key = SMF_SUBJ_TEMPLATE;
+	} else {
+		subj_key = DEF_SUBJ_TEMPLATE;
+	}
+
+	if ((subj_fmt = fmd_msg_gettext_key(nhdl->nh_msghdl, NULL,
+	    FMNOTIFY_MSG_DOMAIN, subj_key)) == NULL) {
+		nd_error(nhdl, "Failed to contruct subject format");
+		return (-1); /* libfmd_msg error */
+	}
+
+	if (is_fm_event) {
+		/* LINTED: E_SEC_PRINTF_VAR_FMT */
+		len = snprintf(NULL, 0, subj_fmt, hostname,
+		    ev_info->ei_diagcode);
+		subj = alloca(len + 1);
+		/* LINTED: E_SEC_PRINTF_VAR_FMT */
+		(void) snprintf(subj, len + 1, subj_fmt, hostname,
+		    ev_info->ei_diagcode);
+	} else if (is_smf_event) {
+		/* LINTED: E_SEC_PRINTF_VAR_FMT */
+		len = snprintf(NULL, 0, subj_fmt, hostname, ev_info->ei_fmri,
+		    ev_info->ei_from_state, ev_info->ei_to_state);
+		subj = alloca(len + 1);
+		/* LINTED: E_SEC_PRINTF_VAR_FMT */
+		(void) snprintf(subj, len + 1, subj_fmt, hostname,
+		    ev_info->ei_fmri, ev_info->ei_from_state,
+		    ev_info->ei_to_state);
+	} else {
+		/* LINTED: E_SEC_PRINTF_VAR_FMT */
+		len = snprintf(NULL, 0, subj_fmt, hostname);
+		subj = alloca(len + 1);
+		/* LINTED: E_SEC_PRINTF_VAR_FMT */
+		(void) snprintf(subj, len + 1, subj_fmt, hostname);
+	}
+
+	/*
+	 * Here we add some X-headers to our mail message for use by mail
+	 * filtering agents.  We add headers for the following bits of event
+	 * data for all events
+	 *
+	 * hostname
+	 * msg id (diagcode)
+	 * event class
+	 * event severity
+	 * event uuid
+	 *
+	 * For SMF transition events, we'll have the following add'l X-headers
+	 *
+	 * from-state
+	 * to-state
+	 * service fmri
+	 *
+	 * We follow the X-headers with standard Reply-To and Subject headers.
+	 */
+	if (is_fm_event) {
+		len = snprintf(NULL, 0, "%s: %s\n%s: %s\n%s: %s\n%s: %s\n"
+		    "%s: %s\nReply-To: %s\nSubject: %s\n\n", XHDR_HOSTNAME,
+		    hostname, XHDR_CLASS, ev_info->ei_class, XHDR_UUID,
+		    ev_info->ei_uuid, XHDR_MSGID, ev_info->ei_diagcode,
+		    XHDR_SEVERITY, ev_info->ei_severity, eprefs->ep_reply_to,
+		    subj);
+
+		*headers = calloc(len + 1, sizeof (char));
+
+		(void) snprintf(*headers, len + 1, "%s: %s\n%s: %s\n%s: %s\n"
+		    "%s: %s\n%s: %s\nReply-To: %s\nSubject: %s\n\n",
+		    XHDR_HOSTNAME, hostname, XHDR_CLASS, ev_info->ei_class,
+		    XHDR_UUID, ev_info->ei_uuid, XHDR_MSGID,
+		    ev_info->ei_diagcode, XHDR_SEVERITY, ev_info->ei_severity,
+		    eprefs->ep_reply_to, subj);
+	} else if (is_smf_event) {
+		len = snprintf(NULL, 0, "%s: %s\n%s: %s\n%s: %s\n%s: %s\n"
+		    "%s: %s\n%s: %s\n%s: %s\nReply-To: %s\n"
+		    "Subject: %s\n\n", XHDR_HOSTNAME, hostname, XHDR_CLASS,
+		    ev_info->ei_class, XHDR_MSGID, ev_info->ei_diagcode,
+		    XHDR_SEVERITY, ev_info->ei_severity, XHDR_FMRI,
+		    ev_info->ei_fmri, XHDR_FROM_STATE, ev_info->ei_from_state,
+		    XHDR_TO_STATE, ev_info->ei_to_state, eprefs->ep_reply_to,
+		    subj);
+
+		*headers = calloc(len + 1, sizeof (char));
+
+		(void) snprintf(*headers, len + 1, "%s: %s\n%s: %s\n%s: %s\n"
+		    "%s: %s\n%s: %s\n%s: %s\n%s: %s\nReply-To: %s\n"
+		    "Subject: %s\n\n", XHDR_HOSTNAME, hostname, XHDR_CLASS,
+		    ev_info->ei_class, XHDR_MSGID, ev_info->ei_diagcode,
+		    XHDR_SEVERITY, ev_info->ei_severity, XHDR_FMRI,
+		    ev_info->ei_fmri, XHDR_FROM_STATE, ev_info->ei_from_state,
+		    XHDR_TO_STATE, ev_info->ei_to_state, eprefs->ep_reply_to,
+		    subj);
+	} else {
+		len = snprintf(NULL, 0, "%s: %s\n%s: %s\n%s: %s\n%s: %s\n"
+		    "Reply-To: %s\nSubject: %s\n\n", XHDR_HOSTNAME,
+		    hostname, XHDR_CLASS, ev_info->ei_class, XHDR_MSGID,
+		    ev_info->ei_diagcode, XHDR_SEVERITY, ev_info->ei_severity,
+		    eprefs->ep_reply_to, subj);
+
+		*headers = calloc(len + 1, sizeof (char));
+
+		(void) snprintf(*headers, len + 1, "%s: %s\n%s: %s\n%s: %s\n"
+		    "%s: %s\nReply-To: %s\nSubject: %s\n\n",
+		    XHDR_HOSTNAME, hostname, XHDR_CLASS, ev_info->ei_class,
+		    XHDR_MSGID, ev_info->ei_diagcode, XHDR_SEVERITY,
+		    ev_info->ei_severity, eprefs->ep_reply_to, subj);
+	}
+	return (0);
+}
+
+static void
+send_email(nd_hdl_t *nhdl, const char *headers, const char *body,
+    const char *recip)
+{
+	FILE *mp;
+	char sm_cli[PATH_MAX];
+
+	/*
+	 * Open a pipe to sendmail and pump out the email message
+	 */
+	(void) snprintf(sm_cli, PATH_MAX, "%s -t %s", SENDMAIL, recip);
+
+	nd_debug(nhdl, "Sending email notification to %s", recip);
+	if ((mp = popen(sm_cli, "w")) == NULL) {
+		nd_error(nhdl, "Failed to open pipe to %s (%s)", SENDMAIL,
+		    strerror(errno));
+		return;
+	}
+	if (fprintf(mp, "%s", headers) < 0)
+		nd_error(nhdl, "Failed to write to pipe (%s)", strerror(errno));
+
+	if (fprintf(mp, "%s\n.\n", body) < 0)
+		nd_error(nhdl, "Failed to write to pipe (%s)",
+		    strerror(errno));
+
+	(void) pclose(mp);
+}
+
+static void
+send_email_template(nd_hdl_t *nhdl, nd_ev_info_t *ev_info, email_pref_t *eprefs)
+{
+	char *msg, *headers;
+
+	if (build_headers(nhdl, ev_info, eprefs, &headers) != 0)
+		return;
+
+	/*
+	 * If the user specified a message body template, then we pass it
+	 * through a private interface in libfmd_msg, which will return a string
+	 * with any expansion tokens decoded.
+	 */
+	if ((msg = fmd_msg_decode_tokens(ev_info->ei_payload,
+	    eprefs->ep_template, ev_info->ei_url)) == NULL) {
+		nd_error(nhdl, "Failed to parse msg template");
+		free(headers);
+		return;
+	}
+	for (int i = 0; i < eprefs->ep_num_recips; i++)
+		send_email(nhdl, headers, msg, eprefs->ep_recips[i]);
+
+	free(msg);
+	free(headers);
+}
+
+static int
+get_email_prefs(nd_hdl_t *nhdl, fmev_t ev, email_pref_t **eprefs)
+{
+	nvlist_t **p_nvl = NULL;
+	email_pref_t *ep;
+	uint_t npref, tn1 = 0, tn2 = 0;
+	char **tmparr1, **tmparr2;
+	int r, ret = -1;
+
+	r = nd_get_notify_prefs(nhdl, "smtp", ev, &p_nvl, &npref);
+	if (r == SCF_ERROR_NOT_FOUND) {
+		/*
+		 * No email notification preferences specified for this type of
+		 * event, so we're done
+		 */
+		return (-1);
+	} else if (r != 0) {
+		nd_error(nhdl, "Failed to retrieve notification preferences "
+		    "for this event");
+		return (-1);
+	}
+
+	if ((ep = malloc(sizeof (email_pref_t))) == NULL) {
+		nd_error(nhdl, "Failed to allocate space for email preferences "
+		    "(%s)", strerror(errno));
+		goto eprefs_done;
+	}
+	(void) memset(ep, 0, sizeof (email_pref_t));
+
+	/*
+	 * For SMF state transition events, pref_nvl may contain two sets of
+	 * preferences, which will have to be merged.
+	 *
+	 * The "smtp" nvlist can contain up to four members:
+	 *
+	 * "active"	- boolean - used to toggle notfications
+	 * "to"		- a string array of email recipients
+	 * "reply-to"	- a string array containing the reply-to addresses
+	 *		- this is optional and defaults to root@localhost
+	 * "msg_template" - the pathname of a user-supplied message body
+	 *		template
+	 *
+	 * In the case that we have two sets of preferences, we will merge them
+	 * using the following rules:
+	 *
+	 * "active" will be set to true, if it is true in either set
+	 *
+	 * The "reply-to" and "to" lists will be merged, with duplicate email
+	 * addresses removed.
+	 */
+	if (npref == 2) {
+		boolean_t *act1, *act2;
+		char **arr1, **arr2, **strarr, **reparr1, **reparr2;
+		uint_t n1, n2, arrsz, repsz;
+
+		r = nvlist_lookup_boolean_array(p_nvl[0], "active", &act1, &n1);
+		r += nvlist_lookup_boolean_array(p_nvl[1], "active", &act2,
+		    &n2);
+		r += nvlist_lookup_string_array(p_nvl[0], "to", &arr1, &n1);
+		r += nvlist_lookup_string_array(p_nvl[1], "to", &arr2, &n2);
+
+		if (r != 0) {
+			nd_error(nhdl, "Malformed email notification "
+			    "preferences");
+			nd_dump_nvlist(nhdl, p_nvl[0]);
+			nd_dump_nvlist(nhdl, p_nvl[1]);
+			goto eprefs_done;
+		} else if (!act1[0] && !act2[0]) {
+			nd_debug(nhdl, "Email notification is disabled");
+			goto eprefs_done;
+		}
+
+		if (nd_split_list(nhdl, arr1[0], ",", &tmparr1, &tn1) != 0 ||
+		    nd_split_list(nhdl, arr2[0], ",", &tmparr2, &tn2) != 0) {
+			nd_error(nhdl, "Error parsing \"to\" lists");
+			nd_dump_nvlist(nhdl, p_nvl[0]);
+			nd_dump_nvlist(nhdl, p_nvl[1]);
+			goto eprefs_done;
+		}
+
+		if ((ep->ep_num_recips = nd_merge_strarray(nhdl, tmparr1, tn1,
+		    tmparr2, tn2, &ep->ep_recips)) < 0) {
+			nd_error(nhdl, "Error merging email recipient lists");
+			goto eprefs_done;
+		}
+
+		r = nvlist_lookup_string_array(p_nvl[0], "reply-to", &arr1,
+		    &n1);
+		r += nvlist_lookup_string_array(p_nvl[1], "reply-to", &arr2,
+		    &n2);
+		repsz = n1 = n2 = 0;
+		if (!r &&
+		    nd_split_list(nhdl, arr1[0], ",", &reparr1, &n1) != 0 ||
+		    nd_split_list(nhdl, arr2[0], ",", &reparr2, &n2) != 0 ||
+		    (repsz = nd_merge_strarray(nhdl, tmparr1, n1, tmparr2, n2,
+		    &strarr)) != 0 ||
+		    nd_join_strarray(nhdl, strarr, repsz, &ep->ep_reply_to)
+		    != 0) {
+
+			ep->ep_reply_to = strdup("root@localhost");
+		}
+		if (n1)
+			nd_free_strarray(reparr1, n1);
+		if (n2)
+			nd_free_strarray(reparr2, n2);
+		if (repsz > 0)
+			nd_free_strarray(strarr, repsz);
+
+		if (nvlist_lookup_string_array(p_nvl[0], "msg_template",
+		    &strarr, &arrsz) == 0)
+			ep->ep_template_path = strdup(strarr[0]);
+	} else {
+		char **strarr, **tmparr;
+		uint_t arrsz;
+		boolean_t *active;
+
+		/*
+		 * Both the "active" and "to" notification preferences are
+		 * required, so if we have trouble looking either of these up
+		 * we return an error.  We will also return an error if "active"
+		 * is set to false.  Returning an error will cause us to not
+		 * send a notification for this event.
+		 */
+		r = nvlist_lookup_boolean_array(p_nvl[0], "active", &active,
+		    &arrsz);
+		r += nvlist_lookup_string_array(p_nvl[0], "to", &strarr,
+		    &arrsz);
+
+		if (r != 0) {
+			nd_error(nhdl, "Malformed email notification "
+			    "preferences");
+			nd_dump_nvlist(nhdl, p_nvl[0]);
+			goto eprefs_done;
+		} else if (!active[0]) {
+			nd_debug(nhdl, "Email notification is disabled");
+			goto eprefs_done;
+		}
+
+		if (nd_split_list(nhdl, strarr[0], ",", &tmparr, &arrsz)
+		    != 0) {
+			nd_error(nhdl, "Error parsing \"to\" list");
+			goto eprefs_done;
+		}
+		ep->ep_num_recips = arrsz;
+		ep->ep_recips = tmparr;
+
+		if (nvlist_lookup_string_array(p_nvl[0], "msg_template",
+		    &strarr, &arrsz) == 0)
+			ep->ep_template_path = strdup(strarr[0]);
+
+		if (nvlist_lookup_string_array(p_nvl[0], "reply-to", &strarr,
+		    &arrsz) == 0)
+			ep->ep_reply_to = strdup(strarr[0]);
+		else
+			ep->ep_reply_to = strdup("root@localhost");
+	}
+	ret = 0;
+	*eprefs = ep;
+eprefs_done:
+	if (ret != 0) {
+		if (ep->ep_recips)
+			nd_free_strarray(ep->ep_recips, ep->ep_num_recips);
+		if (ep->ep_reply_to)
+			free(ep->ep_reply_to);
+		free(ep);
+	}
+	if (tn1)
+		nd_free_strarray(tmparr1, tn1);
+	if (tn2)
+		nd_free_strarray(tmparr2, tn2);
+	nd_free_nvlarray(p_nvl, npref);
+
+	return (ret);
+}
+
+/*ARGSUSED*/
+static void
+irpt_cbfunc(fmev_t ev, const char *class, nvlist_t *nvl, void *arg)
+{
+	char *body_fmt, *headers = NULL, *body = NULL, tstamp[32];
+	struct tm ts;
+	size_t len;
+	nd_ev_info_t *ev_info = NULL;
+	email_pref_t *eprefs;
+
+	nd_debug(nhdl, "Received event of class %s", class);
+
+	if (get_email_prefs(nhdl, ev, &eprefs) < 0)
+		return;
+
+	if (nd_get_event_info(nhdl, class, ev, &ev_info) != 0)
+		goto irpt_done;
+
+	/*
+	 * If the user specified a template, then we pass it through a script,
+	 * which post-processes any expansion macros.  Then we attempt to read
+	 * it in and then send the message.  Otherwise we carry on with the rest
+	 * of this function which will contruct the message body from one of the
+	 * default templates.
+	 */
+	if (eprefs->ep_template != NULL)
+		free(eprefs->ep_template);
+
+	if (eprefs->ep_template_path != NULL &&
+	    process_template(ev_info, eprefs) == 0) {
+		send_email_template(nhdl, ev_info, eprefs);
+		goto irpt_done;
+	}
+
+	/*
+	 * Fetch and format the event timestamp
+	 */
+	if (fmev_localtime(ev, &ts) == NULL) {
+		nd_error(nhdl, "Malformed event: failed to retrieve "
+		    "timestamp");
+		goto irpt_done;
+	}
+	(void) strftime(tstamp, sizeof (tstamp), NULL, &ts);
+
+	/*
+	 * We have two message body templates to choose from.  One for SMF
+	 * service transition events and a generic one for any other
+	 * uncommitted ireport.
+	 */
+	if (strncmp(class, "ireport.os.smf", 14) == 0) {
+		/*
+		 * For SMF state transition events we have a standard message
+		 * template that we fill in based on the payload of the event.
+		 */
+		if ((body_fmt = fmd_msg_gettext_key(nhdl->nh_msghdl, NULL,
+		    FMNOTIFY_MSG_DOMAIN, SMF_MSG_TEMPLATE)) == NULL) {
+			nd_error(nhdl, "Failed to format message body");
+			goto irpt_done;
+		}
+
+		/* LINTED: E_SEC_PRINTF_VAR_FMT */
+		len = snprintf(NULL, 0, body_fmt, hostname, tstamp,
+		    ev_info->ei_fmri, ev_info->ei_from_state,
+		    ev_info->ei_to_state, ev_info->ei_descr,
+		    ev_info->ei_reason);
+		body = calloc(len, sizeof (char));
+		/* LINTED: E_SEC_PRINTF_VAR_FMT */
+		(void) snprintf(body, len, body_fmt, hostname, tstamp,
+		    ev_info->ei_fmri, ev_info->ei_from_state,
+		    ev_info->ei_to_state, ev_info->ei_descr,
+		    ev_info->ei_reason);
+	} else {
+		if ((body_fmt = fmd_msg_gettext_key(nhdl->nh_msghdl, NULL,
+		    FMNOTIFY_MSG_DOMAIN, IREPORT_MSG_TEMPLATE)) == NULL) {
+			nd_error(nhdl, "Failed to format message body");
+			goto irpt_done;
+		}
+		/* LINTED: E_SEC_PRINTF_VAR_FMT */
+		len = snprintf(NULL, 0, body_fmt, hostname, tstamp, class);
+		body = calloc(len, sizeof (char));
+		/* LINTED: E_SEC_PRINTF_VAR_FMT */
+		(void) snprintf(body, len, body_fmt, hostname, tstamp, class);
+	}
+
+	if (build_headers(nhdl, ev_info, eprefs, &headers) != 0)
+		goto irpt_done;
+
+	/*
+	 * Everything is ready, so now we just iterate through the list of
+	 * recipents, sending an email notification to each one.
+	 */
+	for (int i = 0; i < eprefs->ep_num_recips; i++)
+		send_email(nhdl, headers, body, eprefs->ep_recips[i]);
+
+irpt_done:
+	free(headers);
+	free(body);
+	if (ev_info)
+		nd_free_event_info(ev_info);
+	if (eprefs->ep_recips)
+		nd_free_strarray(eprefs->ep_recips, eprefs->ep_num_recips);
+	if (eprefs->ep_reply_to)
+		free(eprefs->ep_reply_to);
+	free(eprefs);
+}
+
+/*
+ * There is a lack of uniformity in how the various entries in our diagnosis
+ * are terminated.  Some end with one newline, others with two.  This makes the
+ * output look a bit ugly.  Therefore we postprocess the message before sending
+ * it, removing consecutive occurences of newlines.
+ */
+static void
+postprocess_msg(char *msg)
+{
+	int i = 0, j = 0;
+	char *buf;
+
+	if ((buf = malloc(strlen(msg) + 1)) == NULL)
+		return;
+
+	buf[j++] = msg[i++];
+	for (i = 1; i < strlen(msg); i++) {
+		if (!(msg[i] == '\n' && msg[i - 1] == '\n'))
+			buf[j++] = msg[i];
+	}
+	buf[j] = '\0';
+	(void) strncpy(msg, buf, j+1);
+	free(buf);
+}
+
+/*ARGSUSED*/
+static void
+listev_cb(fmev_t ev, const char *class, nvlist_t *nvl, void *arg)
+{
+	char *body = NULL, *headers = NULL;
+	nd_ev_info_t *ev_info = NULL;
+	boolean_t domsg;
+	email_pref_t *eprefs;
+
+	nd_debug(nhdl, "Received event of class %s", class);
+
+	if (get_email_prefs(nhdl, ev, &eprefs) < 0)
+		return;
+
+	if (nd_get_event_info(nhdl, class, ev, &ev_info) != 0)
+		goto listcb_done;
+
+	/*
+	 * If the message payload member is set to 0, then it's an event we
+	 * typically suppress messaging on, so we won't send an email for it.
+	 */
+	if (nvlist_lookup_boolean_value(ev_info->ei_payload, FM_SUSPECT_MESSAGE,
+	    &domsg) == 0 && !domsg) {
+		nd_debug(nhdl, "Messaging suppressed for this event");
+		goto listcb_done;
+	}
+
+	/*
+	 * If the user specified a template, then we pass it through a script,
+	 * which post-processes any expansion macros.  Then we attempt to read
+	 * it in and then send the message.  Otherwise we carry on with the rest
+	 * of this function which will contruct the message body from one of the
+	 * default templates.
+	 */
+	if (eprefs->ep_template != NULL)
+		free(eprefs->ep_template);
+
+	if (eprefs->ep_template_path != NULL &&
+	    process_template(ev_info, eprefs) == 0) {
+		send_email_template(nhdl, ev_info, eprefs);
+		goto listcb_done;
+	}
+
+	/*
+	 * Format the message body
+	 *
+	 * For FMA list.* events we use the same message that the
+	 * syslog-msgs agent would emit as the message body
+	 *
+	 */
+	if ((body = fmd_msg_gettext_nv(nhdl->nh_msghdl, NULL,
+	    ev_info->ei_payload)) == NULL) {
+		nd_error(nhdl, "Failed to format message body");
+		nd_dump_nvlist(nhdl, ev_info->ei_payload);
+		goto listcb_done;
+	}
+	postprocess_msg(body);
+
+	if (build_headers(nhdl, ev_info, eprefs, &headers) != 0)
+		goto listcb_done;
+
+	/*
+	 * Everything is ready, so now we just iterate through the list of
+	 * recipents, sending an email notification to each one.
+	 */
+	for (int i = 0; i < eprefs->ep_num_recips; i++)
+		send_email(nhdl, headers, body, eprefs->ep_recips[i]);
+
+listcb_done:
+	free(headers);
+	free(body);
+	if (ev_info)
+		nd_free_event_info(ev_info);
+	if (eprefs->ep_recips)
+		nd_free_strarray(eprefs->ep_recips, eprefs->ep_num_recips);
+	if (eprefs->ep_reply_to)
+		free(eprefs->ep_reply_to);
+	free(eprefs);
+}
+
+int
+main(int argc, char *argv[])
+{
+	struct rlimit rlim;
+	struct sigaction act;
+	sigset_t set;
+	char c;
+	boolean_t run_fg = B_FALSE;
+
+	if ((nhdl = malloc(sizeof (nd_hdl_t))) == NULL) {
+		(void) fprintf(stderr, "Failed to allocate space for notifyd "
+		    "handle (%s)", strerror(errno));
+		return (1);
+	}
+	(void) memset(nhdl, 0, sizeof (nd_hdl_t));
+
+	nhdl->nh_keep_running = B_TRUE;
+	nhdl->nh_log_fd = stderr;
+	nhdl->nh_pname = argv[0];
+
+	get_svc_config();
+
+	/*
+	 * In the case where we get started outside of SMF, args passed on the
+	 * command line override SMF property setting
+	 */
+	while (optind < argc) {
+		while ((c = getopt(argc, argv, optstr)) != -1) {
+			switch (c) {
+			case 'd':
+				nhdl->nh_debug = B_TRUE;
+				break;
+			case 'f':
+				run_fg = B_TRUE;
+				break;
+			case 'R':
+				nhdl->nh_rootdir = strdup(optarg);
+				break;
+			default:
+				free(nhdl);
+				return (usage(nhdl->nh_pname));
+			}
+		}
+	}
+
+	/*
+	 * Set up a signal handler for SIGTERM (and SIGINT if we'll
+	 * be running in the foreground) to ensure sure we get a chance to exit
+	 * in an orderly fashion.  We also catch SIGHUP, which will be sent to
+	 * us by SMF if the service is refreshed.
+	 */
+	(void) sigfillset(&set);
+	(void) sigfillset(&act.sa_mask);
+	act.sa_handler = nd_sighandler;
+	act.sa_flags = 0;
+
+	(void) sigaction(SIGTERM, &act, NULL);
+	(void) sigdelset(&set, SIGTERM);
+	(void) sigaction(SIGHUP, &act, NULL);
+	(void) sigdelset(&set, SIGHUP);
+
+	if (run_fg) {
+		(void) sigaction(SIGINT, &act, NULL);
+		(void) sigdelset(&set, SIGINT);
+	} else
+		nd_daemonize(nhdl);
+
+	rlim.rlim_cur = RLIM_INFINITY;
+	rlim.rlim_max = RLIM_INFINITY;
+	(void) setrlimit(RLIMIT_CORE, &rlim);
+
+	/*
+	 * We need to be root to initialize our libfmevent handle (because that
+	 * involves reading/writing to /dev/sysevent), so we do this before
+	 * calling __init_daemon_priv.
+	 */
+	nhdl->nh_evhdl = fmev_shdl_init(LIBFMEVENT_VERSION_2, NULL, NULL, NULL);
+	if (nhdl->nh_evhdl == NULL) {
+		(void) sleep(5);
+		nd_abort(nhdl, "failed to initialize libfmevent: %s",
+		    fmev_strerror(fmev_errno));
+	}
+
+	/*
+	 * If we're in the global zone, reset all of our privilege sets to
+	 * the minimum set of required privileges.  Since we've already
+	 * initialized our libmevent handle, we no no longer need to run as
+	 * root, so we change our uid/gid to noaccess (60002).
+	 *
+	 * __init_daemon_priv will also set the process core path for us
+	 *
+	 */
+	if (getzoneid() == GLOBAL_ZONEID)
+		if (__init_daemon_priv(
+		    PU_RESETGROUPS | PU_LIMITPRIVS | PU_INHERITPRIVS,
+		    60002, 60002, PRIV_PROC_SETID, NULL) != 0)
+			nd_abort(nhdl, "additional privileges required to run");
+
+	nhdl->nh_msghdl = fmd_msg_init(nhdl->nh_rootdir, FMD_MSG_VERSION);
+	if (nhdl->nh_msghdl == NULL)
+		nd_abort(nhdl, "failed to initialize libfmd_msg");
+
+	(void) gethostname(hostname, MAXHOSTNAMELEN + 1);
+	/*
+	 * Set up our event subscriptions.  We subscribe to everything and then
+	 * consult libscf when we receive an event to determine whether to send
+	 * an email notification.
+	 */
+	nd_debug(nhdl, "Subscribing to ireport.* events");
+	if (fmev_shdl_subscribe(nhdl->nh_evhdl, "ireport.*", irpt_cbfunc,
+	    NULL) != FMEV_SUCCESS) {
+		nd_abort(nhdl, "fmev_shdl_subscribe failed: %s",
+		    fmev_strerror(fmev_errno));
+	}
+
+	nd_debug(nhdl, "Subscribing to list.* events");
+	if (fmev_shdl_subscribe(nhdl->nh_evhdl, "list.*", listev_cb,
+	    NULL) != FMEV_SUCCESS) {
+		nd_abort(nhdl, "fmev_shdl_subscribe failed: %s",
+		    fmev_strerror(fmev_errno));
+	}
+
+	/*
+	 * We run until someone kills us
+	 */
+	while (nhdl->nh_keep_running)
+		(void) sigsuspend(&set);
+
+	free(nhdl->nh_rootdir);
+	free(nhdl);
+
+	return (0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fm/notify/smtp-notify/common/smtp-notify.xml	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,142 @@
+<?xml version="1.0"?>
+<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
+<!--
+ 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
+
+	NOTE:  This service manifest is not editable; its contents will
+	be overwritten by package or patch operations, including
+	operating system upgrade.  Make customizations in a different
+	file.
+-->
+
+<service_bundle type='manifest' name='SUNWckr:fmd'>
+
+<service
+	name='system/fm/smtp-notify'
+	type='service'
+	version='1'>
+
+	<create_default_instance enabled='true' />
+
+	<!--
+	 Just one instance of smtp-notify should be running in any OS instance.
+	-->
+	<single_instance />
+
+	<!--
+	    Add a dependency on SUNWfmd being installed
+	-->
+	<dependency
+	    name='SUNWfmd'
+	    grouping='require_all'
+	    restart_on='none'
+	    type='path'>
+		<service_fmri value='file://localhost/usr/lib/fm/notify/smtp-notify' />
+	</dependency>
+
+	<dependency
+	    name='startup_req'
+	    grouping='require_all'
+	    restart_on='none'
+	    type='service'>
+		<service_fmri value='svc:/milestone/multi-user:default' />
+		<service_fmri value='svc:/system/fmd:default' />
+		<service_fmri value='svc:/network/sendmail-client:default' />
+	</dependency>
+
+	<exec_method
+	    type='method'
+	    name='start'
+	    exec='/usr/lib/fm/notify/smtp-notify'
+	    timeout_seconds='0' />
+
+	<exec_method
+	    type='method'
+	    name='stop'
+	    exec=':kill'
+	    timeout_seconds='60' />
+
+        <exec_method
+                type='method'
+                name='refresh'
+                exec=':kill -HUP'
+                timeout_seconds='30'>
+                <method_context>
+                        <method_credential user='root' group='root' />
+                </method_context>
+        </exec_method>
+
+	<property_group name="general" type="framework">
+		<propval name='action_authorization' type='astring'
+                    value='solaris.smf.manage.smtp-notify' />
+		<propval name='value_authorization' type='astring'
+                    value='solaris.smf.value.smtp-notify' />
+	</property_group>
+
+	<property_group name="config" type="application">
+		<propval name="debug" type="boolean" value="false"/>
+		<propval name="rootdir" type="astring" value="/"/>
+		<propval name='action_authorization' type='astring'
+                    value='solaris.smf.manage.smtp-notify' />
+		<propval name='value_authorization' type='astring'
+                    value='solaris.smf.value.smtp-notify' />
+	</property_group>
+
+	<stability value='Evolving' />
+
+	<template>
+	    	<common_name>
+		    	<loctext xml:lang='C'>
+				Solaris Email Event Notification Agent
+			</loctext>
+		</common_name>
+		<documentation>
+			<manpage title='smtp-notify' section='1M'
+				manpath='/usr/share/man' />
+		</documentation>
+		<pg_pattern name='config' type='application' target='this'
+		    required='false'>
+			<prop_pattern name='debug' type='boolean'
+			    required='false'>
+				<description>
+					<loctext xml:lang='C'>
+When set to true, smtp-notify will spool debug messages to /var/fm/notify/smtp-notify.log
+					</loctext>
+				</description>
+				<visibility value='readwrite'/>
+				<cardinality min='1' max='1'/>
+			</prop_pattern>
+			<prop_pattern name='rootdir' type='astring'
+			    required='false'>
+				<description>
+					<loctext xml:lang='C'>
+The root directory that will be used for all pathnames evaluated by smtp-notify
+					</loctext>
+				</description>
+				<visibility value='readwrite'/>
+				<cardinality min='1' max='1'/>
+			</prop_pattern>
+		</pg_pattern>	 
+	</template>
+</service>
+
+</service_bundle>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fm/notify/smtp-notify/i386/Makefile	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,27 @@
+#
+# 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.cmd
+include ../Makefile.com
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fm/notify/smtp-notify/sparc/Makefile	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,27 @@
+#
+# 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.cmd
+include ../Makefile.com
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fm/notify/snmp-notify/Makefile	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,28 @@
+#
+# 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.
+#
+
+SUBDIRS = $(MACH)
+
+include ../Makefile.subdirs
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fm/notify/snmp-notify/Makefile.com	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,103 @@
+#
+# 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.
+#
+
+.KEEP_STATE:
+.SUFFIXES:
+
+SRCS += snmp-notify.c
+OBJS = $(SRCS:%.c=%.o)
+LINTFILES = $(SRCS:%.c=%.ln)
+
+PROG = snmp-notify
+ROOTLIBFM = $(ROOT)/usr/lib/fm
+ROOTLIBNOTIFY = $(ROOT)/usr/lib/fm/notify
+ROOTPROG = $(ROOTLIBNOTIFY)/$(PROG)
+
+ROOTMANIFESTDIR = $(ROOTSVCSYSTEM)/fm
+ROOTMANIFEST = $(ROOTMANIFESTDIR)/$(PROG).xml
+ROOTNOTIFYPARAMS = $(ROOTMANIFESTDIR)/notify-params.xml
+$(ROOTMANIFEST) := FILEMODE = 0444
+$(ROOTNOTIFYPARAMS) := FILEMODE = 0444
+
+$(NOT_RELEASE_BUILD)CPPFLAGS += -DDEBUG
+CPPFLAGS += -I. -I../common -I../../../../../lib/fm/libfmnotify/common
+C99MODE	= $(C99_ENABLE)
+CFLAGS += $(CTF_FLAGS) $(CCVERBOSE) $(XSTRCONST)
+LDLIBS += -L$(ROOT)/usr/lib/fm -lnvpair -lfmevent -lfmd_msg -lfmnotify \
+	-lumem -lnetsnmp -lnetsnmpagent
+LDFLAGS += -R/usr/lib/fm
+LINTFLAGS += -mnu
+
+.NO_PARALLEL:
+.PARALLEL: $(OBJS) $(LINTFILES)
+
+all: $(PROG)
+
+$(PROG): $(OBJS)
+	$(LINK.c) $(OBJS) -o $@ $(LDLIBS)
+	$(CTFMERGE) -L VERSION -o $@ $(OBJS)
+	$(POST_PROCESS)
+
+%.o: ../common/%.c
+	$(COMPILE.c) $<
+	$(CTFCONVERT_O)
+
+%.o: %.c
+	$(COMPILE.c) $<
+	$(CTFCONVERT_O)
+
+clean:
+	$(RM) $(OBJS) $(LINTFILES)
+
+clobber: clean
+	$(RM) $(PROG)
+
+%.ln: ../common/%.c
+	$(LINT.c) -c $<
+
+%.ln: %.c
+	$(LINT.c) -c $<
+
+lint: $(LINTFILES)
+	$(LINT) $(LINTFLAGS) $(LINTFILES)
+
+$(ROOTLIBNOTIFY):
+	$(INS.dir)
+
+$(ROOTLIBNOTIFY)/%: %
+	$(INS.file)
+
+$(ROOTMANIFESTDIR):
+	$(INS.dir)
+
+$(ROOTMANIFESTDIR)/%.xml: ../common/%.xml
+	$(INS.file)
+
+$(ROOTMANIFESTDIR)/notify-params.xml: ../../notify-params.xml
+	$(INS.file) ../../notify-params.xml
+
+install_h:
+
+install: all $(ROOTLIBNOTIFY) $(ROOTPROG) $(ROOTMANIFESTDIR) $(ROOTMANIFEST) $(ROOTNOTIFYPARAMS)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fm/notify/snmp-notify/common/snmp-notify.c	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,687 @@
+/*
+ * 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/fm/protocol.h>
+#include <fm/fmd_snmp.h>
+#include <fm/fmd_msg.h>
+#include <fm/libfmevent.h>
+#include <net-snmp/net-snmp-config.h>
+#include <net-snmp/net-snmp-includes.h>
+#include <net-snmp/agent/net-snmp-agent-includes.h>
+#include <errno.h>
+#include <locale.h>
+#include <netdb.h>
+#include <signal.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <limits.h>
+#include <alloca.h>
+#include <priv_utils.h>
+#include <zone.h>
+#include "libfmnotify.h"
+
+/*
+ * Debug messages can be enabled by setting the debug property to true
+ *
+ * # svccfg -s svc:/system/fm/snmp-notify setprop config/debug=true
+ */
+#define	SVCNAME		"system/fm/snmp-notify"
+
+typedef struct ireport_trap {
+	char *host;
+	char *msgid;
+	char *desc;
+	long long tstamp;
+	char *fmri;
+	uint32_t from_state;
+	uint32_t to_state;
+	char *reason;
+	boolean_t is_stn_event;
+} ireport_trap_t;
+
+static nd_hdl_t *nhdl;
+static const char optstr[] = "dfR:";
+static const char SNMP_SUPPCONF[] = "fmd-trapgen";
+static char hostname[MAXHOSTNAMELEN + 1];
+
+static int
+usage(const char *pname)
+{
+	(void) fprintf(stderr, "Usage: %s [-df] [-R <altroot>]\n", pname);
+
+	(void) fprintf(stderr,
+	    "\t-d  enable debug mode\n"
+	    "\t-f  stay in foreground\n"
+	    "\t-R  specify alternate root\n");
+
+	return (1);
+}
+
+/*
+ * If someone does an "svcadm refresh" on us, then this function gets called,
+ * which rereads our service configuration.
+ */
+static void
+get_svc_config()
+{
+	int s = 0;
+	uint8_t val;
+
+	s = nd_get_boolean_prop(nhdl, SVCNAME, "config", "debug", &val);
+	nhdl->nh_debug = val;
+
+	s += nd_get_astring_prop(nhdl, SVCNAME, "config", "rootdir",
+	    &(nhdl->nh_rootdir));
+
+	if (s != 0)
+		nd_error(nhdl, "Failed to read retrieve service "
+		    "properties");
+}
+
+static void
+nd_sighandler(int sig)
+{
+	if (sig == SIGHUP)
+		get_svc_config();
+	else
+		nd_cleanup(nhdl);
+}
+
+static int
+get_snmp_prefs(nd_hdl_t *nhdl, nvlist_t **pref_nvl, uint_t npref)
+{
+	boolean_t *a1, *a2;
+	uint_t n;
+	int r;
+
+	/*
+	 * For SMF state transition events, pref_nvl contain two sets of
+	 * preferences, which will have to be merged.
+	 *
+	 * The "snmp" nvlist currently only supports a single boolean member,
+	 * "active" which will be set to true, if it is true in either set
+	 */
+	if (npref == 2) {
+		r = nvlist_lookup_boolean_array(pref_nvl[0], "active", &a1, &n);
+		r += nvlist_lookup_boolean_array(pref_nvl[1], "active", &a2,
+		    &n);
+		if (r != 0) {
+			nd_debug(nhdl, "Malformed snmp notification "
+			    "preferences");
+			nd_dump_nvlist(nhdl, pref_nvl[0]);
+			nd_dump_nvlist(nhdl, pref_nvl[1]);
+			return (-1);
+		} else if (!a1[0] && !a2[0]) {
+			nd_debug(nhdl, "SNMP notification is disabled");
+			return (-1);
+		}
+	} else {
+		if (nvlist_lookup_boolean_array(pref_nvl[0], "active",
+		    &a1, &n)) {
+			nd_debug(nhdl, "Malformed snmp notification "
+			    "preferences");
+			nd_dump_nvlist(nhdl, pref_nvl[0]);
+			return (-1);
+		} else if (!a1[0]) {
+			nd_debug(nhdl, "SNMP notification is disabled");
+			return (-1);
+		}
+	}
+	return (0);
+}
+
+static void
+send_ireport_trap(ireport_trap_t *t)
+{
+	static const oid sunIreportTrap_oid[] =
+	    { SUNIREPORTTRAP_OID };
+	const size_t sunIreportTrap_len =
+	    OID_LENGTH(sunIreportTrap_oid);
+
+	static const oid sunIreportHostname_oid[] =
+	    { SUNIREPORTHOSTNAME_OID };
+	static const oid sunIreportMsgid_oid[] =
+	    { SUNIREPORTMSGID_OID };
+	static const oid sunIreportDescription_oid[] =
+	    { SUNIREPORTDESCRIPTION_OID };
+	static const oid sunIreportTime_oid[] =
+	    { SUNIREPORTTIME_OID };
+
+	static const oid sunIreportSmfFmri_oid[] =
+	    { SUNIREPORTSMFFMRI_OID };
+	static const oid sunIreportSmfFromState_oid[] =
+	    { SUNIREPORTSMFFROMSTATE_OID };
+	static const oid sunIreportSmfToState_oid[] =
+	    { SUNIREPORTSMFTOSTATE_OID };
+	static const oid sunIreportSmfTransitionReason_oid[] =
+	    { SUNIREPORTTRANSITIONREASON_OID };
+	const size_t
+	    sunIreport_base_len = OID_LENGTH(sunIreportHostname_oid);
+
+	size_t var_len = sunIreport_base_len + 1;
+	oid var_name[MAX_OID_LEN];
+
+	netsnmp_variable_list *notification_vars = NULL;
+
+	size_t dt_len;
+	uchar_t dt[11], *tdt;
+	time_t ts = t->tstamp;
+
+	tdt = date_n_time(&ts, &dt_len);
+	/*
+	 * We know date_n_time is broken, it returns a buffer from
+	 * its stack. So we copy before we step over it!
+	 */
+	for (int i = 0; i < dt_len; ++i)
+		dt[i] = tdt[i];
+
+	if (var_len > MAX_OID_LEN) {
+		nd_error(nhdl, "var_len %d > MAX_OID_LEN %d\n", var_len,
+		    MAX_OID_LEN);
+		return;
+	}
+
+	(void) memcpy(var_name, sunIreportHostname_oid, sunIreport_base_len *
+	    sizeof (oid));
+	(void) snmp_varlist_add_variable(&notification_vars, var_name,
+	    sunIreport_base_len + 1, ASN_OCTET_STR, (uchar_t *)t->host,
+	    strlen(t->host));
+
+	(void) memcpy(var_name, sunIreportMsgid_oid,
+	    sunIreport_base_len * sizeof (oid));
+	(void) snmp_varlist_add_variable(&notification_vars, var_name,
+	    sunIreport_base_len + 1, ASN_OCTET_STR, (uchar_t *)t->msgid,
+	    strlen(t->msgid));
+
+	(void) memcpy(var_name, sunIreportDescription_oid,
+	    sunIreport_base_len * sizeof (oid));
+	(void) snmp_varlist_add_variable(&notification_vars, var_name,
+	    sunIreport_base_len + 1, ASN_OCTET_STR, (uchar_t *)t->desc,
+	    strlen(t->desc));
+
+	(void) memcpy(var_name, sunIreportTime_oid, sunIreport_base_len *
+	    sizeof (oid));
+	(void) snmp_varlist_add_variable(&notification_vars, var_name,
+	    sunIreport_base_len + 1, ASN_OCTET_STR, dt, dt_len);
+
+	if (t->is_stn_event) {
+		(void) memcpy(var_name, sunIreportSmfFmri_oid,
+		    sunIreport_base_len * sizeof (oid));
+		(void) snmp_varlist_add_variable(&notification_vars, var_name,
+		    sunIreport_base_len + 1, ASN_OCTET_STR, (uchar_t *)t->fmri,
+		    strlen(t->fmri));
+
+		(void) memcpy(var_name, sunIreportSmfFromState_oid,
+		    sunIreport_base_len * sizeof (oid));
+		(void) snmp_varlist_add_variable(&notification_vars, var_name,
+		    sunIreport_base_len + 1, ASN_INTEGER,
+		    (uchar_t *)&t->from_state, sizeof (uint32_t));
+
+		(void) memcpy(var_name, sunIreportSmfToState_oid,
+		    sunIreport_base_len * sizeof (oid));
+		(void) snmp_varlist_add_variable(&notification_vars, var_name,
+		    sunIreport_base_len + 1, ASN_INTEGER,
+		    (uchar_t *)&t->to_state, sizeof (uint32_t));
+
+		(void) memcpy(var_name, sunIreportSmfTransitionReason_oid,
+		    sunIreport_base_len * sizeof (oid));
+		(void) snmp_varlist_add_variable(&notification_vars, var_name,
+		    sunIreport_base_len + 1, ASN_OCTET_STR,
+		    (uchar_t *)t->reason, strlen(t->reason));
+	}
+
+	/*
+	 * This function is capable of sending both v1 and v2/v3 traps.
+	 * Which is sent to a specific destination is determined by the
+	 * configuration file(s).
+	 */
+	send_enterprise_trap_vars(SNMP_TRAP_ENTERPRISESPECIFIC,
+	    sunIreportTrap_oid[sunIreportTrap_len - 1],
+	    (oid *)sunIreportTrap_oid, sunIreportTrap_len - 2,
+	    notification_vars);
+	nd_debug(nhdl, "Sent SNMP trap for %s", t->msgid);
+
+	snmp_free_varbind(notification_vars);
+
+}
+
+/*ARGSUSED*/
+static void
+send_fm_trap(const char *uuid, const char *code, const char *url)
+{
+	static const oid sunFmProblemTrap_oid[] = { SUNFMPROBLEMTRAP_OID };
+	const size_t sunFmProblemTrap_len = OID_LENGTH(sunFmProblemTrap_oid);
+
+	static const oid sunFmProblemUUID_oid[] =
+	    { SUNFMPROBLEMTABLE_OID, 1, SUNFMPROBLEM_COL_UUID };
+	static const oid sunFmProblemCode_oid[] =
+	    { SUNFMPROBLEMTABLE_OID, 1, SUNFMPROBLEM_COL_CODE };
+	static const oid sunFmProblemURL_oid[] =
+	    { SUNFMPROBLEMTABLE_OID, 1, SUNFMPROBLEM_COL_URL };
+
+	const size_t sunFmProblem_base_len = OID_LENGTH(sunFmProblemUUID_oid);
+
+	size_t uuid_len = strlen(uuid);
+	size_t var_len = sunFmProblem_base_len + 1 + uuid_len;
+	oid var_name[MAX_OID_LEN];
+
+	netsnmp_variable_list *notification_vars = NULL;
+
+	/*
+	 * The format of our trap varbinds' oids is as follows:
+	 *
+	 * +-----------------------+---+--------+----------+------+
+	 * | SUNFMPROBLEMTABLE_OID | 1 | column | uuid_len | uuid |
+	 * +-----------------------+---+--------+----------+------+
+	 *					 \---- index ----/
+	 *
+	 * A common mistake here is to send the trap with varbinds that
+	 * do not contain the index.  All the indices are the same, and
+	 * all the oids are the same length, so the only thing we need to
+	 * do for each varbind is set the table and column parts of the
+	 * variable name.
+	 */
+
+	if (var_len > MAX_OID_LEN)
+		return;
+
+	var_name[sunFmProblem_base_len] = (oid)uuid_len;
+	for (int i = 0; i < uuid_len; i++)
+		var_name[i + sunFmProblem_base_len + 1] = (oid)uuid[i];
+
+	/*
+	 * Ordinarily, we would need to add the OID of the trap itself
+	 * to the head of the variable list; this is required by SNMP v2.
+	 * However, send_enterprise_trap_vars does this for us as a part
+	 * of converting between v1 and v2 traps, so we skip directly to
+	 * the objects we're sending.
+	 */
+
+	(void) memcpy(var_name, sunFmProblemUUID_oid,
+	    sunFmProblem_base_len * sizeof (oid));
+	(void) snmp_varlist_add_variable(&notification_vars, var_name, var_len,
+	    ASN_OCTET_STR, (uchar_t *)uuid, strlen(uuid));
+	(void) memcpy(var_name, sunFmProblemCode_oid,
+	    sunFmProblem_base_len * sizeof (oid));
+	(void) snmp_varlist_add_variable(&notification_vars, var_name, var_len,
+	    ASN_OCTET_STR, (uchar_t *)code, strlen(code));
+	(void) memcpy(var_name, sunFmProblemURL_oid,
+	    sunFmProblem_base_len * sizeof (oid));
+	(void) snmp_varlist_add_variable(&notification_vars, var_name, var_len,
+	    ASN_OCTET_STR, (uchar_t *)url, strlen(url));
+
+	/*
+	 * This function is capable of sending both v1 and v2/v3 traps.
+	 * Which is sent to a specific destination is determined by the
+	 * configuration file(s).
+	 */
+	send_enterprise_trap_vars(SNMP_TRAP_ENTERPRISESPECIFIC,
+	    sunFmProblemTrap_oid[sunFmProblemTrap_len - 1],
+	    (oid *)sunFmProblemTrap_oid, sunFmProblemTrap_len - 2,
+	    notification_vars);
+	nd_debug(nhdl, "Sent SNMP trap for %s", code);
+
+	snmp_free_varbind(notification_vars);
+}
+
+/*
+ * The SUN-IREPORT-MIB declares the following enum to represent SMF service
+ * states.
+ *
+ * offline(0), online(1), degraded(2), disabled(3), maintenance(4),
+ * uninitialized(5)
+ *
+ * This function converts a string representation of an SMF service state
+ * to it's corresponding enum val.
+ */
+static int
+state_to_val(char *statestr, uint32_t *stateval)
+{
+	if (strcmp(statestr, "offline") == 0)
+		*stateval = 0;
+	else if (strcmp(statestr, "online") == 0)
+		*stateval = 1;
+	else if (strcmp(statestr, "degraded") == 0)
+		*stateval = 2;
+	else if (strcmp(statestr, "disabled") == 0)
+		*stateval = 3;
+	else if (strcmp(statestr, "maintenance") == 0)
+		*stateval = 4;
+	else if (strcmp(statestr, "uninitialized") == 0)
+		*stateval = 5;
+	else
+		return (-1);
+	return (0);
+}
+
+/*ARGSUSED*/
+static void
+ireport_cb(fmev_t ev, const char *class, nvlist_t *nvl, void *arg)
+{
+	nvlist_t **pref_nvl = NULL;
+	nd_ev_info_t *ev_info = NULL;
+	ireport_trap_t swtrap;
+	uint_t npref;
+	int ret;
+
+	nd_debug(nhdl, "Received event of class %s", class);
+
+	ret = nd_get_notify_prefs(nhdl, "snmp", ev, &pref_nvl, &npref);
+	if (ret == SCF_ERROR_NOT_FOUND) {
+		/*
+		 * No snmp notification preferences specified for this type of
+		 * event, so we're done
+		 */
+		return;
+	} else if (ret != 0) {
+		nd_error(nhdl, "Failed to retrieve notification preferences "
+		    "for this event");
+		return;
+	}
+
+	if (get_snmp_prefs(nhdl, pref_nvl, npref) != 0)
+		goto irpt_done;
+
+	if (nd_get_event_info(nhdl, class, ev, &ev_info) != 0)
+		goto irpt_done;
+
+	swtrap.host = hostname;
+	swtrap.msgid = ev_info->ei_diagcode;
+	swtrap.desc = ev_info->ei_descr;
+	swtrap.tstamp = (time_t)fmev_time_sec(ev);
+
+	if (strncmp(class, "ireport.os.smf", 14) == 0) {
+		swtrap.fmri = ev_info->ei_fmri;
+		if (state_to_val(ev_info->ei_from_state, &swtrap.from_state)
+		    < 0 ||
+		    state_to_val(ev_info->ei_to_state, &swtrap.to_state) < 0) {
+			nd_error(nhdl, "Malformed event - invalid svc state");
+			nd_dump_nvlist(nhdl, ev_info->ei_payload);
+			goto irpt_done;
+		}
+		swtrap.reason = ev_info->ei_reason;
+		swtrap.is_stn_event = B_TRUE;
+	}
+	send_ireport_trap(&swtrap);
+irpt_done:
+	if (ev_info)
+		nd_free_event_info(ev_info);
+	nd_free_nvlarray(pref_nvl, npref);
+}
+
+/*ARGSUSED*/
+static void
+list_cb(fmev_t ev, const char *class, nvlist_t *nvl, void *arg)
+{
+	char *uuid;
+	uint8_t version;
+	nd_ev_info_t *ev_info = NULL;
+	nvlist_t **pref_nvl = NULL;
+	uint_t npref;
+	int ret;
+	boolean_t domsg;
+
+	nd_debug(nhdl, "Received event of class %s", class);
+
+	ret = nd_get_notify_prefs(nhdl, "snmp", ev, &pref_nvl, &npref);
+	if (ret == SCF_ERROR_NOT_FOUND) {
+		/*
+		 * No snmp notification preferences specified for this type of
+		 * event, so we're done
+		 */
+		return;
+	} else if (ret != 0) {
+		nd_error(nhdl, "Failed to retrieve notification preferences "
+		    "for this event");
+		return;
+	}
+
+	if (get_snmp_prefs(nhdl, pref_nvl, npref) != 0)
+		goto listcb_done;
+
+	if (nd_get_event_info(nhdl, class, ev, &ev_info) != 0)
+		goto listcb_done;
+
+	/*
+	 * If the message payload member is set to 0, then it's an event we
+	 * typically suppress messaging on, so we won't send a trap for it.
+	 */
+	if (nvlist_lookup_boolean_value(ev_info->ei_payload, FM_SUSPECT_MESSAGE,
+	    &domsg) == 0 && !domsg) {
+		nd_debug(nhdl, "Messaging suppressed for this event");
+		goto listcb_done;
+	}
+
+	if (nvlist_lookup_uint8(ev_info->ei_payload, FM_VERSION, &version)
+	    != 0 || version > FM_SUSPECT_VERSION) {
+		nd_error(nhdl, "invalid event version: %u", version);
+		goto listcb_done;
+	}
+
+	(void) nvlist_lookup_string(ev_info->ei_payload, FM_SUSPECT_UUID,
+	    &uuid);
+
+	if (strcmp(ev_info->ei_url, ND_UNKNOWN) != 0)
+		send_fm_trap(uuid, ev_info->ei_diagcode, ev_info->ei_url);
+	else
+		nd_error(nhdl, "failed to format url for %s", uuid);
+listcb_done:
+	nd_free_nvlarray(pref_nvl, npref);
+	if (ev_info)
+		nd_free_event_info(ev_info);
+}
+
+static int
+init_sma(void)
+{
+	int err;
+
+	/*
+	 * The only place we could possibly log is syslog, but the
+	 * full agent doesn't normally log there.  It would be confusing
+	 * if this agent did so; therefore we disable logging entirely.
+	 */
+	snmp_disable_log();
+
+	/*
+	 * Net-SNMP has a provision for reading an arbitrary number of
+	 * configuration files.  A configuration file is read if it has
+	 * had any handlers registered for it, or if it's the value in
+	 * of NETSNMP_DS_LIB_APPTYPE.  Our objective here is to read
+	 * both snmpd.conf and fmd-trapgen.conf.
+	 */
+	if ((err = netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID,
+	    NETSNMP_DS_AGENT_ROLE, 0 /* MASTER_AGENT */)) != SNMPERR_SUCCESS)
+		return (err);
+
+	init_agent_read_config("snmpd");
+	if ((err = netsnmp_ds_set_string(NETSNMP_DS_LIBRARY_ID,
+	    NETSNMP_DS_LIB_APPTYPE, SNMP_SUPPCONF)) != SNMPERR_SUCCESS)
+		return (err);
+	if (register_app_config_handler("trapsink", snmpd_parse_config_trapsink,
+	    snmpd_free_trapsinks, "host [community] [port]") == NULL)
+		return (SNMPERR_MALLOC);
+	if (register_app_config_handler("trap2sink",
+	    snmpd_parse_config_trap2sink, NULL, "host [community] [port]") ==
+	    NULL)
+		return (SNMPERR_MALLOC);
+	if (register_app_config_handler("trapsess", snmpd_parse_config_trapsess,
+	    NULL, "[snmpcmdargs] host") == NULL)
+		return (SNMPERR_MALLOC);
+
+	init_traps();
+	init_snmp(SNMP_SUPPCONF);
+
+	return (SNMPERR_SUCCESS);
+}
+
+int
+main(int argc, char *argv[])
+{
+	struct rlimit rlim;
+	struct sigaction act;
+	sigset_t set;
+	char c;
+	boolean_t run_fg = B_FALSE;
+
+	if ((nhdl = malloc(sizeof (nd_hdl_t))) == NULL) {
+		(void) fprintf(stderr, "Failed to allocate space for notifyd "
+		    "handle (%s)", strerror(errno));
+		return (1);
+	}
+	bzero(nhdl, sizeof (nd_hdl_t));
+	nhdl->nh_keep_running = B_TRUE;
+	nhdl->nh_log_fd = stderr;
+	nhdl->nh_pname = argv[0];
+
+	get_svc_config();
+
+	/*
+	 * In the case where we get started outside of SMF, args passed on the
+	 * command line override SMF property setting
+	 */
+	while (optind < argc) {
+		while ((c = getopt(argc, argv, optstr)) != -1) {
+			switch (c) {
+			case 'd':
+				nhdl->nh_debug = B_TRUE;
+				break;
+			case 'f':
+				run_fg = B_TRUE;
+				break;
+			case 'R':
+				nhdl->nh_rootdir = strdup(optarg);
+				break;
+			default:
+				free(nhdl);
+				return (usage(nhdl->nh_pname));
+			}
+		}
+	}
+
+	/*
+	 * Set up a signal handler for SIGTERM (and SIGINT if we'll
+	 * be running in the foreground) to ensure sure we get a chance to exit
+	 * in an orderly fashion.  We also catch SIGHUP, which will be sent to
+	 * us by SMF if the service is refreshed.
+	 */
+	(void) sigfillset(&set);
+	(void) sigfillset(&act.sa_mask);
+	act.sa_handler = nd_sighandler;
+	act.sa_flags = 0;
+
+	(void) sigaction(SIGTERM, &act, NULL);
+	(void) sigdelset(&set, SIGTERM);
+	(void) sigaction(SIGHUP, &act, NULL);
+	(void) sigdelset(&set, SIGHUP);
+
+	if (run_fg) {
+		(void) sigaction(SIGINT, &act, NULL);
+		(void) sigdelset(&set, SIGINT);
+	} else
+		nd_daemonize(nhdl);
+
+	rlim.rlim_cur = RLIM_INFINITY;
+	rlim.rlim_max = RLIM_INFINITY;
+	(void) setrlimit(RLIMIT_CORE, &rlim);
+
+	/*
+	 * We need to be root initialize our libfmevent handle (because that
+	 * involves reading/writing to /dev/sysevent), so we do this before
+	 * calling __init_daemon_priv.
+	 */
+	nhdl->nh_evhdl = fmev_shdl_init(LIBFMEVENT_VERSION_2, NULL, NULL, NULL);
+	if (nhdl->nh_evhdl == NULL) {
+		(void) sleep(5);
+		nd_abort(nhdl, "failed to initialize libfmevent: %s",
+		    fmev_strerror(fmev_errno));
+	}
+
+	/*
+	 * If we're in the global zone, reset all of our privilege sets to
+	 * the minimum set of required privileges.   We also change our
+	 * uid/gid to noaccess/noaccess
+	 *
+	 * __init_daemon_priv will also set the process core path for us
+	 *
+	 */
+	if (getzoneid() == GLOBAL_ZONEID)
+		if (__init_daemon_priv(
+		    PU_RESETGROUPS | PU_LIMITPRIVS | PU_INHERITPRIVS,
+		    60002, 60002, PRIV_FILE_DAC_READ, NULL) != 0)
+			nd_abort(nhdl, "additional privileges required to run");
+
+	nhdl->nh_msghdl = fmd_msg_init(nhdl->nh_rootdir, FMD_MSG_VERSION);
+	if (nhdl->nh_msghdl == NULL)
+		nd_abort(nhdl, "failed to initialize libfmd_msg");
+
+	if (init_sma() != SNMPERR_SUCCESS)
+		nd_abort(nhdl, "SNMP initialization failed");
+
+	(void) gethostname(hostname, MAXHOSTNAMELEN + 1);
+	/*
+	 * Set up our event subscriptions.  We subscribe to everything and then
+	 * consult libscf when we receive an event to determine what (if any)
+	 * notification to send.
+	 */
+	nd_debug(nhdl, "Subscribing to ireport.os.smf.* events");
+	if (fmev_shdl_subscribe(nhdl->nh_evhdl, "ireport.os.smf.*",
+	    ireport_cb, NULL) != FMEV_SUCCESS) {
+		nd_abort(nhdl, "fmev_shdl_subscribe failed: %s",
+		    fmev_strerror(fmev_errno));
+	}
+
+	nd_debug(nhdl, "Subscribing to list.* events");
+	if (fmev_shdl_subscribe(nhdl->nh_evhdl, "list.*", list_cb,
+	    NULL) != FMEV_SUCCESS) {
+		nd_abort(nhdl, "fmev_shdl_subscribe failed: %s",
+		    fmev_strerror(fmev_errno));
+	}
+
+	/*
+	 * We run until someone kills us
+	 */
+	while (nhdl->nh_keep_running)
+		(void) sigsuspend(&set);
+
+	/*
+	 * snmp_shutdown, which we would normally use here, calls free_slots,
+	 * a callback that is supposed to tear down the pkcs11 state; however,
+	 * it abuses C_Finalize, causing fmd to drop core on shutdown.  Avoid
+	 * this by shutting down the library piecemeal.
+	 */
+	snmp_store(SNMP_SUPPCONF);
+	snmp_alarm_unregister_all();
+	(void) snmp_close_sessions();
+	shutdown_mib();
+	unregister_all_config_handlers();
+	netsnmp_ds_shutdown();
+
+	free(nhdl->nh_rootdir);
+	free(nhdl);
+
+	return (0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fm/notify/snmp-notify/common/snmp-notify.xml	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,142 @@
+<?xml version="1.0"?>
+<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
+<!--
+ 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
+
+	NOTE:  This service manifest is not editable; its contents will
+	be overwritten by package or patch operations, including
+	operating system upgrade.  Make customizations in a different
+	file.
+-->
+
+<service_bundle type='manifest' name='SUNWckr:fmd'>
+
+<service
+	name='system/fm/snmp-notify'
+	type='service'
+	version='1'>
+
+	<create_default_instance enabled='false' />
+
+	<!--
+	 Just one instance of snmp-notify should be running in any OS instance.
+	-->
+	<single_instance />
+
+	<!--
+	    Add a dependency on SUNWfmd being installed
+	-->
+	<dependency
+	    name='SUNWfmd'
+	    grouping='require_all'
+	    restart_on='none'
+	    type='path'>
+		<service_fmri value='file://localhost/usr/lib/fm/notify/snmp-notify' />
+	</dependency>
+
+	<dependency
+	    name='startup_req'
+	    grouping='require_all'
+	    restart_on='none'
+	    type='service'>
+		<service_fmri value='svc:/milestone/multi-user:default' />
+		<service_fmri value='svc:/system/fmd:default' />
+		<service_fmri value='svc:/application/management/net-snmp:default' />
+	</dependency>
+
+	<exec_method
+	    type='method'
+	    name='start'
+	    exec='/usr/lib/fm/notify/snmp-notify'
+	    timeout_seconds='0' />
+
+	<exec_method
+	    type='method'
+	    name='stop'
+	    exec=':kill'
+	    timeout_seconds='60' />
+
+        <exec_method
+                type='method'
+                name='refresh'
+                exec=':kill -HUP'
+                timeout_seconds='30'>
+                <method_context>
+                        <method_credential user='root' group='root' />
+                </method_context>
+        </exec_method>
+
+	<property_group name="general" type="framework">
+		<propval name='action_authorization' type='astring'
+                    value='solaris.smf.manage.snmp-notify' />
+		<propval name='value_authorization' type='astring'
+                    value='solaris.smf.value.snmp-notify' />
+	</property_group>
+
+	<property_group name="config" type="application">
+		<propval name="debug" type="boolean" value="false"/>
+		<propval name="rootdir" type="astring" value="/"/>
+		<propval name='action_authorization' type='astring'
+                    value='solaris.smf.manage.snmp-notify' />
+		<propval name='value_authorization' type='astring'
+                    value='solaris.smf.value.snmp-notify' />
+	</property_group>
+
+	<stability value='Evolving' />
+
+	<template>
+	    	<common_name>
+		    	<loctext xml:lang='C'>
+				Solaris SNMP Event Notification Agent
+			</loctext>
+		</common_name>
+		<documentation>
+			<manpage title='snmp-notify' section='1M'
+				manpath='/usr/share/man' />
+		</documentation>
+		<pg_pattern name='config' type='application' target='this'
+		    required='false'>
+			<prop_pattern name='debug' type='boolean'
+			    required='false'>
+				<description>
+					<loctext xml:lang='C'>
+When set to true, smtp-notify will spool debug messages to /var/fm/notify/snmp-notify.log
+					</loctext>
+				</description>
+				<visibility value='readwrite'/>
+				<cardinality min='1' max='1'/>
+			</prop_pattern>
+			<prop_pattern name='rootdir' type='astring'
+			    required='false'>
+				<description>
+					<loctext xml:lang='C'>
+The root directory that will be used for all pathnames evaluated by snmp-notify
+					</loctext>
+				</description>
+				<visibility value='readwrite'/>
+				<cardinality min='1' max='1'/>
+			</prop_pattern>
+		</pg_pattern>	 
+	</template>
+</service>
+
+</service_bundle>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fm/notify/snmp-notify/i386/Makefile	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,27 @@
+#
+# 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.cmd
+include ../Makefile.com
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fm/notify/snmp-notify/sparc/Makefile	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,27 @@
+#
+# 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.cmd
+include ../Makefile.com
--- a/usr/src/cmd/fm/schemes/Makefile	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/fm/schemes/Makefile	Fri Jul 30 17:04:17 2010 +1000
@@ -19,8 +19,7 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
+# Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
 #
 
 SUBDIRS = \
@@ -32,6 +31,7 @@
 	mem \
 	mod \
 	pkg \
+	sw \
 	svc \
 	zfs
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fm/schemes/sw/Makefile	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,31 @@
+#
+# 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.cmd
+
+SUBDIRS = $(MACH)
+$(BUILD64)SUBDIRS += $(MACH64)
+
+include ../../Makefile.subdirs
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fm/schemes/sw/Makefile.com	Fri Jul 30 17:04:17 2010 +1000
@@ -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.
+#
+
+include ../../Makefile.com
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fm/schemes/sw/amd64/Makefile	Fri Jul 30 17:04:17 2010 +1000
@@ -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.com
+include $(SRC)/Makefile.master.64
+include ../../Makefile.targ
+
+LDLIBS += -ltopo
+LDFLAGS += -L$(ROOT)/usr/lib/fm/$(MACH64) -R/usr/lib/fm/$(MACH64)
+LINTFLAGS += -L$(ROOT)/usr/lib/fm/$(MACH64)
+
+install: all $(ROOTPROG64)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fm/schemes/sw/i386/Makefile	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,33 @@
+#
+# 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.com
+include ../../Makefile.targ
+
+LDLIBS += -ltopo
+LDFLAGS += -L$(ROOT)/usr/lib/fm -R/usr/lib/fm
+LINTFLAGS += -L$(ROOT)/usr/lib/fm
+
+install: all $(ROOTPROG)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fm/schemes/sw/scheme.c	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,96 @@
+/*
+ * 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 <fm/fmd_fmri.h>
+#include <fm/libtopo.h>
+#include <fm/topo_mod.h>
+#include <string.h>
+#include <sys/fm/protocol.h>
+
+int
+fmd_fmri_init(void)
+{
+	return (0);
+}
+
+void
+fmd_fmri_fini(void)
+{
+}
+
+ssize_t
+fmd_fmri_nvl2str(nvlist_t *nvl, char *buf, size_t buflen)
+{
+	int err;
+	ssize_t len;
+	topo_hdl_t *thp;
+	char *str;
+
+	if ((thp = fmd_fmri_topo_hold(TOPO_VERSION)) == NULL)
+		return (fmd_fmri_set_errno(EINVAL));
+
+	if (topo_fmri_nvl2str(thp, nvl, &str, &err) != 0) {
+		fmd_fmri_topo_rele(thp);
+		return (fmd_fmri_set_errno(EINVAL));
+	}
+
+	if (buf != NULL)
+		len = snprintf(buf, buflen, "%s", str);
+	else
+		len = strlen(str);
+
+	topo_hdl_strfree(thp, str);
+	fmd_fmri_topo_rele(thp);
+
+	return (len);
+}
+
+/*ARGSUSED*/
+int
+fmd_fmri_present(nvlist_t *nvl)
+{
+	return (1);
+}
+
+/*ARGSUSED*/
+int
+fmd_fmri_replaced(nvlist_t *nvl)
+{
+	return (FMD_OBJ_STATE_UNKNOWN);
+}
+
+/*ARGSUSED*/
+int
+fmd_fmri_service_state(nvlist_t *nvl)
+{
+	return (FMD_SERVICE_STATE_UNKNOWN);
+}
+
+/*ARGSUSED*/
+int
+fmd_fmri_unusable(nvlist_t *nvl)
+{
+	return (0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fm/schemes/sw/sparc/Makefile	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,33 @@
+#
+# 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.com
+include ../../Makefile.targ
+
+LDLIBS += -ltopo
+LDFLAGS += -L$(ROOT)/usr/lib/fm -R/usr/lib/fm
+LINTFLAGS += -L$(ROOT)/usr/lib/fm
+
+install: all $(ROOTPROG)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fm/schemes/sw/sparcv9/Makefile	Fri Jul 30 17:04:17 2010 +1000
@@ -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.com
+include $(SRC)/Makefile.master.64
+include ../../Makefile.targ
+
+LDLIBS += -ltopo
+LDFLAGS += -L$(ROOT)/usr/lib/fm/$(MACH64) -R/usr/lib/fm/$(MACH64)
+LINTFLAGS += -L$(ROOT)/usr/lib/fm/$(MACH64)
+
+install: all $(ROOTPROG64)
--- a/usr/src/cmd/fm/scripts/dictck.pl	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/fm/scripts/dictck.pl	Fri Jul 30 17:04:17 2010 +1000
@@ -19,10 +19,7 @@
 #
 # CDDL HEADER END
 #
-# ident	"%Z%%M%	%I%	%E% SMI"
-#
-# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
+# Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
 #
 #
 # dictck -- Sanity check a .dict file and optionally the corresponding .po file
@@ -56,6 +53,11 @@
 };
 
 #
+# Category 1 event classes
+#
+my @cat1ev = qw(fault defect upset ereport list ireport);
+
+#
 # usage -- print a usage message and exit
 #
 sub usage {
@@ -182,10 +184,11 @@
 
 		# check for duplicate or unexpected keys
 		my %keys;
+		my $cat1pat = join('|', @cat1ev);
 		foreach my $e (split(/\s/, $lhs)) {
 			die "$name:$line: unknown event type \"$e\"\n"
 			    unless $e =~
-			    /^(fault|defect|upset|ereport|list)\..*[^.]$/;
+			    /^($cat1pat)\..*[^.]$/;
 			die "$name:$line: key repeated: \"$e\"\n"
 			    if defined($keys{$e});
 			$keys{$e} = 1;
--- a/usr/src/cmd/logadm/Makefile	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/logadm/Makefile	Fri Jul 30 17:04:17 2010 +1000
@@ -19,8 +19,7 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
+# Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
 #
 
 PROG= logadm
@@ -40,6 +39,18 @@
 
 CLOBBERFILES += $(TESTS)
 
+LOGADMDIR = $(ROOT)/etc/logadm.d
+
+MFSTFILES =	logadm-upgrade.xml
+MANIFESTDIR =	$(ROOT)/lib/svc/manifest/system
+MANIFEST =	$(MFSTFILES:%=$(MANIFESTDIR)/%)
+$(MANIFEST) :=	FILEMODE = 0444
+
+METHODFILES =	logadm-upgrade
+METHODDIR =	$(ROOT)/lib/svc/method
+METHOD =	$(METHODFILES:%=$(METHODDIR)/%)
+$(METHOD) :=	FILEMODE = 0555
+
 .KEEP_STATE:
 
 all: $(PROG)
@@ -51,7 +62,23 @@
 	$(LINK.c) -o $@ $(OBJS) $(LDLIBS)
 	$(POST_PROCESS)
 
-install: all $(ROOTUSRSBINPROG) $(ROOTETC)/$(CONFIGFILE)
+install: all $(ROOTUSRSBINPROG) $(ROOTETC)/$(CONFIGFILE) $(LOGADMDIR) \
+             $(MANIFESTDIR) $(MANIFEST) $(METHODDIR) $(METHOD)
+
+$(LOGADMDIR):
+	$(INS.dir)
+
+$(MANIFESTDIR):
+	$(INS.dir)
+
+$(MANIFESTDIR)/% : %
+	$(INS.file)
+
+$(METHODDIR):
+	$(INS.dir)
+
+$(METHODDIR)/% : %
+	$(INS.file)
 
 $(POFILE): $(POFILES)
 	$(RM) $@
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/logadm/logadm-upgrade	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,138 @@
+#!/sbin/sh
+#
+# 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.
+#
+
+. /lib/svc/share/smf_include.sh
+
+LOGADM=/etc/logadm.conf
+LOGADM_D=${LOGADM%conf}d
+LS=/usr/bin/ls
+AWK=/usr/bin/awk
+GREP=/usr/bin/grep
+
+#
+# This is a temporary service to allow addition (only) to /etc/logadm.conf
+# It is temporary in the sense that logadm(1M) should have its configuration
+# migrated to SMF in the future.
+#
+
+#
+# Display error message and exits with error code
+#
+msg_exit()
+{
+	exit_code=$1
+	msg=$2
+
+	echo "${msg}"
+	exit ${exit_code}
+}
+
+#
+# If there is no /etc/logadm.d we can bail
+#
+if [ ! -d ${LOGADM_D} ]; then
+	exit ${SMF_EXIT_OK}
+fi
+
+#
+# Cache files
+#
+files=$(${LS} -t  ${LOGADM} ${LOGADM_D}/*)
+
+#
+# If there is no /etc/logadm.conf create it and make sure it has the
+# right ownership and permissions.
+# Make sure this is done AFTER $files is set. Otherwise /etc/logadm.conf will be
+# newer than all files is /etc/logadm.d and they will be skipped.
+#
+if [ ! -f ${LOGADM} ]; then
+	touch ${LOGADM}
+	chmod 644 ${LOGADM}
+	chown root:sys ${LOGADM}
+fi
+
+for f in ${files}
+do
+	#
+	# If it is not a file, we skip it.
+	#
+	if [ ! -f ${f} ]; then
+		continue
+	fi
+
+	#
+	# We stop when files at /etc/logadm.d are older than /etc/logadm.conf
+	#
+	if [ ${f} = ${LOGADM} ]; then
+		break
+	fi
+
+	#
+	# We ignore files that are not owned by root, group sys
+	# and have permissions different than 444
+	#
+	perm=$(${LS} -l ${f} | ${AWK} '{printf("%s %s:%s", $1, $3, $4)}')
+	if [ $? != 0 ]; then
+		msg_exit ${SMF_EXIT_ERR_FATAL} "${perm}"
+	fi
+	if [ "${perm}" != "-r--r--r-- root:sys" ]; then
+		echo "Unexpected permission/ownership for ${f}"
+		echo "    expected -r--r--r-- root:sys but got ${perm}"
+		echo "    skipping ${f}"
+		continue
+	fi
+
+	#
+	# Discard comments (lines starting with #)
+	#
+	${GREP} -v '^#' ${f} | while read entry
+	do
+		sig=$(echo ${entry} | ${AWK} '{printf("%s\>", $1);}' 2>&1)
+		if [ $? != 0 ]; then # only happens if awk(1) fails
+			msg_exit ${SMF_EXIT_ERR_FATAL} "${sig}"
+		fi
+
+		#
+		# if ${sig} is null but the previous command succeeded, we skip
+		#
+		if [ ! ${sig} ]; then
+			continue;
+		fi
+
+		err_msg=$(${GREP} ^${sig} ${LOGADM} 2>&1)
+		case $? in
+		'1')
+			echo "${entry}" >> ${LOGADM}
+			;;
+		'0')
+			;;
+		*)
+			msg_exit ${SMF_EXIT_ERR_FATAL} "${err_msg}"
+		esac
+	done
+done
+
+exit ${SMF_EXIT_OK}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/logadm/logadm-upgrade.xml	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,84 @@
+<?xml version="1.0"?>
+<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
+<!--
+ 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
+
+	NOTE:  This service manifest is not editable; its contents will
+	be overwritten by package or patch operations, including
+	operating system upgrade.  Make customizations in a different
+	file.
+-->
+
+<service_bundle type='manifest' name='SUNWcs:logadm-upgrade'>
+
+<service
+	name='system/logadm-upgrade'
+	type='service'
+	version='1'>
+
+	<create_default_instance enabled='true' />
+
+	<single_instance />
+
+	<dependency
+		name='filesystem-minimal'
+		type='service'
+		grouping='require_all'
+		restart_on='none'>
+		<service_fmri value='svc:/system/filesystem/minimal' />
+	</dependency>
+
+	<exec_method
+		type='method'
+		name='start'
+		exec='/lib/svc/method/logadm-upgrade'
+		timeout_seconds='60' />
+
+	<exec_method
+		type='method'
+		name='stop'
+		exec=':true'
+		timeout_seconds='0' />
+
+	<exec_method
+		type='method'
+		name='refresh'
+		exec='/lib/svc/method/logadm-upgrade'
+		timeout_seconds='60' />
+
+	<property_group name='startd' type='framework'>
+		<propval name='duration' type='astring' value='transient' />
+	</property_group>
+
+	<stability value='Unstable' />
+
+	<template>
+		<common_name>
+			<loctext xml:lang='C'>
+logadm upgrade
+			</loctext>
+		</common_name>
+	</template>
+</service>
+
+</service_bundle>
+
--- a/usr/src/cmd/logadm/logadm.conf	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/logadm/logadm.conf	Fri Jul 30 17:04:17 2010 +1000
@@ -18,8 +18,7 @@
 #
 # CDDL HEADER END
 #
-# Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
+# Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
 #
 # logadm.conf
 #
--- a/usr/src/cmd/mdb/common/kmdb/kmdb_kvm.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/mdb/common/kmdb/kmdb_kvm.c	Fri Jul 30 17:04:17 2010 +1000
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #include <kmdb/kmdb_kvm.h>
@@ -546,6 +545,7 @@
 {
 	kmt_data_t *kmt = mdb.m_target->t_data;
 	struct utsname uts;
+	char uuid[37];
 	kreg_t tt;
 
 	if (mdb_tgt_readsym(mdb.m_target, MDB_TGT_AS_VIRT, &uts, sizeof (uts),
@@ -561,6 +561,17 @@
 	mdb_printf("operating system: %s %s (%s)\n",
 	    uts.release, uts.version, uts.machine);
 
+	if (mdb_tgt_readsym(mdb.m_target, MDB_TGT_AS_VIRT, uuid, sizeof (uuid),
+	    "genunix", "dump_osimage_uuid") != sizeof (uuid)) {
+		warn("failed to read 'dump_osimage_uuid' string from kernel\n");
+		(void) strcpy(uuid, "(error)");
+	} else if (*uuid == '\0') {
+		(void) strcpy(uuid, "(not set)");
+	} else if (uuid[36] != '\0') {
+		(void) strcpy(uuid, "(invalid)");
+	}
+	mdb_printf("image uuid: %s\n", uuid);
+
 	if (kmt->kmt_cpu != NULL) {
 		mdb_printf("CPU-specific support: %s\n",
 		    kmt_cpu_name(kmt->kmt_cpu));
--- a/usr/src/cmd/mdb/common/mdb/mdb_kvm.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/mdb/common/mdb/mdb_kvm.c	Fri Jul 30 17:04:17 2010 +1000
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 /*
@@ -485,9 +484,18 @@
 	if (kt->k_dumphdr) {
 		dumphdr_t *dh = kt->k_dumphdr;
 
+		mdb_printf("image uuid: %s\n", dh->dump_uuid[0] != '\0' ?
+		    dh->dump_uuid : "(not set)");
 		mdb_printf("panic message: %s\n", dh->dump_panicstring);
 
 		kt->k_dump_print_content(dh, kt->k_dumpcontent);
+	} else {
+		char uuid[37];
+
+		if (mdb_readsym(uuid, 37, "dump_osimage_uuid") == 37 &&
+		    uuid[36] == '\0') {
+			mdb_printf("image uuid: %s\n", uuid);
+		}
 	}
 
 	return (DCMD_OK);
--- a/usr/src/cmd/mdb/common/modules/svc.startd/startd.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/mdb/common/modules/svc.startd/startd.c	Fri Jul 30 17:04:17 2010 +1000
@@ -19,12 +19,9 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <mdb/mdb_modapi.h>
 
 #include <libuutil.h>
@@ -59,6 +56,14 @@
 	u_longlong_t lookups;
 	u_longlong_t dep_inserts, dep_cycle_ns, dep_insert_ns;
 	size_t graph_num, restarter_num;
+	uint64_t ct_maint;
+	uint64_t ct_hwerr;
+	uint64_t ct_service;
+	uint64_t ct_global;
+	uint64_t ct_noprefs;
+	uint64_t ct_from_uninit;
+	uint64_t ct_bad_state;
+	uint64_t ct_ovr_prefs;
 
 	if (mdb_readvar(&lookups, "dictionary_lookups") == -1) {
 		mdb_warn("failed to read 'dictionary_lookups' value\n");
@@ -109,18 +114,71 @@
 		return (DCMD_ERR);
 	}
 
+	if (mdb_readvar(&ct_maint, "stev_ct_maint") == -1) {
+		mdb_warn("failed to read 'stev_ct_maint'\n");
+		return (DCMD_ERR);
+	}
+
+	if (mdb_readvar(&ct_hwerr, "stev_ct_hwerr") == -1) {
+		mdb_warn("failed to read 'stev_ct_hwerr'\n");
+		return (DCMD_ERR);
+	}
+
+	if (mdb_readvar(&ct_service, "stev_ct_service") == -1) {
+		mdb_warn("failed to read 'stev_ct_service'\n");
+		return (DCMD_ERR);
+	}
+
+	if (mdb_readvar(&ct_global, "stev_ct_global") == -1) {
+		mdb_warn("failed to read 'stev_ct_global'\n");
+		return (DCMD_ERR);
+	}
+
+	if (mdb_readvar(&ct_noprefs, "stev_ct_noprefs") == -1) {
+		mdb_warn("failed to read 'stev_ct_noprefs'\n");
+		return (DCMD_ERR);
+	}
+
+	if (mdb_readvar(&ct_from_uninit, "stev_ct_from_uninit") == -1) {
+		mdb_warn("failed to read 'stev_ct_from_uninit'\n");
+		return (DCMD_ERR);
+	}
+
+	if (mdb_readvar(&ct_bad_state, "stev_ct_bad_state") == -1) {
+		mdb_warn("failed to read 'stev_ct_bad_state'\n");
+		return (DCMD_ERR);
+	}
+
+	if (mdb_readvar(&ct_ovr_prefs, "stev_ct_ovr_prefs") == -1) {
+		mdb_warn("failed to read 'stev_ct_ovr_prefs'\n");
+		return (DCMD_ERR);
+	}
+
 	mdb_printf(
-	    "         dictionary lookups: %llu\n"
-	    "        average lookup time: %llu us\n"
-	    "graph dependency insertions: %llu\n"
-	    "   average cycle-check time: %llu us\n"
-	    " avg dependency insert time: %llu us\n"
-	    "number of nodes in dgraph: %llu\n"
-	    "number of nodes in instance_list: %llu\n", lookups,
+	    "General stats\n"
+	    "              dictionary lookups: %llu\n"
+	    "             average lookup time: %llu us\n"
+	    "     graph dependency insertions: %llu\n"
+	    "        average cycle-check time: %llu us\n"
+	    "      avg dependency insert time: %llu us\n"
+	    "       number of nodes in dgraph: %llu\n"
+	    "number of nodes in instance_list: %llu\n"
+	    "\nState Transition Events\n"
+	    "                     maintenance: %llu\n"
+	    "                  hardware error: %llu\n"
+	    "           service specific pref: %llu\n"
+	    "                system wide pref: %llu\n"
+	    "            no prefs, not raised: %llu\n"
+	    "          from unint, not raised: %llu\n"
+	    "           bad state, not raised: %llu\n"
+	    "           override pref, raised: %llu\n", lookups,
 	    lookups ? ns_total / (1000 * lookups) : 0, dep_inserts,
 	    dep_inserts ? dep_cycle_ns / (1000 * dep_inserts) : 0,
 	    dep_inserts ? dep_insert_ns / (1000 * dep_inserts) : 0,
-	    (u_longlong_t)graph_num, (u_longlong_t)restarter_num);
+	    (u_longlong_t)graph_num, (u_longlong_t)restarter_num,
+	    ct_maint, ct_hwerr, ct_service, ct_global, ct_noprefs,
+	    ct_from_uninit, ct_bad_state, ct_ovr_prefs);
+
 
 	return (DCMD_OK);
 }
--- a/usr/src/cmd/savecore/Makefile.com	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/savecore/Makefile.com	Fri Jul 30 17:04:17 2010 +1000
@@ -19,8 +19,7 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
+# Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
 #
 
 PROG= savecore
@@ -29,10 +28,20 @@
 
 include ../../Makefile.cmd
 
+C99MODE = $(C99_ENABLE)
+
 CFLAGS += $(CCVERBOSE)
 CFLAGS64 += $(CCVERBOSE)
 CPPFLAGS += -D_LARGEFILE64_SOURCE=1 -DBZ_NO_STDIO -I$(SRC)/uts/common
 
+#
+# savecore is compiled with bits from $(SRC)/common/bzip2 and some function
+# symbols there are defined as weak; if you leave them out of
+# savecore.c it will compile, but trying to call that function
+# will jump to 0.  So we use -ztext to avoid that.
+#
+LDFLAGS += -ztext
+
 BZIP2OBJS =	bz2blocksort.o	\
 		bz2compress.o	\
 		bz2decompress.o	\
@@ -52,7 +61,22 @@
 clean:
 	$(RM) $(OBJS) $(BZIP2OBJS)
 
-lint:	lint_SRCS
+lint := CPPFLAGS += -I$(SRC)/common
+
+#
+# Linting the usr/src/common/bzip2 source produces reams of complaints.
+# So we only lint regular SRCS, but we need to excuse two complaints
+# related to bz_internal_error.
+#
+
+lint := BZ2LINTCOPOUTS = -erroff=E_NAME_USED_NOT_DEF2
+lint := BZ2LINTCOPOUTS += -erroff=E_NAME_DEF_NOT_USED2
+
+lint := LINTFLAGS += $(BZ2LINTCOPOUTS)
+lint := LINTFLAGS64 += $(BZ2LINTCOPOUTS)
+
+lint:	$(LINTSRCS)
+	$(LINT.c) $(SRCS) $(LDLIBS)
 
 include ../../Makefile.targ
 
--- a/usr/src/cmd/savecore/amd64/Makefile	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/savecore/amd64/Makefile	Fri Jul 30 17:04:17 2010 +1000
@@ -2,9 +2,8 @@
 # CDDL HEADER START
 #
 # The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License").  You may not use this file except in compliance
-# with the License.
+# 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.
@@ -20,13 +19,13 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
-#
-# ident	"%Z%%M%	%I%	%E% SMI"
+# Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
 #
 
 include ../Makefile.com
 include ../../Makefile.cmd.64
 
+LDLIBS += -L$(ROOT)/usr/lib/fm/amd64 -lfmevent -lnvpair
+LDFLAGS += -R/usr/lib/fm/amd64
+
 install: all $(ROOTPROG64)
--- a/usr/src/cmd/savecore/i386/Makefile	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/savecore/i386/Makefile	Fri Jul 30 17:04:17 2010 +1000
@@ -2,9 +2,8 @@
 # CDDL HEADER START
 #
 # The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License").  You may not use this file except in compliance
-# with the License.
+# 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.
@@ -19,13 +18,12 @@
 #
 # CDDL HEADER END
 #
-#
-# Copyright (c) 1998 by Sun Microsystems, Inc.
-# All rights reserved.
-#
-#ident	"%Z%%M%	%I%	%E% SMI"
+# Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
 #
 
 include ../Makefile.com
 
+LDLIBS += -L$(ROOT)/usr/lib/fm -lfmevent -lnvpair
+LDFLAGS += -R/usr/lib/fm
+
 install: all $(ROOTPROG32)
--- a/usr/src/cmd/savecore/savecore.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/savecore/savecore.c	Fri Jul 30 17:04:17 2010 +1000
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1983, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #include <stdio.h>
@@ -37,15 +36,22 @@
 #include <pthread.h>
 #include <limits.h>
 #include <atomic.h>
+#include <libnvpair.h>
+#include <libintl.h>
 #include <sys/mem.h>
 #include <sys/statvfs.h>
 #include <sys/dumphdr.h>
 #include <sys/dumpadm.h>
 #include <sys/compress.h>
+#include <sys/panic.h>
 #include <sys/sysmacros.h>
 #include <sys/stat.h>
 #include <sys/resource.h>
 #include <bzip2/bzlib.h>
+#include <sys/fm/util.h>
+#include <fm/libfmevent.h>
+#include <sys/int_fmtio.h>
+
 
 /* fread/fwrite buffer size */
 #define	FBUFSIZE		(1ULL << 20)
@@ -56,13 +62,15 @@
 /* create this file if metrics collection is enabled in the kernel */
 #define	METRICSFILE "METRICS.csv"
 
-static char 	progname[9] = "savecore";
+static char	progname[9] = "savecore";
 static char	*savedir;		/* savecore directory */
 static char	*dumpfile;		/* source of raw crash dump */
-static long	bounds;			/* numeric suffix */
+static long	bounds = -1;		/* numeric suffix */
 static long	pagesize;		/* dump pagesize */
 static int	dumpfd = -1;		/* dumpfile descriptor */
 static dumphdr_t corehdr, dumphdr;	/* initial and terminal dumphdrs */
+static boolean_t dump_incomplete;	/* dumphdr indicates incomplete */
+static boolean_t fm_panic;		/* dump is the result of fm_panic */
 static offset_t	endoff;			/* offset of end-of-dump header */
 static int	verbose;		/* chatty mode */
 static int	disregard_valid_flag;	/* disregard valid flag */
@@ -76,6 +84,75 @@
 static volatile uint64_t zpages;	/* count of zero pages not written */
 static dumpdatahdr_t datahdr;		/* compression info */
 static long	coreblksize;		/* preferred write size (st_blksize) */
+static int	cflag;			/* run as savecore -c */
+static int	mflag;			/* run as savecore -m */
+
+/*
+ * Payload information for the events we raise.  These are used
+ * in raise_event to determine what payload to include.
+ */
+#define	SC_PAYLOAD_SAVEDIR	0x0001	/* Include savedir in event */
+#define	SC_PAYLOAD_INSTANCE	0x0002	/* Include bounds instance number */
+#define	SC_PAYLOAD_IMAGEUUID	0x0004	/* Include dump OS instance uuid */
+#define	SC_PAYLOAD_CRASHTIME	0x0008	/* Include epoch crashtime */
+#define	SC_PAYLOAD_PANICSTR	0x0010	/* Include panic string */
+#define	SC_PAYLOAD_PANICSTACK	0x0020	/* Include panic string */
+#define	SC_PAYLOAD_FAILREASON	0x0040	/* Include failure reason */
+#define	SC_PAYLOAD_DUMPCOMPLETE	0x0080	/* Include completeness indicator */
+#define	SC_PAYLOAD_ISCOMPRESSED	0x0100	/* Dump is in vmdump.N form */
+#define	SC_PAYLOAD_DUMPADM_EN	0x0200	/* Is dumpadm enabled or not? */
+#define	SC_PAYLOAD_FM_PANIC	0x0400	/* Panic initiated by FMA */
+#define	SC_PAYLOAD_JUSTCHECKING	0x0800	/* Run with -c flag? */
+
+enum sc_event_type {
+	SC_EVENT_DUMP_PENDING,
+	SC_EVENT_SAVECORE_FAILURE,
+	SC_EVENT_DUMP_AVAILABLE
+};
+
+/*
+ * Common payload
+ */
+#define	_SC_PAYLOAD_CMN \
+    SC_PAYLOAD_IMAGEUUID | \
+    SC_PAYLOAD_CRASHTIME | \
+    SC_PAYLOAD_PANICSTR | \
+    SC_PAYLOAD_PANICSTACK | \
+    SC_PAYLOAD_DUMPCOMPLETE | \
+    SC_PAYLOAD_FM_PANIC | \
+    SC_PAYLOAD_SAVEDIR
+
+static const struct {
+	const char *sce_subclass;
+	uint32_t sce_payload;
+} sc_event[] = {
+	/*
+	 * SC_EVENT_DUMP_PENDING
+	 */
+	{
+		"dump_pending_on_device",
+		_SC_PAYLOAD_CMN | SC_PAYLOAD_DUMPADM_EN |
+		    SC_PAYLOAD_JUSTCHECKING
+	},
+
+	/*
+	 * SC_EVENT_SAVECORE_FAILURE
+	 */
+	{
+		"savecore_failure",
+		_SC_PAYLOAD_CMN | SC_PAYLOAD_INSTANCE | SC_PAYLOAD_FAILREASON
+	},
+
+	/*
+	 * SC_EVENT_DUMP_AVAILABLE
+	 */
+	{
+		"dump_available",
+		_SC_PAYLOAD_CMN | SC_PAYLOAD_INSTANCE | SC_PAYLOAD_ISCOMPRESSED
+	},
+};
+
+static void raise_event(enum sc_event_type, char *);
 
 static void
 usage(void)
@@ -85,22 +162,83 @@
 	exit(1);
 }
 
+#define	SC_SL_NONE	0x0001	/* no syslog */
+#define	SC_SL_ERR	0x0002	/* syslog if !interactive, LOG_ERR */
+#define	SC_SL_WARN	0x0004	/* syslog if !interactive, LOG_WARNING */
+#define	SC_IF_VERBOSE	0x0008	/* message only if -v */
+#define	SC_IF_ISATTY	0x0010	/* message only if interactive */
+#define	SC_EXIT_OK	0x0020	/* exit(0) */
+#define	SC_EXIT_ERR	0x0040	/* exit(1) */
+#define	SC_EXIT_PEND	0x0080	/* exit(2) */
+#define	SC_EXIT_FM	0x0100	/* exit(3) */
+
+#define	_SC_ALLEXIT	(SC_EXIT_OK | SC_EXIT_ERR | SC_EXIT_PEND | SC_EXIT_FM)
+
 static void
-logprint(int logpri, int showmsg, int exitcode, char *message, ...)
+logprint(uint32_t flags, char *message, ...)
 {
 	va_list args;
 	char buf[1024];
+	int do_always = ((flags & (SC_IF_VERBOSE | SC_IF_ISATTY)) == 0);
+	int do_ifverb = (flags & SC_IF_VERBOSE) && verbose;
+	int do_ifisatty = (flags & SC_IF_ISATTY) && interactive;
+	int code;
+	static int logprint_raised = 0;
 
-	if (showmsg) {
+	if (do_always || do_ifverb || do_ifisatty) {
 		va_start(args, message);
+		/*LINTED: E_SEC_PRINTF_VAR_FMT*/
 		(void) vsnprintf(buf, sizeof (buf), message, args);
 		(void) fprintf(stderr, "%s: %s\n", progname, buf);
-		if (!interactive && logpri >= 0)
-			syslog(logpri, buf);
+		if (!interactive) {
+			switch (flags & (SC_SL_NONE | SC_SL_ERR | SC_SL_WARN)) {
+			case SC_SL_ERR:
+				/*LINTED: E_SEC_PRINTF_VAR_FMT*/
+				syslog(LOG_ERR, buf);
+				break;
+
+			case SC_SL_WARN:
+				/*LINTED: E_SEC_PRINTF_VAR_FMT*/
+				syslog(LOG_WARNING, buf);
+				break;
+
+			default:
+				break;
+			}
+		}
 		va_end(args);
 	}
-	if (exitcode >= 0)
-		exit(exitcode);
+
+	switch (flags & _SC_ALLEXIT) {
+	case 0:
+		return;
+
+	case SC_EXIT_OK:
+		code = 0;
+		break;
+
+	case SC_EXIT_PEND:
+		code = 2;
+		break;
+
+	case SC_EXIT_FM:
+		code = 3;
+		break;
+
+	case SC_EXIT_ERR:
+	default:
+		/*
+		 * Raise an ireport saying why we are exiting.  Do not
+		 * raise if run as savecore -m.  If something in the
+		 * raise_event codepath calls logprint avoid recursion.
+		 */
+		if (!mflag && logprint_raised++ == 0)
+			raise_event(SC_EVENT_SAVECORE_FAILURE, buf);
+		code = 1;
+		break;
+	}
+
+	exit(code);
 }
 
 /*
@@ -112,7 +250,7 @@
 	int fd;
 
 	if ((fd = open64(name, oflags, mode)) == -1)
-		logprint(LOG_ERR, 1, 1, "open(\"%s\"): %s",
+		logprint(SC_SL_ERR | SC_EXIT_ERR, "open(\"%s\"): %s",
 		    name, strerror(errno));
 	return (fd);
 }
@@ -121,7 +259,7 @@
 Fread(void *buf, size_t size, FILE *f)
 {
 	if (fread(buf, size, 1, f) != 1)
-		logprint(LOG_ERR, 1, 1, "fread: ferror %d feof %d",
+		logprint(SC_SL_ERR | SC_EXIT_ERR, "fread: ferror %d feof %d",
 		    ferror(f), feof(f));
 }
 
@@ -129,14 +267,16 @@
 Fwrite(void *buf, size_t size, FILE *f)
 {
 	if (fwrite(buf, size, 1, f) != 1)
-		logprint(LOG_ERR, 1, 1, "fwrite: %s", strerror(errno));
+		logprint(SC_SL_ERR | SC_EXIT_ERR, "fwrite: %s",
+		    strerror(errno));
 }
 
 static void
 Fseek(offset_t off, FILE *f)
 {
 	if (fseeko64(f, off, SEEK_SET) != 0)
-		logprint(LOG_ERR, 1, 1, "fseeko64: %s", strerror(errno));
+		logprint(SC_SL_ERR | SC_EXIT_ERR, "fseeko64: %s",
+		    strerror(errno));
 }
 
 typedef struct stat64 Stat_t;
@@ -145,7 +285,7 @@
 Fstat(int fd, Stat_t *sb, const char *fname)
 {
 	if (fstat64(fd, sb) != 0)
-		logprint(LOG_ERR, 1, 1, "fstat(\"%s\"): %s", fname,
+		logprint(SC_SL_ERR | SC_EXIT_ERR, "fstat(\"%s\"): %s", fname,
 		    strerror(errno));
 }
 
@@ -153,7 +293,7 @@
 Stat(const char *fname, Stat_t *sb)
 {
 	if (stat64(fname, sb) != 0)
-		logprint(LOG_ERR, 1, 1, "stat(\"%s\"): %s", fname,
+		logprint(SC_SL_ERR | SC_EXIT_ERR, "stat(\"%s\"): %s", fname,
 		    strerror(errno));
 }
 
@@ -163,10 +303,10 @@
 	ssize_t sz = pread64(fd, buf, size, off);
 
 	if (sz < 0)
-		logprint(LOG_ERR, 1, 1,
+		logprint(SC_SL_ERR | SC_EXIT_ERR,
 		    "pread: %s", strerror(errno));
 	else if (sz != size)
-		logprint(LOG_ERR, 1, 1,
+		logprint(SC_SL_ERR | SC_EXIT_ERR,
 		    "pread: size %ld != %ld", sz, size);
 }
 
@@ -174,7 +314,8 @@
 Pwrite(int fd, void *buf, size_t size, off64_t off)
 {
 	if (pwrite64(fd, buf, size, off) != size)
-		logprint(LOG_ERR, 1, 1, "pwrite: %s", strerror(errno));
+		logprint(SC_SL_ERR | SC_EXIT_ERR, "pwrite: %s",
+		    strerror(errno));
 }
 
 static void *
@@ -183,7 +324,8 @@
 	void *buf;
 
 	if ((buf = calloc(size, 1)) == NULL)
-		logprint(LOG_ERR, 1, 1, "calloc: %s", strerror(errno));
+		logprint(SC_SL_ERR | SC_EXIT_ERR, "calloc: %s",
+		    strerror(errno));
 	return (buf);
 }
 
@@ -214,30 +356,31 @@
 	pagesize = dumphdr.dump_pagesize;
 
 	if (dumphdr.dump_magic != DUMP_MAGIC)
-		logprint(-1, 1, 0, "bad magic number %x",
+		logprint(SC_SL_NONE | SC_EXIT_OK, "bad magic number %x",
 		    dumphdr.dump_magic);
 
 	if ((dumphdr.dump_flags & DF_VALID) == 0 && !disregard_valid_flag)
-		logprint(-1, verbose, 0, "dump already processed");
+		logprint(SC_SL_NONE | SC_IF_VERBOSE | SC_EXIT_OK,
+		    "dump already processed");
 
 	if (dumphdr.dump_version != DUMP_VERSION)
-		logprint(-1, verbose, 0,
+		logprint(SC_SL_NONE | SC_IF_VERBOSE | SC_EXIT_OK,
 		    "dump version (%d) != %s version (%d)",
 		    dumphdr.dump_version, progname, DUMP_VERSION);
 
 	if (dumphdr.dump_wordsize != DUMP_WORDSIZE)
-		logprint(-1, 1, 0,
+		logprint(SC_SL_NONE | SC_EXIT_OK,
 		    "dump is from %u-bit kernel - cannot save on %u-bit kernel",
 		    dumphdr.dump_wordsize, DUMP_WORDSIZE);
 
 	if (datahdr.dump_datahdr_magic == DUMP_DATAHDR_MAGIC) {
 		if (datahdr.dump_datahdr_version != DUMP_DATAHDR_VERSION)
-			logprint(-1, verbose, 0,
+			logprint(SC_SL_NONE | SC_IF_VERBOSE | SC_EXIT_OK,
 			    "dump data version (%d) != %s data version (%d)",
 			    datahdr.dump_datahdr_version, progname,
 			    DUMP_DATAHDR_VERSION);
 	} else {
-		memset(&datahdr, 0, sizeof (datahdr));
+		(void) memset(&datahdr, 0, sizeof (datahdr));
 		datahdr.dump_maxcsize = pagesize;
 	}
 
@@ -257,7 +400,8 @@
 		 */
 		if (!filemode)
 			Pwrite(dumpfd, &dumphdr, sizeof (dumphdr), endoff);
-		logprint(LOG_ERR, 1, 1, "initial dump header corrupt");
+		logprint(SC_SL_ERR | SC_EXIT_ERR,
+		    "initial dump header corrupt");
 	}
 }
 
@@ -268,7 +412,8 @@
 	int64_t spacefree, dumpsize, minfree, datasize;
 
 	if (statvfs(".", &fsb) < 0)
-		logprint(LOG_ERR, 1, 1, "statvfs: %s", strerror(errno));
+		logprint(SC_SL_ERR | SC_EXIT_ERR, "statvfs: %s",
+		    strerror(errno));
 
 	dumpsize = dumphdr.dump_data - dumphdr.dump_start;
 	datasize = dumphdr.dump_npages * pagesize;
@@ -279,10 +424,11 @@
 
 	spacefree = (int64_t)fsb.f_bavail * fsb.f_frsize;
 	minfree = 1024LL * read_number_from_file("minfree", 1024);
-	if (spacefree < minfree + dumpsize)
-		logprint(LOG_ERR, 1, 1,
+	if (spacefree < minfree + dumpsize) {
+		logprint(SC_SL_ERR | SC_EXIT_ERR,
 		    "not enough space in %s (%lld MB avail, %lld MB needed)",
 		    savedir, spacefree >> 20, (minfree + dumpsize) >> 20);
+	}
 }
 
 static void
@@ -296,7 +442,7 @@
 	char *inbuf = Zalloc(FBUFSIZE);
 	FILE *in = fdopen(dup(dumpfd), "rb");
 
-	setvbuf(in, inbuf, _IOFBF, FBUFSIZE);
+	(void) setvbuf(in, inbuf, _IOFBF, FBUFSIZE);
 	Fseek(dumphdr.dump_map, in);
 
 	corehdr.dump_data = corehdr.dump_map + roundup(dump_mapsize, pagesize);
@@ -339,7 +485,7 @@
 
 	Pwrite(corefd, dmp, dump_mapsize, corehdr.dump_map);
 	free(dmp);
-	fclose(in);
+	(void) fclose(in);
 	free(inbuf);
 }
 
@@ -369,7 +515,7 @@
  * This supports older kernels with latest savecore.
  */
 static void
-CopyPages(offset_t dumpoff, offset_t *offp, int fd, char *buf, size_t sz)
+CopyPages(offset_t *offp, int fd, char *buf, size_t sz)
 {
 	uint32_t csize;
 	FILE *in = fdopen(dup(dumpfd), "rb");
@@ -378,8 +524,8 @@
 	char *outbuf = Zalloc(FBUFSIZE);
 	pgcnt_t np = dumphdr.dump_npages;
 
-	setvbuf(out, outbuf, _IOFBF, FBUFSIZE);
-	setvbuf(in, buf, _IOFBF, sz);
+	(void) setvbuf(out, outbuf, _IOFBF, FBUFSIZE);
+	(void) setvbuf(in, buf, _IOFBF, sz);
 	Fseek(dumphdr.dump_data, in);
 
 	Fseek(*offp, out);
@@ -388,7 +534,7 @@
 		Fwrite(&csize, sizeof (uint32_t), out);
 		*offp += sizeof (uint32_t);
 		if (csize > pagesize || csize == 0) {
-			logprint(LOG_ERR, 1, -1,
+			logprint(SC_SL_ERR,
 			    "CopyPages: page %lu csize %d (0x%x) pagesize %d",
 			    dumphdr.dump_npages - np, csize, csize,
 			    pagesize);
@@ -399,8 +545,8 @@
 		*offp += csize;
 		np--;
 	}
-	fclose(in);
-	fclose(out);
+	(void) fclose(in);
+	(void) fclose(out);
 	free(outbuf);
 	free(buf);
 }
@@ -418,7 +564,7 @@
 	offset_t coreoff;
 	size_t nb;
 
-	logprint(LOG_ERR, verbose, -1,
+	logprint(SC_SL_ERR | SC_IF_VERBOSE,
 	    "Copying %s to %s/%s\n", dumpfile, savedir, corefile);
 
 	/*
@@ -465,7 +611,7 @@
 		Copy(dumphdr.dump_data, datahdr.dump_data_csize, &coreoff,
 		    corefd, inbuf, bufsz);
 	else
-		CopyPages(dumphdr.dump_data, &coreoff, corefd, inbuf, bufsz);
+		CopyPages(&coreoff, corefd, inbuf, bufsz);
 
 	/*
 	 * Now write the modified dump header to front and end of the copy.
@@ -478,7 +624,7 @@
 	 *
 	 * Pad with zeros to each DUMP_OFFSET boundary.
 	 */
-	memset(inbuf, 0, DUMP_OFFSET);
+	(void) memset(inbuf, 0, DUMP_OFFSET);
 
 	nb = DUMP_OFFSET - (coreoff & (DUMP_OFFSET - 1));
 	if (nb > 0) {
@@ -628,7 +774,7 @@
 	}
 
 	/* init worker threads */
-	pthread_mutex_lock(&lock);
+	(void) pthread_mutex_lock(&lock);
 	threads_active = 1;
 	threads_stop = 0;
 	for (t = tinfo; t != endtinfo; t++) {
@@ -639,10 +785,10 @@
 			break;
 		}
 		if (pthread_create(&t->tid, NULL, runstreams, t) != 0)
-			logprint(LOG_ERR, 1, 1, "pthread_create: %s",
+			logprint(SC_SL_ERR | SC_EXIT_ERR, "pthread_create: %s",
 			    strerror(errno));
 	}
-	pthread_mutex_unlock(&lock);
+	(void) pthread_mutex_unlock(&lock);
 }
 
 static void
@@ -650,12 +796,12 @@
 {
 	stream_t *s;
 
-	pthread_mutex_lock(&lock);
+	(void) pthread_mutex_lock(&lock);
 	for (s = streams; s != endstreams; s++) {
 		while (s->bound || s->blocks.head != NULL)
-			pthread_cond_wait(&cvbarrier, &lock);
+			(void) pthread_cond_wait(&cvbarrier, &lock);
 	}
-	pthread_mutex_unlock(&lock);
+	(void) pthread_mutex_unlock(&lock);
 }
 
 static void
@@ -665,12 +811,12 @@
 
 	if (threads_active) {
 		sbarrier();
-		pthread_mutex_lock(&lock);
+		(void) pthread_mutex_lock(&lock);
 		threads_stop = 1;
-		pthread_cond_signal(&cvwork);
-		pthread_mutex_unlock(&lock);
+		(void) pthread_cond_signal(&cvwork);
+		(void) pthread_mutex_unlock(&lock);
 		for (t = tinfo; t != endtinfo; t++)
-			pthread_join(t->tid, NULL);
+			(void) pthread_join(t->tid, NULL);
 		free(tinfo);
 		tinfo = NULL;
 		threads_active = 0;
@@ -682,10 +828,10 @@
 {
 	block_t *b;
 
-	pthread_mutex_lock(&lock);
+	(void) pthread_mutex_lock(&lock);
 	while ((b = deqh(&freeblocks)) == NULL)
-		pthread_cond_wait(&cvfree, &lock);
-	pthread_mutex_unlock(&lock);
+		(void) pthread_cond_wait(&cvfree, &lock);
+	(void) pthread_mutex_unlock(&lock);
 	return (b);
 }
 
@@ -707,6 +853,7 @@
 	size_t sz;
 	uint64_t *pl;
 
+	/*LINTED:E_BAD_PTR_CAST_ALIGN*/
 	pl = (uint64_t *)(buf);
 	for (sz = 0; sz < pagesize; sz += sizeof (*pl))
 		if (*pl++ != 0)
@@ -732,7 +879,6 @@
 static void
 lzjbblock(int corefd, stream_t *s, char *block, size_t blocksz)
 {
-	int rc = 0;
 	int in = 0;
 	int csize;
 	int doflush;
@@ -750,13 +896,13 @@
 	while (in < blocksz) {
 		switch (s->state) {
 		case STREAMSTART:
-			memcpy(&sh, block + in, sizeof (sh));
+			(void) memcpy(&sh, block + in, sizeof (sh));
 			in += sizeof (sh);
 			if (strcmp(DUMP_STREAM_MAGIC, sh.stream_magic) != 0)
-				logprint(LOG_ERR, 1, 1,
+				logprint(SC_SL_ERR | SC_EXIT_ERR,
 				    "LZJB STREAMSTART: bad stream header");
 			if (sh.stream_npages > datahdr.dump_maxrange)
-				logprint(LOG_ERR, 1, 1,
+				logprint(SC_SL_ERR | SC_EXIT_ERR,
 				    "LZJB STREAMSTART: bad range: %d > %d",
 				    sh.stream_npages, datahdr.dump_maxrange);
 			s->pagenum = sh.stream_pagenum;
@@ -767,18 +913,18 @@
 			s->state = STREAMPAGES;
 			break;
 		case STREAMPAGES:
-			memcpy(&sc, block + in, cs);
+			(void) memcpy(&sc, block + in, cs);
 			in += cs;
 			csize = DUMP_GET_CSIZE(sc);
 			if (csize > pagesize)
-				logprint(LOG_ERR, 1, 1,
+				logprint(SC_SL_ERR | SC_EXIT_ERR,
 				    "LZJB STREAMPAGES: bad csize=%d", csize);
 
 			out =  s->blkbuf + PTOB(s->nout);
 			dsize = decompress(block + in, out, csize, pagesize);
 
 			if (dsize != pagesize)
-				logprint(LOG_ERR, 1, 1,
+				logprint(SC_SL_ERR | SC_EXIT_ERR,
 				    "LZJB STREAMPAGES: dsize %d != pagesize %d",
 				    dsize, pagesize);
 
@@ -811,7 +957,7 @@
 void
 bz_internal_error(int errcode)
 {
-	logprint(LOG_ERR, 1, 1, "bz_internal_error: err %s\n",
+	logprint(SC_SL_ERR | SC_EXIT_ERR, "bz_internal_error: err %s\n",
 	    BZ2_bzErrorString(errcode));
 }
 
@@ -836,7 +982,7 @@
 		if (rc == BZ_STREAM_END) {
 			rc = BZ2_bzDecompressReset(&s->strm);
 			if (rc != BZ_OK)
-				logprint(LOG_ERR, 1, 1,
+				logprint(SC_SL_ERR | SC_EXIT_ERR,
 				    "BZ2_bzDecompressReset: %s",
 				    BZ2_bzErrorString(rc));
 			continue;
@@ -864,7 +1010,7 @@
 		s->init = 1;
 		rc = BZ2_bzDecompressInit(&s->strm, 0, 0);
 		if (rc != BZ_OK)
-			logprint(LOG_ERR, 1, 1,
+			logprint(SC_SL_ERR | SC_EXIT_ERR,
 			    "BZ2_bzDecompressInit: %s", BZ2_bzErrorString(rc));
 		if (s->blkbuf == NULL)
 			s->blkbuf = Zalloc(coreblksize);
@@ -880,10 +1026,10 @@
 			if (!bz2decompress(s, &s->sh, sizeof (s->sh)))
 				return;
 			if (strcmp(DUMP_STREAM_MAGIC, s->sh.stream_magic) != 0)
-				logprint(LOG_ERR, 1, 1,
+				logprint(SC_SL_ERR | SC_EXIT_ERR,
 				    "BZ2 STREAMSTART: bad stream header");
 			if (s->sh.stream_npages > datahdr.dump_maxrange)
-				logprint(LOG_ERR, 1, 1,
+				logprint(SC_SL_ERR | SC_EXIT_ERR,
 				    "BZ2 STREAMSTART: bad range: %d > %d",
 				    s->sh.stream_npages, datahdr.dump_maxrange);
 			s->pagenum = s->sh.stream_pagenum;
@@ -950,7 +1096,7 @@
 	block_t *b;
 	int bound;
 
-	pthread_mutex_lock(&lock);
+	(void) pthread_mutex_lock(&lock);
 	while (!threads_stop) {
 		bound = 0;
 		for (s = streams; s != endstreams; s++) {
@@ -958,10 +1104,10 @@
 				continue;
 			s->bound = 1;
 			bound = 1;
-			pthread_cond_signal(&cvwork);
+			(void) pthread_cond_signal(&cvwork);
 			while (s->blocks.head != NULL) {
 				b = deqh(&s->blocks);
-				pthread_mutex_unlock(&lock);
+				(void) pthread_mutex_unlock(&lock);
 
 				if (datahdr.dump_clevel < DUMP_CLEVEL_BZIP2)
 					lzjbblock(t->corefd, s, b->block,
@@ -970,21 +1116,21 @@
 					bz2block(t->corefd, s, b->block,
 					    b->size);
 
-				pthread_mutex_lock(&lock);
+				(void) pthread_mutex_lock(&lock);
 				enqt(&freeblocks, b);
-				pthread_cond_signal(&cvfree);
+				(void) pthread_cond_signal(&cvfree);
 
 				report_progress();
 			}
 			s->bound = 0;
-			pthread_cond_signal(&cvbarrier);
+			(void) pthread_cond_signal(&cvbarrier);
 		}
 		if (!bound && !threads_stop)
-			pthread_cond_wait(&cvwork, &lock);
+			(void) pthread_cond_wait(&cvwork, &lock);
 	}
-	close(t->corefd);
-	pthread_cond_signal(&cvwork);
-	pthread_mutex_unlock(&lock);
+	(void) close(t->corefd);
+	(void) pthread_cond_signal(&cvwork);
+	(void) pthread_mutex_unlock(&lock);
 	return (arg);
 }
 
@@ -1041,18 +1187,19 @@
 	char *inbuf = Zalloc(insz);
 	uint32_t csize;
 	dumpcsize_t dcsize;
-	dumpstreamhdr_t sh;
 	int nstreams = datahdr.dump_nstreams;
 	int maxcsize = datahdr.dump_maxcsize;
 	int nout, tag, doflush;
 
 	dumpf = fdopen(dup(dumpfd), "rb");
 	if (dumpf == NULL)
-		logprint(LOG_ERR, 1, 1, "fdopen: %s", strerror(errno));
+		logprint(SC_SL_ERR | SC_EXIT_ERR, "fdopen: %s",
+		    strerror(errno));
 
-	setvbuf(dumpf, inbuf, _IOFBF, insz);
+	(void) setvbuf(dumpf, inbuf, _IOFBF, insz);
 	Fseek(dumphdr.dump_data, dumpf);
 
+	/*LINTED: E_CONSTANT_CONDITION*/
 	while (1) {
 
 		/*
@@ -1067,16 +1214,16 @@
 		if (tag != 0) {		/* a stream block */
 
 			if (nstreams == 0)
-				logprint(LOG_ERR, 1, 1,
+				logprint(SC_SL_ERR | SC_EXIT_ERR,
 				    "starting data header is missing");
 
 			if (tag > nstreams)
-				logprint(LOG_ERR, 1, 1,
+				logprint(SC_SL_ERR | SC_EXIT_ERR,
 				    "stream tag %d not in range 1..%d",
 				    tag, nstreams);
 
 			if (csize > maxcsize)
-				logprint(LOG_ERR, 1, 1,
+				logprint(SC_SL_ERR | SC_EXIT_ERR,
 				    "block size 0x%x > max csize 0x%x",
 				    csize, maxcsize);
 
@@ -1089,16 +1236,16 @@
 			b->size = csize;
 			Fread(b->block, csize, dumpf);
 
-			pthread_mutex_lock(&lock);
+			(void) pthread_mutex_lock(&lock);
 			enqt(&s->blocks, b);
 			if (!s->bound)
-				pthread_cond_signal(&cvwork);
-			pthread_mutex_unlock(&lock);
+				(void) pthread_cond_signal(&cvwork);
+			(void) pthread_mutex_unlock(&lock);
 
 		} else if (csize > 0) {		/* one lzjb page */
 
 			if (csize > pagesize)
-				logprint(LOG_ERR, 1, 1,
+				logprint(SC_SL_ERR | SC_EXIT_ERR,
 				    "csize 0x%x > pagesize 0x%x",
 				    csize, pagesize);
 
@@ -1115,7 +1262,7 @@
 			dsize = decompress(cpage, out, csize, pagesize);
 
 			if (dsize != pagesize)
-				logprint(LOG_ERR, 1, 1,
+				logprint(SC_SL_ERR | SC_EXIT_ERR,
 				    "dsize 0x%x != pagesize 0x%x",
 				    dsize, pagesize);
 
@@ -1161,8 +1308,8 @@
 
 	stopstreams();
 	if (tracef != NULL)
-		fclose(tracef);
-	fclose(dumpf);
+		(void) fclose(tracef);
+	(void) fclose(dumpf);
 	if (inbuf)
 		free(inbuf);
 	if (cpage)
@@ -1195,7 +1342,8 @@
 	Fstat(corefd, &st, corefile);
 
 	if (verbose > 1)
-		printf("%s: %ld block size\n", corefile, st.st_blksize);
+		(void) printf("%s: %ld block size\n", corefile,
+		    (long)st.st_blksize);
 	coreblksize = st.st_blksize;
 	if (coreblksize < MINCOREBLKSIZE || !ISP2(coreblksize))
 		coreblksize = MINCOREBLKSIZE;
@@ -1218,7 +1366,7 @@
 	ksyms_dsize = decompress(ksyms_cbase, ksyms_base, ksyms_csize,
 	    ksyms_size);
 	if (ksyms_dsize != ksyms_size)
-		logprint(LOG_WARNING, 1, -1,
+		logprint(SC_SL_WARN,
 		    "bad data in symbol table, %lu of %lu bytes saved",
 		    ksyms_dsize, ksyms_size);
 
@@ -1257,7 +1405,7 @@
 		    dumphdr.dump_npages);
 
 	if (saved != dumphdr.dump_npages)
-		logprint(LOG_WARNING, 1, -1, "bad data after page %ld", saved);
+		logprint(SC_SL_WARN, "bad data after page %ld", saved);
 
 	/*
 	 * Write out the modified dump headers.
@@ -1314,18 +1462,19 @@
 			break;
 
 		if (ld.ld_magic != LOG_MAGIC)
-			logprint(LOG_ERR, verbose, 0, "bad magic %x",
-			    ld.ld_magic);
+			logprint(SC_SL_ERR | SC_IF_VERBOSE | SC_EXIT_ERR,
+			    "bad magic %x", ld.ld_magic);
 
 		if (dat.len >= DUMP_LOGSIZE)
-			logprint(LOG_ERR, verbose, 0, "bad size %d",
-			    ld.ld_msgsize);
+			logprint(SC_SL_ERR | SC_IF_VERBOSE | SC_EXIT_ERR,
+			    "bad size %d", ld.ld_msgsize);
 
 		Pread(dumpfd, ctl.buf, ctl.len, dumpoff);
 		dumpoff += ctl.len;
 
 		if (ld.ld_csum != checksum32(ctl.buf, ctl.len))
-			logprint(LOG_ERR, verbose, 0, "bad log_ctl checksum");
+			logprint(SC_SL_ERR | SC_IF_VERBOSE | SC_EXIT_OK,
+			    "bad log_ctl checksum");
 
 		lc.flags |= SL_LOGONLY;
 
@@ -1333,10 +1482,12 @@
 		dumpoff += dat.len;
 
 		if (ld.ld_msum != checksum32(dat.buf, dat.len))
-			logprint(LOG_ERR, verbose, 0, "bad message checksum");
+			logprint(SC_SL_ERR | SC_IF_VERBOSE | SC_EXIT_OK,
+			    "bad message checksum");
 
 		if (putpmsg(logfd, &ctl, &dat, 1, MSG_BAND) == -1)
-			logprint(LOG_ERR, 1, 1, "putpmsg: %s", strerror(errno));
+			logprint(SC_SL_ERR | SC_EXIT_ERR, "putpmsg: %s",
+			    strerror(errno));
 
 		ld.ld_magic = 0;	/* clear magic so we never save twice */
 		Pwrite(dumpfd, &ld, sizeof (log_dump_t), ldoff);
@@ -1350,25 +1501,152 @@
 	long b = -1;
 	const char *p = strrchr(f, '/');
 
-	sscanf(p ? p + 1 : f, "vmdump.%ld", &b);
+	(void) sscanf(p ? p + 1 : f, "vmdump.%ld", &b);
 	return (b);
 }
 
+static void
+stack_retrieve(char *stack)
+{
+	summary_dump_t sd;
+	offset_t dumpoff = -(DUMP_OFFSET + DUMP_LOGSIZE +
+	    DUMP_ERPTSIZE);
+	dumpoff -= DUMP_SUMMARYSIZE;
+
+	dumpfd = Open(dumpfile, O_RDWR | O_DSYNC, 0644);
+	dumpoff = llseek(dumpfd, dumpoff, SEEK_END) & -DUMP_OFFSET;
+
+	Pread(dumpfd, &sd, sizeof (summary_dump_t), dumpoff);
+	dumpoff += sizeof (summary_dump_t);
+
+	if (sd.sd_magic == 0) {
+		*stack = '\0';
+		return;
+	}
+
+	if (sd.sd_magic != SUMMARY_MAGIC) {
+		*stack = '\0';
+		logprint(SC_SL_NONE | SC_IF_VERBOSE,
+		    "bad summary magic %x", sd.sd_magic);
+		return;
+	}
+	Pread(dumpfd, stack, STACK_BUF_SIZE, dumpoff);
+	if (sd.sd_ssum != checksum32(stack, STACK_BUF_SIZE))
+		logprint(SC_SL_NONE | SC_IF_VERBOSE, "bad stack checksum");
+}
+
+static void
+raise_event(enum sc_event_type evidx, char *warn_string)
+{
+	uint32_t pl = sc_event[evidx].sce_payload;
+	char panic_stack[STACK_BUF_SIZE];
+	nvlist_t *attr = NULL;
+	char uuidbuf[36 + 1];
+	int err = 0;
+
+	if (nvlist_alloc(&attr, NV_UNIQUE_NAME, 0) != 0)
+		goto publish;	/* try to send payload-free event */
+
+	if (pl & SC_PAYLOAD_SAVEDIR && savedir != NULL)
+		err |= nvlist_add_string(attr, "dumpdir", savedir);
+
+	if (pl & SC_PAYLOAD_INSTANCE && bounds != -1)
+		err |= nvlist_add_int64(attr, "instance", bounds);
+
+	if (pl & SC_PAYLOAD_ISCOMPRESSED) {
+		err |= nvlist_add_boolean_value(attr, "compressed",
+		    csave ? B_TRUE : B_FALSE);
+	}
+
+	if (pl & SC_PAYLOAD_DUMPADM_EN) {
+		char *disabled = defread("DUMPADM_ENABLE=no");
+
+		err |= nvlist_add_boolean_value(attr, "savecore-enabled",
+		    disabled ? B_FALSE : B_TRUE);
+	}
+
+	if (pl & SC_PAYLOAD_IMAGEUUID) {
+		(void) strncpy(uuidbuf, corehdr.dump_uuid, 36);
+		uuidbuf[36] = '\0';
+		err |= nvlist_add_string(attr, "os-instance-uuid", uuidbuf);
+	}
+
+	if (pl & SC_PAYLOAD_CRASHTIME) {
+		err |= nvlist_add_int64(attr, "crashtime",
+		    (int64_t)corehdr.dump_crashtime);
+	}
+
+	if (pl & SC_PAYLOAD_PANICSTR && corehdr.dump_panicstring[0] != '\0') {
+		err |= nvlist_add_string(attr, "panicstr",
+		    corehdr.dump_panicstring);
+	}
+
+	if (pl & SC_PAYLOAD_PANICSTACK) {
+		stack_retrieve(panic_stack);
+
+		if (panic_stack[0] != '\0') {
+			/*
+			 * The summary page may not be present if the dump
+			 * was previously recorded compressed.
+			 */
+			(void) nvlist_add_string(attr, "panicstack",
+			    panic_stack);
+		}
+	}
+
+	/* add warning string if this is an ireport for dump failure */
+	if (pl & SC_PAYLOAD_FAILREASON && warn_string != NULL)
+		(void) nvlist_add_string(attr, "failure-reason", warn_string);
+
+	if (pl & SC_PAYLOAD_DUMPCOMPLETE)
+		err |= nvlist_add_boolean_value(attr, "dump-incomplete",
+		    dump_incomplete ? B_TRUE : B_FALSE);
+
+	if (pl & SC_PAYLOAD_FM_PANIC) {
+		err |= nvlist_add_boolean_value(attr, "fm-panic",
+		    fm_panic ? B_TRUE : B_FALSE);
+	}
+
+	if (pl & SC_PAYLOAD_JUSTCHECKING) {
+		err |= nvlist_add_boolean_value(attr, "will-attempt-savecore",
+		    cflag ? B_FALSE : B_TRUE);
+	}
+
+	if (err)
+		logprint(SC_SL_WARN, "Errors while constructing '%s' "
+		    "event payload; will try to publish anyway.");
+publish:
+	if (fmev_rspublish_nvl(FMEV_RULESET_ON_SUNOS,
+	    "panic", sc_event[evidx].sce_subclass, FMEV_HIPRI,
+	    attr) != FMEV_SUCCESS) {
+		logprint(SC_SL_ERR, "failed to publish '%s' event: %s",
+		    sc_event[evidx].sce_subclass, fmev_strerror(fmev_errno));
+		nvlist_free(attr);
+	}
+
+}
+
+
 int
 main(int argc, char *argv[])
 {
-	int i, n, c, bfd;
-	int mflag = 0;
+	int i, c, bfd;
 	Stat_t st;
 	struct rlimit rl;
 	long filebounds = -1;
 	char namelist[30], corefile[30], boundstr[30];
 
+	if (geteuid() != 0) {
+		(void) fprintf(stderr, "%s: %s %s\n", progname,
+		    gettext("you must be root to use"), progname);
+		exit(1);
+	}
+
 	startts = gethrtime();
 
-	getrlimit(RLIMIT_NOFILE, &rl);
+	(void) getrlimit(RLIMIT_NOFILE, &rl);
 	rl.rlim_cur = rl.rlim_max;
-	setrlimit(RLIMIT_NOFILE, &rl);
+	(void) setrlimit(RLIMIT_NOFILE, &rl);
 
 	openlog(progname, LOG_ODELAY, LOG_AUTH);
 
@@ -1377,7 +1655,7 @@
 	if (savedir != NULL)
 		savedir = strdup(savedir);
 
-	while ((c = getopt(argc, argv, "Lvdmf:")) != EOF) {
+	while ((c = getopt(argc, argv, "Lvcdmf:")) != EOF) {
 		switch (c) {
 		case 'L':
 			livedump++;
@@ -1385,6 +1663,9 @@
 		case 'v':
 			verbose++;
 			break;
+		case 'c':
+			cflag++;
+			break;
 		case 'd':
 			disregard_valid_flag++;
 			break;
@@ -1402,13 +1683,16 @@
 
 	interactive = isatty(STDOUT_FILENO);
 
+	if (cflag && livedump)
+		usage();
+
 	if (dumpfile == NULL || livedump)
 		dumpfd = Open("/dev/dump", O_RDONLY, 0444);
 
 	if (dumpfile == NULL) {
 		dumpfile = Zalloc(MAXPATHLEN);
 		if (ioctl(dumpfd, DIOCGETDEV, dumpfile) == -1)
-			logprint(-1, interactive, 1,
+			logprint(SC_SL_NONE | SC_IF_ISATTY | SC_EXIT_ERR,
 			    "no dump device configured");
 	}
 
@@ -1422,7 +1706,8 @@
 		usage();
 
 	if (livedump && ioctl(dumpfd, DIOCDUMP, NULL) == -1)
-		logprint(-1, 1, 1, "dedicated dump device required");
+		logprint(SC_SL_NONE | SC_EXIT_ERR,
+		    "dedicated dump device required");
 
 	(void) close(dumpfd);
 	dumpfd = -1;
@@ -1452,8 +1737,10 @@
 
 		STRLOG_MAKE_MSGID(fmt, msgid);
 
+		/* LINTED: E_SEC_SPRINTF_UNBOUNDED_COPY */
 		(void) sprintf(msg, "%s: [ID %u FACILITY_AND_PRIORITY] ",
 		    progname, msgid);
+		/* LINTED: E_SEC_PRINTF_VAR_FMT */
 		(void) sprintf(msg + strlen(msg), fmt,
 		    dumphdr.dump_panicstring);
 
@@ -1471,15 +1758,54 @@
 		(void) close(logfd);
 	}
 
-	if (chdir(savedir) == -1)
-		logprint(LOG_ERR, 1, 1, "chdir(\"%s\"): %s",
-		    savedir, strerror(errno));
+	if ((dumphdr.dump_flags & DF_COMPLETE) == 0) {
+		logprint(SC_SL_WARN, "incomplete dump on dump device");
+		dump_incomplete = B_TRUE;
+	}
+
+	if (dumphdr.dump_fm_panic)
+		fm_panic = B_TRUE;
+
+	/*
+	 * We have a valid dump on a dump device and know as much about
+	 * it as we're going to at this stage.  Raise an event for
+	 * logging and so that FMA can open a case for this panic.
+	 * Avoid this step for FMA-initiated panics - FMA will replay
+	 * ereports off the dump device independently of savecore and
+	 * will make a diagnosis, so we don't want to open two cases
+	 * for the same event.  Also avoid raising an event for a
+	 * livedump, or when we inflating a compressed dump.
+	 */
+	if (!fm_panic && !livedump && !filemode)
+		raise_event(SC_EVENT_DUMP_PENDING, NULL);
+
+	logprint(SC_SL_WARN, "System dump time: %s",
+	    ctime(&dumphdr.dump_crashtime));
 
-	if ((dumphdr.dump_flags & DF_COMPLETE) == 0)
-		logprint(LOG_WARNING, 1, -1, "incomplete dump on dump device");
+	/*
+	 * Option -c is designed for use from svc-dumpadm where we know
+	 * that dumpadm -n is in effect but run savecore -c just to
+	 * get the above dump_pending_on_device event raised.  If it is run
+	 * interactively then just print further panic details.
+	 */
+	if (cflag) {
+		char *disabled = defread("DUMPADM_ENABLE=no");
+		int lvl = interactive ? SC_SL_WARN : SC_SL_ERR;
+		int ec = fm_panic ? SC_EXIT_FM : SC_EXIT_PEND;
 
-	logprint(LOG_WARNING, 1, -1, "System dump time: %s",
-	    ctime(&dumphdr.dump_crashtime));
+		logprint(lvl | ec,
+		    "Panic crashdump pending on dump device%s "
+		    "run savecore(1M) manually to extract. "
+		    "Image UUID %s%s.",
+		    disabled ? " but dumpadm -n in effect;" : ";",
+		    corehdr.dump_uuid,
+		    fm_panic ?  "(fault-management initiated)" : "");
+		/*NOTREACHED*/
+	}
+
+	if (chdir(savedir) == -1)
+		logprint(SC_SL_ERR | SC_EXIT_ERR, "chdir(\"%s\"): %s",
+		    savedir, strerror(errno));
 
 	check_space(csave);
 
@@ -1495,12 +1821,20 @@
 
 		datahdr.dump_metrics = 0;
 
-		logprint(LOG_ERR, 1, -1,
+		logprint(SC_SL_ERR,
 		    "Saving compressed system crash dump in %s/%s",
 		    savedir, corefile);
 
 		copy_crashfile(corefile);
 
+		/*
+		 * Raise a fault management event that indicates the system
+		 * has panicked. We know a reasonable amount about the
+		 * condition at this time, but the dump is still compressed.
+		 */
+		if (!livedump && !fm_panic)
+			raise_event(SC_EVENT_DUMP_AVAILABLE, NULL);
+
 		if (metrics_size > 0) {
 			int sec = (gethrtime() - startts) / 1000 / 1000 / 1000;
 			FILE *mfile = fopen(METRICSFILE, "a");
@@ -1513,37 +1847,39 @@
 				sec = 1;
 
 			if (mfile == NULL) {
-				logprint(LOG_WARNING, 1, -1,
+				logprint(SC_SL_WARN,
 				    "Can't create %s:\n%s",
 				    METRICSFILE, metrics);
 			} else {
-				fprintf(mfile, "[[[[,,,");
+				(void) fprintf(mfile, "[[[[,,,");
 				for (i = 0; i < argc; i++)
-					fprintf(mfile, "%s ", argv[i]);
-				fprintf(mfile, "\n");
-				fprintf(mfile, ",,,%s %s %s %s %s\n",
+					(void) fprintf(mfile, "%s ", argv[i]);
+				(void) fprintf(mfile, "\n");
+				(void) fprintf(mfile, ",,,%s %s %s %s %s\n",
 				    dumphdr.dump_utsname.sysname,
 				    dumphdr.dump_utsname.nodename,
 				    dumphdr.dump_utsname.release,
 				    dumphdr.dump_utsname.version,
 				    dumphdr.dump_utsname.machine);
-				fprintf(mfile, ",,,%s dump time %s\n",
+				(void) fprintf(mfile, ",,,%s dump time %s\n",
 				    dumphdr.dump_flags & DF_LIVE ? "Live" :
 				    "Crash", ctime(&dumphdr.dump_crashtime));
-				fprintf(mfile, ",,,%s/%s\n", savedir, corefile);
-				fprintf(mfile, "Metrics:\n%s\n", metrics);
-				fprintf(mfile, "Copy pages,%ld\n", dumphdr.
-				    dump_npages);
-				fprintf(mfile, "Copy time,%d\n", sec);
-				fprintf(mfile, "Copy pages/sec,%ld\n",
+				(void) fprintf(mfile, ",,,%s/%s\n", savedir,
+				    corefile);
+				(void) fprintf(mfile, "Metrics:\n%s\n",
+				    metrics);
+				(void) fprintf(mfile, "Copy pages,%ld\n",
+				    dumphdr.  dump_npages);
+				(void) fprintf(mfile, "Copy time,%d\n", sec);
+				(void) fprintf(mfile, "Copy pages/sec,%ld\n",
 				    dumphdr.dump_npages / sec);
-				fprintf(mfile, "]]]]\n");
-				fclose(mfile);
+				(void) fprintf(mfile, "]]]]\n");
+				(void) fclose(mfile);
 			}
 			free(metrics);
 		}
 
-		logprint(LOG_ERR, 1, -1,
+		logprint(SC_SL_ERR,
 		    "Decompress the crash dump with "
 		    "\n'savecore -vf %s/%s'",
 		    savedir, corefile);
@@ -1554,17 +1890,20 @@
 
 		if (interactive && filebounds >= 0 && access(corefile, F_OK)
 		    == 0)
-			logprint(-1, 1, 1,
+			logprint(SC_SL_NONE | SC_EXIT_ERR,
 			    "%s already exists: remove with "
 			    "'rm -f %s/{unix,vmcore}.%ld'",
 			    corefile, savedir, bounds);
 
-		logprint(LOG_ERR, 1, -1,
+		logprint(SC_SL_ERR,
 		    "saving system crash dump in %s/{unix,vmcore}.%ld",
 		    savedir, bounds);
 
 		build_corefile(namelist, corefile);
 
+		if (!livedump && !filemode && !fm_panic)
+			raise_event(SC_EVENT_DUMP_AVAILABLE, NULL);
+
 		if (access(METRICSFILE, F_OK) == 0) {
 			int sec = (gethrtime() - startts) / 1000 / 1000 / 1000;
 			FILE *mfile = fopen(METRICSFILE, "a");
@@ -1572,23 +1911,24 @@
 			if (sec < 1)
 				sec = 1;
 
-			fprintf(mfile, "[[[[,,,");
+			(void) fprintf(mfile, "[[[[,,,");
 			for (i = 0; i < argc; i++)
-				fprintf(mfile, "%s ", argv[i]);
-			fprintf(mfile, "\n");
-			fprintf(mfile, ",,,%s/%s\n", savedir, corefile);
-			fprintf(mfile, ",,,%s %s %s %s %s\n",
+				(void) fprintf(mfile, "%s ", argv[i]);
+			(void) fprintf(mfile, "\n");
+			(void) fprintf(mfile, ",,,%s/%s\n", savedir, corefile);
+			(void) fprintf(mfile, ",,,%s %s %s %s %s\n",
 			    dumphdr.dump_utsname.sysname,
 			    dumphdr.dump_utsname.nodename,
 			    dumphdr.dump_utsname.release,
 			    dumphdr.dump_utsname.version,
 			    dumphdr.dump_utsname.machine);
-			fprintf(mfile, "Uncompress pages,%ld\n", saved);
-			fprintf(mfile, "Uncompress time,%d\n", sec);
-			fprintf(mfile, "Uncompress pages/sec,%ld\n",
-			    saved / sec);
-			fprintf(mfile, "]]]]\n");
-			fclose(mfile);
+			(void) fprintf(mfile, "Uncompress pages,%"PRIu64"\n",
+			    saved);
+			(void) fprintf(mfile, "Uncompress time,%d\n", sec);
+			(void) fprintf(mfile, "Uncompress pages/sec,%"
+			    PRIu64"\n", saved / sec);
+			(void) fprintf(mfile, "]]]]\n");
+			(void) fclose(mfile);
 		}
 	}
 
@@ -1602,7 +1942,7 @@
 	if (verbose) {
 		int sec = (gethrtime() - startts) / 1000 / 1000 / 1000;
 
-		printf("%d:%02d dump %s is done\n",
+		(void) printf("%d:%02d dump %s is done\n",
 		    sec / 60, sec % 60,
 		    csave ? "copy" : "decompress");
 	}
@@ -1612,11 +1952,11 @@
 
 		for (i = 1, nw = 0; i <= BTOP(coreblksize); ++i)
 			nw += hist[i] * i;
-		printf("pages count     %%\n");
+		(void) printf("pages count     %%\n");
 		for (i = 0; i <= BTOP(coreblksize); ++i) {
 			if (hist[i] == 0)
 				continue;
-			printf("%3d   %5u  %6.2f\n",
+			(void) printf("%3d   %5u  %6.2f\n",
 			    i, hist[i], 100.0 * hist[i] * i / nw);
 		}
 	}
--- a/usr/src/cmd/savecore/sparc/Makefile	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/savecore/sparc/Makefile	Fri Jul 30 17:04:17 2010 +1000
@@ -2,9 +2,8 @@
 # CDDL HEADER START
 #
 # The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License").  You may not use this file except in compliance
-# with the License.
+# 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.
@@ -20,12 +19,12 @@
 # CDDL HEADER END
 #
 #
-# Copyright (c) 1998 by Sun Microsystems, Inc.
-# All rights reserved.
-#
-#ident	"%Z%%M%	%I%	%E% SMI"
+# Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
 #
 
 include ../Makefile.com
 
+LDLIBS += -L$(ROOT)/usr/lib/fm -lfmevent -lnvpair
+LDFLAGS += -R/usr/lib/fm
+
 install: all $(ROOTPROG32)
--- a/usr/src/cmd/savecore/sparcv9/Makefile	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/savecore/sparcv9/Makefile	Fri Jul 30 17:04:17 2010 +1000
@@ -2,9 +2,8 @@
 # CDDL HEADER START
 #
 # The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License").  You may not use this file except in compliance
-# with the License.
+# 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.
@@ -20,13 +19,13 @@
 # CDDL HEADER END
 #
 #
-# Copyright (c) 1998 by Sun Microsystems, Inc.
-# All rights reserved.
-#
-#ident	"%Z%%M%	%I%	%E% SMI"
+# Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
 #
 
 include ../Makefile.com
 include ../../Makefile.cmd.64
 
+LDLIBS += -L$(ROOT)/usr/lib/fm/sparcv9 -lfmevent -lnvpair
+LDFLAGS += -R/usr/lib/fm/sparcv9
+
 install: all $(ROOTPROG64)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svc/common/notify_params.c	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,333 @@
+/*
+ * 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 <libintl.h>
+#include <libnvpair.h>
+#include <libscf.h>
+#include <libscf_priv.h>
+#include <libuutil.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "notify_params.h"
+
+static struct events {
+	const char *s;
+	int32_t c;
+} smf_st_events[] = {
+	{ "to-uninitialized", SCF_TRANS(0, SCF_STATE_UNINIT) },
+	{ "from-uninitialized",	SCF_TRANS(SCF_STATE_UNINIT, 0) },
+	{ "uninitialized", SCF_TRANS(SCF_STATE_UNINIT, SCF_STATE_UNINIT) },
+	{ "to-maintenance", SCF_TRANS(0, SCF_STATE_MAINT) },
+	{ "from-maintenance", SCF_TRANS(SCF_STATE_MAINT, 0) },
+	{ "maintenance", SCF_TRANS(SCF_STATE_MAINT, SCF_STATE_MAINT) },
+	{ "to-offline", SCF_TRANS(0, SCF_STATE_OFFLINE) },
+	{ "from-offline", SCF_TRANS(SCF_STATE_OFFLINE, 0) },
+	{ "offline", SCF_TRANS(SCF_STATE_OFFLINE, SCF_STATE_OFFLINE) },
+	{ "to-disabled", SCF_TRANS(0, SCF_STATE_DISABLED) },
+	{ "from-disabled", SCF_TRANS(SCF_STATE_DISABLED, 0) },
+	{ "disabled", SCF_TRANS(SCF_STATE_DISABLED, SCF_STATE_DISABLED) },
+	{ "to-online", SCF_TRANS(0, SCF_STATE_ONLINE) },
+	{ "from-online", SCF_TRANS(SCF_STATE_ONLINE, 0) },
+	{ "online", SCF_TRANS(SCF_STATE_ONLINE, SCF_STATE_ONLINE) },
+	{ "to-degraded", SCF_TRANS(0, SCF_STATE_DEGRADED) },
+	{ "from-degraded", SCF_TRANS(SCF_STATE_DEGRADED, 0) },
+	{ "degraded", SCF_TRANS(SCF_STATE_DEGRADED, SCF_STATE_DEGRADED) },
+	{ "to-all", SCF_TRANS(0, SCF_STATE_ALL) },
+	{ "from-all", SCF_TRANS(SCF_STATE_ALL, 0) },
+	{ "all", SCF_TRANS(SCF_STATE_ALL, SCF_STATE_ALL) },
+	{ NULL, 0 }
+};
+
+static struct fma_tags {
+	const char *t;
+	const char *s;
+} fma_tags[] = {
+	{ "problem-diagnosed", "list.suspect" },
+	{ "problem-updated", "list.updated" },
+	{ "problem-repaired", "list.repaired" },
+	{ "problem-resolved", "list.resolved" },
+	{ NULL, NULL }
+};
+
+static char *fma_classes[] = {
+	"list.",
+	"ireport.",
+	NULL
+};
+
+/*
+ * get_fma_tag()
+ * return a pointer to the fma tag at the passed index. NULL if no entry exist
+ * for index
+ */
+const char *
+get_fma_tag(uint32_t index)
+{
+	if (index > (sizeof (fma_tags) / sizeof (struct fma_tags)))
+		return (NULL);
+
+	return (fma_tags[index].t);
+}
+
+/*
+ * get_fma_class()
+ * return a pointer to the fma class at the passed index. NULL if no entry exist
+ * for index
+ */
+const char *
+get_fma_class(uint32_t index)
+{
+	if (index > (sizeof (fma_tags) / sizeof (struct fma_tags)))
+		return (NULL);
+
+	return (fma_tags[index].s);
+}
+
+/*
+ * is_fma_token()
+ * check if the parameter is an fma token by comparing with the
+ * fma_classes[] and the fma_tags[] arrays.
+ */
+int
+is_fma_token(const char *t)
+{
+	int i;
+
+	for (i = 0; fma_classes[i]; ++i)
+		if (strncmp(t, fma_classes[i], strlen(fma_classes[i])) == 0)
+			return (1);
+
+	for (i = 0; fma_tags[i].t; ++i)
+		if (strcmp(t, fma_tags[i].t) == 0)
+			return (1);
+
+	return (0);
+}
+
+/*
+ * has_fma_tag()
+ * returns 1 if there is an fma tag for the passed class, 0 otherwise
+ */
+int
+has_fma_tag(const char *c)
+{
+	int i;
+
+	for (i = 0; fma_tags[i].s; ++i)
+		if (strcmp(c, fma_tags[i].s) == 0)
+			return (1);
+
+	return (0);
+}
+
+const char *
+de_tag(const char *tag)
+{
+	int i;
+
+	for (i = 0; fma_tags[i].t; ++i)
+		if (strcmp(tag, fma_tags[i].t) == 0)
+			return (fma_tags[i].s);
+
+	return (tag);
+}
+
+const char *
+re_tag(const char *fma_event)
+{
+	int i;
+
+	for (i = 0; fma_tags[i].s; ++i)
+		if (strcmp(fma_event, fma_tags[i].s) == 0)
+			return (fma_tags[i].t);
+
+	return (fma_event);
+}
+
+int32_t
+string_to_tset(const char *str)
+{
+	int i;
+
+	for (i = 0; smf_st_events[i].s != NULL; ++i) {
+		if (strcmp(str, smf_st_events[i].s) == 0)
+			return (smf_st_events[i].c);
+	}
+
+	return (0);
+}
+
+const char *
+tset_to_string(int32_t t)
+{
+	int i;
+
+	for (i = 0; smf_st_events[i].s != NULL; ++i) {
+		if (smf_st_events[i].c == t)
+			return (smf_st_events[i].s);
+	}
+
+	return (NULL);
+}
+
+void
+safe_printf(const char *fmt, ...)
+{
+	va_list va;
+
+	va_start(va, fmt);
+	if (vprintf(fmt, va) < 0)
+		uu_die(gettext("Error writing to stdout"));
+	va_end(va);
+}
+
+static uint32_t
+notify_params_get_version(nvlist_t *nvl)
+{
+	uint32_t v;
+
+	if (nvl == NULL)
+		return (0xFFFFFFFFU);
+
+	if (nvlist_lookup_uint32(nvl, SCF_NOTIFY_NAME_VERSION, &v) != 0)
+		return (0xFFFFFFFFU);
+	else
+		return (v);
+}
+
+static void
+nvpair_print(nvpair_t *p)
+{
+	char **v;
+	uint_t n;
+	int i;
+
+	safe_printf("            %s:", nvpair_name(p));
+	(void) nvpair_value_string_array(p, &v, &n);
+	for (i = 0; i < n; ++i) {
+		safe_printf(" %s", v[i]);
+	}
+	safe_printf("\n");
+}
+
+static void
+params_type_print(nvlist_t *p, const char *name, const char *fmri)
+{
+	nvpair_t *tnvp, *nvp;
+	nvlist_t *nvl;
+	boolean_t *a;
+	uint_t n;
+	int has_output = 0;
+
+	/* for each event e print all notification parameters */
+	for (tnvp = nvlist_next_nvpair(p, NULL); tnvp != NULL;
+	    tnvp = nvlist_next_nvpair(p, tnvp)) {
+		/* We only want the NVLIST memebers */
+		if (nvpair_type(tnvp) != DATA_TYPE_NVLIST)
+			continue;
+
+		if (nvpair_value_nvlist(tnvp, &nvl) != 0)
+			uu_die("nvpair_value_nvlist");
+
+		if (!has_output)
+			if (fmri == NULL)
+				safe_printf(gettext("    Event: %s\n"), name);
+			else
+				safe_printf(gettext(
+				    "    Event: %s (source: %s)\n"),
+				    name, fmri);
+
+		has_output = 1;
+
+		safe_printf(gettext("        Notification Type: %s\n"),
+		    nvpair_name(tnvp));
+
+		if (nvlist_lookup_boolean_array(nvl, PARAM_ACTIVE, &a, &n) != 0)
+			uu_warn(gettext("Missing 'active' property"));
+		else
+			safe_printf(gettext("            Active: %s\n"),
+			    *a ? "true" : "false");
+
+		for (nvp = nvlist_next_nvpair(nvl, NULL); nvp != NULL;
+		    nvp = nvlist_next_nvpair(nvl, nvp)) {
+			if (nvpair_type(nvp) != DATA_TYPE_STRING_ARRAY)
+				continue;
+			nvpair_print(nvp);
+		}
+		safe_printf("\n");
+	}
+}
+
+void
+listnotify_print(nvlist_t *nvl, const char *event)
+{
+	char *fmri;
+	nvlist_t **params;
+	size_t n;
+	int32_t tset;
+	int i;
+
+	/*
+	 * Check the nvl we got is from a version we understand
+	 */
+	if (nvl != NULL && notify_params_get_version(nvl) !=
+	    SCF_NOTIFY_PARAMS_VERSION)
+		uu_die(gettext("libscf(3LIB) mismatch\n"));
+
+	if (nvl != NULL && nvlist_lookup_nvlist_array(nvl, SCF_NOTIFY_PARAMS,
+	    &params, &n) != 0)
+		/* Sanity check. If we get here nvl is bad! */
+		uu_die(gettext("nvlist_lookup_nvlist_array\n"));
+
+	if (event == NULL) {
+		/* this is an SMF state transition nvlist */
+		for (i = 0; i < n; ++i) {
+			nvlist_t *p = *(params + i);
+
+			if (nvlist_lookup_string(p,
+			    SCF_NOTIFY_PARAMS_SOURCE_NAME, &fmri) != 0)
+				fmri = NULL;
+			if (nvlist_lookup_int32(p, SCF_NOTIFY_NAME_TSET,
+			    &tset) != 0)
+				uu_die("nvlist_lookup_int32");
+			params_type_print(p, tset_to_string(tset), fmri);
+		}
+	} else {
+		/* this is FMA event nvlist */
+		if (nvl == NULL) { /* preferences not found */
+			return;
+		}
+		for (i = 0; i < n; ++i) {
+			nvlist_t *p = *(params + i);
+
+			if (nvlist_lookup_string(p,
+			    SCF_NOTIFY_PARAMS_SOURCE_NAME, &fmri) != 0)
+				fmri = NULL;
+			params_type_print(p, event, fmri);
+		}
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svc/common/notify_params.h	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+#ifndef	_CMD_COMMON_NOTIFY_PARAMS_H
+#define	_CMD_COMMON_NOTIFY_PARAMS_H
+
+#include <libnvpair.h>
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+#define	PARAM_ACTIVE	((const char *) "active")
+#define	PARAM_INACTIVE	((const char *) "inactive")
+#define	PARAM_SMTP_TO	((const char *) "to")
+
+const char *get_fma_tag(uint32_t);
+const char *get_fma_class(uint32_t);
+int is_fma_token(const char *);
+int has_fma_tag(const char *);
+const char *de_tag(const char *);
+const char *re_tag(const char *);
+int32_t string_to_tset(const char *);
+const char *tset_to_string(int32_t);
+void listnotify_print(nvlist_t *, const char *);
+void safe_printf(const char *, ...);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _CMD_COMMON_NOTIFY_PARAMS_H */
--- a/usr/src/cmd/svc/dtd/service_bundle.dtd.1	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/svc/dtd/service_bundle.dtd.1	Fri Jul 30 17:04:17 2010 +1000
@@ -939,6 +939,44 @@
 
 <!ATTLIST template>
 
+<!-- Notification Parameters -->
+
+<!ELEMENT paramval EMPTY>
+
+<!ATTLIST paramval
+	name		CDATA #REQUIRED
+	value		CDATA #REQUIRED>
+
+<!ELEMENT parameter
+	( value_node* )>
+
+<!ATTLIST parameter
+	name		CDATA #REQUIRED>
+
+<!ELEMENT event EMPTY>
+
+<!ATTLIST event
+	value		CDATA #REQUIRED>
+
+<!ELEMENT type
+	( ( parameter | paramval )* )>
+
+<!ATTLIST type
+	name		CDATA #REQUIRED
+	active		( true | false ) "true" >
+
+<!--
+  notification parameters
+
+    This element sets the notification parameters for Software Events and
+    Fault Management problem lifecycle events.
+-->
+
+<!ELEMENT notification_parameters
+	( event, type+ )>
+
+<!ATTLIST notification_parameters>
+
 <!-- Services and instances -->
 
 <!--
@@ -991,7 +1029,8 @@
 
 <!ELEMENT instance
 	( restarter?, dependency*, dependent*, method_context?,
-	exec_method*, property_group*, template? ) >
+	exec_method*, notification_parameters*, property_group*,
+	template? ) >
 
 <!ATTLIST instance
 	name		CDATA #REQUIRED
@@ -1021,7 +1060,8 @@
 <!ELEMENT service
 	( create_default_instance?, single_instance?, restarter?,
 	dependency*, dependent*, method_context?, exec_method*,
-	property_group*, instance*, stability?, template? ) >
+	notification_parameters*, property_group*, instance*,
+	stability?, template? ) >
 
 <!ATTLIST service
 	name		CDATA #REQUIRED
--- a/usr/src/cmd/svc/milestone/fs-usr	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/svc/milestone/fs-usr	Fri Jul 30 17:04:17 2010 +1000
@@ -60,6 +60,14 @@
 	fi
 }
 
+#
+# Write a unique id into this kernel image; this will be included
+# in the dump header and panicbuf of any crashdump of this image.
+#
+if [ -x /usr/sbin/dumpadm ]; then
+	/usr/sbin/dumpadm -i
+fi
+
 rootiszfs=0
 # get the fstype of root
 readmnttab / </etc/mnttab
--- a/usr/src/cmd/svc/milestone/global.xml	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/svc/milestone/global.xml	Fri Jul 30 17:04:17 2010 +1000
@@ -1,8 +1,7 @@
 <?xml version="1.0"?>
 <!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
 <!--
- Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- Use is subject to license terms.
+ Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
 
  CDDL HEADER START
 
@@ -758,7 +757,83 @@
 				</description>
 			</prop_pattern>
 		</pg_pattern>
-
+		<pg_pattern type='notify_params' target='all' required='false'>
+			<common_name>
+				<loctext xml:lang='C'>
+FMA and SMF notification parameters
+				</loctext>
+			</common_name>
+			<description>
+				<loctext xml:lang='C'>
+Parameters for notification of FMA events and SMF state transitions.
+				</loctext>
+			</description>
+			<prop_pattern name='smtp,active' type='boolean'
+			    required='false'>
+				<common_name>
+					<loctext xml:lang='C'>
+smtp notification active
+					</loctext>
+				</common_name>
+				<description>
+					<loctext xml:lang='C'>
+Notifcation mechanism smtp active status.
+					</loctext>
+				</description>
+			</prop_pattern>
+			<prop_pattern name='smtp,to' type='astring'
+			    required='false'>
+				<common_name>
+					<loctext xml:lang='C'>
+smtp notification recipient
+					</loctext>
+				</common_name>
+				<description>
+					<loctext xml:lang='C'>
+Recipient for smtp notification mechanism.
+					</loctext>
+				</description>
+			</prop_pattern>
+			<prop_pattern name='smtp,reply-to' type='astring'
+			    required='false'>
+				<common_name>
+					<loctext xml:lang='C'>
+smtp notification reply-to
+					</loctext>
+				</common_name>
+				<description>
+					<loctext xml:lang='C'>
+Header reply-to for smtp notification mechanism.
+					</loctext>
+				</description>
+			</prop_pattern>
+			<prop_pattern name='snmp,active' type='boolean'
+			    required='false'>
+				<common_name>
+					<loctext xml:lang='C'>
+snmp notification active
+					</loctext>
+				</common_name>
+				<description>
+					<loctext xml:lang='C'>
+Notifcation mechanism snmp active status.
+					</loctext>
+				</description>
+			</prop_pattern>
+			<prop_pattern name='syslog,active' type='boolean'
+			    required='false'>
+				<common_name>
+					<loctext xml:lang='C'>
+syslog notification active
+					</loctext>
+				</common_name>
+				<description>
+					<loctext xml:lang='C'>
+Notifcation mechanism syslog active status.
+					</loctext>
+				</description>
+			</prop_pattern>
+		</pg_pattern>
 	</template>
 </service>
 
--- a/usr/src/cmd/svc/milestone/restarter.xml	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/svc/milestone/restarter.xml	Fri Jul 30 17:04:17 2010 +1000
@@ -1,8 +1,7 @@
 <?xml version="1.0"?>
 <!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
 <!--
- Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- Use is subject to license terms.
+ Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
 
  CDDL HEADER START
 
@@ -392,6 +391,15 @@
 				</value>
 				</constraints>
 			</prop_pattern>
+			<prop_pattern name='info_event_all' type='boolean'
+			    required='false'>
+				<description>
+					<loctext xml:lang='C'>
+Override notification parameters and raise Information Events on all state transitions
+					</loctext>
+				</description>
+				<visibility value='hidden' />
+			</prop_pattern>
 		</pg_pattern>
 
 		<pg_pattern name='system' type='framework'
--- a/usr/src/cmd/svc/startd/Makefile	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/svc/startd/Makefile	Fri Jul 30 17:04:17 2010 +1000
@@ -19,8 +19,7 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
+# Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
 #
 
 PROG = svc.startd
@@ -88,7 +87,8 @@
 	-luutil
 
 LDLIBS_i386 +=	-lgrubmgmt
-LDLIBS +=	$(LDLIBS_$(MACH))
+LDLIBS +=	$(LDLIBS_$(MACH)) -L $(ROOT)/usr/lib/fm -lfmevent
+LDFLAGS +=	-R /usr/lib/fm
 
 FILEMODE = 0555
 
@@ -105,7 +105,7 @@
 all: $(PROG)
 
 $(PROG): $(ALLOBJS)
-	$(LINK.c) -o $@ $(ALLOBJS) $(LDLIBS)
+	$(LINK.c) -o $@ $(ALLOBJS) $(LDLIBS) $(LDFLAGS) $(CTFMERGE_HOOK)
 	$(POST_PROCESS)
 
 $(POFILE): $(POFILES)
--- a/usr/src/cmd/svc/startd/graph.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/svc/startd/graph.c	Fri Jul 30 17:04:17 2010 +1000
@@ -103,6 +103,36 @@
  *   subtree (eg. multiple DISABLE events on vertices in the same subtree) then
  *   once the first vertex is disabled (GV_TODISABLE flag is removed), we
  *   continue to propagate the offline event to the vertex's dependencies.
+ *
+ *
+ * SMF state transition notifications
+ *
+ *   When an instance of a service managed by SMF changes state, svc.startd may
+ *   publish a GPEC sysevent. All transitions to or from maintenance, a
+ *   transition cause by a hardware error will generate an event.
+ *   Other transitions will generate an event if there exist notification
+ *   parameter for that transition. Notification parameters are stored in the
+ *   SMF repository for the service/instance they refer to. System-wide
+ *   notification parameters are stored in the global instance.
+ *   svc.startd can be told to send events for all SMF state transitions despite
+ *   of notification parameters by setting options/info_events_all to true in
+ *   restarter:default
+ *
+ *   The set of transitions that generate events is cached in the
+ *   dgraph_vertex_t gv_stn_tset for service/instance and in the global
+ *   stn_global for the system-wide set. They are re-read when instances are
+ *   refreshed.
+ *
+ *   The GPEC events published by svc.startd are consumed by fmd(1M). After
+ *   processing these events, fmd(1M) publishes the processed events to
+ *   notification agents. The notification agents read the notification
+ *   parameters from the SMF repository through libscf(3LIB) interfaces and send
+ *   the notification, or not, based on those parameters.
+ *
+ *   Subscription and publishing to the GPEC channels is done with the
+ *   libfmevent(3LIB) wrappers fmev_[r]publish_*() and
+ *   fmev_shdl_(un)subscribe().
+ *
  */
 
 #include <sys/uadmin.h>
@@ -111,8 +141,10 @@
 #include <assert.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <fm/libfmevent.h>
 #include <libscf.h>
 #include <libscf_priv.h>
+#include <librestart.h>
 #include <libuutil.h>
 #include <locale.h>
 #include <poll.h>
@@ -142,6 +174,28 @@
 #define	VERTEX_REMOVED	0	/* vertex has been freed  */
 #define	VERTEX_INUSE	1	/* vertex is still in use */
 
+#define	IS_ENABLED(v) ((v)->gv_flags & (GV_ENABLED | GV_ENBLD_NOOVR))
+
+/*
+ * stn_global holds the tset for the system wide notification parameters.
+ * It is updated on refresh of svc:/system/svc/global:default
+ *
+ * There are two assumptions that relax the need for a mutex:
+ *     1. 32-bit value assignments are atomic
+ *     2. Its value is consumed only in one point at
+ *     dgraph_state_transition_notify(). There are no test and set races.
+ *
+ *     If either assumption is broken, we'll need a mutex to synchronize
+ *     access to stn_global
+ */
+int32_t stn_global;
+/*
+ * info_events_all holds a flag to override notification parameters and send
+ * Information events for all state transitions.
+ * same about the need of a mutex here.
+ */
+int info_events_all;
+
 /*
  * Services in these states are not considered 'down' by the
  * milestone/shutdown code.
@@ -858,7 +912,8 @@
 		abort();
 	}
 
-	restarter_protocol_send_event(v->gv_name, v->gv_restarter_channel, e);
+	restarter_protocol_send_event(v->gv_name, v->gv_restarter_channel, e,
+	    v->gv_reason);
 }
 
 static void
@@ -3096,6 +3151,7 @@
 	int err;
 	int *path;
 	int deathrow;
+	int32_t tset;
 
 	restarter_fmri[0] = '\0';
 
@@ -3236,7 +3292,8 @@
 
 init_state:
 		switch (err = _restarter_commit_states(h, &idata,
-		    RESTARTER_STATE_UNINIT, RESTARTER_STATE_NONE, NULL)) {
+		    RESTARTER_STATE_UNINIT, RESTARTER_STATE_NONE,
+		    restarter_get_str_short(restarter_str_insert_in_graph))) {
 		case 0:
 			break;
 
@@ -3361,6 +3418,17 @@
 		bad_error("libscf_get_basic_instance_data", err);
 	}
 
+	if ((tset = libscf_get_stn_tset(inst)) == -1) {
+		log_framework(LOG_WARNING,
+		    "Failed to get notification parameters for %s: %s\n",
+		    v->gv_name, scf_strerror(scf_error()));
+		v->gv_stn_tset = 0;
+	} else {
+		v->gv_stn_tset = tset;
+	}
+	if (strcmp(v->gv_name, SCF_INSTANCE_GLOBAL) == 0)
+		stn_global = v->gv_stn_tset;
+
 	if (enabled == -1) {
 		startd_free(restarter_fmri, max_scf_value_size);
 		return (0);
@@ -3382,7 +3450,7 @@
 	if (err != 0) {
 		instance_data_t idata;
 		uint_t count = 0, msecs = ALLOC_DELAY;
-		const char *reason;
+		restarter_str_t reason;
 
 		if (err == ECONNABORTED) {
 			startd_free(restarter_fmri, max_scf_value_size);
@@ -3394,10 +3462,10 @@
 		if (err == EINVAL) {
 			log_framework(LOG_ERR, emsg_invalid_restarter,
 			    v->gv_name, restarter_fmri);
-			reason = "invalid_restarter";
+			reason = restarter_str_invalid_restarter;
 		} else {
 			handle_cycle(v->gv_name, path);
-			reason = "dependency_cycle";
+			reason = restarter_str_dependency_cycle;
 		}
 
 		startd_free(restarter_fmri, max_scf_value_size);
@@ -3417,7 +3485,8 @@
 
 set_maint:
 		switch (err = _restarter_commit_states(h, &idata,
-		    RESTARTER_STATE_MAINT, RESTARTER_STATE_NONE, reason)) {
+		    RESTARTER_STATE_MAINT, RESTARTER_STATE_NONE,
+		    restarter_get_str_short(reason))) {
 		case 0:
 			break;
 
@@ -4246,6 +4315,7 @@
 {
 	int r;
 	int enabled;
+	int32_t tset;
 
 	assert(MUTEX_HELD(&dgraph_lock));
 	assert(v->gv_type == GVT_INST);
@@ -4271,6 +4341,16 @@
 		bad_error("libscf_get_basic_instance_data", r);
 	}
 
+	if ((tset = libscf_get_stn_tset(inst)) == -1) {
+		log_framework(LOG_WARNING,
+		    "Failed to get notification parameters for %s: %s\n",
+		    v->gv_name, scf_strerror(scf_error()));
+		tset = 0;
+	}
+	v->gv_stn_tset = tset;
+	if (strcmp(v->gv_name, SCF_INSTANCE_GLOBAL) == 0)
+		stn_global = tset;
+
 	if (enabled == -1)
 		return (EINVAL);
 
@@ -4573,6 +4653,131 @@
 	graph_walk_dependencies(v, disable_nonsubgraph_leaves, arg);
 }
 
+static int
+stn_restarter_state(restarter_instance_state_t rstate)
+{
+	static const struct statemap {
+		restarter_instance_state_t restarter_state;
+		int scf_state;
+	} map[] = {
+		{ RESTARTER_STATE_UNINIT, SCF_STATE_UNINIT },
+		{ RESTARTER_STATE_MAINT, SCF_STATE_MAINT },
+		{ RESTARTER_STATE_OFFLINE, SCF_STATE_OFFLINE },
+		{ RESTARTER_STATE_DISABLED, SCF_STATE_DISABLED },
+		{ RESTARTER_STATE_ONLINE, SCF_STATE_ONLINE },
+		{ RESTARTER_STATE_DEGRADED, SCF_STATE_DEGRADED }
+	};
+
+	int i;
+
+	for (i = 0; i < sizeof (map) / sizeof (map[0]); i++) {
+		if (rstate == map[i].restarter_state)
+			return (map[i].scf_state);
+	}
+
+	return (-1);
+}
+
+/*
+ * State transition counters
+ * Not incremented atomically - indicative only
+ */
+static uint64_t stev_ct_maint;
+static uint64_t stev_ct_hwerr;
+static uint64_t stev_ct_service;
+static uint64_t stev_ct_global;
+static uint64_t stev_ct_noprefs;
+static uint64_t stev_ct_from_uninit;
+static uint64_t stev_ct_bad_state;
+static uint64_t stev_ct_ovr_prefs;
+
+static void
+dgraph_state_transition_notify(graph_vertex_t *v,
+    restarter_instance_state_t old_state, restarter_str_t reason)
+{
+	restarter_instance_state_t new_state = v->gv_state;
+	int stn_transition, maint;
+	int from, to;
+	nvlist_t *attr;
+	fmev_pri_t pri = FMEV_LOPRI;
+	int raise = 0;
+
+	if ((from = stn_restarter_state(old_state)) == -1 ||
+	    (to = stn_restarter_state(new_state)) == -1) {
+		stev_ct_bad_state++;
+		return;
+	}
+
+	stn_transition = from << 16 | to;
+
+	maint = (to == SCF_STATE_MAINT || from == SCF_STATE_MAINT);
+
+	if (maint) {
+		/*
+		 * All transitions to/from maintenance state must raise
+		 * an event.
+		 */
+		raise++;
+		pri = FMEV_HIPRI;
+		stev_ct_maint++;
+	} else if (reason == restarter_str_ct_ev_hwerr) {
+		/*
+		 * All transitions caused by hardware fault must raise
+		 * an event
+		 */
+		raise++;
+		pri = FMEV_HIPRI;
+		stev_ct_hwerr++;
+	} else if (stn_transition & v->gv_stn_tset) {
+		/*
+		 * Specifically enabled event.
+		 */
+		raise++;
+		stev_ct_service++;
+	} else if (from == SCF_STATE_UNINIT) {
+		/*
+		 * Only raise these if specifically selected above.
+		 */
+		stev_ct_from_uninit++;
+	} else if (stn_transition & stn_global &&
+	    (IS_ENABLED(v) == 1 || to == SCF_STATE_DISABLED)) {
+		raise++;
+		stev_ct_global++;
+	} else {
+		stev_ct_noprefs++;
+	}
+
+	if (info_events_all) {
+		stev_ct_ovr_prefs++;
+		raise++;
+	}
+	if (!raise)
+		return;
+
+	if (nvlist_alloc(&attr, NV_UNIQUE_NAME, 0) != 0 ||
+	    nvlist_add_string(attr, "fmri", v->gv_name) != 0 ||
+	    nvlist_add_uint32(attr, "reason-version",
+	    restarter_str_version()) || nvlist_add_string(attr, "reason-short",
+	    restarter_get_str_short(reason)) != 0 ||
+	    nvlist_add_string(attr, "reason-long",
+	    restarter_get_str_long(reason)) != 0 ||
+	    nvlist_add_int32(attr, "transition", stn_transition) != 0) {
+		log_framework(LOG_WARNING,
+		    "FMEV: %s could not create nvlist for transition "
+		    "event: %s\n", v->gv_name, strerror(errno));
+		nvlist_free(attr);
+		return;
+	}
+
+	if (fmev_rspublish_nvl(FMEV_RULESET_SMF, "state-transition",
+	    instance_state_str[new_state], pri, attr) != FMEV_SUCCESS) {
+		log_framework(LOG_DEBUG,
+		    "FMEV: %s failed to publish transition event: %s\n",
+		    v->gv_name, fmev_strerror(fmev_errno));
+		nvlist_free(attr);
+	}
+}
+
 /*
  * Find the vertex for inst_name.  If it doesn't exist, return ENOENT.
  * Otherwise set its state to state.  If the instance has entered a state
@@ -4587,11 +4792,13 @@
  */
 static int
 dgraph_set_instance_state(scf_handle_t *h, const char *inst_name,
-    restarter_instance_state_t state, restarter_error_t serr)
+    protocol_states_t *states)
 {
 	graph_vertex_t *v;
 	int err = 0;
 	restarter_instance_state_t old_state;
+	restarter_instance_state_t state = states->ps_state;
+	restarter_error_t serr = states->ps_err;
 
 	MUTEX_LOCK(&dgraph_lock);
 
@@ -4623,7 +4830,11 @@
 	old_state = v->gv_state;
 	v->gv_state = state;
 
+	v->gv_reason = states->ps_reason;
 	err = gt_transition(h, v, serr, old_state);
+	if (err == 0 && v->gv_state != old_state) {
+		dgraph_state_transition_notify(v, old_state, states->ps_reason);
+	}
 
 	MUTEX_UNLOCK(&dgraph_lock);
 	return (err);
@@ -5559,8 +5770,7 @@
 	case GRAPH_UPDATE_STATE_CHANGE: {
 		protocol_states_t *states = e->gpe_data;
 
-		switch (r = dgraph_set_instance_state(h, e->gpe_inst,
-		    states->ps_state, states->ps_err)) {
+		switch (r = dgraph_set_instance_state(h, e->gpe_inst, states)) {
 		case 0:
 		case ENOENT:
 			break;
@@ -6380,6 +6590,12 @@
 		return (0);
 	}
 
+	/*
+	 * update the information events flag
+	 */
+	if (strcmp(pg_name, SCF_PG_OPTIONS) == 0)
+		info_events_all = libscf_get_info_events_all(pg);
+
 	prop = safe_scf_property_create(h);
 	val = safe_scf_value_create(h);
 
--- a/usr/src/cmd/svc/startd/libscf.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/svc/startd/libscf.c	Fri Jul 30 17:04:17 2010 +1000
@@ -20,8 +20,7 @@
  */
 
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 
@@ -39,6 +38,8 @@
 
 #define	SMF_SNAPSHOT_RUNNING	"running"
 
+#define	INFO_EVENTS_ALL "info_events_all"
+
 char *
 inst_fmri_to_svc_fmri(const char *fmri)
 {
@@ -417,6 +418,90 @@
 	return (r);
 }
 
+/*
+ * libscf_get_stn_tset
+ */
+int32_t
+libscf_get_stn_tset(scf_instance_t *inst)
+{
+	scf_handle_t		*h = scf_instance_handle(inst);
+	scf_propertygroup_t	*pg = scf_pg_create(h);
+	char			*pgname = NULL;
+	int32_t			t, f, tset;
+
+	assert(inst != NULL);
+
+	pgname =  startd_alloc(max_scf_fmri_size);
+	if (h == NULL || pg == NULL) {
+		tset = -1;
+		goto cleanup;
+	}
+
+	for (tset = 0, t = 1; t < SCF_STATE_ALL; t <<= 1) {
+		f = t << 16;
+
+		(void) strcpy(pgname, SCF_STN_PREFIX_TO);
+		(void) strlcat(pgname, smf_state_to_string(t),
+		    max_scf_fmri_size);
+
+		if (scf_instance_get_pg_composed(inst, NULL, pgname, pg) ==
+		    SCF_SUCCESS) {
+			tset |= t;
+		} else if (scf_error() != SCF_ERROR_NOT_FOUND && scf_error() !=
+		    SCF_ERROR_DELETED) {
+			tset = -1;
+			goto cleanup;
+		}
+
+		(void) strcpy(pgname, SCF_STN_PREFIX_FROM);
+		(void) strlcat(pgname, smf_state_to_string(t),
+		    max_scf_fmri_size);
+
+		if (scf_instance_get_pg_composed(inst, NULL, pgname, pg) ==
+		    SCF_SUCCESS) {
+			tset |= f;
+		} else if (scf_error() != SCF_ERROR_NOT_FOUND && scf_error() !=
+		    SCF_ERROR_DELETED) {
+			tset = -1;
+			goto cleanup;
+		}
+	}
+
+cleanup:
+	scf_pg_destroy(pg);
+	startd_free(pgname, max_scf_fmri_size);
+
+	return (tset);
+}
+
+static int32_t
+libscf_get_global_stn_tset(scf_handle_t *h)
+{
+	scf_instance_t	*inst = scf_instance_create(h);
+	int32_t		tset = -1;
+
+	if (inst == NULL) {
+		goto cleanup;
+	}
+
+	if (scf_handle_decode_fmri(h, SCF_INSTANCE_GLOBAL, NULL, NULL, inst,
+	    NULL, NULL, SCF_DECODE_FMRI_REQUIRE_INSTANCE) != 0) {
+		goto cleanup;
+	}
+
+	tset = libscf_get_stn_tset(inst);
+
+cleanup:
+	scf_instance_destroy(inst);
+
+	if (tset == -1)
+		log_framework(LOG_WARNING,
+		    "Failed to get system wide notification parameters: %s\n",
+		    scf_strerror(scf_error()));
+
+	return (tset);
+}
+
 static int
 libscf_read_state(const scf_propertygroup_t *pg, const char *prop_name,
     restarter_instance_state_t *state)
@@ -724,6 +809,26 @@
 }
 
 /*
+ * get info event property from restarter:default
+ */
+int
+libscf_get_info_events_all(scf_propertygroup_t *pg)
+{
+	uint8_t	v;
+	int r = 0;
+
+	if (get_boolean(pg, INFO_EVENTS_ALL, &v) == 0) {
+		r = v;
+	} else if (scf_error() != SCF_ERROR_NOT_FOUND) {
+		uu_warn("Failed get_boolean %s/%s: %s\n",
+		    SCF_PG_OPTIONS, INFO_EVENTS_ALL,
+		    scf_strerror(scf_error()));
+	}
+
+	return (r);
+}
+
+/*
  * int get_count()
  *   Fetches the value of a count property of the given property group.
  *   Returns
@@ -1126,7 +1231,6 @@
 	return (0);
 }
 
-
 /*
  * Sets pg to the name property group of s_inst.  If it doesn't exist, it is
  * added.
@@ -2677,6 +2781,7 @@
 	return (ret);
 }
 
+extern int32_t stn_global;
 /*
  * Call dgraph_add_instance() for each instance in the repository.
  */
@@ -2688,7 +2793,6 @@
 	scf_instance_t *inst;
 	scf_iter_t *svc_iter;
 	scf_iter_t *inst_iter;
-	int ret;
 
 	scope = safe_scf_scope_create(h);
 	svc = safe_scf_service_create(h);
@@ -2698,9 +2802,12 @@
 
 	deathrow_init();
 
-	if ((ret = scf_handle_get_local_scope(h, scope)) !=
+	stn_global = libscf_get_global_stn_tset(h);
+
+	if (scf_handle_get_local_scope(h, scope) !=
 	    SCF_SUCCESS)
-		uu_die("retrieving local scope failed: %d\n", ret);
+		uu_die("retrieving local scope failed: %s\n",
+		    scf_strerror(scf_error()));
 
 	if (scf_iter_scope_services(svc_iter, scope) == -1)
 		uu_die("walking local scope's services failed\n");
@@ -3602,8 +3709,9 @@
 	idata.i_state = RESTARTER_STATE_NONE;
 	idata.i_next_state = RESTARTER_STATE_NONE;
 set_state:
-	switch (r = _restarter_commit_states(h, &idata, RESTARTER_STATE_ONLINE,
-	    RESTARTER_STATE_NONE, NULL)) {
+	switch (r = _restarter_commit_states(h, &idata,
+	    RESTARTER_STATE_ONLINE, RESTARTER_STATE_NONE,
+	    restarter_get_str_short(restarter_str_insert_in_graph))) {
 	case 0:
 		break;
 
--- a/usr/src/cmd/svc/startd/method.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/svc/startd/method.c	Fri Jul 30 17:04:17 2010 +1000
@@ -20,8 +20,7 @@
  */
 
 /*
- * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 /*
@@ -1025,7 +1024,7 @@
 	scf_instance_t	*s_inst = NULL;
 	int r, exit_code;
 	boolean_t retryable;
-	const char *aux;
+	restarter_str_t reason;
 
 	assert(0 <= info->sf_method_type && info->sf_method_type <= 2);
 
@@ -1063,7 +1062,7 @@
 		 */
 		(void) restarter_instance_update_states(local_handle, inst,
 		    inst->ri_i.i_state, RESTARTER_STATE_NONE, RERR_NONE,
-		    NULL);
+		    restarter_str_none);
 		goto out;
 
 	case EINVAL:
@@ -1105,7 +1104,7 @@
 		 */
 		(void) restarter_instance_update_states(local_handle, inst,
 		    inst->ri_i.i_next_state, RESTARTER_STATE_NONE,
-		    info->sf_event_type, NULL);
+		    info->sf_event_type, info->sf_reason);
 
 		(void) update_fault_count(inst, FAULT_COUNT_RESET);
 
@@ -1145,16 +1144,17 @@
 	else
 		log_transition(inst, START_FAILED_OTHER);
 
-	if (r == ELOOP)
-		aux = "restarting_too_quickly";
-	else if (retryable)
-		aux = "fault_threshold_reached";
-	else
-		aux = "method_failed";
+	if (r == ELOOP) {
+		reason = restarter_str_restarting_too_quickly;
+	} else if (retryable) {
+		reason = restarter_str_fault_threshold_reached;
+	} else {
+		reason = restarter_str_method_failed;
+	}
 
 	(void) restarter_instance_update_states(local_handle, inst,
 	    RESTARTER_STATE_MAINT, RESTARTER_STATE_NONE, RERR_FAULT,
-	    (char *)aux);
+	    reason);
 
 	if (!method_is_transient(inst, info->sf_method_type) &&
 	    inst->ri_i.i_primary_ctid != 0)
--- a/usr/src/cmd/svc/startd/protocol.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/svc/startd/protocol.c	Fri Jul 30 17:04:17 2010 +1000
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 /*
@@ -215,7 +214,8 @@
  *   Enqueue a restarter event.
  */
 static void
-restarter_event_enqueue(const char *inst, restarter_event_type_t event)
+restarter_event_enqueue(const char *inst, restarter_event_type_t event,
+    int32_t reason)
 {
 	restarter_protocol_event_t *e;
 	int r;
@@ -226,6 +226,7 @@
 	e->rpe_inst = startd_alloc(strlen(inst) + 1);
 	(void) strlcpy(e->rpe_inst, inst, strlen(inst)+1);
 	e->rpe_type = event;
+	e->rpe_reason = reason;
 
 	MUTEX_LOCK(&restarter_queue->rpeq_lock);
 	uu_list_node_init(e, &e->rpe_link, restarter_protocol_event_queue_pool);
@@ -277,6 +278,7 @@
 {
 	char *fmri = (char *)cookie;
 	char *instance_name;
+	int32_t reason;
 	nvlist_t *attr_list = NULL;
 	int state, next_state;
 	char str_state[MAX_SCF_STATE_STRING_SZ];
@@ -298,13 +300,16 @@
 	    &next_state) != 0) ||
 	    (nvlist_lookup_int32(attr_list, RESTARTER_NAME_ERROR, &err) != 0) ||
 	    (nvlist_lookup_string(attr_list, RESTARTER_NAME_INSTANCE,
-	    &instance_name) != 0))
+	    &instance_name) != 0) ||
+	    (nvlist_lookup_int32(attr_list, RESTARTER_NAME_REASON, &reason) !=
+	    0))
 		uu_die("%s: can't decode nvlist\n", fmri);
 
 	states = startd_alloc(sizeof (protocol_states_t));
 	states->ps_state = state;
 	states->ps_state_next = next_state;
 	states->ps_err = err;
+	states->ps_reason = reason;
 
 	graph_protocol_send_event(instance_name, GRAPH_UPDATE_STATE_CHANGE,
 	    states);
@@ -415,7 +420,7 @@
 
 void
 restarter_protocol_send_event(const char *inst, evchan_t *chan,
-    restarter_event_type_t event)
+    restarter_event_type_t event, int32_t reason)
 {
 	nvlist_t *attr;
 	int ret;
@@ -425,7 +430,7 @@
 	 * queue the event locally.
 	 */
 	if (chan == NULL) {
-		restarter_event_enqueue(inst, event);
+		restarter_event_enqueue(inst, event, reason);
 		MUTEX_LOCK(&ru->restarter_update_lock);
 		ru->restarter_update_wakeup = 1;
 		(void) pthread_cond_broadcast(&ru->restarter_update_cv);
@@ -440,7 +445,9 @@
 	    event_names[event], chan, inst);
 	if (nvlist_alloc(&attr, NV_UNIQUE_NAME, 0) != 0 ||
 	    nvlist_add_uint32(attr, RESTARTER_NAME_TYPE, event) != 0 ||
-	    nvlist_add_string(attr, RESTARTER_NAME_INSTANCE, (char *)inst) != 0)
+	    nvlist_add_string(attr, RESTARTER_NAME_INSTANCE, (char *)inst) !=
+	    0 || nvlist_add_uint32(attr, RESTARTER_NAME_REASON,
+	    reason) != 0)
 		uu_die("Allocation failure\n");
 
 	if ((ret = restarter_event_publish_retry(chan, "protocol", "restarter",
--- a/usr/src/cmd/svc/startd/protocol.h	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/svc/startd/protocol.h	Fri Jul 30 17:04:17 2010 +1000
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * 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.
@@ -20,15 +19,12 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #ifndef	_PROTOCOL_H
 #define	_PROTOCOL_H
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <startd.h>
 
 #ifdef	__cplusplus
@@ -45,6 +41,7 @@
 	restarter_instance_state_t	ps_state;
 	restarter_instance_state_t	ps_state_next;
 	restarter_error_t		ps_err;
+	restarter_str_t			ps_reason;
 } protocol_states_t;
 
 
@@ -71,6 +68,7 @@
 typedef struct restarter_protocol_event {
 	char			*rpe_inst;
 	restarter_event_type_t	rpe_type;
+	int32_t			rpe_reason;
 
 	uu_list_node_t		rpe_link;
 } restarter_protocol_event_t;
@@ -94,7 +92,7 @@
 void restarter_protocol_init();
 evchan_t *restarter_protocol_init_delegate(char *);
 void restarter_protocol_send_event(const char *, evchan_t *,
-    restarter_event_type_t);
+    restarter_event_type_t, int32_t);
 restarter_protocol_event_t *restarter_event_dequeue();
 void restarter_event_requeue(restarter_protocol_event_t *);
 void restarter_event_release(restarter_protocol_event_t *);
--- a/usr/src/cmd/svc/startd/restarter.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/svc/startd/restarter.c	Fri Jul 30 17:04:17 2010 +1000
@@ -324,6 +324,7 @@
 	restarter_instance_state_t state, next_state;
 	protocol_states_t *ps;
 	pid_t start_pid;
+	restarter_str_t reason = restarter_str_insert_in_graph;
 
 	MUTEX_LOCK(&instance_list.ril_lock);
 
@@ -475,6 +476,14 @@
 				do_commit_states = B_TRUE;
 			} else {
 				/*
+				 * The reason for transition will depend on
+				 * state.
+				 */
+				if (st->st_initial == 0)
+					reason = restarter_str_startd_restart;
+				else if (state == RESTARTER_STATE_MAINT)
+					reason = restarter_str_bad_repo_state;
+				/*
 				 * Inform the restarter of our state without
 				 * changing the STIME in the repository.
 				 */
@@ -482,6 +491,7 @@
 				inst->ri_i.i_state = ps->ps_state = state;
 				inst->ri_i.i_next_state = ps->ps_state_next =
 				    next_state;
+				ps->ps_reason = reason;
 
 				graph_protocol_send_event(inst->ri_i.i_fmri,
 				    GRAPH_UPDATE_STATE_CHANGE, ps);
@@ -635,7 +645,7 @@
 
 	if (do_commit_states)
 		(void) restarter_instance_update_states(h, inst, state,
-		    next_state, RERR_NONE, NULL);
+		    next_state, RERR_NONE, reason);
 
 	log_framework(LOG_DEBUG, "%s is a %s-style service\n", name,
 	    service_style(inst->ri_flags));
@@ -803,7 +813,8 @@
 int
 restarter_instance_update_states(scf_handle_t *h, restarter_inst_t *ri,
     restarter_instance_state_t new_state,
-    restarter_instance_state_t new_state_next, restarter_error_t err, char *aux)
+    restarter_instance_state_t new_state_next, restarter_error_t err,
+    restarter_str_t reason)
 {
 	protocol_states_t *states;
 	int e;
@@ -818,7 +829,7 @@
 
 retry:
 	e = _restarter_commit_states(h, &ri->ri_i, new_state, new_state_next,
-	    aux);
+	    restarter_get_str_short(reason));
 	switch (e) {
 	case 0:
 		break;
@@ -861,6 +872,7 @@
 	states->ps_state = new_state;
 	states->ps_state_next = new_state_next;
 	states->ps_err = err;
+	states->ps_reason = reason;
 	graph_protocol_send_event(ri->ri_i.i_fmri, GRAPH_UPDATE_STATE_CHANGE,
 	    (void *)states);
 
@@ -999,6 +1011,7 @@
 	const char *cp;
 	int err;
 	restarter_error_t re;
+	restarter_str_t	reason;
 
 	assert(MUTEX_HELD(&inst->ri_lock));
 	assert(inst->ri_method_thread == 0);
@@ -1006,30 +1019,37 @@
 	switch (cause) {
 	case RSTOP_EXIT:
 		re = RERR_RESTART;
+		reason = restarter_str_ct_ev_exit;
 		cp = "all processes in service exited";
 		break;
 	case RSTOP_CORE:
 		re = RERR_FAULT;
+		reason = restarter_str_ct_ev_core;
 		cp = "process dumped core";
 		break;
 	case RSTOP_SIGNAL:
 		re = RERR_FAULT;
+		reason = restarter_str_ct_ev_signal;
 		cp = "process received fatal signal from outside the service";
 		break;
 	case RSTOP_HWERR:
 		re = RERR_FAULT;
+		reason = restarter_str_ct_ev_hwerr;
 		cp = "process killed due to uncorrectable hardware error";
 		break;
 	case RSTOP_DEPENDENCY:
 		re = RERR_RESTART;
+		reason = restarter_str_dependency_activity;
 		cp = "dependency activity requires stop";
 		break;
 	case RSTOP_DISABLE:
 		re = RERR_RESTART;
+		reason = restarter_str_disable_request;
 		cp = "service disabled";
 		break;
 	case RSTOP_RESTART:
 		re = RERR_RESTART;
+		reason = restarter_str_restart_request;
 		cp = "service restarting";
 		break;
 	default:
@@ -1076,7 +1096,7 @@
 		 */
 		switch (err = restarter_instance_update_states(local_handle,
 		    inst, inst->ri_i.i_state, RESTARTER_STATE_OFFLINE, re,
-		    NULL)) {
+		    reason)) {
 		case 0:
 		case ECONNRESET:
 			break;
@@ -1102,7 +1122,7 @@
 
 		switch (err = restarter_instance_update_states(local_handle,
 		    inst, inst->ri_i.i_next_state, RESTARTER_STATE_NONE, re,
-		    NULL)) {
+		    reason)) {
 		case 0:
 		case ECONNRESET:
 			break;
@@ -1125,7 +1145,7 @@
 
 	switch (err = restarter_instance_update_states(local_handle, inst,
 	    inst->ri_i.i_state, inst->ri_i.i_enabled ? RESTARTER_STATE_OFFLINE :
-	    RESTARTER_STATE_DISABLED, RERR_NONE, NULL)) {
+	    RESTARTER_STATE_DISABLED, RERR_NONE, reason)) {
 	case 0:
 	case ECONNRESET:
 		break;
@@ -1139,6 +1159,7 @@
 	info->sf_id = inst->ri_id;
 	info->sf_method_type = METHOD_STOP;
 	info->sf_event_type = re;
+	info->sf_reason = reason;
 	inst->ri_method_thread = startd_thread_create(method_thread, info);
 
 	return (0);
@@ -1177,6 +1198,7 @@
 	int r;
 	uint_t tries = 0, msecs = ALLOC_DELAY;
 	const char *cp;
+	restarter_str_t	reason;
 
 	assert(MUTEX_HELD(&rip->ri_lock));
 
@@ -1190,9 +1212,11 @@
 	switch (cause) {
 	case RUNMAINT_CLEAR:
 		cp = "clear requested";
+		reason = restarter_str_clear_request;
 		break;
 	case RUNMAINT_DISABLE:
 		cp = "disable requested";
+		reason = restarter_str_disable_request;
 		break;
 	default:
 #ifndef NDEBUG
@@ -1208,7 +1232,7 @@
 	    "%s.\n", rip->ri_i.i_fmri, cp);
 
 	(void) restarter_instance_update_states(h, rip, RESTARTER_STATE_UNINIT,
-	    RESTARTER_STATE_NONE, RERR_RESTART, "none");
+	    RESTARTER_STATE_NONE, RERR_RESTART, reason);
 
 	/*
 	 * If we did ADMIN_MAINT_ON_IMMEDIATE, then there might still be
@@ -1294,9 +1318,12 @@
  *     ECONNRESET - h was rebound
  */
 static int
-enable_inst(scf_handle_t *h, restarter_inst_t *inst, restarter_event_type_t e)
+enable_inst(scf_handle_t *h, restarter_inst_t *inst,
+    restarter_instance_qentry_t *riq)
 {
 	restarter_instance_state_t state;
+	restarter_event_type_t e = riq->riq_type;
+	restarter_str_t reason = restarter_str_per_configuration;
 	int r;
 
 	assert(MUTEX_HELD(&inst->ri_lock));
@@ -1320,9 +1347,19 @@
 			log_instance(inst, B_FALSE, "Enabled.");
 			log_framework(LOG_DEBUG, "%s: Instance enabled.\n",
 			    inst->ri_i.i_fmri);
+
+			/*
+			 * If we are coming from DISABLED, it was obviously an
+			 * enable request. If we are coming from UNINIT, it may
+			 * have been a sevice in MAINT that was cleared.
+			 */
+			if (riq->riq_reason == restarter_str_clear_request)
+				reason = restarter_str_clear_request;
+			else if (state == RESTARTER_STATE_DISABLED)
+				reason = restarter_str_enable_request;
 			(void) restarter_instance_update_states(h, inst,
 			    RESTARTER_STATE_OFFLINE, RESTARTER_STATE_NONE,
-			    RERR_NONE, NULL);
+			    RERR_NONE, reason);
 		} else {
 			log_framework(LOG_DEBUG, "Restarter: "
 			    "Not changing state of %s for enable command.\n",
@@ -1352,9 +1389,19 @@
 			log_instance(inst, B_FALSE, "Disabled.");
 			log_framework(LOG_DEBUG, "%s: Instance disabled.\n",
 			    inst->ri_i.i_fmri);
+
+			/*
+			 * If we are coming from OFFLINE, it was obviously a
+			 * disable request. But if we are coming from
+			 * UNINIT, it may have been a disable request for a
+			 * service in MAINT.
+			 */
+			if (riq->riq_reason == restarter_str_disable_request ||
+			    state == RESTARTER_STATE_OFFLINE)
+				reason = restarter_str_disable_request;
 			(void) restarter_instance_update_states(h, inst,
 			    RESTARTER_STATE_DISABLED, RESTARTER_STATE_NONE,
-			    RERR_RESTART, NULL);
+			    RERR_RESTART, reason);
 			return (0);
 
 		case RESTARTER_STATE_DISABLED:
@@ -1386,9 +1433,11 @@
 }
 
 static void
-start_instance(scf_handle_t *local_handle, restarter_inst_t *inst)
+start_instance(scf_handle_t *local_handle, restarter_inst_t *inst,
+    int32_t reason)
 {
 	fork_info_t *info;
+	restarter_str_t	new_reason;
 
 	assert(MUTEX_HELD(&inst->ri_lock));
 	assert(instance_in_transition(inst) == 0);
@@ -1397,6 +1446,18 @@
 	log_framework(LOG_DEBUG, "%s: trying to start instance\n",
 	    inst->ri_i.i_fmri);
 
+	/*
+	 * We want to keep the original reason for restarts and clear actions
+	 */
+	switch (reason) {
+	case restarter_str_restart_request:
+	case restarter_str_clear_request:
+		new_reason = reason;
+		break;
+	default:
+		new_reason = restarter_str_dependencies_satisfied;
+	}
+
 	/* Services in the disabled and maintenance state are ignored */
 	if (inst->ri_i.i_state == RESTARTER_STATE_MAINT ||
 	    inst->ri_i.i_state == RESTARTER_STATE_DISABLED ||
@@ -1418,13 +1479,14 @@
 	log_framework(LOG_DEBUG, "%s: starting instance.\n", inst->ri_i.i_fmri);
 
 	(void) restarter_instance_update_states(local_handle, inst,
-	    inst->ri_i.i_state, RESTARTER_STATE_ONLINE, RERR_NONE, "none");
+	    inst->ri_i.i_state, RESTARTER_STATE_ONLINE, RERR_NONE, new_reason);
 
 	info = startd_zalloc(sizeof (fork_info_t));
 
 	info->sf_id = inst->ri_id;
 	info->sf_method_type = METHOD_START;
 	info->sf_event_type = RERR_NONE;
+	info->sf_reason = new_reason;
 	inst->ri_method_thread = startd_thread_create(method_thread, info);
 }
 
@@ -1445,18 +1507,19 @@
 
 static void
 maintain_instance(scf_handle_t *h, restarter_inst_t *rip, int immediate,
-    const char *aux)
+    restarter_str_t reason)
 {
 	fork_info_t *info;
 	scf_instance_t *scf_inst = NULL;
 
 	assert(MUTEX_HELD(&rip->ri_lock));
-	assert(aux != NULL);
+	assert(reason != restarter_str_none);
 	assert(rip->ri_method_thread == 0);
 
-	log_instance(rip, B_TRUE, "Stopping for maintenance due to %s.", aux);
+	log_instance(rip, B_TRUE, "Stopping for maintenance due to %s.",
+	    restarter_get_str_short(reason));
 	log_framework(LOG_DEBUG, "%s: stopping for maintenance due to %s.\n",
-	    rip->ri_i.i_fmri, aux);
+	    rip->ri_i.i_fmri, restarter_get_str_short(reason));
 
 	/* Services in the maintenance state are ignored */
 	if (rip->ri_i.i_state == RESTARTER_STATE_MAINT) {
@@ -1467,12 +1530,12 @@
 	}
 
 	/*
-	 * If aux state is "service_request" and
+	 * If reason state is restarter_str_service_request and
 	 * restarter_actions/auxiliary_fmri property is set with a valid fmri,
 	 * copy the fmri to restarter/auxiliary_fmri so svcs -x can use.
 	 */
-	if (strcmp(aux, "service_request") == 0 && libscf_fmri_get_instance(h,
-	    rip->ri_i.i_fmri, &scf_inst) == 0) {
+	if (reason == restarter_str_service_request &&
+	    libscf_fmri_get_instance(h, rip->ri_i.i_fmri, &scf_inst) == 0) {
 		if (restarter_inst_validate_ractions_aux_fmri(scf_inst) == 0) {
 			if (restarter_inst_set_aux_fmri(scf_inst))
 				log_framework(LOG_DEBUG, "%s: "
@@ -1504,12 +1567,12 @@
 
 		(void) restarter_instance_update_states(h, rip,
 		    RESTARTER_STATE_MAINT, RESTARTER_STATE_NONE, RERR_RESTART,
-		    (char *)aux);
+		    reason);
 		return;
 	}
 
 	(void) restarter_instance_update_states(h, rip, rip->ri_i.i_state,
-	    RESTARTER_STATE_MAINT, RERR_NONE, (char *)aux);
+	    RESTARTER_STATE_MAINT, RERR_NONE, reason);
 
 	log_transition(rip, MAINT_REQUESTED);
 
@@ -1517,6 +1580,7 @@
 	info->sf_id = rip->ri_id;
 	info->sf_method_type = METHOD_STOP;
 	info->sf_event_type = RERR_RESTART;
+	info->sf_reason = reason;
 	rip->ri_method_thread = startd_thread_create(method_thread, info);
 }
 
@@ -1582,12 +1646,14 @@
 	if (instance_started(rip)) {
 		/* Refresh does not change the state. */
 		(void) restarter_instance_update_states(h, rip,
-		    rip->ri_i.i_state, rip->ri_i.i_state, RERR_NONE, NULL);
+		    rip->ri_i.i_state, rip->ri_i.i_state, RERR_NONE,
+		    restarter_str_refresh);
 
 		info = startd_zalloc(sizeof (*info));
 		info->sf_id = rip->ri_id;
 		info->sf_method_type = METHOD_REFRESH;
 		info->sf_event_type = RERR_REFRESH;
+		info->sf_reason = NULL;
 
 		assert(rip->ri_method_thread == 0);
 		rip->ri_method_thread =
@@ -1655,11 +1721,11 @@
 		switch (event->riq_type) {
 		case RESTARTER_EVENT_TYPE_ENABLE:
 		case RESTARTER_EVENT_TYPE_DISABLE:
-			(void) enable_inst(h, inst, event->riq_type);
+			(void) enable_inst(h, inst, event);
 			break;
 
 		case RESTARTER_EVENT_TYPE_ADMIN_DISABLE:
-			if (enable_inst(h, inst, event->riq_type) == 0)
+			if (enable_inst(h, inst, event) == 0)
 				reset_start_times(inst);
 			break;
 
@@ -1676,33 +1742,35 @@
 			break;
 
 		case RESTARTER_EVENT_TYPE_START:
-			start_instance(h, inst);
+			start_instance(h, inst, event->riq_reason);
 			break;
 
 		case RESTARTER_EVENT_TYPE_DEPENDENCY_CYCLE:
-			maintain_instance(h, inst, 0, "dependency_cycle");
+			maintain_instance(h, inst, 0,
+			    restarter_str_dependency_cycle);
 			break;
 
 		case RESTARTER_EVENT_TYPE_INVALID_DEPENDENCY:
-			maintain_instance(h, inst, 0, "invalid_dependency");
+			maintain_instance(h, inst, 0,
+			    restarter_str_invalid_dependency);
 			break;
 
 		case RESTARTER_EVENT_TYPE_ADMIN_MAINT_ON:
 			if (event_from_tty(h, inst) == 0)
 				maintain_instance(h, inst, 0,
-				    "service_request");
+				    restarter_str_service_request);
 			else
 				maintain_instance(h, inst, 0,
-				    "administrative_request");
+				    restarter_str_administrative_request);
 			break;
 
 		case RESTARTER_EVENT_TYPE_ADMIN_MAINT_ON_IMMEDIATE:
 			if (event_from_tty(h, inst) == 0)
 				maintain_instance(h, inst, 1,
-				    "service_request");
+				    restarter_str_service_request);
 			else
 				maintain_instance(h, inst, 1,
-				    "administrative_request");
+				    restarter_str_administrative_request);
 			break;
 
 		case RESTARTER_EVENT_TYPE_ADMIN_MAINT_OFF:
@@ -1807,6 +1875,7 @@
 
 	qe = startd_zalloc(sizeof (restarter_instance_qentry_t));
 	qe->riq_type = e->rpe_type;
+	qe->riq_reason = e->rpe_reason;
 
 	uu_list_node_init(qe, &qe->riq_link, restarter_queue_pool);
 	r = uu_list_insert_before(ri->ri_queue, NULL, qe);
--- a/usr/src/cmd/svc/startd/startd.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/svc/startd/startd.c	Fri Jul 30 17:04:17 2010 +1000
@@ -20,8 +20,7 @@
  */
 
 /*
- * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 /*
@@ -325,6 +324,7 @@
 	return (tid);
 }
 
+extern int info_events_all;
 
 static int
 read_startd_config(void)
@@ -393,7 +393,8 @@
 	idata.i_next_state = RESTARTER_STATE_NONE;
 timestamp:
 	switch (r = _restarter_commit_states(hndl, &idata,
-	    RESTARTER_STATE_ONLINE, RESTARTER_STATE_NONE, NULL)) {
+	    RESTARTER_STATE_ONLINE, RESTARTER_STATE_NONE,
+	    restarter_get_str_short(restarter_str_insert_in_graph))) {
 	case 0:
 		break;
 
@@ -471,6 +472,9 @@
 	if (scf_pg_get_name(pg, NULL, 0) < 0)
 		goto scfout;
 
+	/* get info_events_all */
+	info_events_all = libscf_get_info_events_all(pg);
+
 	/* Iterate through. */
 	iter = safe_scf_iter_create(hndl);
 
--- a/usr/src/cmd/svc/startd/startd.h	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/svc/startd/startd.h	Fri Jul 30 17:04:17 2010 +1000
@@ -20,8 +20,7 @@
  */
 
 /*
- * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #ifndef	_STARTD_H
@@ -333,6 +332,9 @@
 	 * Currently, only relevant for GVT_SVC and GVT_INST type vertices.
 	 */
 	int 				gv_refs;
+
+	int32_t				gv_stn_tset;
+	int32_t				gv_reason;
 } graph_vertex_t;
 
 typedef struct graph_edge {
@@ -341,6 +343,9 @@
 	graph_vertex_t	*ge_parent;
 } graph_edge_t;
 
+int libscf_get_info_events_all(scf_propertygroup_t *);
+int32_t libscf_get_stn_tset(scf_instance_t *);
+
 /*
  * Restarter transition outcomes
  */
@@ -471,6 +476,7 @@
 
 typedef struct restarter_instance_qentry {
 	restarter_event_type_t	riq_type;
+	int32_t			riq_reason;
 	uu_list_node_t		riq_link;
 } restarter_instance_qentry_t;
 
@@ -478,6 +484,7 @@
 	int			sf_id;
 	int			sf_method_type;
 	restarter_error_t	sf_event_type;
+	restarter_str_t		sf_reason;
 } fork_info_t;
 
 typedef struct wait_info {
@@ -724,7 +731,7 @@
 int instance_in_transition(restarter_inst_t *);
 int restarter_instance_update_states(scf_handle_t *, restarter_inst_t *,
     restarter_instance_state_t, restarter_instance_state_t, restarter_error_t,
-    char *);
+    restarter_str_t);
 int stop_instance_fmri(scf_handle_t *, const char *, uint_t);
 restarter_inst_t *inst_lookup_by_id(int);
 void restarter_mark_pending_snapshot(const char *, uint_t);
--- a/usr/src/cmd/svc/svccfg/Makefile	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/svc/svccfg/Makefile	Fri Jul 30 17:04:17 2010 +1000
@@ -20,8 +20,7 @@
 #
 
 #
-# Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
+# Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
 #
 
 MYPROG =	svccfg
@@ -43,7 +42,8 @@
 		svccfg_grammar.o \
 		svccfg_lex.o \
 		manifest_find.o \
-		manifest_hash.o 
+		manifest_hash.o \
+		notify_params.o
 OBJS =		$(MYOBJS)
 
 POFILES = 	$(SRCS:%.c=%.po) \
@@ -79,7 +79,7 @@
 CLOBBERFILES += svccfg_lex.c svccfg_grammar.c svccfg_grammar.h \
     $(MYPROG:%=%-native)
 
-SVCCFG_EXTRA_LIBS = -lxml2 -lscf -ll -luutil -lumem -lmd5
+SVCCFG_EXTRA_LIBS = -lxml2 -lscf -ll -luutil -lumem -lmd5 -lnvpair
 $(NOT_NATIVE)SVCCFG_EXTRA_LIBS += -ltecla
 
 LIBSCF		= $(SRC)/lib/libscf
@@ -89,7 +89,7 @@
 debug := COPTFLAG = -g
 
 lint := LINTFLAGS = -mux
-lint := SVCCFG_EXTRA_LIBS = -lscf -ll -luutil -lumem -lmd5
+lint := SVCCFG_EXTRA_LIBS = -lscf -ll -luutil -lumem -lmd5 -lnvpair
 
 LDLIBS += $(SVCCFG_EXTRA_LIBS)
 
--- a/usr/src/cmd/svc/svccfg/svccfg.h	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/svc/svccfg/svccfg.h	Fri Jul 30 17:04:17 2010 +1000
@@ -99,6 +99,7 @@
 	SC_DOC_LINK,
 	SC_DOCUMENTATION,
 	SC_ENABLED,
+	SC_EVENT,
 	SC_EXEC_METHOD,
 	SC_FMRI,
 	SC_HOST,
@@ -117,7 +118,10 @@
 	SC_NET_ADDR,
 	SC_NET_ADDR_V4,
 	SC_NET_ADDR_V6,
+	SC_NOTIFICATION_PARAMETERS,
 	SC_OPAQUE,
+	SC_PARAMETER,
+	SC_PARAMVAL,
 	SC_PG_PATTERN,
 	SC_PROP_PATTERN,
 	SC_PROPERTY,
@@ -132,6 +136,7 @@
 	SC_STABILITY,
 	SC_TEMPLATE,
 	SC_TIME,
+	SC_TYPE,
 	SC_UNITS,
 	SC_URI,
 	SC_USTRING,
@@ -380,6 +385,7 @@
 extern const char * const type_attr;
 extern const char * const value_attr;
 extern const char * const enabled_attr;
+extern const char * const active_attr;
 extern const char * const scf_pg_general;
 extern const char * const scf_group_framework;
 extern const char * const true;
@@ -467,6 +473,10 @@
 int lscf_store_hash(const char *, unsigned char *);
 int lscf_service_cleanup(void *, scf_walkinfo_t *);
 int lscf_hash_cleanup();
+void lscf_delnotify(const char *, int);
+void lscf_listnotify(const char *, int);
+int lscf_setnotify(uu_list_t *);
+
 CPL_MATCH_FN(complete_select);
 CPL_MATCH_FN(complete_command);
 
@@ -495,6 +505,15 @@
 void tmpl_property_init(property_t *);
 tmpl_validate_status_t tmpl_validate_bundle(bundle_t *, tmpl_errors_t **);
 
+#define	FMA_TOKENS	0
+#define	MIXED_TOKENS	-1
+#define	INVALID_TOKENS	-2
+
+char **tokenize(char *, const char *);
+int32_t check_tokens(char **);
+const char *de_tag(const char *);
+const char *tset_to_string(int32_t);
+
 #ifdef	__cplusplus
 }
 #endif
--- a/usr/src/cmd/svc/svccfg/svccfg.l	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/svc/svccfg/svccfg.l	Fri Jul 30 17:04:17 2010 +1000
@@ -21,8 +21,7 @@
  */
 
 /*
- * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 
@@ -71,6 +70,12 @@
  */
 %Start	WORD
 
+/*
+ * The default value of lex for transitions is 2000 and it seems we reached it.
+ * So we are bumping it up!
+ */
+%a 3000
+
 %%
 
 #.*$			;	/* comments */
@@ -116,6 +121,10 @@
 <INITIAL>revert		{ BEGIN WORD; return (SCC_REVERT); }
 <INITIAL>refresh	{ BEGIN WORD; return (SCC_REFRESH); }
 
+<INITIAL>delnotify	{ BEGIN WORD; return (SCC_DELNOTIFY); }
+<INITIAL>listnotify	{ BEGIN WORD; return (SCC_LISTNOTIFY); }
+<INITIAL>setnotify	{ BEGIN WORD; return (SCC_SETNOTIFY); }
+
 [^ \t\n">=()]+		{
 				if ((yylval.str = strdup(yytext)) == NULL) {
 					yyerror(gettext("Out of memory"));
--- a/usr/src/cmd/svc/svccfg/svccfg.y	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/svc/svccfg/svccfg.y	Fri Jul 30 17:04:17 2010 +1000
@@ -52,6 +52,7 @@
 %token SCC_LISTSNAP SCC_SELECTSNAP SCC_REVERT SCC_REFRESH
 %token SCS_REDIRECT SCS_NEWLINE SCS_EQUALS SCS_LPAREN SCS_RPAREN
 %token SCV_WORD SCV_STRING
+%token SCC_DELNOTIFY SCC_SETNOTIFY SCC_LISTNOTIFY
 
 %type <tok> command_token
 %type <str> SCV_WORD SCV_STRING
@@ -108,6 +109,9 @@
 	| revert_cmd
 	| refresh_cmd
 	| unknown_cmd
+	| delnotify_cmd
+	| listnotify_cmd
+	| setnotify_cmd
 	| error terminator	{ semerr(gettext("Syntax error.\n")); }
 
 unknown_cmd : SCV_WORD terminator
@@ -548,6 +552,71 @@
 refresh_cmd: SCC_REFRESH terminator	{ lscf_refresh(); }
 	| SCC_REFRESH error terminator	{ synerr(SCC_REFRESH); return(0); }
 
+delnotify_cmd : SCC_DELNOTIFY SCV_WORD terminator
+	{
+		lscf_delnotify($2, 0);
+		free($2);
+	}
+	| SCC_DELNOTIFY SCV_WORD SCV_WORD terminator
+	{
+		if (strcmp($2, "-g") == 0) {
+			lscf_delnotify($3, 1);
+			free($2);
+			free($3);
+		} else {
+			synerr(SCC_DELNOTIFY);
+			free($2);
+			free($3);
+			return(0);
+		}
+	}
+	| SCC_DELNOTIFY error terminator { synerr(SCC_DELNOTIFY); return(0); }
+
+listnotify_cmd : SCC_LISTNOTIFY terminator
+	{
+		lscf_listnotify("all", 0);
+	}	
+	| SCC_LISTNOTIFY SCV_WORD terminator
+	{
+		if (strcmp($2, "-g") == 0) {
+			lscf_listnotify("all", 1);
+		} else {
+			lscf_listnotify($2, 0);
+		}
+		free($2);
+	}
+	| SCC_LISTNOTIFY SCV_WORD SCV_WORD terminator
+	{
+		if (strcmp($2, "-g") == 0) {
+			lscf_listnotify($3, 1);
+			free($2);
+			free($3);
+		} else {
+			synerr(SCC_LISTNOTIFY);
+			free($2);
+			free($3);
+			return(0);
+		}
+	}
+	| SCC_LISTNOTIFY error terminator { synerr(SCC_LISTNOTIFY); return(0); }
+
+setnotify_cmd : SCC_SETNOTIFY string_list terminator
+	{
+		string_list_t *slp;
+		void *cookie = NULL;
+
+		if (lscf_setnotify($2) == -2)
+			synerr(SCC_SETNOTIFY);
+
+		while ((slp = uu_list_teardown($2, &cookie)) != NULL) {
+			free(slp->str);
+			free(slp);
+		}
+
+		uu_list_destroy($2);
+	}
+	| SCC_SETNOTIFY error terminator { synerr(SCC_SETNOTIFY); return(0); }
+
 terminator : SCS_NEWLINE
 
 string_list :
@@ -624,3 +693,6 @@
 	| SCC_REVERT		{ $$ = SCC_REVERT; }
 	| SCC_REFRESH		{ $$ = SCC_REFRESH; }
 	| SCC_DESCRIBE		{ $$ = SCC_DESCRIBE; }
+	| SCC_DELNOTIFY		{ $$ = SCC_DELNOTIFY; }
+	| SCC_LISTNOTIFY	{ $$ = SCC_LISTNOTIFY; }
+	| SCC_SETNOTIFY		{ $$ = SCC_SETNOTIFY; }
--- a/usr/src/cmd/svc/svccfg/svccfg_engine.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/svc/svccfg/svccfg_engine.c	Fri Jul 30 17:04:17 2010 +1000
@@ -1074,7 +1074,9 @@
 		    "Property group commands: listpg addpg delpg\n"
 		    "Property commands:	 listprop setprop delprop editprop\n"
 		    "Property value commands: addpropvalue delpropvalue "
-		    "setenv unsetenv\n"));
+		    "setenv unsetenv\n"
+		    "Notification parameters: "
+		    "listnotify setnotify delnotify\n"));
 		return;
 	}
 
--- a/usr/src/cmd/svc/svccfg/svccfg_help.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/svc/svccfg/svccfg_help.c	Fri Jul 30 17:04:17 2010 +1000
@@ -20,8 +20,7 @@
  */
 
 /*
- * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 
@@ -145,5 +144,22 @@
 "a repository, inform the instance's restarter to re-read the updated\n"
 "configuration."
 	},
+	{ SCC_DELNOTIFY, "delnotify <[-g] tset | class>\n\n"
+"Delete the existing notification parameters for the specified class or\n"
+"transition set. If the -g option is used, the notification parameters in\n"
+"svc:/system/svc/global:default are deleted.\n"
+	},
+	{ SCC_LISTNOTIFY, "listnotify <[-g] tset | class>\n\n"
+"Display the existing notification parameters for the specified class or\n"
+"transition set. If the -g option is used, the notification parameters in\n"
+"svc:/system/svc/global:default are displayed.\n"
+	},
+	{ SCC_SETNOTIFY,
+"setnotify {[-g] tset | class} <notification parameters> ...\n\n"
+"Set notification parameters for the specified class or transition set.\n"
+"If the -g option is used, the notification parameters are set in\n"
+"svc:/system/svc/global:default.\n"
+"This command overwrites existing notification parameters."
+	},
 	{ 0, NULL }
 };
--- a/usr/src/cmd/svc/svccfg/svccfg_libscf.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/svc/svccfg/svccfg_libscf.c	Fri Jul 30 17:04:17 2010 +1000
@@ -33,6 +33,7 @@
 #include <fnmatch.h>
 #include <inttypes.h>
 #include <libintl.h>
+#include <libnvpair.h>
 #include <libscf.h>
 #include <libscf_priv.h>
 #include <libtecla.h>
@@ -54,6 +55,7 @@
 #include <sys/mman.h>
 
 #include "svccfg.h"
+#include "notify_params.h"
 #include "manifest_hash.h"
 #include "manifest_find.h"
 
@@ -105,6 +107,7 @@
 	xmlNodePtr	dependents;
 	xmlNodePtr	method_context;
 	xmlNodePtr	exec_methods;
+	xmlNodePtr	notify_params;
 	xmlNodePtr	property_groups;
 	xmlNodePtr	instances;
 	xmlNodePtr	stability;
@@ -130,6 +133,14 @@
 };
 
 /*
+ * Likewise for type (for notification parameters) elements.
+ */
+struct params_elts {
+	xmlNodePtr	paramval;
+	xmlNodePtr	parameter;
+};
+
+/*
  * This structure is for snaplevel lists.  They are convenient because libscf
  * only allows traversing snaplevels in one direction.
  */
@@ -208,6 +219,7 @@
 static const char *emsg_permission_denied;
 static const char *emsg_create_xml;
 static const char *emsg_cant_modify_snapshots;
+static const char *emsg_invalid_for_snapshot;
 static const char *emsg_read_only;
 static const char *emsg_deleted;
 static const char *emsg_invalid_pg_name;
@@ -285,15 +297,42 @@
 	NULL
 };
 
-static void
-safe_printf(const char *fmt, ...)
-{
-	va_list va;
-
-	va_start(va, fmt);
-	if (vprintf(fmt, va) < 0)
-		uu_die(gettext("Error writing to stdout"));
-	va_end(va);
+static struct uri_scheme {
+	const char *scheme;
+	const char *protocol;
+} uri_scheme[] = {
+	{ "mailto", "smtp" },
+	{ "snmp", "snmp" },
+	{ "syslog", "syslog" },
+	{ NULL, NULL }
+};
+#define	URI_SCHEME_NUM ((sizeof (uri_scheme) / \
+    sizeof (struct uri_scheme)) - 1)
+
+static int
+check_uri_scheme(const char *scheme)
+{
+	int i;
+
+	for (i = 0; uri_scheme[i].scheme != NULL; ++i) {
+		if (strcmp(scheme, uri_scheme[i].scheme) == 0)
+			return (i);
+	}
+
+	return (-1);
+}
+
+static int
+check_uri_protocol(const char *p)
+{
+	int i;
+
+	for (i = 0; uri_scheme[i].protocol != NULL; ++i) {
+		if (strcmp(p, uri_scheme[i].protocol) == 0)
+			return (i);
+	}
+
+	return (-1);
 }
 
 /*
@@ -931,6 +970,8 @@
 	emsg_permission_denied = gettext("Permission denied.\n");
 	emsg_create_xml = gettext("Could not create XML node.\n");
 	emsg_cant_modify_snapshots = gettext("Cannot modify snapshots.\n");
+	emsg_invalid_for_snapshot =
+	    gettext("Invalid operation on a snapshot.\n");
 	emsg_read_only = gettext("Backend read-only.\n");
 	emsg_deleted = gettext("Current selection has been deleted.\n");
 	emsg_invalid_pg_name =
@@ -9995,6 +10036,176 @@
 }
 
 /*
+ * Process parameter and paramval elements
+ */
+static void
+export_parameter(scf_property_t *prop, const char *name,
+    struct params_elts *elts)
+{
+	xmlNodePtr param;
+	scf_error_t err = 0;
+	int ret;
+
+	if (scf_property_get_value(prop, exp_val) == SCF_SUCCESS) {
+		if ((param = xmlNewNode(NULL, (xmlChar *)"paramval")) == NULL)
+			uu_die(emsg_create_xml);
+
+		safe_setprop(param, name_attr, name);
+
+		if (scf_value_get_as_string(exp_val, exp_str, exp_str_sz) < 0)
+			scfdie();
+		safe_setprop(param, value_attr, exp_str);
+
+		if (elts->paramval == NULL)
+			elts->paramval = param;
+		else
+			(void) xmlAddSibling(elts->paramval, param);
+
+		return;
+	}
+
+	err = scf_error();
+
+	if (err != SCF_ERROR_CONSTRAINT_VIOLATED &&
+	    err != SCF_ERROR_NOT_FOUND)
+		scfdie();
+
+	if ((param = xmlNewNode(NULL, (xmlChar *)"parameter")) == NULL)
+		uu_die(emsg_create_xml);
+
+	safe_setprop(param, name_attr, name);
+
+	if (err == SCF_ERROR_CONSTRAINT_VIOLATED) {
+		if (scf_iter_property_values(exp_val_iter, prop) != SCF_SUCCESS)
+			scfdie();
+
+		while ((ret = scf_iter_next_value(exp_val_iter, exp_val)) ==
+		    1) {
+			xmlNodePtr vn;
+
+			if ((vn = xmlNewChild(param, NULL,
+			    (xmlChar *)"value_node", NULL)) == NULL)
+				uu_die(emsg_create_xml);
+
+			if (scf_value_get_as_string(exp_val, exp_str,
+			    exp_str_sz) < 0)
+				scfdie();
+
+			safe_setprop(vn, value_attr, exp_str);
+		}
+		if (ret != 0)
+			scfdie();
+	}
+
+	if (elts->parameter == NULL)
+		elts->parameter = param;
+	else
+		(void) xmlAddSibling(elts->parameter, param);
+}
+
+/*
+ * Process notification parameters for a service or instance
+ */
+static void
+export_notify_params(scf_propertygroup_t *pg, struct entity_elts *elts)
+{
+	xmlNodePtr n, event, *type;
+	struct params_elts *eelts;
+	int ret, err, i;
+
+	n = xmlNewNode(NULL, (xmlChar *)"notification_parameters");
+	event = xmlNewNode(NULL, (xmlChar *)"event");
+	if (n == NULL || event == NULL)
+		uu_die(emsg_create_xml);
+
+	/* event value */
+	if (scf_pg_get_name(pg, exp_str, max_scf_name_len + 1) < 0)
+		scfdie();
+	safe_setprop(event, value_attr, exp_str);
+
+	(void) xmlAddChild(n, event);
+
+	if ((type = calloc(URI_SCHEME_NUM, sizeof (xmlNodePtr))) == NULL ||
+	    (eelts = calloc(URI_SCHEME_NUM,
+	    sizeof (struct params_elts))) == NULL)
+		uu_die(gettext("Out of memory.\n"));
+
+	err = 0;
+
+	if (scf_iter_pg_properties(exp_prop_iter, pg) != SCF_SUCCESS)
+		scfdie();
+
+	while ((ret = scf_iter_next_property(exp_prop_iter, exp_prop)) == 1) {
+		char *t, *p;
+
+		if (scf_property_get_name(exp_prop, exp_str, exp_str_sz) < 0)
+			scfdie();
+
+		if ((t = strtok_r(exp_str, ",", &p)) == NULL || p == NULL) {
+			/*
+			 * this is not a well formed notification parameters
+			 * element, we should export as regular pg
+			 */
+			err = 1;
+			break;
+		}
+
+		if ((i = check_uri_protocol(t)) < 0) {
+			err = 1;
+			break;
+		}
+
+		if (type[i] == NULL) {
+			if ((type[i] = xmlNewNode(NULL, (xmlChar *)"type")) ==
+			    NULL)
+				uu_die(emsg_create_xml);
+
+			safe_setprop(type[i], name_attr, t);
+		}
+		if (strcmp(p, active_attr) == 0) {
+			if (set_attr_from_prop(exp_prop, type[i],
+			    active_attr) != 0) {
+				err = 1;
+				break;
+			}
+			continue;
+		}
+		/*
+		 * We export the parameter
+		 */
+		export_parameter(exp_prop, p, &eelts[i]);
+	}
+
+	if (ret == -1)
+		scfdie();
+
+	if (err == 1) {
+		for (i = 0; i < URI_SCHEME_NUM; ++i)
+			xmlFree(type[i]);
+		free(type);
+
+		export_pg(pg, elts, 0);
+
+		return;
+	} else {
+		for (i = 0; i < URI_SCHEME_NUM; ++i)
+			if (type[i] != NULL) {
+				(void) xmlAddChildList(type[i],
+				    eelts[i].paramval);
+				(void) xmlAddChildList(type[i],
+				    eelts[i].parameter);
+				(void) xmlAddSibling(event, type[i]);
+			}
+	}
+	free(type);
+
+	if (elts->notify_params == NULL)
+		elts->notify_params = n;
+	else
+		(void) xmlAddSibling(elts->notify_params, n);
+}
+
+/*
  * Process the general property group for an instance.
  */
 static void
@@ -10153,6 +10364,9 @@
 		} else if (strcmp(exp_str, SCF_GROUP_TEMPLATE) == 0) {
 			export_template(exp_pg, &elts, &template_elts);
 			continue;
+		} else if (strcmp(exp_str, SCF_NOTIFY_PARAMS_PG_TYPE) == 0) {
+			export_notify_params(exp_pg, &elts);
+			continue;
 		}
 
 		/* Ordinary pg. */
@@ -10173,8 +10387,8 @@
 
 	if (isdefault && elts.restarter == NULL &&
 	    elts.dependencies == NULL && elts.method_context == NULL &&
-	    elts.exec_methods == NULL && elts.property_groups == NULL &&
-	    elts.template == NULL) {
+	    elts.exec_methods == NULL && elts.notify_params == NULL &&
+	    elts.property_groups == NULL && elts.template == NULL) {
 		xmlChar *eval;
 
 		/* This is a default instance */
@@ -10197,6 +10411,7 @@
 		(void) xmlAddChildList(n, elts.dependents);
 		(void) xmlAddChild(n, elts.method_context);
 		(void) xmlAddChildList(n, elts.exec_methods);
+		(void) xmlAddChildList(n, elts.notify_params);
 		(void) xmlAddChildList(n, elts.property_groups);
 		(void) xmlAddChild(n, elts.template);
 
@@ -10276,6 +10491,9 @@
 		} else if (strcmp(exp_str, SCF_GROUP_TEMPLATE) == 0) {
 			export_template(exp_pg, &elts, &template_elts);
 			continue;
+		} else if (strcmp(exp_str, SCF_NOTIFY_PARAMS_PG_TYPE) == 0) {
+			export_notify_params(exp_pg, &elts);
+			continue;
 		}
 
 		export_pg(exp_pg, &elts, flags);
@@ -10310,6 +10528,7 @@
 	(void) xmlAddChildList(snode, elts.dependents);
 	(void) xmlAddChild(snode, elts.method_context);
 	(void) xmlAddChildList(snode, elts.exec_methods);
+	(void) xmlAddChildList(snode, elts.notify_params);
 	(void) xmlAddChildList(snode, elts.property_groups);
 	(void) xmlAddChildList(snode, elts.instances);
 	(void) xmlAddChild(snode, elts.stability);
@@ -15183,6 +15402,534 @@
 	goto out;
 }
 
+#define	PARAM_ACTIVE	((const char *) "active")
+#define	PARAM_INACTIVE	((const char *) "inactive")
+#define	PARAM_SMTP_TO	((const char *) "to")
+
+/*
+ * tokenize()
+ * Breaks down the string according to the tokens passed.
+ * Caller is responsible for freeing array of pointers returned.
+ * Returns NULL on failure
+ */
+char **
+tokenize(char *str, const char *sep)
+{
+	char *token, *lasts;
+	char **buf;
+	int n = 0;	/* number of elements */
+	int size = 8;	/* size of the array (initial) */
+
+	buf = safe_malloc(size * sizeof (char *));
+
+	for (token = strtok_r(str, sep, &lasts); token != NULL;
+	    token = strtok_r(NULL, sep, &lasts), ++n) {
+		if (n + 1 >= size) {
+			size *= 2;
+			if ((buf = realloc(buf, size * sizeof (char *))) ==
+			    NULL) {
+				uu_die(gettext("Out of memory"));
+			}
+		}
+		buf[n] = token;
+	}
+	/* NULL terminate the pointer array */
+	buf[n] = NULL;
+
+	return (buf);
+}
+
+int32_t
+check_tokens(char **p)
+{
+	int32_t smf = 0;
+	int32_t fma = 0;
+
+	while (*p) {
+		int32_t t = string_to_tset(*p);
+
+		if (t == 0) {
+			if (is_fma_token(*p) == 0)
+				return (INVALID_TOKENS);
+			fma = 1; /* this token is an fma event */
+		} else {
+			smf |= t;
+		}
+
+		if (smf != 0 && fma == 1)
+			return (MIXED_TOKENS);
+		++p;
+	}
+
+	if (smf > 0)
+		return (smf);
+	else if (fma == 1)
+		return (FMA_TOKENS);
+
+	return (INVALID_TOKENS);
+}
+
+static int
+get_selection_str(char *fmri, size_t sz)
+{
+	if (g_hndl == NULL) {
+		semerr(emsg_entity_not_selected);
+		return (-1);
+	} else if (cur_level != NULL) {
+		semerr(emsg_invalid_for_snapshot);
+		return (-1);
+	} else {
+		lscf_get_selection_str(fmri, sz);
+	}
+
+	return (0);
+}
+
+void
+lscf_delnotify(const char *set, int global)
+{
+	char *str = strdup(set);
+	char **pgs;
+	char **p;
+	int32_t tset;
+	char *fmri = NULL;
+
+	if (str == NULL)
+		uu_die(gettext("Out of memory.\n"));
+
+	pgs = tokenize(str, ",");
+
+	if ((tset = check_tokens(pgs)) > 0) {
+		size_t sz = max_scf_fmri_len + 1;
+
+		fmri = safe_malloc(sz);
+		if (global) {
+			(void) strlcpy(fmri, SCF_INSTANCE_GLOBAL, sz);
+		} else if (get_selection_str(fmri, sz) != 0) {
+			goto out;
+		}
+
+		if (smf_notify_del_params(SCF_SVC_TRANSITION_CLASS, fmri,
+		    tset) != SCF_SUCCESS) {
+			uu_warn(gettext("Failed smf_notify_del_params: %s\n"),
+			    scf_strerror(scf_error()));
+		}
+	} else if (tset == FMA_TOKENS) {
+		if (global) {
+			semerr(gettext("Can't use option '-g' with FMA event "
+			    "definitions\n"));
+			goto out;
+		}
+
+		for (p = pgs; *p; ++p) {
+			if (smf_notify_del_params(de_tag(*p), NULL, NULL) !=
+			    SCF_SUCCESS) {
+				uu_warn(gettext("Failed for \"%s\": %s\n"), *p,
+				    scf_strerror(scf_error()));
+				goto out;
+			}
+		}
+	} else if (tset == MIXED_TOKENS) {
+		semerr(gettext("Can't mix SMF and FMA event definitions\n"));
+		goto out;
+	} else {
+		uu_die(gettext("Invalid input.\n"));
+	}
+
+out:
+	free(fmri);
+	free(pgs);
+	free(str);
+}
+
+void
+lscf_listnotify(const char *set, int global)
+{
+	char *str = safe_strdup(set);
+	char **pgs;
+	char **p;
+	int32_t tset;
+	nvlist_t *nvl;
+	char *fmri = NULL;
+
+	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0)
+		uu_die(gettext("Out of memory.\n"));
+
+	pgs = tokenize(str, ",");
+
+	if ((tset = check_tokens(pgs)) > 0) {
+		size_t sz = max_scf_fmri_len + 1;
+
+		fmri = safe_malloc(sz);
+		if (global) {
+			(void) strlcpy(fmri, SCF_INSTANCE_GLOBAL, sz);
+		} else if (get_selection_str(fmri, sz) != 0) {
+			goto out;
+		}
+
+		if (_scf_get_svc_notify_params(fmri, nvl, tset, 1, 1) !=
+		    SCF_SUCCESS) {
+			if (scf_error() != SCF_ERROR_NOT_FOUND &&
+			    scf_error() != SCF_ERROR_DELETED)
+				uu_warn(gettext(
+				    "Failed listnotify: %s\n"),
+				    scf_strerror(scf_error()));
+			goto out;
+		}
+
+		listnotify_print(nvl, NULL);
+	} else if (tset == FMA_TOKENS) {
+		if (global) {
+			semerr(gettext("Can't use option '-g' with FMA event "
+			    "definitions\n"));
+			goto out;
+		}
+
+		for (p = pgs; *p; ++p) {
+			if (_scf_get_fma_notify_params(de_tag(*p), nvl, 1) !=
+			    SCF_SUCCESS) {
+				/*
+				 * if the preferences have just been deleted
+				 * or does not exist, just skip.
+				 */
+				if (scf_error() == SCF_ERROR_NOT_FOUND ||
+				    scf_error() == SCF_ERROR_DELETED)
+					continue;
+				uu_warn(gettext(
+				    "Failed listnotify: %s\n"),
+				    scf_strerror(scf_error()));
+				goto out;
+			}
+			listnotify_print(nvl, re_tag(*p));
+		}
+	} else if (tset == MIXED_TOKENS) {
+		semerr(gettext("Can't mix SMF and FMA event definitions\n"));
+		goto out;
+	} else {
+		semerr(gettext("Invalid input.\n"));
+	}
+
+out:
+	nvlist_free(nvl);
+	free(fmri);
+	free(pgs);
+	free(str);
+}
+
+static char *
+strip_quotes_and_blanks(char *s)
+{
+	char *start = s;
+	char *end = strrchr(s, '\"');
+
+	if (s[0] == '\"' && end != NULL && *(end + 1) == '\0') {
+		start = s + 1;
+		while (isblank(*start))
+			start++;
+		while (isblank(*(end - 1)) && end > start) {
+			end--;
+		}
+		*end = '\0';
+	}
+
+	return (start);
+}
+
+static int
+set_active(nvlist_t *mech, const char *hier_part)
+{
+	boolean_t b;
+
+	if (*hier_part == '\0' || strcmp(hier_part, PARAM_ACTIVE) == 0) {
+		b = B_TRUE;
+	} else if (strcmp(hier_part, PARAM_INACTIVE) == 0) {
+		b = B_FALSE;
+	} else {
+		return (-1);
+	}
+
+	if (nvlist_add_boolean_value(mech, PARAM_ACTIVE, b) != 0)
+		uu_die(gettext("Out of memory.\n"));
+
+	return (0);
+}
+
+static int
+add_snmp_params(nvlist_t *mech, char *hier_part)
+{
+	return (set_active(mech, hier_part));
+}
+
+static int
+add_syslog_params(nvlist_t *mech, char *hier_part)
+{
+	return (set_active(mech, hier_part));
+}
+
+/*
+ * add_mailto_paramas()
+ * parse the hier_part of mailto URI
+ * mailto:<addr>[?<header1>=<value1>[&<header2>=<value2>]]
+ * or mailto:{[active]|inactive}
+ */
+static int
+add_mailto_params(nvlist_t *mech, char *hier_part)
+{
+	const char *tok = "?&";
+	char *p;
+	char *lasts;
+	char *param;
+	char *val;
+
+	/*
+	 * If the notification parametes are in the form of
+	 *
+	 *   malito:{[active]|inactive}
+	 *
+	 * we set the property accordingly and return.
+	 * Otherwise, we make the notification type active and
+	 * process the hier_part.
+	 */
+	if (set_active(mech, hier_part) == 0)
+		return (0);
+	else if (set_active(mech, PARAM_ACTIVE) != 0)
+		return (-1);
+
+	if ((p = strtok_r(hier_part, tok, &lasts)) == NULL) {
+		/*
+		 * sanity check: we only get here if hier_part = "", but
+		 * that's handled by set_active
+		 */
+		uu_die("strtok_r");
+	}
+
+	if (nvlist_add_string(mech, PARAM_SMTP_TO, p) != 0)
+		uu_die(gettext("Out of memory.\n"));
+
+	while ((p = strtok_r(NULL, tok, &lasts)) != NULL)
+		if ((param = strtok_r(p, "=", &val)) != NULL)
+			if (nvlist_add_string(mech, param, val) != 0)
+				uu_die(gettext("Out of memory.\n"));
+
+	return (0);
+}
+
+static int
+uri_split(char *uri, char **scheme, char **hier_part)
+{
+	int r = -1;
+
+	if ((*scheme = strtok_r(uri, ":", hier_part)) == NULL ||
+	    *hier_part == NULL) {
+		semerr(gettext("'%s' is not an URI\n"), uri);
+		return (r);
+	}
+
+	if ((r = check_uri_scheme(*scheme)) < 0) {
+		semerr(gettext("Unkown URI scheme: %s\n"), *scheme);
+		return (r);
+	}
+
+	return (r);
+}
+
+static int
+process_uri(nvlist_t *params, char *uri)
+{
+	char *scheme;
+	char *hier_part;
+	nvlist_t *mech;
+	int index;
+	int r;
+
+	if ((index = uri_split(uri, &scheme, &hier_part)) < 0)
+		return (-1);
+
+	if (nvlist_alloc(&mech, NV_UNIQUE_NAME, 0) != 0)
+		uu_die(gettext("Out of memory.\n"));
+
+	switch (index) {
+	case 0:
+		/* error messages displayed by called function */
+		r = add_mailto_params(mech, hier_part);
+		break;
+
+	case 1:
+		if ((r = add_snmp_params(mech, hier_part)) != 0)
+			semerr(gettext("Not valid parameters: '%s'\n"),
+			    hier_part);
+		break;
+
+	case 2:
+		if ((r = add_syslog_params(mech, hier_part)) != 0)
+			semerr(gettext("Not valid parameters: '%s'\n"),
+			    hier_part);
+		break;
+
+	default:
+		r = -1;
+	}
+
+	if (r == 0 && nvlist_add_nvlist(params, uri_scheme[index].protocol,
+	    mech) != 0)
+		uu_die(gettext("Out of memory.\n"));
+
+	nvlist_free(mech);
+	return (r);
+}
+
+static int
+set_params(nvlist_t *params, char **p)
+{
+	char *uri;
+
+	if (p == NULL)
+		/* sanity check */
+		uu_die("set_params");
+
+	while (*p) {
+		uri = strip_quotes_and_blanks(*p);
+		if (process_uri(params, uri) != 0)
+			return (-1);
+
+		++p;
+	}
+
+	return (0);
+}
+
+static int
+setnotify(const char *e, char **p, int global)
+{
+	char *str = safe_strdup(e);
+	char **events;
+	int32_t tset;
+	int r = -1;
+	nvlist_t *nvl, *params;
+	char *fmri = NULL;
+
+	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 ||
+	    nvlist_alloc(&params, NV_UNIQUE_NAME, 0) != 0 ||
+	    nvlist_add_uint32(nvl, SCF_NOTIFY_NAME_VERSION,
+	    SCF_NOTIFY_PARAMS_VERSION) != 0)
+		uu_die(gettext("Out of memory.\n"));
+
+	events = tokenize(str, ",");
+
+	if ((tset = check_tokens(events)) > 0) {
+		/* SMF state transitions parameters */
+		size_t sz = max_scf_fmri_len + 1;
+
+		fmri = safe_malloc(sz);
+		if (global) {
+			(void) strlcpy(fmri, SCF_INSTANCE_GLOBAL, sz);
+		} else if (get_selection_str(fmri, sz) != 0) {
+			goto out;
+		}
+
+		if (nvlist_add_string(nvl, SCF_NOTIFY_NAME_FMRI, fmri) != 0 ||
+		    nvlist_add_int32(nvl, SCF_NOTIFY_NAME_TSET, tset) != 0)
+			uu_die(gettext("Out of memory.\n"));
+
+		if ((r = set_params(params, p)) == 0) {
+			if (nvlist_add_nvlist(nvl, SCF_NOTIFY_PARAMS,
+			    params) != 0)
+				uu_die(gettext("Out of memory.\n"));
+
+			if (smf_notify_set_params(SCF_SVC_TRANSITION_CLASS,
+			    nvl) != SCF_SUCCESS) {
+				r = -1;
+				uu_warn(gettext(
+				    "Failed smf_notify_set_params(3SCF): %s\n"),
+				    scf_strerror(scf_error()));
+			}
+		}
+	} else if (tset == FMA_TOKENS) {
+		/* FMA event parameters */
+		if (global) {
+			semerr(gettext("Can't use option '-g' with FMA event "
+			    "definitions\n"));
+			goto out;
+		}
+
+		if ((r = set_params(params, p)) != 0)
+			goto out;
+
+		if (nvlist_add_nvlist(nvl, SCF_NOTIFY_PARAMS, params) != 0)
+			uu_die(gettext("Out of memory.\n"));
+
+		while (*events) {
+			if (smf_notify_set_params(de_tag(*events), nvl) !=
+			    SCF_SUCCESS)
+				uu_warn(gettext(
+				    "Failed smf_notify_set_params(3SCF) for "
+				    "event %s: %s\n"), *events,
+				    scf_strerror(scf_error()));
+			events++;
+		}
+	} else if (tset == MIXED_TOKENS) {
+		semerr(gettext("Can't mix SMF and FMA event definitions\n"));
+	} else {
+		/* Sanity check */
+		uu_die(gettext("Invalid input.\n"));
+	}
+
+out:
+	nvlist_free(nvl);
+	nvlist_free(params);
+	free(fmri);
+	free(str);
+
+	return (r);
+}
+
+int
+lscf_setnotify(uu_list_t *args)
+{
+	int argc;
+	char **argv = NULL;
+	string_list_t *slp;
+	int global;
+	char *events;
+	char **p;
+	int i;
+	int ret;
+
+	if ((argc = uu_list_numnodes(args)) < 2)
+		goto usage;
+
+	argv = calloc(argc + 1, sizeof (char *));
+	if (argv == NULL)
+		uu_die(gettext("Out of memory.\n"));
+
+	for (slp = uu_list_first(args), i = 0;
+	    slp != NULL;
+	    slp = uu_list_next(args, slp), ++i)
+		argv[i] = slp->str;
+
+	argv[i] = NULL;
+
+	if (strcmp(argv[0], "-g") == 0) {
+		global = 1;
+		events = argv[1];
+		p = argv + 2;
+	} else {
+		global = 0;
+		events = argv[0];
+		p = argv + 1;
+	}
+
+	ret = setnotify(events, p, global);
+
+out:
+	free(argv);
+	return (ret);
+
+usage:
+	ret = -2;
+	goto out;
+}
+
 /*
  * Creates a list of instance name strings associated with a service. If
  * wohandcrafted flag is set, get only instances that have a last-import
--- a/usr/src/cmd/svc/svccfg/svccfg_xml.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/svc/svccfg/svccfg_xml.c	Fri Jul 30 17:04:17 2010 +1000
@@ -54,6 +54,7 @@
 #include "manifest_hash.h"
 
 #include "svccfg.h"
+#include "notify_params.h"
 
 /*
  * snprintf(3C) format strings for constructing property names that include
@@ -92,6 +93,7 @@
 const char * const value_attr = "value";
 const char * const version_attr = "version";
 const char * const xml_lang_attr = "xml:lang";
+const char * const active_attr = "active";
 
 /* Attribute values */
 const char * const all_value = "all";
@@ -118,6 +120,7 @@
 	"doc_link",			/* SC_DOC_LINK */
 	"documentation",		/* SC_DOCUMENTATION */
 	"enabled",			/* SC_ENABLED */
+	"event",			/* SC_EVENT */
 	"exec_method",			/* SC_EXEC_METHOD */
 	"fmri_list",			/* SC_FMRI */
 	"host_list",			/* SC_HOST */
@@ -136,7 +139,10 @@
 	"net_address_list",		/* SC_NET_ADDR */
 	"net_address_v4_list",		/* SC_NET_ADDR_V4 */
 	"net_address_v6_list",		/* SC_NET_ADDR_V6 */
+	"notification_parameters",	/* SC_NOTIFICATION_PARAMETERS */
 	"opaque_list",			/* SC_OPAQUE */
+	"parameter",			/* SC_PARAMETER */
+	"paramval",			/* SC_PARAMVAL */
 	"pg_pattern",			/* SC_PG_PATTERN */
 	"prop_pattern",			/* SC_PROP_PATTERN */
 	"property",			/* SC_PROPERTY */
@@ -151,6 +157,7 @@
 	"stability",			/* SC_STABILITY */
 	"template",			/* SC_TEMPLATE */
 	"time_list",			/* SC_TIME */
+	"type",				/* SC_TYPE */
 	"units",			/* SC_UNITS */
 	"uri_list",			/* SC_URI */
 	"ustring_list",			/* SC_USTRING */
@@ -181,6 +188,7 @@
 	"",				/* SC_DOC_LINK */
 	"",				/* SC_DOCUMENTATION */
 	"",				/* SC_ENABLED */
+	"",				/* SC_EVENT */
 	"",				/* SC_EXEC_METHOD */
 	"fmri",				/* SC_FMRI */
 	"host",				/* SC_HOST */
@@ -199,7 +207,10 @@
 	"net_address",			/* SC_NET_ADDR */
 	"net_address_v4",		/* SC_NET_ADDR_V4 */
 	"net_address_v6",		/* SC_NET_ADDR_V6 */
+	"",				/* SC_NOTIFICATION_PARAMETERS */
 	"opaque",			/* SC_OPAQUE */
+	"",				/* SC_PARAMETER */
+	"",				/* SC_PARAMVAL */
 	"",				/* SC_PG_PATTERN */
 	"",				/* SC_PROP_PATTERN */
 	"",				/* SC_PROPERTY */
@@ -214,6 +225,7 @@
 	"",				/* SC_STABILITY */
 	"",				/* SC_TEMPLATE */
 	"time",				/* SC_TIME */
+	"",				/* SC_TYPE */
 	"",				/* SC_UNITS */
 	"uri",				/* SC_URI */
 	"ustring",			/* SC_USTRING */
@@ -1419,6 +1431,223 @@
 	return (0);
 }
 
+static void
+lxml_get_paramval(pgroup_t *pgrp, const char *propname, xmlNodePtr pval)
+{
+	property_t *p;
+	char *value;
+	char *prop;
+
+	if ((prop = strdup(propname)) == NULL)
+		uu_die(gettext("Out of memory.\n"));
+
+	value = (char *)xmlGetProp(pval, (xmlChar *)value_attr);
+	if (value == NULL || *value == '\0')
+		uu_die(gettext("property value missing for property '%s/%s'\n"),
+		    pgrp->sc_pgroup_name, propname);
+	p = internal_property_create(prop, SCF_TYPE_ASTRING, 1, value);
+
+	(void) internal_attach_property(pgrp, p);
+}
+
+static void
+lxml_get_parameter(pgroup_t *pgrp, const char *propname, xmlNodePtr param)
+{
+	property_t *p = internal_property_new();
+
+	if ((p->sc_property_name = strdup(propname)) == NULL)
+		uu_die(gettext("Out of memory.\n"));
+	p->sc_value_type = SCF_TYPE_ASTRING;
+
+	(void) lxml_get_value(p, SC_ASTRING, param);
+
+	(void) internal_attach_property(pgrp, p);
+}
+
+static void
+lxml_get_type(pgroup_t *pgrp, xmlNodePtr type)
+{
+	property_t *p;
+	xmlChar *name;
+	xmlChar *active;
+	xmlNodePtr cursor;
+	uint64_t active_val;
+	size_t sz = max_scf_name_len + 1;
+	char *propname = safe_malloc(sz);
+
+	if (pgrp->sc_parent->sc_op == SVCCFG_OP_APPLY)
+		lxml_validate_element(type);
+
+	name = xmlGetProp(type, (xmlChar *)name_attr);
+	if (name == NULL || *name == '\0')
+		uu_die(gettext("attribute name missing in element 'type'\n"));
+
+	for (cursor = type->xmlChildrenNode; cursor != NULL;
+	    cursor = cursor->next) {
+		xmlChar *pname;
+
+		if (lxml_ignorable_block(cursor))
+			continue;
+
+		pname = xmlGetProp(cursor, (xmlChar *)name_attr);
+		if (pname == NULL || *pname == '\0')
+			uu_die(gettext(
+			    "attribute name missing in sub-element of type\n"));
+
+		if (snprintf(propname, sz, "%s,%s", (char *)name,
+		    (char *)pname) >= sz)
+			uu_die(gettext("name '%s,%s' is too long\n"),
+			    (char *)name, (char *)pname);
+		xmlFree(pname);
+
+		switch (lxml_xlate_element(cursor->name)) {
+		case SC_PARAMETER:
+			lxml_get_parameter(pgrp, propname, cursor);
+			break;
+
+		case SC_PARAMVAL:
+			lxml_get_paramval(pgrp, propname, cursor);
+			break;
+
+		default:
+			uu_die(gettext("unknown element %s\n"), cursor->name);
+		}
+	}
+
+	active = xmlGetProp(type, (xmlChar *)active_attr);
+	if (active == NULL || strcmp(true, (const char *)active) == 0)
+		active_val = 1;
+	else
+		active_val = 0;
+	xmlFree(active);
+
+	if (snprintf(propname, sz, "%s,%s", (char *)name,
+	    SCF_PROPERTY_ACTIVE_POSTFIX) >= sz)
+		uu_die(gettext("name '%s,%s' is too long\n"),
+		    (char *)name, SCF_PROPERTY_ACTIVE_POSTFIX);
+
+	p = internal_property_create(propname, SCF_TYPE_BOOLEAN, 1, active_val);
+
+	(void) internal_attach_property(pgrp, p);
+
+	xmlFree(name);
+}
+
+static void
+lxml_get_event(entity_t *entity, const char *pgname, xmlNodePtr np)
+{
+	xmlNodePtr cursor;
+	pgroup_t *pgrp;
+
+	pgrp = internal_pgroup_find_or_create(entity, pgname,
+	    SCF_NOTIFY_PARAMS_PG_TYPE);
+	for (cursor = np->xmlChildrenNode; cursor != NULL;
+	    cursor = cursor->next) {
+		if (lxml_ignorable_block(cursor))
+			continue;
+
+		switch (lxml_xlate_element(cursor->name)) {
+		case SC_EVENT:
+			continue;
+
+		case SC_TYPE:
+			lxml_get_type(pgrp, cursor);
+			break;
+
+		default:
+			uu_warn(gettext("illegal element '%s' on "
+			    "notification parameters\n"), cursor->name);
+		}
+	}
+}
+
+static int
+lxml_get_notification_parameters(entity_t *entity, xmlNodePtr np)
+{
+	char *event = NULL;
+	char **pgs = NULL;
+	char **p;
+	char *pgname = NULL;
+	xmlNodePtr cursor;
+	int32_t tset, t;
+	size_t sz = max_scf_name_len + 1;
+	int count;
+	int r = -1;
+
+	for (count = 0, cursor = np->xmlChildrenNode; cursor != NULL;
+	    cursor = cursor->next) {
+		if (lxml_ignorable_block(cursor))
+			continue;
+
+		if (lxml_xlate_element(cursor->name) == SC_EVENT) {
+			xmlChar *s;
+
+			count++;
+			if (count > 1)
+				uu_die(gettext("Can't have more than 1 element "
+				    "event in a notification parameter\n"));
+			s = xmlGetProp(cursor, (xmlChar *)value_attr);
+			if (s == NULL || (event = strdup((char *)s)) == NULL)
+				uu_die(gettext("couldn't allocate memory"));
+			xmlFree(s);
+		}
+	}
+
+	pgs = tokenize(event, ",");
+
+	switch (tset = check_tokens(pgs)) {
+	case INVALID_TOKENS:
+		uu_die(gettext("Invalid input.\n"));
+		/*NOTREACHED*/
+	case MIXED_TOKENS:
+		semerr(gettext("Can't mix SMF and FMA event definitions\n"));
+		goto out;
+	case FMA_TOKENS:
+		/* make sure this is SCF_NOTIFY_PARAMS_INST */
+		if (entity->sc_etype != SVCCFG_INSTANCE_OBJECT ||
+		    strcmp(entity->sc_fmri, SCF_NOTIFY_PARAMS_INST) != 0) {
+			semerr(gettext(
+			    "Non-SMF transition evenst must go to %s\n"),
+			    SCF_NOTIFY_PARAMS_INST);
+			goto out;
+		}
+		pgname = safe_malloc(sz);
+		for (p = pgs; *p; ++p) {
+			if (snprintf(pgname, sz, "%s,%s", de_tag(*p),
+			    SCF_NOTIFY_PG_POSTFIX) >= sz)
+				uu_die(gettext("event name too long: %s\n"),
+				    *p);
+
+			lxml_get_event(entity, pgname, np);
+		}
+
+	default:	/* smf state transition tokens */
+		if (entity->sc_etype == SVCCFG_SERVICE_OBJECT &&
+		    strcmp(entity->sc_fmri, SCF_SERVICE_GLOBAL) == 0) {
+			semerr(gettext(
+			    "Can't set events for global service\n"));
+			goto out;
+		}
+		for (t = 0x1; t < SCF_STATE_ALL; t <<= 1) {
+			if (t & tset) {
+				lxml_get_event(entity, tset_to_string(t), np);
+			}
+			if ((t << 16) & tset) {
+				lxml_get_event(entity, tset_to_string(t << 16),
+				    np);
+			}
+		}
+	}
+
+	r = 0;
+out:
+	free(pgname);
+	free(pgs);
+	free(event);
+
+	return (r);
+}
+
 /*
  * Add a property containing the localized text from the manifest.  The
  * property is added to the property group at pg.  The name of the created
@@ -3042,6 +3271,10 @@
 			if (lxml_get_template(i, cursor) != 0)
 				return (-1);
 			break;
+		case SC_NOTIFICATION_PARAMETERS:
+			if (lxml_get_notification_parameters(i, cursor) != 0)
+				return (-1);
+			break;
 		default:
 			uu_die(gettext(
 			    "illegal element \"%s\" on instance \"%s\"\n"),
@@ -3244,6 +3477,10 @@
 			if (lxml_get_template(s, cursor) != 0)
 				return (-1);
 			break;
+		case SC_NOTIFICATION_PARAMETERS:
+			if (lxml_get_notification_parameters(s, cursor) != 0)
+				return (-1);
+			break;
 		case SC_STABILITY:
 			(void) lxml_get_entity_stability(s, cursor);
 			break;
--- a/usr/src/cmd/svc/svcs/Makefile	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/svc/svcs/Makefile	Fri Jul 30 17:04:17 2010 +1000
@@ -19,29 +19,34 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
+# Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
 #
 
 PROG =		svcs
 OBJS =		svcs.o explain.o
-SRCS =		$(OBJS:%.o=%.c)
+MYOBJS =	notify_params.o
+SRCS =		$(OBJS:%.o=%.c) $(MYOBJS:%.o=../common/%.c)
 POFILES = 	$(OBJS:.o=.po)
 
 include ../../Makefile.cmd
 include ../../Makefile.ctf
 
 POFILE =	$(PROG)_all.po
-LDLIBS +=	-lcontract -lscf -luutil -lumem
+LDLIBS +=	-lcontract -lscf -luutil -lumem -lnvpair
+CPPFLAGS += -I ../common
 
-lint := LINTFLAGS = -ux
+lint := LINTFLAGS = -mux
 
 .KEEP_STATE:
 
 all: $(PROG)
 
-$(PROG): $(OBJS)
-	$(LINK.c) -o $@ $(OBJS) $(LDLIBS)
+%.o: ../common/%.c
+	$(COMPILE.c) $(OUTPUT_OPTION) $<
+	$(POST_PROCESS_O)
+
+$(PROG): $(OBJS) $(MYOBJS)
+	$(LINK.c) -o $@ $(OBJS) $(MYOBJS) $(LDLIBS)
 	$(POST_PROCESS)
 
 $(POFILE): $(POFILES)
@@ -50,7 +55,7 @@
 install: all $(ROOTPROG)
 
 clean:
-	$(RM) $(OBJS)
+	$(RM) $(OBJS) $(MYOBJS)
 
 lint: lint_SRCS
 
--- a/usr/src/cmd/svc/svcs/svcs.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/svc/svcs/svcs.c	Fri Jul 30 17:04:17 2010 +1000
@@ -20,8 +20,7 @@
  */
 
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 /*
@@ -48,6 +47,7 @@
  */
 
 #include "svcs.h"
+#include "notify_params.h"
 
 /* Get the byteorder macros to ease sorting. */
 #include <sys/types.h>
@@ -68,6 +68,7 @@
 #include <libscf.h>
 #include <libscf_priv.h>
 #include <libuutil.h>
+#include <libnvpair.h>
 #include <locale.h>
 #include <procfs.h>
 #include <stdarg.h>
@@ -2545,6 +2546,230 @@
 	return (0);
 }
 
+int
+qsort_str_compare(const void *p1, const void *p2)
+{
+	return (strcmp((const char *)p1, (const char *)p2));
+}
+
+/*
+ * get_notify_param_classes()
+ * return the fma classes that don't have a tag in fma_tags[], otherwise NULL
+ */
+static char **
+get_notify_param_classes()
+{
+	scf_handle_t		*h = _scf_handle_create_and_bind(SCF_VERSION);
+	scf_instance_t		*inst = scf_instance_create(h);
+	scf_snapshot_t		*snap = scf_snapshot_create(h);
+	scf_snaplevel_t		*slvl = scf_snaplevel_create(h);
+	scf_propertygroup_t	*pg = scf_pg_create(h);
+	scf_iter_t		*iter = scf_iter_create(h);
+	int size = 4;
+	int n = 0;
+	size_t sz = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1;
+	int err;
+	char *pgname = safe_malloc(sz);
+	char **buf = safe_malloc(size * sizeof (char *));
+
+	if (h == NULL || inst == NULL || snap == NULL || slvl == NULL ||
+	    pg == NULL || iter == NULL) {
+		uu_die(gettext("Failed object creation: %s\n"),
+		    scf_strerror(scf_error()));
+	}
+
+	if (scf_handle_decode_fmri(h, SCF_NOTIFY_PARAMS_INST, NULL, NULL, inst,
+	    NULL, NULL, SCF_DECODE_FMRI_EXACT) != 0)
+		uu_die(gettext("Failed to decode %s: %s\n"),
+		    SCF_NOTIFY_PARAMS_INST, scf_strerror(scf_error()));
+
+	if (scf_instance_get_snapshot(inst, "running", snap) != 0)
+		uu_die(gettext("Failed to get snapshot: %s\n"),
+		    scf_strerror(scf_error()));
+
+	if (scf_snapshot_get_base_snaplevel(snap, slvl) != 0)
+		uu_die(gettext("Failed to get base snaplevel: %s\n"),
+		    scf_strerror(scf_error()));
+
+	if (scf_iter_snaplevel_pgs_typed(iter, slvl,
+	    SCF_NOTIFY_PARAMS_PG_TYPE) != 0)
+		uu_die(gettext("Failed to get iterator: %s\n"),
+		    scf_strerror(scf_error()));
+
+	while ((err = scf_iter_next_pg(iter, pg)) == 1) {
+		char *c;
+
+		if (scf_pg_get_name(pg, pgname, sz) == -1)
+			uu_die(gettext("Failed to get pg name: %s\n"),
+			    scf_strerror(scf_error()));
+		if ((c = strrchr(pgname, ',')) != NULL)
+			*c = '\0';
+		if (has_fma_tag(pgname))
+			continue;
+		if (!is_fma_token(pgname))
+			/*
+			 * We don't emmit a warning here so that we don't
+			 * pollute the output
+			 */
+			continue;
+
+		if (n + 1 >= size) {
+			size *= 2;
+			buf = realloc(buf, size * sizeof (char *));
+			if (buf == NULL)
+				uu_die(gettext("Out of memory.\n"));
+		}
+		buf[n] = safe_strdup(pgname);
+		++n;
+	}
+	/*
+	 * NULL terminate buf
+	 */
+	buf[n] = NULL;
+	if (err == -1)
+		uu_die(gettext("Failed to iterate pgs: %s\n"),
+		    scf_strerror(scf_error()));
+
+	/* sort the classes */
+	qsort((void *)buf, n, sizeof (char *), qsort_str_compare);
+
+	free(pgname);
+	scf_iter_destroy(iter);
+	scf_pg_destroy(pg);
+	scf_snaplevel_destroy(slvl);
+	scf_snapshot_destroy(snap);
+	scf_instance_destroy(inst);
+	scf_handle_destroy(h);
+
+	return (buf);
+}
+
+/*
+ * get_fma_notify_params()
+ * populates an nvlist_t with notifycation parameters for a given FMA class
+ * returns 0 if the nvlist is populated, 1 otherwise;
+ */
+int
+get_fma_notify_params(nvlist_t *nvl, const char *class)
+{
+	if (_scf_get_fma_notify_params(class, nvl, 0) != 0) {
+		/*
+		 * if the preferences have just been deleted
+		 * or does not exist, just skip.
+		 */
+		if (scf_error() != SCF_ERROR_NOT_FOUND &&
+		    scf_error() != SCF_ERROR_DELETED)
+			uu_warn(gettext(
+			    "Failed get_fma_notify_params %s\n"),
+			    scf_strerror(scf_error()));
+
+		return (1);
+	}
+
+	return (0);
+}
+
+/*
+ * print_notify_fma()
+ * outputs the notification paramets of FMA events.
+ * It first outputs classes in fma_tags[], then outputs the other classes
+ * sorted alphabetically
+ */
+static void
+print_notify_fma(void)
+{
+	nvlist_t *nvl;
+	char **tmp = NULL;
+	char **classes, *p;
+	const char *class;
+	uint32_t i;
+
+	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0)
+		uu_die(gettext("Out of memory.\n"));
+
+	for (i = 0; (class = get_fma_class(i)) != NULL; ++i) {
+		if (get_fma_notify_params(nvl, class) == 0)
+			listnotify_print(nvl, get_fma_tag(i));
+	}
+
+	if ((classes = get_notify_param_classes()) == NULL)
+		goto cleanup;
+
+	tmp = classes;
+	for (p = *tmp; p; ++tmp, p = *tmp) {
+		if (get_fma_notify_params(nvl, p) == 0)
+			listnotify_print(nvl, re_tag(p));
+
+		free(p);
+	}
+
+	free(classes);
+
+cleanup:
+	nvlist_free(nvl);
+}
+
+/*
+ * print_notify_fmri()
+ * prints notifycation parameters for an SMF instance.
+ */
+static void
+print_notify_fmri(const char *fmri)
+{
+	nvlist_t *nvl;
+
+	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0)
+		uu_die(gettext("Out of memory.\n"));
+
+	if (_scf_get_svc_notify_params(fmri, nvl, SCF_TRANSITION_ALL, 0, 0) !=
+	    SCF_SUCCESS) {
+		if (scf_error() != SCF_ERROR_NOT_FOUND &&
+		    scf_error() != SCF_ERROR_DELETED)
+			uu_warn(gettext(
+			    "Failed _scf_get_svc_notify_params: %s\n"),
+			    scf_strerror(scf_error()));
+	} else {
+		if (strcmp(SCF_INSTANCE_GLOBAL, fmri) == 0)
+			safe_printf(
+			    gettext("System wide notification parameters:\n"));
+		safe_printf("%s:\n", fmri);
+		listnotify_print(nvl, NULL);
+	}
+	nvlist_free(nvl);
+}
+
+/*
+ * print_notify_special()
+ * prints notification parameters for FMA events and system wide SMF state
+ * transitions parameters
+ */
+static void
+print_notify_special()
+{
+	safe_printf("Notification parameters for FMA Events\n");
+	print_notify_fma();
+	print_notify_fmri(SCF_INSTANCE_GLOBAL);
+}
+
+/*
+ * print_notify()
+ * callback function to print notification parameters for SMF state transition
+ * instances. It skips global and notify-params instances as they should be
+ * printed by print_notify_special()
+ */
+/* ARGSUSED */
+static int
+print_notify(void *unused, scf_walkinfo_t *wip)
+{
+	if (strcmp(SCF_INSTANCE_GLOBAL, wip->fmri) == 0 ||
+	    strcmp(SCF_NOTIFY_PARAMS_INST, wip->fmri) == 0)
+		return (0);
+
+	print_notify_fmri(wip->fmri);
+
+	return (0);
+}
+
 /*
  * Append a one-lined description of each process in inst's contract(s) and
  * return the augmented string.
@@ -3098,7 +3323,7 @@
 	int show_all = 0;
 	int show_header = 1;
 
-	const char * const options = "aHpvo:R:s:S:dDl?x";
+	const char * const options = "aHpvno:R:s:S:dDl?x";
 
 	(void) setlocale(LC_ALL, "");
 
@@ -3151,6 +3376,13 @@
 			opt_mode = opt;
 			break;
 
+		case 'n':
+			if (opt_mode != 0)
+				argserr(progname);
+
+			opt_mode = opt;
+			break;
+
 		case 'x':
 			if (opt_mode != 0)
 				argserr(progname);
@@ -3214,6 +3446,7 @@
 		case 'd':
 		case 'D':
 		case 'l':
+		case 'n':
 		case 'x':
 			assert(opt_mode == optopt);
 			break;
@@ -3268,6 +3501,18 @@
 		return (exit_status);
 	}
 
+	if (opt_mode == 'n') {
+		print_notify_special();
+		if ((err = scf_walk_fmri(h, argc, argv, SCF_WALK_MULTIPLE,
+		    print_notify, NULL, &exit_status, uu_warn)) != 0) {
+			uu_warn(gettext("failed to iterate over "
+			    "instances: %s\n"), scf_strerror(err));
+			exit_status = UU_EXIT_FATAL;
+		}
+
+		return (exit_status);
+	}
+
 	if (opt_mode == 'x') {
 		explain(opt_verbose, argc, argv);
 
@@ -3378,6 +3623,9 @@
 		free(provider_inst);
 		break;
 
+	case 'n':
+		break;
+
 	default:
 		assert(0);
 		abort();
--- a/usr/src/cmd/syseventd/sysevent.xml	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/cmd/syseventd/sysevent.xml	Fri Jul 30 17:04:17 2010 +1000
@@ -1,15 +1,13 @@
 <?xml version="1.0"?>
 <!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
 <!--
- Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
- Use is subject to license terms.
+ Copyright (c) 2004, 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, Version 1.0 only
- (the "License").  You may not use this file except in compliance
- with the License.
+ 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.
@@ -24,8 +22,6 @@
 
  CDDL HEADER END
 
-	ident	"%Z%%M%	%I%	%E% SMI"
-
 	NOTE:  This service manifest is not editable; its contents will
 	be overwritten by package or patch operations, including
 	operating system upgrade.  Make customizations in a different
@@ -60,6 +56,18 @@
 		<service_fmri value='svc:/milestone/single-user' />
 	</dependent>
 
+	<!--
+		The fmd service exists in all Solaris zones, but only
+		in the global zone (which syseventd is defined in) should
+		it be dependent on this service.
+	-->
+	<dependent
+		name='syseventd-fmd'
+		grouping='require_all'
+		restart_on='none'>
+		<service_fmri value='svc:/system/fmd' />
+	</dependent>
+
 	<exec_method
 	    type='method'
 	    name='start'
--- a/usr/src/common/nvpair/nvpair.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/common/nvpair/nvpair.c	Fri Jul 30 17:04:17 2010 +1000
@@ -20,8 +20,7 @@
  */
 
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #include <sys/stropts.h>
@@ -257,6 +256,12 @@
 	nvl->nvl_pad = 0;
 }
 
+uint_t
+nvlist_nvflag(nvlist_t *nvl)
+{
+	return (nvl->nvl_nvflag);
+}
+
 /*
  * nvlist_alloc - Allocate nvlist.
  */
--- a/usr/src/lib/fm/Makefile	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/lib/fm/Makefile	Fri Jul 30 17:04:17 2010 +1000
@@ -35,6 +35,8 @@
 	libfmd_msg \
 	libfmd_snmp \
 	libfmevent \
+	.WAIT \
+	libfmnotify \
 	topo
 
 sparc_SUBDIRS = \
--- a/usr/src/lib/fm/libfmd_msg/common/fmd_msg.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/lib/fm/libfmd_msg/common/fmd_msg.c	Fri Jul 30 17:04:17 2010 +1000
@@ -20,8 +20,7 @@
  */
 
 /*
- * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 /*
@@ -45,6 +44,8 @@
  *
  * fmd_msg_gettext_nv - format the entire message for the given event
  * fmd_msg_gettext_id - format the entire message for the given event code
+ * fmd_msg_gettext_key - format the entire message for the given dict for the
+ *                       given explicit message key
  *
  * fmd_msg_getitem_nv - format a single message item for the given event
  * fmd_msg_getitem_id - format a single message item for the given event code
@@ -99,6 +100,7 @@
 
 #include <alloca.h>
 #include <assert.h>
+#include <netdb.h>
 #include <pthread.h>
 #include <synch.h>
 #include <strings.h>
@@ -106,6 +108,7 @@
 #include <stdlib.h>
 #include <stdio.h>
 #include <errno.h>
+#include <unistd.h>
 #include <sys/sysmacros.h>
 
 #include <fmd_msg.h>
@@ -1130,6 +1133,125 @@
 }
 
 /*
+ * This is a private interface used by the notification daemons to parse tokens
+ * in user-supplied message templates.
+ */
+char *
+fmd_msg_decode_tokens(nvlist_t *nvl, const char *msg, const char *url)
+{
+	fmd_msg_buf_t buf;
+	wchar_t *h, *u, *w, *p, *q;
+
+	char *s, *expr, host[MAXHOSTNAMELEN + 1];
+	size_t elen;
+	int i;
+
+	u = fmd_msg_mbstowcs(url);
+
+	(void) gethostname(host, MAXHOSTNAMELEN + 1);
+	h = fmd_msg_mbstowcs(host);
+
+	if ((w = fmd_msg_mbstowcs(msg)) == NULL)
+		return (NULL);
+
+	/*
+	 * Now expand any escape sequences in the string, storing the final
+	 * text in 'buf' in wide-character format, and then convert it back
+	 * to multi-byte for return.  We expand the following sequences:
+	 *
+	 * %%   - literal % character
+	 * %h   - hostname
+	 * %s   - base URL for knowledge articles
+	 * %<x> - expression x in the current event, if any
+	 *
+	 * If an invalid sequence is present, it is elided so we can safely
+	 * reserve any future characters for other types of expansions.
+	 */
+	fmd_msg_buf_init(&buf);
+
+	for (q = w, p = w; (p = wcschr(p, L'%')) != NULL; q = p) {
+		if (p > q)
+			fmd_msg_buf_write(&buf, q, (size_t)(p - q));
+
+		switch (p[1]) {
+		case L'%':
+			fmd_msg_buf_write(&buf, p, 1);
+			p += 2;
+			break;
+
+		case L'h':
+			if (h != NULL)
+				fmd_msg_buf_write(&buf, h, wcslen(h));
+
+			p += 2;
+			break;
+
+		case L's':
+			if (u != NULL)
+				fmd_msg_buf_write(&buf, u, wcslen(u));
+
+			p += 2;
+			break;
+
+		case L'<':
+			q = p + 2;
+			p = wcschr(p + 2, L'>');
+
+			if (p == NULL)
+				goto eos;
+
+			/*
+			 * The expression in %< > must be an ASCII string: as
+			 * such allocate its length in bytes plus an extra
+			 * MB_CUR_MAX for slop if a multi-byte character is in
+			 * there, plus another byte for \0.  Since we move a
+			 * byte at a time, any multi-byte chars will just be
+			 * silently overwritten and fail to parse, which is ok.
+			 */
+			elen = (size_t)(p - q);
+			expr = malloc(elen + MB_CUR_MAX + 1);
+
+			if (expr == NULL) {
+				buf.fmb_error = ENOMEM;
+				goto eos;
+			}
+
+			for (i = 0; i < elen; i++)
+				(void) wctomb(&expr[i], q[i]);
+
+			expr[i] = '\0';
+
+			if (nvl != NULL)
+				(void) fmd_msg_nv_parse_nvname(&buf, nvl, expr);
+			else
+				fmd_msg_buf_printf(&buf, "%%<%s>", expr);
+
+			free(expr);
+			p++;
+			break;
+
+		case L'\0':
+			goto eos;
+
+		default:
+			p += 2;
+			break;
+		}
+	}
+eos:
+	fmd_msg_buf_write(&buf, q, wcslen(q) + 1);
+
+	free(h);
+	free(u);
+	free(w);
+
+	s = fmd_msg_buf_read(&buf);
+	fmd_msg_buf_fini(&buf);
+
+	return (s);
+}
+
+/*
  * This function is the main engine for formatting an entire event message.
  * It retrieves the master format string for an event, formats the individual
  * items, and then produces the final string composing all of the items.  The
@@ -1179,7 +1301,8 @@
 	if (nvlist_lookup_int64_array(nvl, FM_SUSPECT_DIAG_TIME,
 	    &tv, &tn) == 0 && tn == 2 && (sec = (time_t)tv[0]) != (time_t)-1 &&
 	    (tmp = localtime_r(&sec, &tm)) != NULL)
-		(void) strftime(date, sizeof (date), "%C", tmp);
+		(void) strftime(date, sizeof (date), "%a %b %e %H:%M:%S %Z %Y",
+		    tmp);
 	else
 		(void) strlcpy(date, FMD_MSG_MISSING, sizeof (date));
 
@@ -1358,6 +1481,63 @@
 	return (fmd_msg_getitem(h, locale, NULL, code, item));
 }
 
+char *
+fmd_msg_gettext_key(fmd_msg_hdl_t *h,
+    const char *locale, const char *dict, const char *key)
+{
+	char *old_b, *old_c, *p, *s;
+
+	fmd_msg_lock();
+
+	/*
+	 * If a non-default text domain binding was requested, save the old
+	 * binding perform the re-bind now that fmd_msg_lock() is held.
+	 */
+	if (h->fmh_binding != NULL) {
+		p = bindtextdomain(dict, NULL);
+		old_b = alloca(strlen(p) + 1);
+		(void) strcpy(old_b, p);
+		(void) bindtextdomain(dict, h->fmh_binding);
+	}
+
+	/*
+	 * Save the current locale string, and if we've been asked to fetch
+	 * the text for a different locale, switch locales now under the lock.
+	 */
+	p = setlocale(LC_ALL, NULL);
+	old_c = alloca(strlen(p) + 1);
+	(void) strcpy(old_c, p);
+
+	if (locale != NULL)
+		(void) setlocale(LC_ALL, locale);
+
+	/*
+	 * First attempt to fetch the string in the current locale.  If this
+	 * fails and we're in a non-default locale, attempt to fall back to the
+	 * C locale and try again.  If it still fails then we return NULL and
+	 * set errno.
+	 */
+	if ((s = dgettext(dict, key)) == key &&
+	    (locale != NULL || strcmp(h->fmh_locale, "C") != 0)) {
+		(void) setlocale(LC_ALL, "C");
+		locale = "C"; /* restore locale */
+
+		if ((s = dgettext(dict, key)) == key) {
+			s = NULL;
+			errno = ENOENT;
+		}
+	}
+	if (locale != NULL)
+		(void) setlocale(LC_ALL, old_c);
+
+	if (h->fmh_binding != NULL)
+		(void) bindtextdomain(dict, old_b);
+
+	fmd_msg_unlock();
+
+	return (s);
+}
+
 /*
  * Common code for fmd_msg_gettext_nv() and fmd_msg_gettext_id(): this function
  * handles locking, changing locales and domains, and restoring i18n state.
--- a/usr/src/lib/fm/libfmd_msg/common/fmd_msg.h	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/lib/fm/libfmd_msg/common/fmd_msg.h	Fri Jul 30 17:04:17 2010 +1000
@@ -20,8 +20,7 @@
  */
 
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #ifndef	_FMD_MSG_H
@@ -73,7 +72,9 @@
 
 extern char *fmd_msg_gettext_nv(fmd_msg_hdl_t *, const char *, nvlist_t *);
 extern char *fmd_msg_gettext_id(fmd_msg_hdl_t *, const char *, const char *);
-
+extern char *fmd_msg_gettext_key(fmd_msg_hdl_t *, const char *, const char *,
+    const char *);
+extern char *fmd_msg_decode_tokens(nvlist_t *, const char *, const char *);
 extern char *fmd_msg_getitem_nv(fmd_msg_hdl_t *,
     const char *, nvlist_t *, fmd_msg_item_t);
 
--- a/usr/src/lib/fm/libfmd_msg/common/mapfile-vers	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/lib/fm/libfmd_msg/common/mapfile-vers	Fri Jul 30 17:04:17 2010 +1000
@@ -38,11 +38,13 @@
 
 SYMBOL_VERSION SUNWprivate {
     global:
+	fmd_msg_decode_tokens;
 	fmd_msg_fini;
 	fmd_msg_getitem_id;
 	fmd_msg_getitem_nv;
 	fmd_msg_gettext_id;
 	fmd_msg_gettext_nv;
+	fmd_msg_gettext_key;
 	fmd_msg_init;
 	fmd_msg_locale_get;
 	fmd_msg_locale_set;
--- a/usr/src/lib/fm/libfmd_snmp/Makefile	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/lib/fm/libfmd_snmp/Makefile	Fri Jul 30 17:04:17 2010 +1000
@@ -19,8 +19,7 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
+# Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
 #
 #
 
@@ -33,7 +32,7 @@
 SUBDIRS = $(MACH) 
 $(BUILD64)SUBDIRS += $(MACH64)
 
-MIBFILES = SUN-FM-MIB.mib
+MIBFILES = SUN-FM-MIB.mib SUN-IREPORT-MIB.mib
 
 ROOTNETSNMPMIBDIR = $(ROOT)/etc/net-snmp/snmp/mibs
 ROOTMIBS = $(MIBFILES:%=$(ROOTNETSNMPMIBDIR)/%)
--- a/usr/src/lib/fm/libfmd_snmp/common/fmd_snmp.h	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/lib/fm/libfmd_snmp/common/fmd_snmp.h	Fri Jul 30 17:04:17 2010 +1000
@@ -20,14 +20,11 @@
  */
 
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
-#ifndef _SUNFM_H
-#define	_SUNFM_H
-
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
+#ifndef _FMD_SNMP_H
+#define	_FMD_SNMP_H
 
 #ifdef	__cplusplus
 extern "C" {
@@ -35,7 +32,7 @@
 
 /*
  * These values are derived from, and must remain consistent with, the
- * MIB definitions.
+ * MIB definitions in SUN-FM-MIB.
  */
 #define	MODNAME_STR	"sunFM"
 #define	SUNFM_OID	1, 3, 6, 1, 4, 1, 42, 2, 195, 1
@@ -114,10 +111,30 @@
 
 #define	SNMP_URL_MSG	"snmp-url"
 
+/*
+ * Definitions from SUN-IREPORT-MIB
+ */
+#define	SUNIREPORT_OID	1, 3, 6, 1, 4, 1, 42, 2, 197, 1
+
+#define	SUNIREPORTNOTIFICATIONENTRY	SUNIREPORT_OID, 1
+
+#define	SUNIREPORTHOSTNAME_OID		SUNIREPORTNOTIFICATIONENTRY, 1
+#define	SUNIREPORTMSGID_OID		SUNIREPORTNOTIFICATIONENTRY, 2
+#define	SUNIREPORTDESCRIPTION_OID	SUNIREPORTNOTIFICATIONENTRY, 3
+#define	SUNIREPORTTIME_OID		SUNIREPORTNOTIFICATIONENTRY, 4
+#define	SUNIREPORTSMFFMRI_OID		SUNIREPORTNOTIFICATIONENTRY, 5
+#define	SUNIREPORTSMFFROMSTATE_OID	SUNIREPORTNOTIFICATIONENTRY, 6
+#define	SUNIREPORTSMFTOSTATE_OID	SUNIREPORTNOTIFICATIONENTRY, 7
+#define	SUNIREPORTTRANSITIONREASON_OID	SUNIREPORTNOTIFICATIONENTRY, 8
+
+#define	SUNIREPORTTRAPS_OID		SUNIREPORT_OID, 2, 0
+#define	SUNIREPORTTRAP_OID		SUNIREPORTTRAPS_OID, 1
+
+
 extern int	init_sunFM(void);
 
 #ifdef	__cplusplus
 }
 #endif
 
-#endif	/* _SUNFM_H */
+#endif	/* _FMD_SNMP_H */
--- a/usr/src/lib/fm/libfmd_snmp/mibs/SUN-FM-MIB.mib	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/lib/fm/libfmd_snmp/mibs/SUN-FM-MIB.mib	Fri Jul 30 17:04:17 2010 +1000
@@ -20,12 +20,9 @@
 --
 
 --
--- Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
--- Use is subject to license terms.
+-- Copyright (c) 2008, 2010,  Oracle and/or its affiliates. All rights reserved.
 --
 
--- ident	"%Z%%M%	%I%	%E% SMI"
-
 SUN-FM-MIB DEFINITIONS ::= BEGIN
 
 IMPORTS
@@ -42,21 +39,20 @@
 
 sunFmMIB MODULE-IDENTITY
 	LAST-UPDATED	"200808040000Z"
-	ORGANIZATION	"Sun Microsystems, Inc."
-	CONTACT-INFO	"Sun Microsystems, Inc.
-			 4150 Network Circle
-			 Santa Clara, CA 95054
+	ORGANIZATION	"Oracle Corporation"
+	CONTACT-INFO	"Oracle Corporation
+			 500 Oracle Parkway
+			 Redwood Shores, CA 94065
 
-			 1-800-555-9SUN or
-			 1-650-960-1300
+			 1.650.506.7000 or
+			 1.800.392.2999
 
-			 http://www.sun.com
+			 http://www.oracle.com
 			 or contact your local support representative"
 	DESCRIPTION
-		"Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
-		 Use is subject to license terms.
+		"Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
 
-		MIB providing access to Sun Fault Manager information"
+		MIB providing access to Oracle Fault Manager information"
 	REVISION	"200808040000Z"
 	DESCRIPTION	"Version: 1.1"
 	::= { fm 1 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/fm/libfmd_snmp/mibs/SUN-IREPORT-MIB.mib	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,204 @@
+--
+-- 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.
+--
+
+SUN-IREPORT-MIB DEFINITIONS ::= BEGIN
+
+IMPORTS
+	products
+		FROM SUN-MIB
+	Gauge32, Unsigned32, OBJECT-TYPE, NOTIFICATION-TYPE, MODULE-IDENTITY
+		FROM SNMPv2-SMI
+	TEXTUAL-CONVENTION, DateAndTime, DisplayString
+		FROM SNMPv2-TC
+	OBJECT-GROUP, NOTIFICATION-GROUP
+		FROM SNMPv2-CONF
+	URLString
+		FROM NETWORK-SERVICES-MIB;
+
+sunIreportMIB MODULE-IDENTITY
+	LAST-UPDATED	"201007220000Z" -- July 22, 2010
+	ORGANIZATION	"Oracle Corporation"
+	CONTACT-INFO	"Oracle Corporation
+			 500 Oracle Parkway
+			 Redwood Shores, CA 94065
+
+			 1.650.506.7000 or
+			 1.800.392.2999
+
+			 http://www.oracle.com
+			 or contact your local support representative"
+	DESCRIPTION
+		"Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+
+		MIB providing access to Oracle Solaris Fault Management
+		Informational Report Notifications"
+		
+	REVISION	"201007220000Z" -- July 22, 2010
+	DESCRIPTION	"Version: 1.0"
+	::= { ireport 1 }
+
+SunIreportSmfFmriString ::= TEXTUAL-CONVENTION
+	STATUS		current
+	DESCRIPTION
+		"Represents the FMRI of an SMF service"
+	SYNTAX		OCTET STRING (SIZE (0..1023))
+
+SunIreportSmfState ::= TEXTUAL-CONVENTION
+	STATUS		current
+	DESCRIPTION
+		"Represents an SMF service state"
+	SYNTAX	INTEGER {
+		offline(0),
+		online(1),
+		degraded(2),
+		disabled(3),
+		maintenance(4),
+		uninitialized(5)
+	}
+
+ireport OBJECT IDENTIFIER ::= { products 197 }
+
+sunIreportNotification       OBJECT-TYPE
+    SYNTAX      SunIreportNotificationEntry
+    MAX-ACCESS  accessible-for-notify
+    STATUS      current
+    DESCRIPTION
+		"Solaris informational event notification"
+	::= { sunIreportMIB 1 }
+
+SunIreportNotificationEntry ::= SEQUENCE {
+ 	sunIreportHostname		DisplayString,
+	sunIreportMsgid			DisplayString,
+	sunIreportDescription		DisplayString,
+	sunIreportTime			DateAndTime,
+	sunIreportSmfFMRI		SunIreportSmfFmriString,
+	sunIreportSmfFromState		SunIreportSmfState,
+	sunIreportSmfToState		SunIreportSmfState,
+	sunIreportSmfTransitionReason	DisplayString
+}
+
+sunIreportHostname OBJECT-TYPE
+	SYNTAX		DisplayString
+	MAX-ACCESS	accessible-for-notify
+	STATUS		current
+	DESCRIPTION
+		"Hostname of the system on which the event occurred"
+	::= { sunIreportNotification 1 }
+
+sunIreportMsgid OBJECT-TYPE
+	SYNTAX		DisplayString
+	MAX-ACCESS	accessible-for-notify
+	STATUS		current
+	DESCRIPTION
+		"Message ID of Knowledge Article associated with this event"
+	::= { sunIreportNotification 2 }
+
+sunIreportDescription OBJECT-TYPE
+	SYNTAX		DisplayString
+	MAX-ACCESS	accessible-for-notify
+	STATUS		current
+	DESCRIPTION
+		"Description of the event"
+	::= { sunIreportNotification 3 }
+
+sunIreportTime OBJECT-TYPE
+	SYNTAX		DateAndTime
+	MAX-ACCESS	accessible-for-notify
+	STATUS		current
+	DESCRIPTION
+		"Timestamp of the event"
+	::= { sunIreportNotification 4 }
+
+sunIreportSmfFMRI OBJECT-TYPE
+	SYNTAX		SunIreportSmfFmriString
+	MAX-ACCESS	accessible-for-notify
+	STATUS		current
+	DESCRIPTION
+		"FMRI of the SMF service asssociated with this event"
+	::= { sunIreportNotification 5 }
+
+sunIreportSmfFromState OBJECT-TYPE
+	SYNTAX		SunIreportSmfState
+	MAX-ACCESS	accessible-for-notify
+	STATUS		current
+	DESCRIPTION
+		"Previous state of the service that transitioned"
+	::= { sunIreportNotification 6 }
+
+sunIreportSmfToState OBJECT-TYPE
+	SYNTAX		SunIreportSmfState
+	MAX-ACCESS	accessible-for-notify
+	STATUS		current
+	DESCRIPTION
+		"Final state of the service that transitioned"
+	::= { sunIreportNotification 7 }
+
+sunIreportSmfTransitionReason OBJECT-TYPE
+	SYNTAX		DisplayString
+	MAX-ACCESS	accessible-for-notify
+	STATUS		current
+	DESCRIPTION
+		"Reason for the state transition"
+	::= { sunIreportNotification 8 }
+
+
+--
+-- RFC 3584 requires that the next-to-last sub-ID be zero to allow for
+-- mapping v2/v3 notifications to v1 traps.
+--
+
+sunIreportTraps OBJECT IDENTIFIER ::= { sunIreportMIB 2 0 }
+
+sunIreportTrap NOTIFICATION-TYPE
+	OBJECTS	{
+		sunIreportHostname,
+		sunIreportMsgid,
+		sunIreportDescription,
+		sunIreportTime,
+		sunIreportSmfFMRI,
+		sunIreportSmfFromState,
+		sunIreportSmfToState,
+		sunIreportSmfTransitionReason
+	}
+	STATUS	current
+	DESCRIPTION
+		"Trap notification that a Solaris informational report has
+		occurred.
+		
+		The last four entries in the trap will only be set for SMF
+		service state transition (STN) events.  The following values for
+		sunIreportMsgid correspond to an STN event:
+		
+		SMF-8000-SR
+		SMF-8000-TC
+		SMF-8000-UQ
+		SMF-8000-VE
+		SMF-8000-WJ
+		SMF-8000-X2"
+
+	::= { sunIreportTraps 1 }
+
+END
+
--- a/usr/src/lib/fm/libfmevent/Makefile	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/lib/fm/libfmevent/Makefile	Fri Jul 30 17:04:17 2010 +1000
@@ -19,14 +19,13 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
+# Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
 #
 
 include ../../Makefile.lib
 include ../Makefile.lib
 
-FMHDRS = libfmevent.h
+FMHDRS = libfmevent.h libfmevent_ruleset.h
 HDRDIR = common
 
 SUBDIRS = $(MACH) 
--- a/usr/src/lib/fm/libfmevent/Makefile.com	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/lib/fm/libfmevent/Makefile.com	Fri Jul 30 17:04:17 2010 +1000
@@ -19,8 +19,7 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
+# Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
 #
 
 LIBRARY = libfmevent.a
@@ -29,7 +28,8 @@
 LIBSRCS = fmev_subscribe.c \
 	fmev_evaccess.c \
 	fmev_errstring.c \
-	fmev_util.c
+	fmev_util.c \
+	fmev_publish.c
 
 OBJECTS = $(LIBSRCS:%.c=%.o)
 
@@ -41,12 +41,15 @@
 
 SRCDIR =	../common
 
+C99MODE = $(C99_ENABLE)
+
 CPPFLAGS += -I../common -I.
 $(NOT_RELEASE_BUILD)CPPFLAGS += -DDEBUG
 
 CFLAGS += $(CCVERBOSE) $(C_BIGPICFLAGS)
 CFLAGS64 += $(CCVERBOSE) $(C_BIGPICFLAGS)
-LDLIBS += -lumem -lnvpair -luutil -lsysevent -lc
+$(DYNLIB) := LDLIBS += -lumem -lnvpair -luutil -lsysevent -L$(ROOTLIBDIR) \
+	-ltopo -lc
 
 LINTFLAGS = -msux
 LINTFLAGS64 = -msux -m64
--- a/usr/src/lib/fm/libfmevent/amd64/Makefile	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/lib/fm/libfmevent/amd64/Makefile	Fri Jul 30 17:04:17 2010 +1000
@@ -19,11 +19,12 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
+# Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
 #
 
 include ../Makefile.com
 include ../../../Makefile.lib.64
 
+DYNFLAGS += -R/usr/lib/fm/$(MACH64)
+
 install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64)
--- a/usr/src/lib/fm/libfmevent/common/fmev_channels.h	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/lib/fm/libfmevent/common/fmev_channels.h	Fri Jul 30 17:04:17 2010 +1000
@@ -20,8 +20,7 @@
  */
 
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #ifndef _FMEV_CHANNELS_H
@@ -39,8 +38,23 @@
 extern "C" {
 #endif
 
+/*
+ * Channel that fmd forwards protocol events on, feeding the subscription
+ * aspect of libfmevent.
+ */
 #define	FMD_SNOOP_CHANNEL	"com.sun:fm:protocol_snoop"
 
+/*
+ * Channels on which published events are dispatched towards fmd for
+ * processing into full protocol events.
+ */
+#define	FMEV_CHAN_USER_PRIV_HV		"com.sun:fm:user_priv_highval"
+#define	FMEV_CHAN_USER_PRIV_LV		"com.sun:fm:user_priv_lowval"
+#define	FMEV_CHAN_USER_NOPRIV_HV	"com.sun:fm:user_nopriv_highval"
+#define	FMEV_CHAN_USER_NOPRIV_LV	"com.sun:fm:user_nopriv_lowval"
+#define	FMEV_CHAN_KERNEL_HV		"com.sun:fm:kernel_highval"
+#define	FMEV_CHAN_KERNEL_LV		"com.sun:fm:kernel_lowval"
+
 #ifdef __cplusplus
 }
 #endif
--- a/usr/src/lib/fm/libfmevent/common/fmev_evaccess.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/lib/fm/libfmevent/common/fmev_evaccess.c	Fri Jul 30 17:04:17 2010 +1000
@@ -20,8 +20,7 @@
  */
 
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 /*
@@ -38,9 +37,8 @@
 
 #include "fmev_impl.h"
 
-#define	API_ENTERV1(iep) \
-	((void) fmev_api_enter(fmev_shdl_cmn(((iep)->ei_hdl)), \
-	LIBFMEVENT_VERSION_1))
+#define	FMEV_API_ENTER(iep, v) \
+	fmev_api_enter(fmev_shdl_cmn(((iep)->ei_hdl)), LIBFMEVENT_VERSION_##v)
 
 typedef struct {
 	uint32_t ei_magic;		/* _FMEVMAGIC */
@@ -137,7 +135,7 @@
 
 	ASSERT(EVENT_VALID(iep));
 
-	API_ENTERV1(iep);
+	(void) FMEV_API_ENTER(iep, 1);
 
 	atomic_inc_32(&iep->ei_refcnt);
 }
@@ -149,7 +147,7 @@
 
 	ASSERT(EVENT_VALID(iep));
 
-	API_ENTERV1(iep);
+	(void) FMEV_API_ENTER(iep, 1);
 
 	if (atomic_dec_32_nv(&iep->ei_refcnt) == 0)
 		fmev_free(iep);
@@ -163,7 +161,8 @@
 
 	ASSERT(EVENT_VALID(iep));
 
-	API_ENTERV1(iep);
+	if (!FMEV_API_ENTER(iep, 1))
+		return (NULL);	/* fmev_errno set */
 
 	if (ev == NULL) {
 		(void) fmev_seterr(FMEVERR_API);
@@ -194,7 +193,8 @@
 
 	ASSERT(EVENT_VALID(iep));
 
-	API_ENTERV1(iep);
+	if (!FMEV_API_ENTER(iep, 1))
+		return (NULL);	/* fmev_errno set */
 
 	if (ev == NULL) {
 		(void) fmev_seterr(FMEVERR_API);
@@ -215,7 +215,8 @@
 
 	ASSERT(EVENT_VALID(iep));
 
-	API_ENTERV1(iep);
+	if (!FMEV_API_ENTER(iep, 1))
+		return (NULL);	/* fmev_errno set */
 
 	if (ev == NULL) {
 		(void) fmev_seterr(FMEVERR_API);
@@ -238,7 +239,8 @@
 	uint64_t timetlimit;
 
 	ASSERT(EVENT_VALID(iep));
-	API_ENTERV1(iep);
+	if (!FMEV_API_ENTER(iep, 1))
+		return (fmev_errno);
 
 #ifdef	_LP64
 	timetlimit = INT64_MAX;
@@ -275,3 +277,14 @@
 	seconds = (time_t)fmev_time_sec(ev);
 	return (localtime_r(&seconds, tm));
 }
+
+fmev_shdl_t
+fmev_ev2shdl(fmev_t ev)
+{
+	fmev_impl_t *iep = FMEV2IMPL(ev);
+
+	if (!FMEV_API_ENTER(iep, 2))
+		return (NULL);
+
+	return (iep->ei_hdl);
+}
--- a/usr/src/lib/fm/libfmevent/common/fmev_impl.h	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/lib/fm/libfmevent/common/fmev_impl.h	Fri Jul 30 17:04:17 2010 +1000
@@ -20,8 +20,7 @@
  */
 
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #ifndef _FMEV_IMPL_H
@@ -44,6 +43,9 @@
 #include <libuutil.h>
 #include <libsysevent.h>
 #include <fm/libfmevent.h>
+#include <fm/libtopo.h>
+
+#include "fmev_channels.h"
 
 #ifdef DEBUG
 #define	ASSERT(x) (assert(x))
@@ -59,8 +61,14 @@
 	void (*hc_free)(void *, size_t);
 };
 
+#define	_FMEV_SHMAGIC	0x5368446c	/* ShDl */
+
 struct fmev_hdl_cmn *fmev_shdl_cmn(fmev_shdl_t);
 
+extern void *dflt_alloc(size_t);
+extern void *dflt_zalloc(size_t);
+extern void dflt_free(void *, size_t);
+
 extern int fmev_api_init(struct fmev_hdl_cmn *);
 extern int fmev_api_enter(struct fmev_hdl_cmn *, uint32_t);
 extern void fmev_api_freetsd(void);
@@ -68,6 +76,7 @@
 extern int fmev_shdl_valid(fmev_shdl_t);
 extern fmev_t fmev_sysev2fmev(fmev_shdl_t, sysevent_t *sep, char **,
     nvlist_t **);
+extern topo_hdl_t *fmev_topohdl(fmev_shdl_t);
 
 #ifdef __cplusplus
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/fm/libfmevent/common/fmev_publish.c	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,536 @@
+/*
+ * 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.
+ */
+
+/*
+ * Simple-minded raw event publication from user context.  See extensive
+ * comments in libfmevent.h.  These interfaces remain Project Private -
+ * they have to evolve before rollout to Public levels.
+ *
+ * Events are dispatched synchronously using the GPEC sysevent mechanism.
+ * The caller context must therefore be one in which a sysevent_evc_publish
+ * (and possibly sysevent_evc_bind if not already bound) is safe.  We will
+ * also allocate and manipulate nvlists.
+ *
+ * Since we use GPEC, which has no least privilege awareness, these interfaces
+ * will only work for would-be producers running as root.
+ *
+ * There is no event rate throttling applied, so we rely on producers
+ * to throttle themselves.  A future refinement should apply mandatory
+ * but tuneable throttling on a per-producer basis.  In this first version
+ * the only throttle is the publication event queue depth - we'll drop
+ * events when the queue is full.
+ *
+ * We can publish over four channels, for privileged/non-privileged and
+ * high/low priority.  Since only privileged producers will work now
+ * (see above) we hardcode priv == B_TRUE and so only two channels are
+ * actually used, separating higher and lower value streams from privileged
+ * producers.
+ */
+
+#include <stdarg.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <atomic.h>
+#include <errno.h>
+#include <pthread.h>
+#include <strings.h>
+
+#include "fmev_impl.h"
+
+static struct {
+	const char *name;		/* channel name */
+	evchan_t *binding;		/* GPEC binding, once bound */
+	const uint32_t flags;		/* flags to use in binding */
+} chaninfo[] = {
+	{ FMEV_CHAN_USER_NOPRIV_LV, NULL, 0 },
+	{ FMEV_CHAN_USER_NOPRIV_HV, NULL, 0 },
+	{ FMEV_CHAN_USER_PRIV_LV, NULL, EVCH_HOLD_PEND_INDEF },
+	{ FMEV_CHAN_USER_PRIV_HV, NULL, EVCH_HOLD_PEND_INDEF}
+};
+
+#define	CHANIDX(priv, pri) (2 * ((priv) != 0) + (pri == FMEV_HIPRI))
+
+#define	CHAN_NAME(priv, pri) (chaninfo[CHANIDX(priv, pri)].name)
+#define	CHAN_BINDING(priv, pri) (chaninfo[CHANIDX(priv, pri)].binding)
+#define	CHAN_FLAGS(priv, pri) (chaninfo[CHANIDX(priv, pri)].flags)
+
+/*
+ * Called after fork in the new child.  We clear the cached event
+ * channel bindings which are only valid in the process that created
+ * them.
+ */
+static void
+clear_bindings(void)
+{
+	int i;
+
+	for (i = 0; i < sizeof (chaninfo) / sizeof chaninfo[0]; i++)
+		chaninfo[i].binding = NULL;
+}
+
+#pragma init(_fmev_publish_init)
+
+static void
+_fmev_publish_init(void)
+{
+	(void) pthread_atfork(NULL, NULL, clear_bindings);
+}
+
+static evchan_t *
+bind_channel(boolean_t priv, fmev_pri_t pri)
+{
+	evchan_t **evcpp = &CHAN_BINDING(priv, pri);
+	evchan_t *evc;
+
+	if (*evcpp != NULL)
+		return (*evcpp);
+
+	if (sysevent_evc_bind(CHAN_NAME(priv, pri), &evc,
+	    EVCH_CREAT | CHAN_FLAGS(priv, pri)) != 0)
+		return (NULL);
+
+	if (atomic_cas_ptr(evcpp, NULL, evc) != NULL)
+		(void) sysevent_evc_unbind(evc);
+
+	return (*evcpp);
+}
+
+static fmev_err_t
+vrfy_ruleset(const char *ruleset)
+{
+	if (ruleset != NULL &&
+	    strnlen(ruleset, FMEV_MAX_RULESET_LEN) == FMEV_MAX_RULESET_LEN)
+		return (FMEVERR_STRING2BIG);
+
+	return (FMEV_OK);
+
+}
+
+static fmev_err_t
+vrfy_class(const char *class)
+{
+	if (class == NULL || *class == '\0')
+		return (FMEVERR_API);
+
+	if (strnlen(class, FMEV_PUB_MAXCLASSLEN) == FMEV_PUB_MAXCLASSLEN)
+		return (FMEVERR_STRING2BIG);
+
+	return (FMEV_OK);
+}
+
+static fmev_err_t
+vrfy_subclass(const char *subclass)
+{
+	if (subclass == NULL || *subclass == '\0')
+		return (FMEVERR_API);
+
+	if (strnlen(subclass, FMEV_PUB_MAXSUBCLASSLEN) ==
+	    FMEV_PUB_MAXSUBCLASSLEN)
+		return (FMEVERR_STRING2BIG);
+
+	return (FMEV_OK);
+}
+
+static fmev_err_t
+vrfy_pri(fmev_pri_t pri)
+{
+	return (pri == FMEV_LOPRI || pri == FMEV_HIPRI ?
+	    FMEV_OK : FMEVERR_API);
+}
+
+const char *
+fmev_pri_string(fmev_pri_t pri)
+{
+	static const char *pristr[] = { "low", "high" };
+
+	if (vrfy_pri(pri) != FMEV_OK)
+		return (NULL);
+
+	return (pristr[pri - FMEV_LOPRI]);
+}
+
+static fmev_err_t
+vrfy(const char **rulesetp, const char **classp, const char **subclassp,
+    fmev_pri_t *prip)
+{
+	fmev_err_t rc = FMEV_OK;
+
+	if (rulesetp && (rc = vrfy_ruleset(*rulesetp)) != FMEV_OK)
+		return (rc);
+
+	if (classp && (rc = vrfy_class(*classp)) != FMEV_OK ||
+	    subclassp && (rc = vrfy_subclass(*subclassp)) != FMEV_OK ||
+	    prip && (rc = vrfy_pri(*prip)) != FMEV_OK)
+		return (rc);
+
+	return (FMEV_OK);
+}
+
+uint_t fmev_va2nvl_maxtuples = 100;
+
+fmev_err_t
+va2nvl(nvlist_t **nvlp, va_list ap, uint_t ntuples)
+{
+	nvlist_t *nvl = NULL;
+	uint_t processed = 0;
+	char *name;
+
+	if (ntuples == 0)
+		return (FMEVERR_INTERNAL);
+
+	if ((name = va_arg(ap, char *)) == NULL || name == FMEV_ARG_TERM)
+		return (FMEVERR_VARARGS_MALFORMED);
+
+	if (ntuples > fmev_va2nvl_maxtuples)
+		return (FMEVERR_VARARGS_TOOLONG);
+
+	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0)
+		return (FMEVERR_ALLOC);
+
+	while (name != NULL && name != FMEV_ARG_TERM && processed <= ntuples) {
+		data_type_t type;
+		int err, nelem;
+
+		type = va_arg(ap, data_type_t);
+
+		switch (type) {
+		case DATA_TYPE_BYTE:
+			err = nvlist_add_byte(nvl, name,
+			    va_arg(ap, uint_t));
+			break;
+		case DATA_TYPE_BYTE_ARRAY:
+			nelem = va_arg(ap, int);
+			err = nvlist_add_byte_array(nvl, name,
+			    va_arg(ap, uchar_t *), nelem);
+			break;
+		case DATA_TYPE_BOOLEAN_VALUE:
+			err = nvlist_add_boolean_value(nvl, name,
+			    va_arg(ap, boolean_t));
+			break;
+		case DATA_TYPE_BOOLEAN_ARRAY:
+			nelem = va_arg(ap, int);
+			err = nvlist_add_boolean_array(nvl, name,
+			    va_arg(ap, boolean_t *), nelem);
+			break;
+		case DATA_TYPE_INT8:
+			err = nvlist_add_int8(nvl, name,
+			    va_arg(ap, int));
+			break;
+		case DATA_TYPE_INT8_ARRAY:
+			nelem = va_arg(ap, int);
+			err = nvlist_add_int8_array(nvl, name,
+			    va_arg(ap, int8_t *), nelem);
+			break;
+		case DATA_TYPE_UINT8:
+			err = nvlist_add_uint8(nvl, name,
+			    va_arg(ap, uint_t));
+			break;
+		case DATA_TYPE_UINT8_ARRAY:
+			nelem = va_arg(ap, int);
+			err = nvlist_add_uint8_array(nvl, name,
+			    va_arg(ap, uint8_t *), nelem);
+			break;
+		case DATA_TYPE_INT16:
+			err = nvlist_add_int16(nvl, name,
+			    va_arg(ap, int));
+			break;
+		case DATA_TYPE_INT16_ARRAY:
+			nelem = va_arg(ap, int);
+			err = nvlist_add_int16_array(nvl, name,
+			    va_arg(ap, int16_t *), nelem);
+			break;
+		case DATA_TYPE_UINT16:
+			err = nvlist_add_uint16(nvl, name,
+			    va_arg(ap, uint_t));
+			break;
+		case DATA_TYPE_UINT16_ARRAY:
+			nelem = va_arg(ap, int);
+			err = nvlist_add_uint16_array(nvl, name,
+			    va_arg(ap, uint16_t *), nelem);
+			break;
+		case DATA_TYPE_INT32:
+			err = nvlist_add_int32(nvl, name,
+			    va_arg(ap, int32_t));
+			break;
+		case DATA_TYPE_INT32_ARRAY:
+			nelem = va_arg(ap, int);
+			err = nvlist_add_int32_array(nvl, name,
+			    va_arg(ap, int32_t *), nelem);
+			break;
+		case DATA_TYPE_UINT32:
+			err = nvlist_add_uint32(nvl, name,
+			    va_arg(ap, uint32_t));
+			break;
+		case DATA_TYPE_UINT32_ARRAY:
+			nelem = va_arg(ap, int);
+			err = nvlist_add_uint32_array(nvl, name,
+			    va_arg(ap, uint32_t *), nelem);
+			break;
+		case DATA_TYPE_INT64:
+			err = nvlist_add_int64(nvl, name,
+			    va_arg(ap, int64_t));
+			break;
+		case DATA_TYPE_INT64_ARRAY:
+			nelem = va_arg(ap, int);
+			err = nvlist_add_int64_array(nvl, name,
+			    va_arg(ap, int64_t *), nelem);
+			break;
+		case DATA_TYPE_UINT64:
+			err = nvlist_add_uint64(nvl, name,
+			    va_arg(ap, uint64_t));
+			break;
+		case DATA_TYPE_UINT64_ARRAY:
+			nelem = va_arg(ap, int);
+			err = nvlist_add_uint64_array(nvl, name,
+			    va_arg(ap, uint64_t *), nelem);
+			break;
+		case DATA_TYPE_STRING:
+			err = nvlist_add_string(nvl, name,
+			    va_arg(ap, char *));
+			break;
+		case DATA_TYPE_STRING_ARRAY:
+			nelem = va_arg(ap, int);
+			err = nvlist_add_string_array(nvl, name,
+			    va_arg(ap, char **), nelem);
+			break;
+		case DATA_TYPE_NVLIST:
+			err = nvlist_add_nvlist(nvl, name,
+			    va_arg(ap, nvlist_t *));
+			break;
+		case DATA_TYPE_NVLIST_ARRAY:
+			nelem = va_arg(ap, int);
+			err = nvlist_add_nvlist_array(nvl, name,
+			    va_arg(ap, nvlist_t **), nelem);
+			break;
+		case DATA_TYPE_HRTIME:
+			err = nvlist_add_hrtime(nvl, name,
+			    va_arg(ap, hrtime_t));
+			break;
+		case DATA_TYPE_DOUBLE:
+			err = nvlist_add_double(nvl, name,
+			    va_arg(ap, double));
+			break;
+		default:
+			err = EINVAL;
+		}
+
+		if (err)
+			break;	/* terminate on first error */
+
+		processed++;
+		name = va_arg(ap, char *);
+	}
+
+	if (name != FMEV_ARG_TERM || processed != ntuples) {
+		*nvlp = NULL;
+		nvlist_free(nvl);
+		return (FMEVERR_VARARGS_MALFORMED);
+	}
+
+	*nvlp = nvl;
+	return (FMEV_SUCCESS);
+}
+
+static fmev_err_t
+do_publish(const char *file, const char *func, int64_t line,
+    const char *ruleset, const char *class, const char *subclass,
+    fmev_pri_t pri, nvlist_t *nvl, uint_t ntuples, va_list ap)
+{
+	fmev_err_t rc = FMEVERR_INTERNAL;
+	boolean_t priv = B_TRUE;
+	nvlist_t *tmpnvl = NULL;
+	nvlist_t *pub;
+	evchan_t *evc;
+
+	if (nvl) {
+		ASSERT(ntuples == 0);
+
+		/*
+		 * Enforce NV_UNIQUE_NAME
+		 */
+		if ((nvlist_nvflag(nvl) & NV_UNIQUE_NAME) != NV_UNIQUE_NAME)
+			return (FMEVERR_NVLIST);
+
+		pub = nvl;
+
+	} else if (ntuples != 0) {
+		fmev_err_t err;
+
+		err = va2nvl(&tmpnvl, ap, ntuples);
+		if (err != FMEV_SUCCESS)
+			return (err);
+
+		pub = tmpnvl;
+	} else {
+		/*
+		 * Even if the caller has no tuples to publish (just an event
+		 * class and subclass), we are going to add some detector
+		 * information so we need some nvlist.
+		 */
+		if (nvlist_alloc(&tmpnvl, NV_UNIQUE_NAME, 0) != 0)
+			return (FMEVERR_ALLOC);
+
+		pub = tmpnvl;
+	}
+
+	evc = bind_channel(priv, pri);
+
+	if (evc == NULL) {
+		rc = FMEVERR_INTERNAL;
+		goto done;
+	}
+
+
+	/*
+	 * Add detector information
+	 */
+	if (file && nvlist_add_string(pub, "__fmev_file", file) != 0 ||
+	    func && nvlist_add_string(pub, "__fmev_func", func) != 0 ||
+	    line != -1 && nvlist_add_int64(pub, "__fmev_line", line) != 0 ||
+	    nvlist_add_int32(pub, "__fmev_pid", getpid()) != 0 ||
+	    nvlist_add_string(pub, "__fmev_execname", getexecname()) != 0) {
+		rc = FMEVERR_ALLOC;
+		goto done;
+	}
+
+	if (ruleset == NULL)
+		ruleset = FMEV_RULESET_DEFAULT;
+
+	/*
+	 * We abuse the GPEC publication arguments as follows:
+	 *
+	 * GPEC argument	Our usage
+	 * -------------------- -----------------
+	 * const char *class	Raw class
+	 * const char *subclass	Raw subclass
+	 * const char *vendor	Ruleset name
+	 * const char *pub_name	Unused
+	 * nvlist_t *attr_list	Event attributes
+	 */
+	rc = (sysevent_evc_publish(evc, class, subclass, ruleset, "",
+	    pub, EVCH_NOSLEEP) == 0) ? FMEV_SUCCESS : FMEVERR_TRANSPORT;
+
+done:
+	/* Free a passed in nvlist iff success */
+	if (nvl && rc == FMEV_SUCCESS)
+		nvlist_free(nvl);
+
+	if (tmpnvl)
+		nvlist_free(tmpnvl);
+
+	return (rc);
+}
+
+fmev_err_t
+_i_fmev_publish_nvl(
+    const char *file, const char *func, int64_t line,
+    const char *ruleset, const char *class, const char *subclass,
+    fmev_pri_t pri, nvlist_t *attr)
+{
+	fmev_err_t rc;
+
+	if ((rc = vrfy(&ruleset, &class, &subclass, &pri)) != FMEV_OK)
+		return (rc);		/* any attr not freed */
+
+	return (do_publish(file, func, line,
+	    ruleset, class, subclass,
+	    pri, attr, 0, NULL));	/* any attr freed iff success */
+}
+
+fmev_err_t
+_i_fmev_publish(
+    const char *file, const char *func, int64_t line,
+    const char *ruleset, const char *class, const char *subclass,
+    fmev_pri_t pri,
+    uint_t ntuples, ...)
+{
+	va_list ap;
+	fmev_err_t rc;
+
+	if ((rc = vrfy(&ruleset, &class, &subclass, &pri)) != FMEV_OK)
+		return (rc);
+
+	if (ntuples != 0)
+		va_start(ap, ntuples);
+
+	rc = do_publish(file, func, line,
+	    ruleset, class, subclass,
+	    pri, NULL, ntuples, ap);
+
+	if (ntuples != 0)
+		va_end(ap);
+
+	return (rc);
+}
+
+
+#pragma	weak fmev_publish = _fmev_publish
+#pragma	weak fmev_rspublish = _fmev_rspublish
+
+static fmev_err_t
+_fmev_publish(const char *class, const char *subclass, fmev_pri_t pri,
+    uint_t ntuples, ...)
+{
+	fmev_err_t rc;
+	va_list ap;
+
+	if ((rc = vrfy(NULL, &class, &subclass, &pri)) != FMEV_OK)
+		return (rc);
+
+	if (ntuples != 0)
+		va_start(ap, ntuples);
+
+	rc = do_publish(NULL, NULL, -1,
+	    FMEV_RULESET_DEFAULT, class, subclass,
+	    pri, NULL, ntuples, ap);
+
+	if (ntuples != 0)
+		va_end(ap);
+
+	return (rc);
+}
+
+static fmev_err_t
+_fmev_rspublish(const char *ruleset, const char *class, const char *subclass,
+    fmev_pri_t pri, uint_t ntuples, ...)
+{
+	fmev_err_t rc;
+	va_list ap;
+
+	if ((rc = vrfy(&ruleset, &class, &subclass, &pri)) != FMEV_OK)
+		return (rc);
+
+	if (ntuples != 0)
+		va_start(ap, ntuples);
+
+	rc = do_publish(NULL, NULL, -1,
+	    ruleset, class, subclass,
+	    pri, NULL, ntuples, ap);
+
+	if (ntuples != 0)
+		va_end(ap);
+
+	return (rc);
+}
--- a/usr/src/lib/fm/libfmevent/common/fmev_subscribe.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/lib/fm/libfmevent/common/fmev_subscribe.c	Fri Jul 30 17:04:17 2010 +1000
@@ -20,8 +20,7 @@
  */
 
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 /*
@@ -38,13 +37,14 @@
 #include <stdlib.h>
 #include <string.h>
 #include <strings.h>
-#include <umem.h>
 #include <unistd.h>
+#include <fm/libtopo.h>
 
 #include <fm/libfmevent.h>
 
 #include "fmev_impl.h"
-#include "fmev_channels.h"
+
+static topo_hdl_t *g_topohdl;
 
 typedef struct {
 	struct fmev_hdl_cmn sh_cmn;
@@ -66,8 +66,8 @@
 
 #define	SHDL_FL_SERIALIZE	0x1
 
-#define	API_ENTERV1(hdl) \
-	fmev_api_enter(&HDL2IHDL(hdl)->sh_cmn, LIBFMEVENT_VERSION_1)
+#define	FMEV_API_ENTER(hdl, v) \
+	fmev_api_enter(&HDL2IHDL(hdl)->sh_cmn, LIBFMEVENT_VERSION_##v)
 
 /*
  * For each subscription on a handle we add a node to an avl tree
@@ -115,7 +115,7 @@
 {
 	fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
 
-	if (!API_ENTERV1(hdl))
+	if (!FMEV_API_ENTER(hdl, 1))
 		return (fmev_errno);
 
 	if (!shdlctl_start(ihdl))
@@ -135,7 +135,7 @@
 {
 	fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
 
-	if (!API_ENTERV1(hdl))
+	if (!FMEV_API_ENTER(hdl, 1))
 		return (fmev_errno);
 
 	if (!shdlctl_start(ihdl))
@@ -152,7 +152,7 @@
 {
 	fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
 
-	if (!API_ENTERV1(hdl))
+	if (!FMEV_API_ENTER(hdl, 1))
 		return (fmev_errno);
 
 	if (!shdlctl_start(ihdl))
@@ -170,7 +170,7 @@
 {
 	fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
 
-	if (!API_ENTERV1(hdl))
+	if (!FMEV_API_ENTER(hdl, 1))
 		return (fmev_errno);
 
 	if (!shdlctl_start(ihdl))
@@ -188,7 +188,7 @@
 {
 	fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
 
-	if (!API_ENTERV1(hdl))
+	if (!FMEV_API_ENTER(hdl, 1))
 		return (fmev_errno);
 
 	if (!shdlctl_start(ihdl))
@@ -252,7 +252,7 @@
 	uint64_t nsid;
 	int serr;
 
-	if (!API_ENTERV1(hdl))
+	if (!FMEV_API_ENTER(hdl, 1))
 		return (fmev_errno);
 
 	if (pat == NULL || func == NULL)
@@ -354,7 +354,7 @@
 	struct fmev_subinfo si;
 	int err;
 
-	if (!API_ENTERV1(hdl))
+	if (!FMEV_API_ENTER(hdl, 1))
 		return (fmev_errno);
 
 	if (pat == NULL)
@@ -386,30 +386,13 @@
 	return (fmev_seterr(rv));
 }
 
-static void *
-dflt_alloc(size_t sz)
-{
-	return (umem_alloc(sz, UMEM_DEFAULT));
-}
-
-static void *
-dflt_zalloc(size_t sz)
-{
-	return (umem_zalloc(sz, UMEM_DEFAULT));
-}
-
-static void
-dflt_free(void *buf, size_t sz)
-{
-	umem_free(buf, sz);
-}
-
 void *
 fmev_shdl_alloc(fmev_shdl_t hdl, size_t sz)
 {
 	fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
 
-	(void) API_ENTERV1(hdl);
+	if (!FMEV_API_ENTER(hdl, 1))
+		return (NULL);
 
 	return (ihdl->sh_cmn.hc_alloc(sz));
 }
@@ -419,7 +402,8 @@
 {
 	fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
 
-	(void) API_ENTERV1(hdl);
+	if (!FMEV_API_ENTER(hdl, 1))
+		return (NULL);
 
 	return (ihdl->sh_cmn.hc_zalloc(sz));
 }
@@ -429,11 +413,44 @@
 {
 	fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
 
-	(void) API_ENTERV1(hdl);
+	if (!FMEV_API_ENTER(hdl, 1))
+		return;
 
 	ihdl->sh_cmn.hc_free(buf, sz);
 }
 
+char *
+fmev_shdl_strdup(fmev_shdl_t hdl, char *src)
+{
+	fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
+	size_t srclen;
+	char *dst;
+
+	if (!FMEV_API_ENTER(hdl, 2))
+		return (NULL);
+
+	srclen = strlen(src);
+
+	if ((dst = ihdl->sh_cmn.hc_alloc(srclen + 1)) == NULL) {
+		(void) fmev_seterr(FMEVERR_ALLOC);
+		return (NULL);
+	}
+
+	(void) strncpy(dst, src, srclen);
+	dst[srclen] = '\0';
+	return (dst);
+}
+
+void
+fmev_shdl_strfree(fmev_shdl_t hdl, char *buf)
+{
+	fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
+
+	(void) FMEV_API_ENTER(hdl, 2);
+
+	ihdl->sh_cmn.hc_free(buf, strlen(buf) + 1);
+}
+
 int
 fmev_shdl_valid(fmev_shdl_t hdl)
 {
@@ -546,11 +563,100 @@
 }
 
 fmev_err_t
+fmev_shdl_getauthority(fmev_shdl_t hdl, nvlist_t **nvlp)
+{
+	fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
+	nvlist_t *propnvl;
+	fmev_err_t rc;
+
+	if (!FMEV_API_ENTER(hdl, 2))
+		return (fmev_errno);
+
+	(void) pthread_mutex_lock(&ihdl->sh_lock);
+
+	if (sysevent_evc_getpropnvl(ihdl->sh_binding, &propnvl) != 0) {
+		*nvlp = NULL;
+		(void) pthread_mutex_unlock(&ihdl->sh_lock);
+		return (fmev_seterr(FMEVERR_UNKNOWN));
+	}
+
+	if (propnvl == NULL) {
+		rc = FMEVERR_BUSY;	/* Other end has not bound */
+	} else {
+		nvlist_t *auth;
+
+		if (nvlist_lookup_nvlist(propnvl, "fmdauth", &auth) == 0) {
+			rc = (nvlist_dup(auth, nvlp, 0) == 0) ? FMEV_SUCCESS :
+			    FMEVERR_ALLOC;
+		} else {
+			rc = FMEVERR_INTERNAL;
+		}
+		nvlist_free(propnvl);
+	}
+
+	(void) pthread_mutex_unlock(&ihdl->sh_lock);
+
+	if (rc != FMEV_SUCCESS) {
+		*nvlp = NULL;
+		(void) fmev_seterr(rc);
+	}
+
+	return (rc);
+}
+
+char *
+fmev_shdl_nvl2str(fmev_shdl_t hdl, nvlist_t *nvl)
+{
+	fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
+	char *fmri, *fmricp;
+	fmev_err_t err;
+	int topoerr;
+
+	if (!FMEV_API_ENTER(hdl, 2))
+		return (NULL);
+
+	if (g_topohdl == NULL) {
+		(void) pthread_mutex_lock(&ihdl->sh_lock);
+		if (g_topohdl == NULL)
+			g_topohdl = topo_open(TOPO_VERSION, NULL, &topoerr);
+		(void) pthread_mutex_unlock(&ihdl->sh_lock);
+
+		if (g_topohdl == NULL) {
+			(void) fmev_seterr(FMEVERR_INTERNAL);
+			return (NULL);
+		}
+	}
+
+	if (topo_fmri_nvl2str(g_topohdl, nvl, &fmri, &topoerr) == 0) {
+		fmricp = fmev_shdl_strdup(hdl, fmri);
+		topo_hdl_strfree(g_topohdl, fmri);
+		return (fmricp);	/* fmev_errno set if strdup failed */
+	}
+
+	switch (topoerr) {
+	case ETOPO_FMRI_NOMEM:
+		err = FMEVERR_ALLOC;
+		break;
+
+	case ETOPO_FMRI_MALFORM:
+	case ETOPO_METHOD_NOTSUP:
+	case ETOPO_METHOD_INVAL:
+	default:
+		err = FMEVERR_INVALIDARG;
+		break;
+	}
+
+	(void) fmev_seterr(err);
+	return (NULL);
+}
+
+fmev_err_t
 fmev_shdl_fini(fmev_shdl_t hdl)
 {
 	fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
 
-	(void) API_ENTERV1(hdl);
+	if (!FMEV_API_ENTER(hdl, 1))
+		return (fmev_errno);
 
 	(void) pthread_mutex_lock(&ihdl->sh_lock);
 
@@ -594,6 +700,11 @@
 
 	ihdl->sh_cmn.hc_magic = 0;
 
+	if (g_topohdl) {
+		topo_close(g_topohdl);
+		g_topohdl = NULL;
+	}
+
 	(void) pthread_mutex_unlock(&ihdl->sh_lock);
 	(void) pthread_mutex_destroy(&ihdl->sh_lock);
 
--- a/usr/src/lib/fm/libfmevent/common/fmev_util.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/lib/fm/libfmevent/common/fmev_util.c	Fri Jul 30 17:04:17 2010 +1000
@@ -20,8 +20,7 @@
  */
 
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 /*
@@ -58,18 +57,26 @@
 int
 fmev_api_init(struct fmev_hdl_cmn *hc)
 {
-	if (!fmev_api_enter(NULL, 0))
+	uint32_t v = hc->hc_api_vers;
+	int rc;
+
+	if (!fmev_api_enter((struct fmev_hdl_cmn *)fmev_api_init, 0))
 		return (0);
-	/*
-	 * We implement only version 1 of the ABI at this point.
-	 */
-	if (hc->hc_api_vers != LIBFMEVENT_VERSION_1) {
+
+	switch (v) {
+	case LIBFMEVENT_VERSION_1:
+	case LIBFMEVENT_VERSION_2:
+		rc = 1;
+		break;
+
+	default:
 		if (key_inited)
 			(void) fmev_seterr(FMEVERR_VERSION_MISMATCH);
-		return (0);
+		rc = 0;
+		break;
 	}
 
-	return (1);
+	return (rc);
 }
 
 /*
@@ -82,6 +89,7 @@
 int
 fmev_api_enter(struct fmev_hdl_cmn *hc, uint32_t ver_intro)
 {
+	uint32_t v;
 	struct fmev_tsd *tsd;
 
 	/* Initialize key on first visit */
@@ -106,13 +114,18 @@
 
 	tsd->ts_lasterr = 0;
 
-	if (hc == NULL) {
-		return (1);
+	if (hc == (struct fmev_hdl_cmn *)fmev_api_init)
+		return (1);	/* special case from fmev_api_init only */
+
+	if (hc == NULL || hc->hc_magic != _FMEV_SHMAGIC) {
+		tsd->ts_lasterr = FMEVERR_API;
+		return (0);
 	}
 
+	v = hc->hc_api_vers;	/* API version opened */
+
 	/* Enforce version adherence. */
-	if (ver_intro > hc->hc_api_vers ||
-	    hc->hc_api_vers > LIBFMEVENT_VERSION_LATEST ||
+	if (ver_intro > v || v > LIBFMEVENT_VERSION_LATEST ||
 	    ver_intro > LIBFMEVENT_VERSION_LATEST) {
 		tsd->ts_lasterr = FMEVERR_VERSION_MISMATCH;
 		return (0);
@@ -178,3 +191,21 @@
 
 	return ((const fmev_err_t *)&tsd->ts_lasterr);
 }
+
+void *
+dflt_alloc(size_t sz)
+{
+	return (umem_alloc(sz, UMEM_DEFAULT));
+}
+
+void *
+dflt_zalloc(size_t sz)
+{
+	return (umem_zalloc(sz, UMEM_DEFAULT));
+}
+
+void
+dflt_free(void *buf, size_t sz)
+{
+	umem_free(buf, sz);
+}
--- a/usr/src/lib/fm/libfmevent/common/libfmevent.h	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/lib/fm/libfmevent/common/libfmevent.h	Fri Jul 30 17:04:17 2010 +1000
@@ -20,8 +20,7 @@
  */
 
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #ifndef _LIBFMEVENT_H
@@ -31,6 +30,7 @@
  * FMA event library.
  *
  * A. Protocol event subscription interfaces (Committed).
+ * B. Raw event publication interfaces (Consolidation Private).
  */
 
 #ifdef __cplusplus
@@ -49,10 +49,43 @@
  * to fmev_shdl_init.  Only interfaces introduced in or prior to the
  * quoted version will be available.  Once introduced an interface
  * only ever changes compatibly.
+ *
+ *				Introduced in
+ *	API Function		LIBFMEVENT_VERSION_*
+ *	-----------------------	--------------------
+ *	fmev_attr_list;		1
+ *	fmev_class;		1
+ *	fmev_dup;		1
+ *	fmev_ev2shdl		2
+ *	fmev_hold;		1
+ *	fmev_localtime;		1
+ *	fmev_rele;		1
+ *	fmev_shdl_alloc;	1
+ *	fmev_shdl_init;		1
+ *	fmev_shdl_fini;		1
+ *	fmev_shdl_free;		1
+ *	fmev_shdl_getauthority	2
+ *	fmev_shdl_nvl2str	2
+ *	fmev_shdl_strdup	2
+ *	fmev_shdl_strfree	2
+ *	fmev_shdl_subscribe;	1
+ *	fmev_shdl_unsubscribe;	1
+ *	fmev_shdl_zalloc;	1
+ *	fmev_shdlctl_serialize;	1
+ *	fmev_shdlctl_sigmask;	1
+ *	fmev_shdlctl_thrattr;	1
+ *	fmev_shdlctl_thrcreate;	1
+ *	fmev_shdlctl_thrsetup;	1
+ *	fmev_strerror;		1
+ *	fmev_timespec;		1
+ *	fmev_time_nsec;		1
+ *	fmev_time_sec;		1
  */
+
 #define	LIBFMEVENT_VERSION_1	1
+#define	LIBFMEVENT_VERSION_2	2
 
-#define	LIBFMEVENT_VERSION_LATEST	LIBFMEVENT_VERSION_1
+#define	LIBFMEVENT_VERSION_LATEST	LIBFMEVENT_VERSION_2
 
 /*
  * Success and error return values.  The descriptive comment for each
@@ -75,7 +108,14 @@
     FMEVERR_BADCLASS, /* Bad event class or class pattern */
     FMEVERR_NOMATCH, /* No match to criteria provided */
     FMEVERR_MAX_SUBSCRIBERS, /* Exceeds maximum subscribers per handle */
-    FMEVERR_INVALIDARG /* Argument is invalid */
+    FMEVERR_INVALIDARG, /* Argument is invalid */
+    FMEVERR_STRING2BIG, /* String argument exceeds maximum length */
+    FMEVERR_VARARGS_MALFORMED, /* Varargs list bad or incorrectly terminated */
+    FMEVERR_VARARGS_TOOLONG, /* Varargs list exceeds maximum length */
+    FMEVERR_BADRULESET, /* Ruleset selected for publication is bad */
+    FMEVERR_BADPRI, /* Priority selected for publication is bad */
+    FMEVERR_TRANSPORT, /* Error in underlying event transport implementation */
+    FMEVERR_NVLIST /* nvlist argument is not of type NV_UNIQUE_NAME */
 } fmev_err_t;
 
 /*
@@ -209,6 +249,14 @@
 extern fmev_err_t fmev_shdl_unsubscribe(fmev_shdl_t, const char *);
 
 /*
+ * Retrieve an authority nvlist for the fault manager that is forwarding
+ * events to us.  This may be NULL if the fault manager has not yet
+ * started up and made the information available.  The caller is
+ * responsible for freeing the nvlist returned.
+ */
+extern fmev_err_t fmev_shdl_getauthority(fmev_shdl_t, nvlist_t **);
+
+/*
  * Event access.  In the common case that the event is processed to
  * completion in the context of the event callback you need only
  * use fmev_attr_list to access the nvlist of event attributes,
@@ -250,6 +298,23 @@
  * past that second.  This can fail with FMEVERR_OVERFLOW if the seconds
  * value does not fit within a time_t;  you can retrieve the 64-bit second
  * and nanosecond values with fmev_time_sec and fmev_time_nsec.
+ *
+ * An FMRI in an event payload is typically in nvlist form, i.e
+ * DATA_TYPE_NVLIST.  That form is useful for extracting individual
+ * component fields, but that requires knowledge of the FMRI scheme and
+ * Public commitment thereof.  FMRIs are typically Private, but in some
+ * cases they can be descriptive such as in listing the ASRU(s) affected
+ * by a fault; so we offer an API member which will blindly render any
+ * FMRI in its string form.  Use fmev_shdl_nvl2str to format an nvlist_t
+ * as a string (if it is recognized as an FMRI); the caller is responsible
+ * for freeing the returned string using fmev_shdl_strfree.  If
+ * fmev_shdl_nvl2str fails it will return NULL with fmev_errno set -
+ * FMEVERR_INVALIDARG if the nvlist_t does not appear to be a valid/known FMRI,
+ * FMEVERR_ALLOC if an allocation for memory for the string failed.
+ *
+ * fmev_ev2shdl will return the fmev_shdl_t with which a received fmev_t
+ * is associated.  It should only be used in an event delivery callback
+ * context and for the event received in that callback.
  */
 
 extern nvlist_t *fmev_attr_list(fmev_t);
@@ -264,6 +329,10 @@
 extern void fmev_rele(fmev_t);
 extern fmev_t fmev_dup(fmev_t);
 
+extern char *fmev_shdl_nvl2str(fmev_shdl_t, nvlist_t *);
+
+extern fmev_shdl_t fmev_ev2shdl(fmev_t);
+
 /*
  * The following will allocate and free memory based on the choices made
  * at fmev_shdl_init.
@@ -271,6 +340,234 @@
 void *fmev_shdl_alloc(fmev_shdl_t, size_t);
 void *fmev_shdl_zalloc(fmev_shdl_t, size_t);
 void fmev_shdl_free(fmev_shdl_t, void *, size_t);
+extern char *fmev_shdl_strdup(fmev_shdl_t, char *);
+extern void fmev_shdl_strfree(fmev_shdl_t, char *);
+
+/*
+ * Part B - Raw Event Publication
+ * ======
+ *
+ * The following interfaces are private to the Solaris system and are
+ * subject to change at any time without notice.  Applications using
+ * these interfaces will fail to run on future releases.  The interfaces
+ * should not be used for any purpose until they are publicly documented
+ * for use outside of Sun.  These interface are *certain* to change
+ * incompatibly, as the current interface is very much purpose-built for
+ * a limited application.
+ *
+ * The interfaces below allow a process to publish a "raw" event
+ * which will be transmitted to the fault manager and post-processed
+ * into a full FMA protocol event.  The post-processing to be applied
+ * is selected by a "ruleset" specified either implicitly or explicitly
+ * at publication.  A ruleset will take the raw event (comprising
+ * class, subclass, priority, raw payload) and mark it up into a full
+ * protocol event; it may also augment the payload through looking up
+ * details that would have been costly to compute at publication time.
+ *
+ * In this first implementation event dispatch is synchronous and blocking,
+ * and not guaranteed to be re-entrant.  This limits the call sites
+ * at which publication calls can be placed, and also means that careful
+ * thought is required before sprinkling event publication code throughout
+ * common system libraries.  The dispatch mechanism amounts to some
+ * nvlist chicanery followed by a sysevent_evc_publish.  A future revision
+ * will relax the context from which one may publish, and add more-powerful
+ * publication interfaces.
+ *
+ * Some publication interfaces (those ending in _nvl) accept a preconstructed
+ * nvlist as raw event payload.  We require that such an nvlist be of type
+ * NV_UNIQUE_NAME.  The publication interfaces all call nvlist_free on any
+ * nvlist that is passed for publication.
+ *
+ * Other publication interfaces allow you to build up the raw event payload
+ * by specifying the members in a varargs list terminated by FMEV_ARG_TERM.
+ * Again we require that payload member names are unique (that is, you cannot
+ * have two members with the same name but different datatype).  See
+ * <sys/nvpair.h> for the data_type_t enumeration of types supported - but
+ * note that DATA_TYPE_BOOLEAN is excluded (DATA_TYPE_BOOLEAN_VALUE is
+ * supported).  A single-valued (non-array type) member is specified with 3
+ * consecutive varargs as:
+ *
+ *	(char *)name, DATA_TYPE_foo, (type)value
+ *
+ * An array-valued member is specified with 4 consecutive varargs as:
+ *
+ *	(char *)name, DATA_TYPE_foo_ARRAY, (int)nelem, (type *)arrayptr
+ *
+ * The varargs list that specifies the nvlist must begin with an
+ * integer that specifies the number of members that follow.  For example:
+ *
+ * uint32_t mode;
+ * char *clientname;
+ * uint32_t ins[NARGS];
+ *
+ * fmev_publish("class", "subclass", FMEV_LOPRI,
+ *	3,
+ *	"mode", DATA_TYPE_UINT32, mode,
+ *	"client", DATA_TYPE_STRING, clientname,
+ *	"ins", DATA_TYPE_UINT32_ARRAY, sizeof (ins) / sizeof (ins[0]), ins,
+ *	FMEV_ARG_TERM);
+ *
+ * The following tables summarize the capabilities of the various
+ * publication interfaces.
+ *
+ *					     Detector
+ * Interface			Ruleset? File/Line Func
+ * ---------------------------- -------- --------- ----
+ * fmev_publish_nvl		default	 Yes	   No
+ * fmev_publish_nvl (C99)	default  Yes	   Yes
+ * fmev_rspublish_nvl		chosen	 Yes	   No
+ * fmev_rspublish_nvl (C99)	chosen	 Yes	   Yes
+ * fmev_publish			default	 No	   No
+ * fmev_publish (C99)		default	 Yes	   Yes
+ * fmev_rspublish		chosen	 No	   No
+ * fmev_rspublish (C99)		chosen	 Yes	   Yes
+ *
+ * Summary: if not using C99 then try to use the _nvl variants as the
+ * varargs variants will not include file, line or function in the
+ * detector.
+ */
+
+/*
+ * In publishing an event you must select a "ruleset" (or accept the
+ * defaults).  Rulesets are listed in the following header.
+ */
+#include <fm/libfmevent_ruleset.h>
+
+/*
+ * In publishing an event we can specify a class and subclass (which
+ * in post-processing combine in some way selected by the ruleset to
+ * form a full event protocol class).  The maximum class and subclass
+ * string lengths are as follows.
+ */
+#define	FMEV_PUB_MAXCLASSLEN	32
+#define	FMEV_PUB_MAXSUBCLASSLEN	32
+
+/*
+ * Events are either high-priority (try really hard not to lose) or
+ * low-priority (can drop, throttle etc).  Convert a fmev_pri_t to
+ * a string with fmev_pri_string().
+ */
+typedef enum fmev_pri {
+	FMEV_LOPRI = 0x1000,
+	FMEV_HIPRI
+} fmev_pri_t;
+
+extern const char *fmev_pri_string(fmev_pri_t);
+
+/*
+ * The varargs event publication interfaces must terminate the list
+ * of nvpair specifications with FMEV_ARG_TERM.  This is to guard
+ * against very easily-made mistakes in those arg lists.
+ */
+#define	FMEV_ARG_TERM	(void *)0xa4a3a2a1
+
+/*
+ * The following are NOT for direct use.
+ */
+extern fmev_err_t _i_fmev_publish_nvl(
+    const char *, const char *, int64_t,
+    const char *, const char *, const char *,
+    fmev_pri_t, nvlist_t *);
+
+extern fmev_err_t _i_fmev_publish(
+    const char *, const char *, int64_t,
+    const char *, const char *, const char *,
+    fmev_pri_t,
+    uint_t, ...);
+
+/*
+ * Post-processing will always generate a "detector" payload member.  In
+ * the case of the _nvl publishing variants the detector information
+ * includes file and line number, and - if your application is compiled
+ * with C99 enabled - function name.
+ */
+#if __STDC_VERSION__ - 0 >= 199901L
+#define	_FMEVFUNC	__func__
+#else
+#define	_FMEVFUNC	NULL
+#endif
+
+/*
+ * All these definitions "return" an fmev_err_t.
+ *
+ * In the _nvl variants you pass a preconstructed event payload; otherwise
+ * you include an integer indicating the number of payload
+ * (name, type, value) tuples that follow, then all those tuples, finally
+ * terminated by FMEV_ARG_TERM.
+ *
+ * In the rspublish variants you select a ruleset from
+ * libfmevent_ruleset.h - just use the final suffix (as in
+ * DEFAULT, EREPORT, ISV).
+ *
+ * The primary classification must not be NULL or the empty string.
+ *
+ *	arg	type		Description
+ *	------- --------------- -------------------------------------------
+ *	ruleset	const char *	Ruleset; can be NULL (implies default ruleset)
+ *	cl1	const char *	Primary classification string
+ *	cl2	const char *	Secondary classification string
+ *	pri	fmev_pri_t	Priority
+ *	nvl	nvlist_t *	Preconstructed attributes; caller must free
+ *	ntuples	int		Number of tuples before FMEV_ARG_TERM
+ *	suffix	-		See above.
+ */
+
+/*
+ * fmev_publish_nvl - Default ruleset implied; class/subclass, pri and an nvl
+ */
+#define	fmev_publish_nvl(cl1, cl2, pri, nvl) \
+	_i_fmev_publish_nvl( \
+	    __FILE__, _FMEVFUNC, __LINE__, \
+	    FMEV_RULESET_DEFAULT, cl1, cl2, \
+	    pri, nvl)
+
+/*
+ * fmev_rspublish_nvl - As fmev_publish_nvl, but with a chosen ruleset.
+ */
+#define	fmev_rspublish_nvl(ruleset, cl1, cl2, pri, nvl) \
+	_i_fmev_publish_nvl( \
+	    __FILE__, _FMEVFUNC, __LINE__, \
+	    ruleset, cl1, cl2, \
+	    pri, nvl)
+
+#if __STDC_VERSION__ - 0 >= 199901L && !defined(__lint)
+
+/*
+ * fmev_publish (C99 version) - Default ruleset; class/subclass, pri, nvpairs
+ */
+#define	fmev_publish(cl1, cl2, pri, ntuples, ...) \
+	_i_fmev_publish( \
+	    __FILE__, __func__, __LINE__, \
+	    FMEV_RULESET_DEFAULT, cl1, cl2, \
+	    pri, \
+	    ntuples, __VA_ARGS__)
+
+
+/*
+ * fmev_rspublish (C99 version) - As fmev_publish, but with a chosen ruleset.
+ */
+#define	fmev_rspublish(ruleset, cl1, cl2, pri, ntuples, ...) \
+	_i_fmev_publish( \
+	    __FILE__, __func__, __LINE__, \
+	    ruleset, cl1, cl2, \
+	    pri, \
+	    ntuples, __VA_ARGS__)
+
+#else
+
+/*
+ * fmev_publish (pre C99)
+ */
+extern fmev_err_t fmev_publish(const char *, const char *,
+    fmev_pri_t, uint_t, ...);
+
+/*
+ * fmev_rspublish (pre C99)
+ */
+extern fmev_err_t fmev_rspublish(const char *, const char *, const char *,
+    fmev_pri_t, uint_t, ...);
+
+#endif /* __STDC_VERSION__ */
 
 #ifdef __cplusplus
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/fm/libfmevent/common/libfmevent_ruleset.h	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ */
+
+#ifndef _LIBFMEVENT_RULESET_H
+#define	_LIBFMEVENT_RULESET_H
+
+/*
+ * Event Rulesets.  A ruleset is selected by a (namespace, subsystem)
+ * combination, which together we call a "ruleset" selection for that
+ * namespace.  The strings can be any ascii string not including
+ * control characters or DEL.
+ *
+ * Selection of a ruleset determines how a "raw" event that we publish
+ * using the libfmevent publication interfaces is post-processed into
+ * a full protocol event.
+ *
+ * New rulesets must follow the FMA Event Registry and Portfolio Review
+ * process.  At this time only FMEV_RULESET_SMF and FMEV_RULESET_ON_SUNOS
+ * rulesets are adopted by that process - the others listed here are
+ * experimental.
+ */
+
+#define	FMEV_MAX_RULESET_LEN	31
+
+#define	FMEV_RS_SEPARATOR		"\012"
+#define	FMEV_MKRS(v, s)			FMEV_V_##v FMEV_RS_SEPARATOR s
+
+/*
+ * Namespaces
+ */
+#define	FMEV_V_ALL		"*"
+#define	FMEV_V_SOLARIS_ON	"solaris-osnet"	/* Solaris ON Consolidation */
+
+/*
+ * Generic and namespace-agnostic rulesets
+ */
+#define	FMEV_RULESET_UNREGISTERED	FMEV_MKRS(ALL, "unregistered")
+#define	FMEV_RULESET_DEFAULT		FMEV_RULESET_UNREGISTERED
+#define	FMEV_RULESET_SMF		FMEV_MKRS(ALL, "smf")
+
+/*
+ * Solaris ON rulesets
+ */
+#define	FMEV_RULESET_ON_EREPORT		FMEV_MKRS(SOLARIS_ON, "ereport")
+#define	FMEV_RULESET_ON_SUNOS		FMEV_MKRS(SOLARIS_ON, "sunos")
+#define	FMEV_RULESET_ON_PRIVATE		FMEV_MKRS(SOLARIS_ON, "private")
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBFMEVENT_RULESET_H */
--- a/usr/src/lib/fm/libfmevent/common/mapfile-vers	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/lib/fm/libfmevent/common/mapfile-vers	Fri Jul 30 17:04:17 2010 +1000
@@ -38,6 +38,15 @@
 
 $mapfile_version 2
 
+SYMBOL_VERSION SUNW_1.2 {
+    global:
+	fmev_ev2shdl;
+	fmev_shdl_getauthority;
+	fmev_shdl_nvl2str;
+	fmev_shdl_strdup;
+	fmev_shdl_strfree;
+} SUNW_1.1;
+
 SYMBOL_VERSION SUNW_1.1 {
     global:
 	fmev_attr_list;
@@ -68,6 +77,11 @@
 
 SYMBOL_VERSION SUNWprivate {
     global:
+	fmev_pri_string;
+	fmev_publish		{ FLAGS = NODYNSORT };
+	fmev_rspublish		{ FLAGS = NODYNSORT };
+	_i_fmev_publish;
+	_i_fmev_publish_nvl;
 	__fmev_errno;
     local:
 	*;
--- a/usr/src/lib/fm/libfmevent/i386/Makefile	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/lib/fm/libfmevent/i386/Makefile	Fri Jul 30 17:04:17 2010 +1000
@@ -19,10 +19,11 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
+# Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
 #
 
 include ../Makefile.com
 
+DYNFLAGS += -R/usr/lib/fm
+
 install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
--- a/usr/src/lib/fm/libfmevent/sparc/Makefile	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/lib/fm/libfmevent/sparc/Makefile	Fri Jul 30 17:04:17 2010 +1000
@@ -19,10 +19,11 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
+# Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
 #
 
 include ../Makefile.com
 
+DYNFLAGS += -R/usr/lib/fm
+
 install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
--- a/usr/src/lib/fm/libfmevent/sparcv9/Makefile	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/lib/fm/libfmevent/sparcv9/Makefile	Fri Jul 30 17:04:17 2010 +1000
@@ -19,11 +19,12 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
+# Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
 #
 
 include ../Makefile.com
 include ../../../Makefile.lib.64
 
+DYNFLAGS += -R/usr/lib/fm/$(MACH64)
+
 install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/fm/libfmnotify/Makefile	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,55 @@
+#
+# 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.lib
+include ../Makefile.lib
+
+SUBDIRS = $(MACH) 
+$(BUILD64)SUBDIRS += $(MACH64)
+
+all := TARGET = all
+clean := TARGET = clean
+clobber := TARGET = clobber
+install := TARGET = install
+lint := TARGET = lint
+test := TARGET = test
+
+.KEEP_STATE:
+
+all clean clobber lint test: $(SUBDIRS)
+
+install: $(SUBDIRS)
+
+install_h:
+
+check: $(CHECKHDRS)
+
+$(SUBDIRS): FRC
+	@cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
+
+include ../../Makefile.targ
+include ../Makefile.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/fm/libfmnotify/Makefile.com	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,63 @@
+#
+# 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.
+#
+
+LIBRARY = libfmnotify.a
+VERS = .1
+
+LIBSRCS = libfmnotify.c
+OBJECTS = $(LIBSRCS:%.c=%.o)
+
+include ../../../Makefile.lib
+include ../../Makefile.lib
+
+SRCS = $(LIBSRCS:%.c=../common/%.c)
+LIBS = $(DYNLIB) $(LINTLIB)
+
+SRCDIR =	../common
+
+C99MODE = $(C99_ENABLE)
+
+CPPFLAGS += -I../common -I.
+CFLAGS += $(CCVERBOSE) $(C_BIGPICFLAGS)
+CFLAGS64 += $(CCVERBOSE) $(C_BIGPICFLAGS)
+
+$(DYNLIB) := LDLIBS += $(MACH_LDLIBS)
+$(DYNLIB) := LDLIBS += -lnvpair -lc -lfmd_msg -lfmevent -lscf -ldiagcode
+
+LINTFLAGS = -msux
+LINTFLAGS64 = -msux -m64
+
+$(LINTLIB) := SRCS = $(SRCDIR)/$(LINTSRC)
+$(LINTLIB) := LINTFLAGS = -nsvx
+$(LINTLIB) := LINTFLAGS64 = -nsvx -m64
+
+.KEEP_STATE:
+
+all: $(LIBS)
+
+lint: $(LINTLIB) lintcheck
+
+include ../../../Makefile.targ
+include ../../Makefile.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/fm/libfmnotify/amd64/Makefile	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,31 @@
+#
+# 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.
+#
+
+MACH_LDLIBS = -L$(ROOT)/usr/lib/fm/$(MACH64)
+
+include ../Makefile.com
+include ../../../Makefile.lib.64
+
+DYNFLAGS += -R/usr/lib/fm/$(MACH64)
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/fm/libfmnotify/common/libfmnotify.c	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,616 @@
+/*
+ * 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 <alloca.h>
+
+#include "libfmnotify.h"
+
+/*ARGSUSED*/
+void
+nd_cleanup(nd_hdl_t *nhdl)
+{
+	nd_debug(nhdl, "Cleaning up ...");
+	if (nhdl->nh_evhdl)
+		(void) fmev_shdl_fini(nhdl->nh_evhdl);
+
+	if (nhdl->nh_msghdl)
+		fmd_msg_fini(nhdl->nh_msghdl);
+
+	nhdl->nh_keep_running = B_FALSE;
+	(void) fclose(nhdl->nh_log_fd);
+}
+
+static void
+get_timestamp(char *buf, size_t bufsize)
+{
+	time_t utc_time;
+	struct tm *p_tm;
+
+	(void) time(&utc_time);
+	p_tm = localtime(&utc_time);
+
+	(void) strftime(buf, bufsize, "%b %d %H:%M:%S", p_tm);
+}
+
+/* PRINTFLIKE2 */
+void
+nd_debug(nd_hdl_t *nhdl, const char *format, ...)
+{
+	char timestamp[64];
+	va_list ap;
+
+	if (nhdl->nh_debug) {
+		get_timestamp(timestamp, sizeof (timestamp));
+		(void) fprintf(nhdl->nh_log_fd, "[ %s ", timestamp);
+		va_start(ap, format);
+		(void) vfprintf(nhdl->nh_log_fd, format, ap);
+		va_end(ap);
+		(void) fprintf(nhdl->nh_log_fd, " ]\n");
+	}
+	(void) fflush(nhdl->nh_log_fd);
+}
+
+void
+nd_dump_nvlist(nd_hdl_t *nhdl, nvlist_t *nvl)
+{
+	if (nhdl->nh_debug)
+		nvlist_print(nhdl->nh_log_fd, nvl);
+}
+
+/* PRINTFLIKE2 */
+void
+nd_error(nd_hdl_t *nhdl, const char *format, ...)
+{
+	char timestamp[64];
+	va_list ap;
+
+	get_timestamp(timestamp, sizeof (timestamp));
+	(void) fprintf(nhdl->nh_log_fd, "[ %s ", timestamp);
+	va_start(ap, format);
+	(void) vfprintf(nhdl->nh_log_fd, format, ap);
+	va_end(ap);
+	(void) fprintf(nhdl->nh_log_fd, " ]\n");
+	(void) fflush(nhdl->nh_log_fd);
+}
+
+/* PRINTFLIKE2 */
+void
+nd_abort(nd_hdl_t *nhdl, const char *format, ...)
+{
+	char timestamp[64];
+	va_list ap;
+
+	get_timestamp(timestamp, sizeof (timestamp));
+	(void) fprintf(nhdl->nh_log_fd, "[ %s ", timestamp);
+	va_start(ap, format);
+	(void) vfprintf(nhdl->nh_log_fd, format, ap);
+	va_end(ap);
+	(void) fprintf(nhdl->nh_log_fd, " ]\n");
+	(void) fflush(nhdl->nh_log_fd);
+	nd_cleanup(nhdl);
+}
+
+void
+nd_daemonize(nd_hdl_t *nhdl)
+{
+	pid_t pid;
+
+	if ((pid = fork()) < 0)
+		nd_abort(nhdl, "Failed to fork child (%s)", strerror(errno));
+	else if (pid > 0)
+		exit(0);
+
+	(void) setsid();
+	(void) close(0);
+	(void) close(1);
+	/*
+	 * We leave stderr open so we can write debug/err messages to the SMF
+	 * service log
+	 */
+	nhdl->nh_is_daemon = B_TRUE;
+}
+
+/*
+ * This function returns a pointer to the specified SMF property group for the
+ * specified SMF service.  The caller is responsible for freeing the property
+ * group.  On failure, the function returns NULL.
+ */
+static scf_propertygroup_t *
+nd_get_pg(nd_hdl_t *nhdl, scf_handle_t *handle, const char *svcname,
+    const char *pgname)
+{
+	scf_scope_t *sc = NULL;
+	scf_service_t *svc = NULL;
+	scf_propertygroup_t *pg = NULL, *ret = NULL;
+
+	sc = scf_scope_create(handle);
+	svc = scf_service_create(handle);
+	pg = scf_pg_create(handle);
+
+	if (sc == NULL || svc == NULL || pg == NULL) {
+		nd_error(nhdl, "Failed to allocate libscf structures");
+		scf_pg_destroy(pg);
+		goto get_pg_done;
+	}
+
+	if (scf_handle_bind(handle) != -1 &&
+	    scf_handle_get_scope(handle, SCF_SCOPE_LOCAL, sc) != -1 &&
+	    scf_scope_get_service(sc, svcname, svc) != -1 &&
+	    scf_service_get_pg(svc, pgname, pg) != -1)
+		ret = pg;
+	else
+		scf_pg_destroy(pg);
+
+get_pg_done:
+	scf_service_destroy(svc);
+	scf_scope_destroy(sc);
+
+	return (ret);
+}
+
+int
+nd_get_astring_prop(nd_hdl_t *nhdl, const char *svcname, const char *pgname,
+    const char *propname, char **val)
+{
+	scf_handle_t *handle = NULL;
+	scf_propertygroup_t *pg;
+	scf_property_t *prop = NULL;
+	scf_value_t *value = NULL;
+	char strval[255];
+	int ret = -1;
+
+	if ((handle = scf_handle_create(SCF_VERSION)) == NULL)
+		return (ret);
+
+	if ((pg = nd_get_pg(nhdl, handle, svcname, pgname)) == NULL) {
+		nd_error(nhdl, "Failed to read retrieve %s "
+		    "property group for %s", pgname, svcname);
+		goto astring_done;
+	}
+	prop = scf_property_create(handle);
+	value = scf_value_create(handle);
+	if (prop == NULL || value == NULL) {
+		nd_error(nhdl, "Failed to allocate SMF structures");
+		goto astring_done;
+	}
+	if (scf_pg_get_property(pg, propname, prop) == -1 ||
+	    scf_property_get_value(prop, value) == -1 ||
+	    scf_value_get_astring(value, strval, 255) == -1) {
+		nd_error(nhdl, "Failed to retrieve %s prop (%s)", propname,
+		    scf_strerror(scf_error()));
+		goto astring_done;
+	}
+	*val = strdup(strval);
+	ret = 0;
+
+astring_done:
+	scf_value_destroy(value);
+	scf_property_destroy(prop);
+	scf_pg_destroy(pg);
+	scf_handle_destroy(handle);
+
+	return (ret);
+}
+
+int
+nd_get_boolean_prop(nd_hdl_t *nhdl, const char *svcname, const char *pgname,
+    const char *propname, uint8_t *val)
+{
+	scf_handle_t *handle = NULL;
+	scf_propertygroup_t *pg;
+	scf_property_t *prop = NULL;
+	scf_value_t *value = NULL;
+	int ret = -1;
+
+	if ((handle = scf_handle_create(SCF_VERSION)) == NULL)
+		return (ret);
+
+	if ((pg = nd_get_pg(nhdl, handle, svcname, pgname)) == NULL) {
+		nd_error(nhdl, "Failed to read retrieve %s "
+		    "property group for %s", pgname, svcname);
+		goto bool_done;
+	}
+	prop = scf_property_create(handle);
+	value = scf_value_create(handle);
+	if (prop == NULL || value == NULL) {
+		nd_error(nhdl, "Failed to allocate SMF structures");
+		goto bool_done;
+	}
+	if (scf_pg_get_property(pg, propname, prop) == -1 ||
+	    scf_property_get_value(prop, value) == -1 ||
+	    scf_value_get_boolean(value, val) == -1) {
+		nd_error(nhdl, "Failed to retrieve %s prop (%s)", propname,
+		    scf_strerror(scf_error()));
+		goto bool_done;
+	}
+	ret = 0;
+
+bool_done:
+	scf_value_destroy(value);
+	scf_property_destroy(prop);
+	scf_pg_destroy(pg);
+	scf_handle_destroy(handle);
+
+	return (ret);
+}
+
+char *
+nd_get_event_fmri(nd_hdl_t *nhdl, fmev_t ev)
+{
+	nvlist_t *ev_nvl, *attr_nvl;
+	char *svcname;
+
+	if ((ev_nvl = fmev_attr_list(ev)) == NULL) {
+		nd_error(nhdl, "Failed to lookup event attr nvlist");
+		return (NULL);
+	}
+	if (nvlist_lookup_nvlist(ev_nvl, "attr", &attr_nvl) ||
+	    nvlist_lookup_string(attr_nvl, "svc-string", &svcname)) {
+		nd_error(nhdl, "Malformed event 0x%p", (void *)ev_nvl);
+		return (NULL);
+	}
+
+	return (strdup((const char *)svcname));
+}
+
+int
+nd_get_notify_prefs(nd_hdl_t *nhdl, const char *mech, fmev_t ev,
+    nvlist_t ***pref_nvl, uint_t *nprefs)
+{
+	nvlist_t *ev_nvl, *top_nvl, **np_nvlarr, *mech_nvl;
+	int ret = 1;
+	uint_t nelem;
+
+	if ((ev_nvl = fmev_attr_list(ev)) == NULL) {
+		nd_error(nhdl, "Failed to lookup event attr nvlist");
+		return (-1);
+	}
+
+	if ((ret = smf_notify_get_params(&top_nvl, ev_nvl)) != SCF_SUCCESS) {
+		ret = scf_error();
+		if (ret == SCF_ERROR_NOT_FOUND) {
+			nd_debug(nhdl, "No notification preferences specified "
+			    "for this event");
+			goto pref_done;
+		} else {
+			nd_error(nhdl, "Error looking up notification "
+			    "preferences (%s)", scf_strerror(ret));
+			nd_dump_nvlist(nhdl, top_nvl);
+			goto pref_done;
+		}
+	}
+
+	if (nvlist_lookup_nvlist_array(top_nvl, SCF_NOTIFY_PARAMS, &np_nvlarr,
+	    &nelem) != 0) {
+		nd_error(nhdl, "Malformed nvlist");
+		nd_dump_nvlist(nhdl, top_nvl);
+		ret = 1;
+		goto pref_done;
+	}
+	*pref_nvl = malloc(nelem * sizeof (nvlist_t *));
+	*nprefs = 0;
+
+	for (int i = 0; i < nelem; i++) {
+		if (nvlist_lookup_nvlist(np_nvlarr[i], mech, &mech_nvl) == 0) {
+			(void) nvlist_dup(mech_nvl, *pref_nvl + *nprefs, 0);
+			++*nprefs;
+		}
+	}
+
+	if (*nprefs == 0) {
+		nd_debug(nhdl, "No %s notification preferences specified",
+		    mech);
+		free(*pref_nvl);
+		ret = SCF_ERROR_NOT_FOUND;
+		goto pref_done;
+	}
+	ret = 0;
+pref_done:
+	nvlist_free(top_nvl);
+	return (ret);
+}
+
+static int
+nd_seq_search(char *key, char **list, uint_t nelem)
+{
+	for (int i = 0; i < nelem; i++)
+		if (strcmp(key, list[i]) == 0)
+			return (1);
+	return (0);
+}
+
+/*
+ * This function takes a single string list and splits it into
+ * an string array (analogous to PERL split)
+ *
+ * The caller is responsible for freeing the array.
+ */
+int
+nd_split_list(nd_hdl_t *nhdl, char *list, char *delim, char ***arr,
+    uint_t *nelem)
+{
+	char *item, *tmpstr;
+	int i = 1, size = 1;
+
+	tmpstr = strdup(list);
+	item = strtok(tmpstr, delim);
+	while (item && strtok(NULL, delim) != NULL)
+		size++;
+	free(tmpstr);
+
+	if ((*arr = calloc(size, sizeof (char *))) == NULL) {
+		nd_error(nhdl, "Error allocating memory (%s)", strerror(errno));
+		return (-1);
+	}
+	if (size == 1)
+		(*arr)[0] = strdup(list);
+	else {
+		tmpstr = strdup(list);
+		item = strtok(tmpstr, delim);
+		(*arr)[0] = strdup(item);
+		while ((item = strtok(NULL, delim)) != NULL)
+			(*arr)[i++] = strdup(item);
+		free(tmpstr);
+	}
+	*nelem = size;
+	return (0);
+}
+
+/*
+ * This function merges two string arrays into a single array, removing any
+ * duplicates
+ *
+ * The caller is responsible for freeing the merged array.
+ */
+int
+nd_merge_strarray(nd_hdl_t *nhdl, char **arr1, uint_t n1, char **arr2,
+    uint_t n2, char ***buf)
+{
+	char **tmparr;
+	int uniq = -1;
+
+	tmparr = alloca((n1 + n2) * sizeof (char *));
+	bzero(tmparr, (n1 + n2) * sizeof (char *));
+
+	while (++uniq < n1)
+		tmparr[uniq] = strdup(arr1[uniq]);
+
+	for (int j = 0; j < n2; j++)
+		if (!nd_seq_search(arr2[j], tmparr, uniq))
+			tmparr[uniq++] = strdup(arr2[j]);
+
+	if ((*buf = calloc(uniq, sizeof (char *))) == NULL) {
+		nd_error(nhdl, "Error allocating memory (%s)", strerror(errno));
+		for (int j = 0; j < uniq; j++) {
+			if (tmparr[j])
+				free(tmparr[j]);
+		}
+		return (-1);
+	}
+
+	bcopy(tmparr, *buf, uniq * sizeof (char *));
+	return (uniq);
+}
+
+void
+nd_free_strarray(char **arr, uint_t arrsz)
+{
+	for (uint_t i = 0; i < arrsz; i++)
+		free(arr[i]);
+	free(arr);
+}
+
+/*
+ * This function joins all the strings in a string array into a single string
+ * Each element will be delimited by a comma
+ *
+ * The caller is responsible for freeing the joined string.
+ */
+int
+nd_join_strarray(nd_hdl_t *nhdl, char **arr, uint_t arrsz, char **buf)
+{
+	uint_t len = 0;
+	char *jbuf;
+	int i;
+
+	/*
+	 * First, figure out how much space we need to allocate to store the
+	 * joined string.
+	 */
+	for (i = 0; i < arrsz; i++)
+		len += strlen(arr[i]) + 1;
+
+	if ((jbuf = calloc(len, sizeof (char))) == NULL) {
+		nd_error(nhdl, "Error allocating memory (%s)", strerror(errno));
+		return (-1);
+	}
+
+	(void) snprintf(jbuf, len, "%s", arr[0]);
+	for (i = 1; i < arrsz; i++)
+		(void) snprintf(jbuf, len, "%s,%s", jbuf, arr[i]);
+
+	*buf = jbuf;
+	return (0);
+}
+
+void
+nd_free_nvlarray(nvlist_t **arr, uint_t arrsz)
+{
+	for (uint_t i = 0; i < arrsz; i++)
+		nvlist_free(arr[i]);
+	free(arr);
+}
+
+/*
+ * This function takes a dictionary name and event class and then uses
+ * libdiagcode to compute the MSG ID.  We need this for looking up messages
+ * for the committed ireport.* events.  For FMA list.* events, the MSG ID is
+ * is contained in the event payload.
+ */
+int
+nd_get_diagcode(nd_hdl_t *nhdl, const char *dict, const char *class, char *buf,
+    size_t buflen)
+{
+	fm_dc_handle_t *dhp;
+	size_t dlen;
+	char *dirpath;
+	const char *key[2];
+	int ret = 0;
+
+	dlen = (strlen(nhdl->nh_rootdir) + strlen(ND_DICTDIR) + 2);
+	dirpath = alloca(dlen);
+	(void) snprintf(dirpath, dlen, "%s/%s", nhdl->nh_rootdir, ND_DICTDIR);
+
+	if ((dhp = fm_dc_opendict(FM_DC_VERSION, dirpath, dict)) == NULL) {
+		nd_error(nhdl, "fm_dc_opendict failed for %s/%s",
+		    dirpath, dict);
+		return (-1);
+	}
+
+	key[0] = class;
+	key[1] = NULL;
+	if (fm_dc_key2code(dhp, key, buf, buflen) < 0) {
+		nd_error(nhdl, "fm_dc_key2code failed for %s", key[0]);
+		ret = -1;
+	}
+	fm_dc_closedict(dhp);
+	return (ret);
+}
+
+/*
+ * This function takes an event and extracts the bits of the event payload that
+ * are of interest to notification daemons and conveniently tucks them into a
+ * single struct.
+ *
+ * The caller is responsible for freeing ev_info and any contained strings and
+ * nvlists.  A convenience function, nd_free_event_info(), is provided for this
+ * purpose.
+ */
+int
+nd_get_event_info(nd_hdl_t *nhdl, const char *class, fmev_t ev,
+    nd_ev_info_t **ev_info)
+{
+	nvlist_t *ev_nvl, *attr_nvl;
+	nd_ev_info_t *evi;
+	char *code, *uuid, *fmri, *from_state, *to_state, *reason;
+
+	if ((evi = calloc(1, sizeof (nd_ev_info_t))) == NULL) {
+		nd_error(nhdl, "Failed to allocate memory");
+		return (-1);
+	}
+
+	/*
+	 * Hold event; class and payload will be valid for as long as
+	 * we hold the event.
+	 */
+	fmev_hold(ev);
+	evi->ei_ev = ev;
+	ev_nvl = fmev_attr_list(ev);
+
+	/*
+	 * Lookup the MSGID, event description and severity and KA URL
+	 *
+	 * For FMA list.* events we just pull it out of the the event nvlist.
+	 * For all other events we call a utility function that computes the
+	 * diagcode using the dict name and class.
+	 */
+	evi->ei_diagcode = calloc(32, sizeof (char));
+	if ((nvlist_lookup_string(ev_nvl, FM_SUSPECT_DIAG_CODE, &code) == 0 &&
+	    strcpy(evi->ei_diagcode, code)) ||
+	    nd_get_diagcode(nhdl, "SMF", class, evi->ei_diagcode, 32)
+	    == 0) {
+		evi->ei_severity = fmd_msg_getitem_id(nhdl->nh_msghdl,
+		    NULL, evi->ei_diagcode, FMD_MSG_ITEM_SEVERITY);
+		evi->ei_descr = fmd_msg_getitem_id(nhdl->nh_msghdl,
+		    NULL, evi->ei_diagcode, FMD_MSG_ITEM_DESC);
+		evi->ei_url = fmd_msg_getitem_id(nhdl->nh_msghdl,
+		    NULL, evi->ei_diagcode, FMD_MSG_ITEM_URL);
+	} else
+		(void) strcpy(evi->ei_diagcode, ND_UNKNOWN);
+
+	if (!evi->ei_severity)
+		evi->ei_severity = strdup(ND_UNKNOWN);
+	if (!evi->ei_descr)
+		evi->ei_descr = strdup(ND_UNKNOWN);
+	if (!evi->ei_url)
+		evi->ei_url = strdup(ND_UNKNOWN);
+
+	evi->ei_payload = ev_nvl;
+	evi->ei_class = fmev_class(ev);
+	if (nvlist_lookup_string(ev_nvl, FM_SUSPECT_UUID, &uuid) == 0)
+		evi->ei_uuid = strdup(uuid);
+	else {
+		nd_error(nhdl, "Malformed event");
+		nd_dump_nvlist(nhdl, evi->ei_payload);
+		nd_free_event_info(evi);
+		return (-1);
+	}
+
+	if (strncmp(class, "ireport.os.smf", 14) == 0) {
+		if ((fmri = nd_get_event_fmri(nhdl, ev)) == NULL) {
+			nd_error(nhdl, "Failed to get fmri from event payload");
+			nd_free_event_info(evi);
+			return (-1);
+		}
+		if (nvlist_lookup_nvlist(evi->ei_payload, "attr", &attr_nvl) ||
+		    nvlist_lookup_string(attr_nvl, "from-state", &from_state) ||
+		    nvlist_lookup_string(attr_nvl, "to-state", &to_state) ||
+		    nvlist_lookup_string(attr_nvl, "reason-long", &reason)) {
+			nd_error(nhdl, "Malformed event");
+			nd_dump_nvlist(nhdl, evi->ei_payload);
+			nd_free_event_info(evi);
+			free(fmri);
+			return (-1);
+		}
+		evi->ei_fmri = fmri;
+		evi->ei_to_state = strdup(to_state);
+		evi->ei_from_state = strdup(from_state);
+		evi->ei_reason = strdup(reason);
+	}
+	*ev_info = evi;
+	return (0);
+}
+
+static void
+condfree(void *buf)
+{
+	if (buf != NULL)
+		free(buf);
+}
+
+void
+nd_free_event_info(nd_ev_info_t *ev_info)
+{
+	condfree(ev_info->ei_severity);
+	condfree(ev_info->ei_descr);
+	condfree(ev_info->ei_diagcode);
+	condfree(ev_info->ei_url);
+	condfree(ev_info->ei_uuid);
+	condfree(ev_info->ei_fmri);
+	condfree(ev_info->ei_from_state);
+	condfree(ev_info->ei_to_state);
+	condfree(ev_info->ei_reason);
+	fmev_rele(ev_info->ei_ev);
+	free(ev_info);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/fm/libfmnotify/common/libfmnotify.h	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,109 @@
+/*
+ * 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.
+ */
+#ifndef _LIBFMNOTIFY_H
+#define	_LIBFMNOTIFY_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <alloca.h>
+#include <errno.h>
+#include <libscf.h>
+#include <limits.h>
+#include <strings.h>
+#include <sys/corectl.h>
+#include <sys/resource.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <fm/diagcode.h>
+#include <fm/fmd_msg.h>
+#include <fm/libfmevent.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define	ND_DICTDIR "usr/lib/fm/dict"
+#define	ND_UNKNOWN "UNKNOWN"
+
+typedef struct nd_hdl {
+	boolean_t	nh_debug;
+	boolean_t	nh_is_daemon;
+	boolean_t	nh_keep_running;
+	/* handle for libfmevent calls */
+	fmev_shdl_t	nh_evhdl;
+	/* handle for libfmd_msg calls */
+	fmd_msg_hdl_t	*nh_msghdl;
+	FILE		*nh_log_fd;
+	char		*nh_rootdir;
+	const char	*nh_pname;
+} nd_hdl_t;
+
+const char FMNOTIFY_MSG_DOMAIN[] = "FMNOTIFY";
+
+typedef struct nd_ev_info {
+	fmev_t ei_ev;
+	const char *ei_class;
+	char *ei_descr;
+	char *ei_severity;
+	char *ei_diagcode;
+	char *ei_url;
+	char *ei_uuid;
+	char *ei_fmri;
+	char *ei_from_state;
+	char *ei_to_state;
+	char *ei_reason;
+	nvlist_t *ei_payload;
+} nd_ev_info_t;
+
+
+void nd_cleanup(nd_hdl_t *);
+void nd_dump_nvlist(nd_hdl_t *, nvlist_t *);
+void nd_debug(nd_hdl_t *, const char *, ...);
+void nd_error(nd_hdl_t *, const char *, ...);
+void nd_abort(nd_hdl_t *, const char *, ...);
+void nd_daemonize(nd_hdl_t *);
+int nd_get_boolean_prop(nd_hdl_t *, const char *, const char *, const char *,
+    uint8_t *);
+int nd_get_astring_prop(nd_hdl_t *, const char *, const char *, const char *,
+    char **);
+char *nd_get_event_fmri(nd_hdl_t *, fmev_t);
+int nd_get_event_info(nd_hdl_t *, const char *, fmev_t, nd_ev_info_t **);
+int nd_get_notify_prefs(nd_hdl_t *, const char *, fmev_t, nvlist_t ***,
+    uint_t *);
+int nd_split_list(nd_hdl_t *, char *, char *, char ***, uint_t *);
+int nd_join_strarray(nd_hdl_t *, char **, uint_t, char **);
+int nd_merge_strarray(nd_hdl_t *, char **, uint_t, char **, uint_t, char ***);
+void nd_free_event_info(nd_ev_info_t *);
+void nd_free_nvlarray(nvlist_t **, uint_t);
+void nd_free_strarray(char **, uint_t);
+int nd_get_diagcode(nd_hdl_t *, const char *, const char *, char *, size_t);
+
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _LIBFMNOTIFY_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/fm/libfmnotify/common/llib-lfmnotify	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+/*LINTLIBRARY*/
+/*PROTOLIB1*/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/fm/libfmnotify/common/mapfile-vers	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,61 @@
+#
+# 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.
+#
+# MAPFILE HEADER START
+#
+# WARNING:  STOP NOW.  DO NOT MODIFY THIS FILE.
+# Object versioning must comply with the rules detailed in
+#
+#	usr/src/lib/README.mapfiles
+#
+# You should not be making modifications here until you've read the most current
+# copy of that file. If you need help, contact a gatekeeper for guidance.
+#
+# MAPFILE HEADER END
+#
+
+$mapfile_version 2
+
+SYMBOL_VERSION SUNWprivate {
+    global:
+	nd_abort;
+	nd_cleanup;
+	nd_daemonize;
+	nd_debug;
+	nd_dump_nvlist;
+	nd_error;
+	nd_free_event_info;
+	nd_free_nvlarray;
+	nd_free_strarray;
+	nd_get_astring_prop;
+	nd_get_boolean_prop;
+	nd_get_diagcode;
+	nd_get_event_fmri;
+	nd_get_event_info;
+	nd_get_notify_prefs;
+	nd_join_strarray;
+	nd_merge_strarray;
+	nd_split_list;
+    local:
+	*;
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/fm/libfmnotify/i386/Makefile	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,32 @@
+#
+# 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.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
+
+MACH_LDLIBS = -L$(ROOT)/usr/lib/fm
+
+DYNFLAGS += -R/usr/lib/fm
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/fm/libfmnotify/sparc/Makefile	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,31 @@
+#
+# 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.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
+
+MACH_LDLIBS = -L$(ROOT)/usr/lib/fm
+
+DYNFLAGS += -R/usr/lib/fm
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/fm/libfmnotify/sparcv9/Makefile	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,32 @@
+#
+# 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.
+#
+
+MACH_LDLIBS = -L$(ROOT)/usr/lib/fm/$(MACH64)
+
+include ../Makefile.com
+include ../../../Makefile.lib.64
+
+DYNFLAGS += -R/usr/lib/fm/$(MACH64)
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64)
--- a/usr/src/lib/fm/topo/libtopo/Makefile.com	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/lib/fm/topo/libtopo/Makefile.com	Fri Jul 30 17:04:17 2010 +1000
@@ -19,8 +19,7 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
+# Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
 #
 
 LIBRARY = libtopo.a
@@ -36,6 +35,7 @@
 	mod.c \
 	pkg.c \
 	svc.c \
+	sw.c \
 	zfs.c
 
 LIBSRCS = \
--- a/usr/src/lib/fm/topo/libtopo/common/dev.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/lib/fm/topo/libtopo/common/dev.c	Fri Jul 30 17:04:17 2010 +1000
@@ -114,6 +114,11 @@
 dev_enum(topo_mod_t *mod, tnode_t *pnode, const char *name,
     topo_instance_t min, topo_instance_t max, void *notused1, void *notused2)
 {
+	/*
+	 * Methods are registered, but there is no enumeration.  Should
+	 * enumeration be added be sure to cater for global vs non-global
+	 * zones.
+	 */
 	(void) topo_method_register(mod, pnode, dev_methods);
 	return (0);
 }
--- a/usr/src/lib/fm/topo/libtopo/common/fmd.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/lib/fm/topo/libtopo/common/fmd.c	Fri Jul 30 17:04:17 2010 +1000
@@ -21,11 +21,9 @@
  */
 
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -96,6 +94,11 @@
 fmd_enum(topo_mod_t *mod, tnode_t *pnode, const char *name, topo_instance_t min,
     topo_instance_t max, void *notused1, void *notused2)
 {
+	/*
+	 * Methods are registered, but there is no enumeration.  Should
+	 * enumeration be added be sure to cater for global vs non-global
+	 * zones.
+	 */
 	(void) topo_method_register(mod, pnode, fmd_methods);
 	return (0);
 }
--- a/usr/src/lib/fm/topo/libtopo/common/hc.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/lib/fm/topo/libtopo/common/hc.c	Fri Jul 30 17:04:17 2010 +1000
@@ -32,6 +32,7 @@
 #include <alloca.h>
 #include <assert.h>
 #include <limits.h>
+#include <zone.h>
 #include <fm/topo_mod.h>
 #include <fm/topo_hc.h>
 #include <fm/fmd_fmri.h>
@@ -311,6 +312,7 @@
 hc_enum(topo_mod_t *mod, tnode_t *pnode, const char *name, topo_instance_t min,
     topo_instance_t max, void *notused1, void *notused2)
 {
+	int isglobal = (getzoneid() == GLOBAL_ZONEID);
 	nvlist_t *pfmri = NULL;
 	nvlist_t *nvl;
 	nvlist_t *auth;
@@ -331,6 +333,9 @@
 		return (topo_mod_seterrno(mod, EINVAL));
 	}
 
+	if (!isglobal)
+		return (0);
+
 	(void) topo_node_resource(pnode, &pfmri, &err);
 	auth = topo_mod_auth(mod, pnode);
 	nvl = hc_fmri_create(mod, pfmri, FM_HC_SCHEME_VERSION, name, min,
--- a/usr/src/lib/fm/topo/libtopo/common/mem.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/lib/fm/topo/libtopo/common/mem.c	Fri Jul 30 17:04:17 2010 +1000
@@ -20,18 +20,16 @@
  */
 
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <ctype.h>
 #include <errno.h>
 #include <kstat.h>
 #include <limits.h>
 #include <strings.h>
 #include <unistd.h>
+#include <zone.h>
 #include <topo_error.h>
 #include <fm/topo_mod.h>
 #include <sys/fm/protocol.h>
@@ -96,9 +94,10 @@
 mem_enum(topo_mod_t *mod, tnode_t *pnode, const char *name,
     topo_instance_t min, topo_instance_t max, void *notused1, void *notused2)
 {
+	int isglobal = (getzoneid() == GLOBAL_ZONEID);
 	topo_mod_t *nmp;
 
-	if ((nmp = topo_mod_load(mod, PLATFORM_MEM_NAME,
+	if (isglobal && (nmp = topo_mod_load(mod, PLATFORM_MEM_NAME,
 	    PLATFORM_MEM_VERSION)) == NULL) {
 		if (topo_mod_errno(mod) == ETOPO_MOD_NOENT) {
 			/*
@@ -114,7 +113,7 @@
 		}
 	}
 
-	if (topo_mod_enumerate(nmp, pnode, PLATFORM_MEM_NAME, name,
+	if (isglobal && topo_mod_enumerate(nmp, pnode, PLATFORM_MEM_NAME, name,
 	    min, max, NULL) < 0) {
 		topo_mod_dprintf(mod, "%s failed to enumerate: %s",
 		    PLATFORM_MEM_NAME, topo_mod_errmsg(mod));
--- a/usr/src/lib/fm/topo/libtopo/common/mod.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/lib/fm/topo/libtopo/common/mod.c	Fri Jul 30 17:04:17 2010 +1000
@@ -20,8 +20,7 @@
  */
 
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #include <limits.h>
@@ -94,6 +93,11 @@
 mod_enum(topo_mod_t *mod, tnode_t *pnode, const char *name,
     topo_instance_t min, topo_instance_t max, void *notused1, void *notused2)
 {
+	/*
+	 * Methods are registered, but there is no enumeration.  Should
+	 * enumeration be added be sure to cater for global vs non-global
+	 * zones.
+	 */
 	(void) topo_method_register(mod, pnode, mod_methods);
 	return (0);
 }
--- a/usr/src/lib/fm/topo/libtopo/common/pkg.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/lib/fm/topo/libtopo/common/pkg.c	Fri Jul 30 17:04:17 2010 +1000
@@ -20,8 +20,7 @@
  */
 
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #include <limits.h>
@@ -74,7 +73,7 @@
 {
 	if (getenv("TOPOPKGDEBUG"))
 		topo_mod_setdebug(mod);
-	topo_mod_dprintf(mod, "initializing mod builtin\n");
+	topo_mod_dprintf(mod, "initializing pkg builtin\n");
 
 	if (version != PKG_VERSION)
 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
@@ -99,6 +98,11 @@
 pkg_enum(topo_mod_t *mod, tnode_t *pnode, const char *name,
     topo_instance_t min, topo_instance_t max, void *notused1, void *notused2)
 {
+	/*
+	 * Methods are registered, but there is no enumeration.  Should
+	 * enumeration be added be sure to cater for global vs non-global
+	 * zones.
+	 */
 	(void) topo_method_register(mod, pnode, pkg_methods);
 	return (0);
 }
--- a/usr/src/lib/fm/topo/libtopo/common/svc.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/lib/fm/topo/libtopo/common/svc.c	Fri Jul 30 17:04:17 2010 +1000
@@ -20,21 +20,20 @@
  */
 
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 /*
  * This provides the basic mechanisms (str2nvl and nvl2str) for dealing with
  * the service schema.  The official version of a svc FMRI has the form:
  *
- * 	svc://[scope@][system-fqn]/service[:instance][@contract-id]
+ *	svc://[scope@][system-fqn]/service[:instance][@contract-id]
  *
  * Where 'service' is a slash-delimited list of names.  Of these fields, the
  * scope, constract-id, and system-fqn are rarely used, leaving the much more
  * common form such as:
  *
- * 	svc:///network/ssh:default
+ *	svc:///network/ssh:default
  *
  * Note that the SMF software typically uses a shorthard form, where the
  * authority is elided (svc:/network/ssh:default).  As this module deals with
@@ -49,6 +48,7 @@
 #include <sys/fm/protocol.h>
 #include <topo_method.h>
 #include <topo_subr.h>
+#include <topo_prop.h>
 #include <alloca.h>
 #include <assert.h>
 #include <svc.h>
@@ -67,11 +67,13 @@
     nvlist_t *, nvlist_t **);
 static int svc_fmri_unusable(topo_mod_t *, tnode_t *, topo_version_t,
     nvlist_t *, nvlist_t **);
-static int svc_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t,
-    topo_instance_t, void *, void *);
-static void svc_release(topo_mod_t *, tnode_t *);
+static int svc_fmri_prop_get(topo_mod_t *, tnode_t *, topo_version_t,
+    nvlist_t *, nvlist_t **);
 
 static const topo_method_t svc_methods[] = {
+	{ TOPO_METH_PROP_GET, TOPO_METH_PROP_GET_DESC,
+	    TOPO_METH_PROP_GET_VERSION, TOPO_STABILITY_INTERNAL,
+	    svc_fmri_prop_get },
 	{ TOPO_METH_NVL2STR, TOPO_METH_NVL2STR_DESC, TOPO_METH_NVL2STR_VERSION,
 	    TOPO_STABILITY_INTERNAL, svc_fmri_nvl2str },
 	{ TOPO_METH_STR2NVL, TOPO_METH_STR2NVL_DESC, TOPO_METH_STR2NVL_VERSION,
@@ -90,6 +92,10 @@
 	{ NULL }
 };
 
+static int svc_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t,
+    topo_instance_t, void *, void *);
+static void svc_release(topo_mod_t *, tnode_t *);
+
 static const topo_modops_t svc_ops =
 	{ svc_enum, svc_release };
 static const topo_modinfo_t svc_info =
@@ -134,6 +140,9 @@
 int
 svc_init(topo_mod_t *mod, topo_version_t version)
 {
+	if (getenv("TOPOSVCDEBUG"))
+		topo_mod_setdebug(mod);
+
 	if (version != SVC_VERSION)
 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
 
@@ -157,13 +166,160 @@
 	topo_mod_unregister(mod);
 }
 
+static tnode_t *
+svc_create_node(topo_mod_t *mod, tnode_t *pnode, char *fmristr)
+{
+	nvlist_t *fmri;
+	tnode_t *tn;
+	char *fixed;
+	ssize_t len;
+	int i, j, err;
+
+	/*
+	 * the scf_{x}_to_fmri interfaces return short-hand svc-scheme FMRI's
+	 * that look like:
+	 *
+	 * svc:/service[:instance]
+	 *
+	 * But all our other code assumes a proper svc-scheme FMRI, so we
+	 * correct the fmri string before we try to convert it to an nvlist.
+	 *
+	 * The short-hand version is kept as the label and can be used when
+	 * dealing with the SMF libraries and CLI's.
+	 */
+	len = strlen(fmristr) + 1;
+	if ((fixed = topo_mod_zalloc(mod, len + 1)) == NULL) {
+		(void) topo_mod_seterrno(mod, EMOD_NOMEM);
+		topo_mod_dprintf(mod, "topo_mod_zalloc() failed: %s",
+		    topo_mod_errmsg(mod));
+		return (NULL);
+	}
+	for (i = 0, j = 0; i < len; i++)
+		if (i == 5)
+			fixed[i] = '/';
+		else
+			fixed[i] = fmristr[j++];
+	fixed[i] = '\0';
+
+	if (topo_mod_str2nvl(mod, fixed, &fmri) < 0) {
+		topo_mod_dprintf(mod, "topo_mod_str2nvl() failed: %s",
+		    topo_mod_errmsg(mod));
+		topo_mod_free(mod, fixed, len + 1);
+		return (NULL);
+	}
+	topo_mod_free(mod, fixed, len + 1);
+
+	if (topo_node_range_create(mod, pnode, fmristr, 0, 0) < 0) {
+		topo_mod_dprintf(mod, "topo_node_range_create() failed: %s",
+		    topo_mod_errmsg(mod));
+		nvlist_free(fmri);
+		return (NULL);
+	}
+	if ((tn = topo_node_bind(mod, pnode, fmristr, 0, fmri)) == NULL) {
+		topo_mod_dprintf(mod, "topo_node_bind() failed: %s",
+		    topo_mod_errmsg(mod));
+		nvlist_free(fmri);
+		return (NULL);
+	}
+	nvlist_free(fmri);
+
+	if (topo_node_label_set(tn, fmristr, &err) != 0) {
+		topo_mod_dprintf(mod, "failed to set label: %s\n",
+		    topo_strerror(err));
+		return (NULL);
+	}
+	(void) topo_method_register(mod, tn, svc_methods);
+
+	return (tn);
+}
+
 /*ARGSUSED*/
 static int
 svc_enum(topo_mod_t *mod, tnode_t *pnode, const char *name,
     topo_instance_t min, topo_instance_t max, void *notused1, void *notused2)
 {
+	scf_handle_t *hdl;
+	scf_scope_t *sc = NULL;
+	scf_iter_t *svc_iter = NULL;
+	scf_iter_t *inst_iter = NULL;
+	scf_service_t *svc = NULL;
+	scf_instance_t *inst = NULL;
+	int ret = -1;
+	char *sfmri, *ifmri;
+	ssize_t slen, ilen;
+	tnode_t *svc_node;
+
 	(void) topo_method_register(mod, pnode, svc_methods);
-	return (0);
+
+	if ((hdl = svc_get_handle(mod)) == NULL)
+		goto out;
+
+	if ((sc = scf_scope_create(hdl)) == NULL ||
+	    (svc = scf_service_create(hdl)) == NULL ||
+	    (inst = scf_instance_create(hdl)) == NULL ||
+	    (svc_iter = scf_iter_create(hdl)) == NULL ||
+	    (inst_iter = scf_iter_create(hdl)) == NULL)
+		goto out;
+
+	if (scf_handle_get_scope(hdl, SCF_SCOPE_LOCAL, sc) != 0)
+		goto out;
+
+	if (scf_iter_scope_services(svc_iter, sc) != 0)
+		goto out;
+
+	while (scf_iter_next_service(svc_iter, svc) == 1) {
+		if (scf_iter_service_instances(inst_iter, svc) != 0)
+			continue;
+
+		if ((slen = scf_service_to_fmri(svc, NULL, 0)) < 0)
+			continue;
+
+		if ((sfmri = topo_mod_zalloc(mod, slen + 1)) == NULL) {
+			(void) topo_mod_seterrno(mod, EMOD_NOMEM);
+			goto out;
+		}
+		if (scf_service_to_fmri(svc, sfmri, slen + 1) == -1)
+			goto out;
+
+		if ((svc_node = svc_create_node(mod, pnode, sfmri)) == NULL) {
+			topo_mod_free(mod, sfmri, slen + 1);
+			/* topo mod errno set */
+			goto out;
+		}
+
+		while (scf_iter_next_instance(inst_iter, inst) == 1) {
+			if ((ilen = scf_instance_to_fmri(inst, NULL, 0)) < 0)
+				continue;
+
+			if ((ifmri = topo_mod_zalloc(mod, ilen + 1))
+			    == NULL) {
+				(void) topo_mod_seterrno(mod, EMOD_NOMEM);
+				topo_mod_free(mod, sfmri, slen + 1);
+				goto out;
+			}
+			if (scf_instance_to_fmri(inst, ifmri, ilen + 1) == -1)
+				goto out;
+
+			if ((svc_node = svc_create_node(mod, svc_node, ifmri))
+			    == NULL) {
+				topo_mod_free(mod, sfmri, slen + 1);
+				topo_mod_free(mod, ifmri, ilen + 1);
+				/* topo mod errno set */
+				goto out;
+			}
+			topo_mod_free(mod, ifmri, ilen + 1);
+		}
+		topo_mod_free(mod, sfmri, slen + 1);
+	}
+	ret = 0;
+out:
+	scf_scope_destroy(sc);
+	scf_service_destroy(svc);
+	scf_instance_destroy(inst);
+	scf_iter_destroy(svc_iter);
+	scf_iter_destroy(inst_iter);
+
+	return (ret);
 }
 
 static void
@@ -187,6 +343,76 @@
 	return (B_TRUE);
 }
 
+static int
+svc_fmri_prop_get(topo_mod_t *mod, tnode_t *node, topo_version_t version,
+    nvlist_t *in, nvlist_t **out)
+{
+	char *svc_name, *svc_inst = NULL;
+	nvlist_t *rsrc, *args;
+	char *pgroup, *pname;
+	tnode_t *svc_node;
+	char *search;
+	size_t len;
+	int err;
+
+	if (version > TOPO_METH_PROP_GET_VERSION)
+		return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW));
+
+	err = nvlist_lookup_string(in, TOPO_PROP_GROUP, &pgroup);
+	err |= nvlist_lookup_string(in, TOPO_PROP_VAL_NAME, &pname);
+	err |= nvlist_lookup_nvlist(in, TOPO_PROP_RESOURCE, &rsrc);
+	if (err != 0)
+		return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
+
+	/*
+	 * Private args to prop method are optional
+	 */
+	if ((err = nvlist_lookup_nvlist(in, TOPO_PROP_PARGS, &args)) != 0) {
+		if (err != ENOENT)
+			return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
+		else
+			args = NULL;
+	}
+
+	/*
+	 * Lookup a topo node named svc:/svc_name[:svc_inst]
+	 */
+	if (nvlist_lookup_string(rsrc, FM_FMRI_SVC_NAME, &svc_name) != 0)
+		return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
+
+	(void) nvlist_lookup_string(rsrc, FM_FMRI_SVC_INSTANCE, &svc_inst);
+
+	len = 5 + strlen(svc_name) +
+	    (svc_inst != NULL ? 1 + strlen(svc_inst) : 0) + 1;
+
+	if ((search = topo_mod_alloc(mod, len)) == NULL)
+		return (topo_mod_seterrno(mod, EMOD_NOMEM));
+
+	(void) snprintf(search, len, "svc:/%s", svc_name);
+	svc_node = topo_node_lookup(node, (const char *)search, 0);
+
+	if (svc_node == NULL) {
+		topo_mod_free(mod, search, len);
+		return (topo_mod_seterrno(mod, EMOD_NODE_NOENT));
+	}
+
+	if (svc_inst != NULL) {
+		(void) snprintf(search, len, "svc:/%s:%s", svc_name, svc_inst);
+		svc_node = topo_node_lookup(svc_node, (const char *)search, 0);
+		if (svc_node == NULL) {
+			topo_mod_free(mod, search, len);
+			return (topo_mod_seterrno(mod, EMOD_NODE_NOENT));
+		}
+	}
+
+	topo_mod_free(mod, search, len);
+
+	err = 0;
+	(void) topo_prop_getprop(svc_node, pgroup, pname, args, out, &err);
+
+	return (err);
+}
+
 /*ARGSUSED*/
 static int
 svc_fmri_nvl2str(topo_mod_t *mod, tnode_t *node, topo_version_t version,
@@ -417,10 +643,56 @@
 }
 
 /*
- * This common function is shared by all consumers (present, unusable, and
- * service_state).  It returns one of the FMD_SERVICE_STATE_* states, where
- * FMD_SERVICE_STATE_UNKNOWN means that the FMRI is not present.
+ * This common function is shared by all consumers (present, replaced,
+ * service state and unusable).
+ *
+ *				svc_get_state succeeds
+ * Case				with FMD_SERVICE_STATE_*
+ * ----------------------------	------------------------
+ * svc name deleted		UNKNOWN
+ * svc name not found		UNKNOWN
+ * no fmri instance		OK
+ * instance deleted		UNKNOWN
+ * instance not found		UNKNOWN
+ *
+ * If none of the above apply and this is a call from the "present"
+ * or "replaced" method (presence_only == B_TRUE) then
+ * svc_get_state returns FMD_SERVICE_STATE_OK.
+ *
+ * The "present" method maps a svc_get_state return of UNKNOWN to
+ * "not present" and a svc_get_state return of OK to "present".
+ *
+ * The "replaced" methods maps a return of UNKNOWN to FMD_OBJ_STATE_NOT_PRESENT
+ * and OK to FMD_OBJ_STATE_UNKNOWN.
+ *
+ * For the "service state" and "unusable" methods svc_get_state goes on
+ * to return the instance state as below, and the two methods map that
+ * result as in the last two columns of the following table:
+ *
+ *			svc_get_state succeeds		Service
+ * Instance state	with FMD_SERVICE_STATE_*	State		Unusable
+ * --------------	-------------------------------	---------------	--------
+ * none			OK				OK
+ * uninitialized	OK				OK
+ * maintenance		UNUSABLE			UNUSABLE	Yes
+ * offline		OK				OK
+ * disabled		OK				OK
+ * online		OK				OK
+ * degraded		DEGRADED			DEGRADED
+ * legacy_run		OK (XXX can we see this?)	OK
+ *
+ * Note that *only* "maintenance" state should map to an unusable service state
+ * or unusable status.  That's because a service entering maintenance state
+ * is modelled as a defect fault diagnosis in FMA, but there is no
+ * corresponding isolation action from a response agent since the the service
+ * is already isolated by virtue of being in maintenance state.  Any transition
+ * from maintenance state, even to offline, is considered a repair.  If on
+ * repair fmd does not see the service usable again then the case hangs
+ * around in the "resolved but not all resources back online" state and
+ * further maintenance events for this service will not show up in fmd state
+ * because case duplicate checking code will find the old case.
  */
+
 static int
 svc_get_state(topo_mod_t *mod, nvlist_t *fmri, boolean_t presence_only,
     int *ret)
@@ -473,10 +745,6 @@
 		}
 	}
 
-	/*
-	 * If there is no instance, then it is always present, and always
-	 * usuable.
-	 */
 	if (nvlist_lookup_string(fmri, FM_FMRI_SVC_INSTANCE, &instance) != 0) {
 		*ret = FMD_SERVICE_STATE_OK;
 		goto out;
@@ -517,12 +785,13 @@
 	if (scf_value_get_astring(val, state, len + 1) < 0)
 		goto error;
 
-	if (strcmp(state, SCF_STATE_STRING_MAINT) == 0)
+	if (strcmp(state, SCF_STATE_STRING_MAINT) == 0) {
 		*ret = FMD_SERVICE_STATE_UNUSABLE;
-	else if (strcmp(state, SCF_STATE_STRING_DEGRADED) == 0)
+	} else if (strcmp(state, SCF_STATE_STRING_DEGRADED) == 0) {
 		*ret = FMD_SERVICE_STATE_DEGRADED;
-	else
+	} else {
 		*ret = FMD_SERVICE_STATE_OK;
+	}
 	goto out;
 
 error:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/fm/topo/libtopo/common/sw.c	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,530 @@
+/*
+ * 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 <libnvpair.h>
+#include <fm/topo_mod.h>
+
+#include <sys/fm/protocol.h>
+#include <sys/types.h>
+
+#include <topo_method.h>
+#include <topo_subr.h>
+#include <sw.h>
+
+static int sw_fmri_nvl2str(topo_mod_t *, tnode_t *, topo_version_t,
+    nvlist_t *, nvlist_t **);
+static int sw_fmri_create(topo_mod_t *, tnode_t *, topo_version_t,
+    nvlist_t *, nvlist_t **);
+
+static const topo_method_t sw_methods[] = {
+	{ TOPO_METH_NVL2STR, TOPO_METH_NVL2STR_DESC, TOPO_METH_NVL2STR_VERSION,
+	    TOPO_STABILITY_INTERNAL, sw_fmri_nvl2str },
+	{ TOPO_METH_FMRI, TOPO_METH_FMRI_DESC, TOPO_METH_FMRI_VERSION,
+	    TOPO_STABILITY_INTERNAL, sw_fmri_create },
+	{ NULL }
+};
+
+static int sw_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t,
+    topo_instance_t, void *, void *);
+static void sw_release(topo_mod_t *, tnode_t *);
+
+static const topo_modops_t sw_ops =
+	{ sw_enum, sw_release };
+
+static const topo_modinfo_t sw_info =
+	{ "sw", FM_FMRI_SCHEME_SW, SW_VERSION, &sw_ops };
+
+int
+sw_init(topo_mod_t *mod, topo_version_t version)
+{
+	if (getenv("TOPOSWDEBUG"))
+		topo_mod_setdebug(mod);
+	topo_mod_dprintf(mod, "initializing sw builtin\n");
+
+	if (version != SW_VERSION)
+		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
+
+	if (topo_mod_register(mod, &sw_info, TOPO_VERSION) != 0) {
+		topo_mod_dprintf(mod, "failed to register sw_info: "
+		    "%s\n", topo_mod_errmsg(mod));
+		return (-1);
+	}
+
+	return (0);
+}
+
+void
+sw_fini(topo_mod_t *mod)
+{
+	topo_mod_unregister(mod);
+}
+
+static int
+sw_get_optl_string(nvlist_t *nvl, char *name, char **dest)
+{
+	if (nvlist_lookup_string(nvl, name, dest) == 0) {
+		return (0);
+	} else {
+		*dest = NULL;
+		return (errno == ENOENT ? 0 : 1);
+	}
+}
+
+static int
+sw_get_optl_int64(nvlist_t *nvl, char *name, int64_t *dest)
+{
+	if (nvlist_lookup_int64(nvl, name, dest) == 0) {
+		return (0);
+	} else {
+		*dest = -1;
+		return (errno == ENOENT ? 0 : 1);
+	}
+}
+
+static int
+sw_get_optl_nvlist(nvlist_t *nvl, char *name, nvlist_t **dest)
+{
+	if (nvlist_lookup_nvlist(nvl, name, dest) == 0) {
+		return (0);
+	} else {
+		*dest = NULL;
+		return (errno == ENOENT ? 0 : 1);
+	}
+}
+
+static int
+sw_add_optl_string(nvlist_t *nvl, char *name, char *val)
+{
+	if (val)
+		return (nvlist_add_string(nvl, name, val) != 0);
+	else
+		return (0);
+}
+
+/*ARGSUSED*/
+static int
+sw_fmri_create(topo_mod_t *mod, tnode_t *node, topo_version_t version,
+    nvlist_t *in, nvlist_t **out)
+{
+	nvlist_t *args, *fmri = NULL, *obj = NULL, *site = NULL, *ctxt = NULL;
+	topo_mod_errno_t moderr;
+	int err = 0;
+
+	char *obj_path, *obj_root;
+	nvlist_t *obj_pkg;
+
+	char *site_token, *site_module, *site_file, *site_func;
+	int64_t site_line;
+
+	char *ctxt_origin, *ctxt_execname, *ctxt_zone;
+	int64_t ctxt_pid, ctxt_ctid;
+	char **ctxt_stack;
+	uint_t ctxt_stackdepth;
+
+
+	if (version > TOPO_METH_FMRI_VERSION)
+		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
+
+	if (nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_NVL, &args) != 0)
+		return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
+
+	if (nvlist_lookup_string(args, "obj_path", &obj_path) != 0)
+		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
+	err |= sw_get_optl_string(args, "obj_root", &obj_root);
+	err |= sw_get_optl_nvlist(args, "obj-pkg", &obj_pkg);
+
+	err |= sw_get_optl_string(args, "site_token", &site_token);
+	err |= sw_get_optl_string(args, "site_module", &site_module);
+	err |= sw_get_optl_string(args, "site_file", &site_file);
+	err |= sw_get_optl_string(args, "site_func", &site_func);
+	err |= sw_get_optl_int64(args, "site_line", &site_line);
+
+	err |= sw_get_optl_string(args, "ctxt_origin", &ctxt_origin);
+	err |= sw_get_optl_string(args, "ctxt_execname", &ctxt_execname);
+	err |= sw_get_optl_string(args, "ctxt_zone", &ctxt_zone);
+	err |= sw_get_optl_int64(args, "ctxt_pid", &ctxt_pid);
+	err |= sw_get_optl_int64(args, "ctxt_ctid", &ctxt_ctid);
+
+	if (nvlist_lookup_string_array(args, "stack", &ctxt_stack,
+	    &ctxt_stackdepth) != 0) {
+		if (errno == ENOENT)
+			ctxt_stack = NULL;
+		else
+			err++;
+	}
+
+	if (err)
+		(void) topo_mod_seterrno(mod, EMOD_FMRI_NVL);
+
+	if (topo_mod_nvalloc(mod, &fmri, NV_UNIQUE_NAME) != 0 ||
+	    topo_mod_nvalloc(mod, &obj, NV_UNIQUE_NAME) != 0) {
+		moderr = EMOD_NOMEM;
+		goto out;
+	}
+
+	/*
+	 * Add standard FMRI members 'version' and 'scheme'.
+	 */
+	err |= nvlist_add_uint8(fmri, FM_VERSION, FM_SW_SCHEME_VERSION);
+	err |= nvlist_add_string(fmri, FM_FMRI_SCHEME, FM_FMRI_SCHEME_SW);
+
+	/*
+	 * Build up the 'object' nvlist.
+	 */
+	err |= nvlist_add_string(obj, FM_FMRI_SW_OBJ_PATH, obj_path);
+	err |= sw_add_optl_string(obj, FM_FMRI_SW_OBJ_ROOT, obj_root);
+	if (obj_pkg)
+		err |= nvlist_add_nvlist(obj, FM_FMRI_SW_OBJ_PKG, obj_pkg);
+
+	/*
+	 * Add 'object' to the fmri.
+	 */
+	if (err == 0)
+		err |= nvlist_add_nvlist(fmri, FM_FMRI_SW_OBJ, obj);
+
+	if (err) {
+		moderr = EMOD_NOMEM;
+		goto out;
+	}
+
+	/*
+	 * Do we have anything for a 'site' nvlist?
+	 */
+	if (site_token == NULL && site_module == NULL && site_file == NULL &&
+	    site_func == NULL && site_line == -1)
+		goto context;
+
+	/*
+	 * Allocate and build 'site' nvlist.
+	 */
+	if (topo_mod_nvalloc(mod, &site, NV_UNIQUE_NAME) != 0) {
+		moderr = EMOD_NOMEM;
+		goto out;
+	}
+
+	err |= sw_add_optl_string(site, FM_FMRI_SW_SITE_TOKEN, site_token);
+	err |= sw_add_optl_string(site, FM_FMRI_SW_SITE_MODULE, site_module);
+	err |= sw_add_optl_string(site, FM_FMRI_SW_SITE_FILE, site_file);
+	err |= sw_add_optl_string(site, FM_FMRI_SW_SITE_FUNC, site_func);
+	if ((site_token || site_module || site_file || site_func) &&
+	    site_line != -1)
+		err |= nvlist_add_int64(site, FM_FMRI_SW_SITE_LINE, site_line);
+
+	/*
+	 * Add 'site' to the fmri.
+	 */
+	if (err == 0)
+		err |= nvlist_add_nvlist(fmri, FM_FMRI_SW_SITE, site);
+
+	if (err) {
+		moderr = EMOD_NOMEM;
+		goto out;
+	}
+
+context:
+	/*
+	 * Do we have anything for a 'context' nvlist?
+	 */
+	if (ctxt_origin || ctxt_execname || ctxt_zone ||
+	    ctxt_pid != -1 || ctxt_ctid != -1 || ctxt_stack != NULL)
+		goto out;
+
+	/*
+	 * Allocate and build 'context' nvlist.
+	 */
+	if (topo_mod_nvalloc(mod, &ctxt, NV_UNIQUE_NAME) != 0) {
+		moderr = EMOD_NOMEM;
+		goto out;
+	}
+
+	err |= sw_add_optl_string(ctxt, FM_FMRI_SW_CTXT_ORIGIN, ctxt_origin);
+	err |= sw_add_optl_string(ctxt, FM_FMRI_SW_CTXT_EXECNAME,
+	    ctxt_execname);
+	err |= sw_add_optl_string(ctxt, FM_FMRI_SW_CTXT_ZONE, ctxt_zone);
+	if (ctxt_pid != -1)
+		err |= nvlist_add_int64(ctxt, FM_FMRI_SW_CTXT_PID, ctxt_pid);
+	if (ctxt_ctid != -1)
+		err |= nvlist_add_int64(ctxt, FM_FMRI_SW_CTXT_CTID, ctxt_ctid);
+	if (ctxt_stack != NULL)
+		err |= nvlist_add_string_array(ctxt, FM_FMRI_SW_CTXT_STACK,
+		    ctxt_stack, ctxt_stackdepth);
+
+	/*
+	 * Add 'context' to the fmri.
+	 */
+	if (err == 0)
+		err |= nvlist_add_nvlist(fmri, FM_FMRI_SW_CTXT, ctxt);
+
+	moderr = err ? EMOD_NOMEM : 0;
+out:
+	if (moderr == 0)
+		*out = fmri;
+
+	if (moderr != 0 && fmri)
+		nvlist_free(fmri);
+
+	if (obj)
+		nvlist_free(obj);
+
+	if (site)
+		nvlist_free(site);
+
+	if (ctxt)
+		nvlist_free(ctxt);
+
+	return (moderr == 0 ? 0 : topo_mod_seterrno(mod, moderr));
+}
+
+
+/*ARGSUSED*/
+static int
+sw_enum(topo_mod_t *mod, tnode_t *pnode, const char *name,
+    topo_instance_t min, topo_instance_t max, void *notused1, void *notused2)
+{
+	(void) topo_method_register(mod, pnode, sw_methods);
+	return (0);
+}
+
+static void
+sw_release(topo_mod_t *mod, tnode_t *node)
+{
+	topo_method_unregister_all(mod, node);
+}
+
+/*
+ * Lookup a string in an nvlist.  Possible return values:
+ *	if 'required' is B_TRUE:
+ *		1 = found
+ *		0 = not found
+ *	if 'required' is B_FALSE:
+ *		1 = found
+ *		0 = not found, but some error other than ENOENT encountered
+ *		-1 = not found, with ENOENT
+ *
+ *	So 0 is an error condition in both cases.
+ *
+ *	In all "not found" cases, *valp is NULLed.
+ */
+static int
+lookup_string(nvlist_t *nvl, char *name, char **valp, boolean_t required)
+{
+	int err;
+
+	err = nvlist_lookup_string(nvl, name, valp);
+
+	/*
+	 * A return value of 1 always means "found"
+	 */
+	if (err == 0)
+		return (1);
+
+	/*
+	 * Failure to lookup for whatever reason NULLs valp
+	 */
+	*valp = NULL;
+
+	/*
+	 * Return 0 if not found but required, or optional but some error
+	 * other than ENOENT was returned.
+	 */
+	if (required == B_TRUE || err != ENOENT)
+		return (0);
+
+	/*
+	 * Return -1 if not found but was optional (and got ENOENT).
+	 */
+	return (-1);
+}
+
+/*ARGSUSED*/
+static int
+sw_fmri_nvl2str(topo_mod_t *mod, tnode_t *node, topo_version_t version,
+    nvlist_t *nvl, nvlist_t **out)
+{
+	nvlist_t *object, *site = NULL, *anvl = NULL;
+	char *file, *func, *token;
+	uint8_t scheme_version;
+	char *path, *root;
+	nvlist_t *fmristr;
+	size_t buflen = 0;
+	int linevalid = 0;
+	char *buf = NULL;
+	ssize_t size = 0;
+	char linebuf[32];
+	int64_t line;
+	int pass;
+	int err;
+
+	if (version > TOPO_METH_NVL2STR_VERSION)
+		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
+
+	if (nvlist_lookup_uint8(nvl, FM_VERSION, &scheme_version) != 0 ||
+	    scheme_version > FM_SW_SCHEME_VERSION)
+		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
+
+	/* Get authority, if present */
+	err = nvlist_lookup_nvlist(nvl, FM_FMRI_AUTHORITY, &anvl);
+	if (err != 0 && err != ENOENT)
+		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
+
+	/*
+	 * The 'object' nvlist is required. It must include the path,
+	 * but the root is optional.
+	 */
+	if (nvlist_lookup_nvlist(nvl, FM_FMRI_SW_OBJ, &object) != 0 ||
+	    !lookup_string(object, FM_FMRI_SW_OBJ_PATH, &path, B_TRUE) ||
+	    !lookup_string(object, FM_FMRI_SW_OBJ_ROOT, &root, B_FALSE))
+		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
+
+	/* The 'site' nvlist is optional */
+	file = func = token = NULL;
+	linevalid = 0;
+	if ((err = nvlist_lookup_nvlist(nvl, FM_FMRI_SW_SITE, &site)) == 0) {
+		/*
+		 * Prefer 'token' to file/func/line
+		 */
+		if (lookup_string(site, FM_FMRI_SW_SITE_TOKEN, &token,
+		    B_FALSE) <= 0) {
+			/*
+			 * If no token then try file, func, line - but
+			 * func and line are meaningless without file.
+			 */
+			if (lookup_string(site, FM_FMRI_SW_SITE_FILE,
+			    &file, B_FALSE) == 1) {
+				(void) lookup_string(site, FM_FMRI_SW_SITE_FUNC,
+				    &func, B_FALSE);
+				if (nvlist_lookup_int64(site,
+				    FM_FMRI_SW_SITE_LINE, &line) == 0)
+					linevalid = 1;
+			}
+		}
+	} else if (err != ENOENT) {
+		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
+	}
+
+	/* On the first pass buf is NULL and size and buflen are 0 */
+	pass = 1;
+again:
+	/*
+	 * sw://[<authority>]/
+	 *	[:root=<object.root]
+	 *	:path=<object.path>
+	 *	[#<fragment-identifier>]
+	 *
+	 *	<fragment-identifier> is one of
+	 *
+	 *		:token=<site.token>
+	 *	or
+	 *		:file=<site.file>[:func=<site.func>][:line=<site.line>]
+	 */
+
+	/* sw:// */
+	topo_fmristr_build(&size, buf, buflen, FM_FMRI_SCHEME_SW,
+	    NULL, "://");
+
+	/* authority, if any */
+	if (anvl != NULL) {
+		nvpair_t *apair;
+		char *aname, *aval;
+
+		for (apair = nvlist_next_nvpair(anvl, NULL);
+		    apair != NULL; apair = nvlist_next_nvpair(anvl, apair)) {
+			if (nvpair_type(apair) != DATA_TYPE_STRING ||
+			    nvpair_value_string(apair, &aval) != 0)
+				continue;
+			aname = nvpair_name(apair);
+			topo_fmristr_build(&size, buf, buflen, ":", NULL, NULL);
+			topo_fmristr_build(&size, buf, buflen, "=",
+			    aname, aval);
+		}
+	}
+
+	/* separating slash */
+	topo_fmristr_build(&size, buf, buflen, "/", NULL, NULL);
+
+	/* :root=... */
+	if (root) {
+		topo_fmristr_build(&size, buf, buflen, root,
+		    ":" FM_FMRI_SW_OBJ_ROOT "=", NULL);
+	}
+
+	/* :path=... */
+	topo_fmristr_build(&size, buf, buflen, path,
+	    ":" FM_FMRI_SW_OBJ_PATH "=", NULL);
+
+	if (token) {
+		/* #:token=... */
+		topo_fmristr_build(&size, buf, buflen, token,
+		    "#:" FM_FMRI_SW_SITE_TOKEN "=", NULL);
+	} else if (file) {
+		/* #:file=... */
+		topo_fmristr_build(&size, buf, buflen, file,
+		    "#:" FM_FMRI_SW_SITE_FILE "=", NULL);
+
+		/* :func=... */
+		if (func) {
+			topo_fmristr_build(&size, buf, buflen, func,
+			    ":" FM_FMRI_SW_SITE_FUNC "=", NULL);
+		}
+
+		/* :line=... */
+		if (linevalid) {
+			if (pass == 1)
+				(void) snprintf(linebuf, sizeof (linebuf),
+				    "%lld", line);
+
+			topo_fmristr_build(&size, buf, buflen, linebuf,
+			    ":" FM_FMRI_SW_SITE_LINE "=", NULL);
+		}
+	}
+
+	if (buf == NULL) {
+		if ((buf = topo_mod_alloc(mod, size + 1)) == NULL)
+			return (topo_mod_seterrno(mod, EMOD_NOMEM));
+
+		buflen = size + 1;
+		size = 0;
+		pass = 2;
+		goto again;
+	}
+
+	/*
+	 * Construct the nvlist to return as the result.
+	 */
+	if (topo_mod_nvalloc(mod, &fmristr, NV_UNIQUE_NAME) != 0) {
+		topo_mod_strfree(mod, buf);
+		return (topo_mod_seterrno(mod, EMOD_NOMEM));
+	}
+
+	if (nvlist_add_string(fmristr, "fmri-string", buf) != 0) {
+		topo_mod_strfree(mod, buf);
+		nvlist_free(fmristr);
+		return (topo_mod_seterrno(mod, EMOD_NOMEM));
+	}
+	topo_mod_strfree(mod, buf);
+	*out = fmristr;
+
+	return (0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/fm/topo/libtopo/common/sw.h	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+#ifndef	_SW_H
+#define	_SW_H
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+#define	SW_VERSION	1
+
+extern int sw_init(topo_mod_t *, topo_version_t);	/* see sw.c */
+extern void sw_fini(topo_mod_t *);			/* see sw.c */
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _SW_H */
--- a/usr/src/lib/fm/topo/libtopo/common/topo_builtin.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/lib/fm/topo/libtopo/common/topo_builtin.c	Fri Jul 30 17:04:17 2010 +1000
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #include <pthread.h>
@@ -39,6 +38,7 @@
 #include <mod.h>
 #include <pkg.h>
 #include <svc.h>
+#include <sw.h>
 #include <zfs.h>
 
 static const struct topo_builtin _topo_builtins[] = {
@@ -48,6 +48,7 @@
 	{ "mem", MEM_VERSION, mem_init, mem_fini },
 	{ "pkg", PKG_VERSION, pkg_init, pkg_fini },
 	{ "svc", SVC_VERSION, svc_init, svc_fini },
+	{ "sw", SW_VERSION, sw_init, sw_fini },
 	{ "zfs", ZFS_VERSION, zfs_init, zfs_fini },
 	{ "mod", MOD_VERSION, mod_init, mod_fini },
 	{ "hc", HC_VERSION, hc_init, hc_fini },		/* hc must go last */
--- a/usr/src/lib/fm/topo/libtopo/common/topo_mod.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/lib/fm/topo/libtopo/common/topo_mod.c	Fri Jul 30 17:04:17 2010 +1000
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 /*
@@ -560,6 +559,70 @@
 	return (nfp);
 }
 
+#define	_SWFMRI_ADD_STRING(nvl, name, val) \
+	((val) ? (nvlist_add_string(nvl, name, val) != 0) : 0)
+
+nvlist_t *
+topo_mod_swfmri(topo_mod_t *mod, int version,
+    char *obj_path, char *obj_root, nvlist_t *obj_pkg,
+    char *site_token, char *site_module, char *site_file, char *site_func,
+    int64_t site_line, char *ctxt_origin, char *ctxt_execname,
+    int64_t ctxt_pid, char *ctxt_zone, int64_t ctxt_ctid,
+    char **ctxt_stack, uint_t ctxt_stackdepth)
+{
+	nvlist_t *fmri, *args;
+	nvlist_t *nfp = NULL;
+	int err;
+
+	if (version != FM_SW_SCHEME_VERSION)
+		return (set_fmri_err(mod, EMOD_FMRI_VERSION));
+
+	if (topo_mod_nvalloc(mod, &args, NV_UNIQUE_NAME) != 0)
+		return (set_fmri_err(mod, EMOD_FMRI_NVL));
+
+	err = 0;
+	err |= _SWFMRI_ADD_STRING(args, "obj_path", obj_path);
+	err |= _SWFMRI_ADD_STRING(args, "obj_root", obj_root);
+	if (obj_pkg)
+		err |= nvlist_add_nvlist(args, "obj_pkg", obj_pkg);
+
+	err |= _SWFMRI_ADD_STRING(args, "site_token", site_token);
+	err |= _SWFMRI_ADD_STRING(args, "site_module", site_module);
+	err |= _SWFMRI_ADD_STRING(args, "site_file", site_file);
+	err |= _SWFMRI_ADD_STRING(args, "site_func", site_func);
+	if (site_line != -1)
+		err |= nvlist_add_int64(args, "site_line", site_line);
+
+	err |= _SWFMRI_ADD_STRING(args, "ctxt_origin", ctxt_origin);
+	err |= _SWFMRI_ADD_STRING(args, "ctxt_execname", ctxt_execname);
+	if (ctxt_pid != -1)
+		err |= nvlist_add_int64(args, "ctxt_pid", ctxt_pid);
+	err |= _SWFMRI_ADD_STRING(args, "ctxt_zone", ctxt_zone);
+	if (ctxt_ctid != -1)
+		err |= nvlist_add_int64(args, "ctxt_ctid", ctxt_ctid);
+	if (ctxt_stack != NULL && ctxt_stackdepth != 0)
+		err |= nvlist_add_string_array(args, "stack", ctxt_stack,
+		    ctxt_stackdepth);
+
+	if (err) {
+		nvlist_free(args);
+		return (set_fmri_err(mod, EMOD_FMRI_NVL));
+	}
+
+	if ((fmri = topo_fmri_create(mod->tm_hdl, FM_FMRI_SCHEME_SW,
+	    FM_FMRI_SCHEME_SW, 0, args, &err)) == NULL) {
+		nvlist_free(args);
+		return (set_fmri_err(mod, err));
+	}
+
+	nvlist_free(args);
+
+	(void) topo_mod_nvdup(mod, fmri, &nfp);
+	nvlist_free(fmri);
+
+	return (nfp);
+}
+
 int
 topo_mod_str2nvl(topo_mod_t *mod, const char *fmristr, nvlist_t **fmri)
 {
--- a/usr/src/lib/fm/topo/libtopo/common/topo_mod.h	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/lib/fm/topo/libtopo/common/topo_mod.h	Fri Jul 30 17:04:17 2010 +1000
@@ -20,8 +20,7 @@
  */
 
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #ifndef _TOPO_MOD_H
@@ -93,6 +92,10 @@
     const char *, int);
 extern nvlist_t *topo_mod_modfmri(topo_mod_t *, int, const char *);
 extern nvlist_t *topo_mod_pkgfmri(topo_mod_t *, int, const char *);
+extern nvlist_t *topo_mod_swfmri(topo_mod_t *, int,
+    char *, char *, nvlist_t *,
+    char *, char *, char *, char *, int64_t,
+    char *, char *, int64_t, char *, int64_t, char **, uint_t);
 extern int topo_mod_nvl2str(topo_mod_t *, nvlist_t *, char **);
 extern int topo_mod_str2nvl(topo_mod_t *, const char *,  nvlist_t **);
 extern int topo_prop_setmutable(tnode_t *node, const char *pgname,
--- a/usr/src/lib/fm/topo/libtopo/common/topo_node.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/lib/fm/topo/libtopo/common/topo_node.c	Fri Jul 30 17:04:17 2010 +1000
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 /*
@@ -390,6 +389,9 @@
 	tnode_t *node;
 	topo_nodehash_t *nhp;
 
+	topo_dprintf(pnode->tn_hdl, TOPO_DBG_MODSVC,
+	    "topo_node_lookup: looking for '%s' instance %d\n", name, inst);
+
 	topo_node_lock(pnode);
 	for (nhp = topo_list_next(&pnode->tn_children); nhp != NULL;
 	    nhp = topo_list_next(nhp)) {
--- a/usr/src/lib/fm/topo/libtopo/common/topo_snap.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/lib/fm/topo/libtopo/common/topo_snap.c	Fri Jul 30 17:04:17 2010 +1000
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 /*
@@ -78,6 +77,7 @@
 #include <sys/systeminfo.h>
 #include <sys/utsname.h>
 #include <uuid/uuid.h>
+#include <zone.h>
 
 #include <fm/libtopo.h>
 #include <sys/fm/protocol.h>
@@ -379,7 +379,7 @@
 		/*
 		 * Now walk the tree and invoke any facility enumeration methods
 		 */
-		if (ret != NULL) {
+		if (ret != NULL && getzoneid() == 0) {
 			if ((twp = topo_walk_init(thp, FM_FMRI_SCHEME_HC,
 			    fac_walker, (void *)0, errp)) == NULL) {
 				return (ret);
--- a/usr/src/lib/fm/topo/libtopo/common/zfs.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/lib/fm/topo/libtopo/common/zfs.c	Fri Jul 30 17:04:17 2010 +1000
@@ -21,8 +21,7 @@
  */
 
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #include <stdio.h>
@@ -103,6 +102,11 @@
 zfs_enum(topo_mod_t *mod, tnode_t *pnode, const char *name, topo_instance_t min,
     topo_instance_t max, void *notused1, void *notused2)
 {
+	/*
+	 * Methods are registered, but there is no enumeration.  Should
+	 * enumeration be added be sure to cater for global vs non-global
+	 * zones.
+	 */
 	(void) topo_method_register(mod, pnode, zfs_methods);
 	return (0);
 }
--- a/usr/src/lib/libnvpair/libnvpair.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/lib/libnvpair/libnvpair.c	Fri Jul 30 17:04:17 2010 +1000
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #include <unistd.h>
@@ -28,6 +27,8 @@
 #include <libintl.h>
 #include <sys/types.h>
 #include <sys/inttypes.h>
+#include <stdarg.h>
+#include <note.h>
 #include "libnvpair.h"
 
 /*
@@ -38,21 +39,531 @@
  *	between kernel and userland, and possibly saving onto disk files.
  */
 
+/*
+ * Print control structure.
+ */
+
+#define	DEFINEOP(opname, vtype) \
+	struct { \
+		int (*op)(struct nvlist_prtctl *, void *, nvlist_t *, \
+		    const char *, vtype); \
+		void *arg; \
+	} opname
+
+#define	DEFINEARROP(opname, vtype) \
+	struct { \
+		int (*op)(struct nvlist_prtctl *, void *, nvlist_t *, \
+		    const char *, vtype, uint_t); \
+		void *arg; \
+	} opname
+
+struct nvlist_printops {
+	DEFINEOP(print_boolean, int);
+	DEFINEOP(print_boolean_value, boolean_t);
+	DEFINEOP(print_byte, uchar_t);
+	DEFINEOP(print_int8, int8_t);
+	DEFINEOP(print_uint8, uint8_t);
+	DEFINEOP(print_int16, int16_t);
+	DEFINEOP(print_uint16, uint16_t);
+	DEFINEOP(print_int32, int32_t);
+	DEFINEOP(print_uint32, uint32_t);
+	DEFINEOP(print_int64, int64_t);
+	DEFINEOP(print_uint64, uint64_t);
+	DEFINEOP(print_double, double);
+	DEFINEOP(print_string, char *);
+	DEFINEOP(print_hrtime, hrtime_t);
+	DEFINEOP(print_nvlist, nvlist_t *);
+	DEFINEARROP(print_boolean_array, boolean_t *);
+	DEFINEARROP(print_byte_array, uchar_t *);
+	DEFINEARROP(print_int8_array, int8_t *);
+	DEFINEARROP(print_uint8_array, uint8_t *);
+	DEFINEARROP(print_int16_array, int16_t *);
+	DEFINEARROP(print_uint16_array, uint16_t *);
+	DEFINEARROP(print_int32_array, int32_t *);
+	DEFINEARROP(print_uint32_array, uint32_t *);
+	DEFINEARROP(print_int64_array, int64_t *);
+	DEFINEARROP(print_uint64_array, uint64_t *);
+	DEFINEARROP(print_string_array, char **);
+	DEFINEARROP(print_nvlist_array, nvlist_t **);
+};
+
+struct nvlist_prtctl {
+	FILE *nvprt_fp;			/* output destination */
+	enum nvlist_indent_mode nvprt_indent_mode; /* see above */
+	int nvprt_indent;		/* absolute indent, or tab depth */
+	int nvprt_indentinc;		/* indent or tab increment */
+	const char *nvprt_nmfmt;	/* member name format, max one %s */
+	const char *nvprt_eomfmt;	/* after member format, e.g. "\n" */
+	const char *nvprt_btwnarrfmt;	/* between array members */
+	int nvprt_btwnarrfmt_nl;	/* nvprt_eoamfmt includes newline? */
+	struct nvlist_printops *nvprt_dfltops;
+	struct nvlist_printops *nvprt_custops;
+};
+
+#define	DFLTPRTOP(pctl, type) \
+	((pctl)->nvprt_dfltops->print_##type.op)
+
+#define	DFLTPRTOPARG(pctl, type) \
+	((pctl)->nvprt_dfltops->print_##type.arg)
+
+#define	CUSTPRTOP(pctl, type) \
+	((pctl)->nvprt_custops->print_##type.op)
+
+#define	CUSTPRTOPARG(pctl, type) \
+	((pctl)->nvprt_custops->print_##type.arg)
+
+#define	RENDER(pctl, type, nvl, name, val) \
+	{ \
+		int done = 0; \
+		if ((pctl)->nvprt_custops && CUSTPRTOP(pctl, type)) { \
+			done = CUSTPRTOP(pctl, type)(pctl, \
+			    CUSTPRTOPARG(pctl, type), nvl, name, val); \
+		} \
+		if (!done) { \
+			(void) DFLTPRTOP(pctl, type)(pctl, \
+			    DFLTPRTOPARG(pctl, type), nvl, name, val); \
+		} \
+		(void) fprintf(pctl->nvprt_fp, pctl->nvprt_eomfmt); \
+	}
+
+#define	ARENDER(pctl, type, nvl, name, arrp, count) \
+	{ \
+		int done = 0; \
+		if ((pctl)->nvprt_custops && CUSTPRTOP(pctl, type)) { \
+			done = CUSTPRTOP(pctl, type)(pctl, \
+			    CUSTPRTOPARG(pctl, type), nvl, name, arrp, count); \
+		} \
+		if (!done) { \
+			(void) DFLTPRTOP(pctl, type)(pctl, \
+			    DFLTPRTOPARG(pctl, type), nvl, name, arrp, count); \
+		} \
+		(void) fprintf(pctl->nvprt_fp, pctl->nvprt_eomfmt); \
+	}
+
+static void nvlist_print_with_indent(nvlist_t *, nvlist_prtctl_t);
+
+/*
+ * ======================================================================
+ * |									|
+ * | Indentation							|
+ * |									|
+ * ======================================================================
+ */
+
 static void
-indent(FILE *fp, int depth)
+indent(nvlist_prtctl_t pctl, int onemore)
+{
+	int depth;
+
+	switch (pctl->nvprt_indent_mode) {
+	case NVLIST_INDENT_ABS:
+		(void) fprintf(pctl->nvprt_fp, "%*s",
+		    pctl->nvprt_indent + onemore * pctl->nvprt_indentinc, "");
+		break;
+
+	case NVLIST_INDENT_TABBED:
+		depth = pctl->nvprt_indent + onemore;
+		while (depth-- > 0)
+			(void) fprintf(pctl->nvprt_fp, "\t");
+	}
+}
+
+/*
+ * ======================================================================
+ * |									|
+ * | Default nvlist member rendering functions.				|
+ * |									|
+ * ======================================================================
+ */
+
+/*
+ * Generate functions to print single-valued nvlist members.
+ *
+ * type_and_variant - suffix to form function name
+ * vtype - C type for the member value
+ * ptype - C type to cast value to for printing
+ * vfmt - format string for pair value, e.g "%d" or "0x%llx"
+ */
+
+#define	NVLIST_PRTFUNC(type_and_variant, vtype, ptype, vfmt) \
+static int \
+nvprint_##type_and_variant(nvlist_prtctl_t pctl, void *private, \
+    nvlist_t *nvl, const char *name, vtype value) \
+{ \
+	FILE *fp = pctl->nvprt_fp; \
+	NOTE(ARGUNUSED(private)) \
+	NOTE(ARGUNUSED(nvl)) \
+	indent(pctl, 1); \
+	(void) fprintf(fp, pctl->nvprt_nmfmt, name); \
+	(void) fprintf(fp, vfmt, (ptype)value); \
+	return (1); \
+}
+
+NVLIST_PRTFUNC(boolean, int, int, "%d")
+NVLIST_PRTFUNC(boolean_value, boolean_t, int, "%d")
+NVLIST_PRTFUNC(byte, uchar_t, uchar_t, "0x%2.2x")
+NVLIST_PRTFUNC(int8, int8_t, int, "%d")
+NVLIST_PRTFUNC(uint8, uint8_t, uint8_t, "0x%x")
+NVLIST_PRTFUNC(int16, int16_t, int16_t, "%d")
+NVLIST_PRTFUNC(uint16, uint16_t, uint16_t, "0x%x")
+NVLIST_PRTFUNC(int32, int32_t, int32_t, "%d")
+NVLIST_PRTFUNC(uint32, uint32_t, uint32_t, "0x%x")
+NVLIST_PRTFUNC(int64, int64_t, longlong_t, "%lld")
+NVLIST_PRTFUNC(uint64, uint64_t, u_longlong_t, "0x%llx")
+NVLIST_PRTFUNC(double, double, double, "0x%llf")
+NVLIST_PRTFUNC(string, char *, char *, "%s")
+NVLIST_PRTFUNC(hrtime, hrtime_t, hrtime_t, "0x%llx")
+
+/*
+ * Generate functions to print array-valued nvlist members.
+ */
+
+#define	NVLIST_ARRPRTFUNC(type_and_variant, vtype, ptype, vfmt) \
+static int \
+nvaprint_##type_and_variant(nvlist_prtctl_t pctl, void *private, \
+    nvlist_t *nvl, const char *name, vtype *valuep, uint_t count) \
+{ \
+	FILE *fp = pctl->nvprt_fp; \
+	uint_t i; \
+	NOTE(ARGUNUSED(private)) \
+	NOTE(ARGUNUSED(nvl)) \
+	for (i = 0; i < count; i++) { \
+		if (i == 0 || pctl->nvprt_btwnarrfmt_nl) { \
+			indent(pctl, 1); \
+			(void) fprintf(fp, pctl->nvprt_nmfmt, name); \
+			if (pctl->nvprt_btwnarrfmt_nl) \
+				(void) fprintf(fp, "[%d]: ", i); \
+		} \
+		if (i != 0) \
+			(void) fprintf(fp, pctl->nvprt_btwnarrfmt); \
+		(void) fprintf(fp, vfmt, (ptype)valuep[i]); \
+	} \
+	return (1); \
+}
+
+NVLIST_ARRPRTFUNC(boolean_array, boolean_t, boolean_t, "%d")
+NVLIST_ARRPRTFUNC(byte_array, uchar_t, uchar_t, "0x%2.2x")
+NVLIST_ARRPRTFUNC(int8_array, int8_t, int8_t, "%d")
+NVLIST_ARRPRTFUNC(uint8_array, uint8_t, uint8_t, "0x%x")
+NVLIST_ARRPRTFUNC(int16_array, int16_t, int16_t, "%d")
+NVLIST_ARRPRTFUNC(uint16_array, uint16_t, uint16_t, "0x%x")
+NVLIST_ARRPRTFUNC(int32_array, int32_t, int32_t, "%d")
+NVLIST_ARRPRTFUNC(uint32_array, uint32_t, uint32_t, "0x%x")
+NVLIST_ARRPRTFUNC(int64_array, int64_t, longlong_t, "%lld")
+NVLIST_ARRPRTFUNC(uint64_array, uint64_t, u_longlong_t, "0x%llx")
+NVLIST_ARRPRTFUNC(string_array, char *, char *, "%s")
+
+/*ARGSUSED*/
+static int
+nvprint_nvlist(nvlist_prtctl_t pctl, void *private,
+    nvlist_t *nvl, const char *name, nvlist_t *value)
+{
+	FILE *fp = pctl->nvprt_fp;
+
+	indent(pctl, 1);
+	(void) fprintf(fp, "%s = (embedded nvlist)\n", name);
+
+	pctl->nvprt_indent += pctl->nvprt_indentinc;
+	nvlist_print_with_indent(value, pctl);
+	pctl->nvprt_indent -= pctl->nvprt_indentinc;
+
+	indent(pctl, 1);
+	(void) fprintf(fp, "(end %s)\n", name);
+
+	return (1);
+}
+
+/*ARGSUSED*/
+static int
+nvaprint_nvlist_array(nvlist_prtctl_t pctl, void *private,
+    nvlist_t *nvl, const char *name, nvlist_t **valuep, uint_t count)
 {
-	while (depth-- > 0)
-		(void) fprintf(fp, "\t");
+	FILE *fp = pctl->nvprt_fp;
+	uint_t i;
+
+	indent(pctl, 1);
+	(void) fprintf(fp, "%s = (array of embedded nvlists)\n", name);
+
+	for (i = 0; i < count; i++) {
+		indent(pctl, 1);
+		(void) fprintf(fp, "(start %s[%d])\n", name, i);
+
+		pctl->nvprt_indent += pctl->nvprt_indentinc;
+		nvlist_print_with_indent(valuep[i], pctl);
+		pctl->nvprt_indent -= pctl->nvprt_indentinc;
+
+		indent(pctl, 1);
+		(void) fprintf(fp, "(end %s[%d])\n", name, i);
+	}
+
+	return (1);
+}
+
+/*
+ * ======================================================================
+ * |									|
+ * | Interfaces that allow control over formatting.			|
+ * |									|
+ * ======================================================================
+ */
+
+void
+nvlist_prtctl_setdest(nvlist_prtctl_t pctl, FILE *fp)
+{
+	pctl->nvprt_fp = fp;
+}
+
+FILE *
+nvlist_prtctl_getdest(nvlist_prtctl_t pctl)
+{
+	return (pctl->nvprt_fp);
+}
+
+
+void
+nvlist_prtctl_setindent(nvlist_prtctl_t pctl, enum nvlist_indent_mode mode,
+    int start, int inc)
+{
+	if (mode < NVLIST_INDENT_ABS || mode > NVLIST_INDENT_TABBED)
+		mode = NVLIST_INDENT_TABBED;
+
+	if (start < 0)
+		start = 0;
+
+	if (inc < 0)
+		inc = 1;
+
+	pctl->nvprt_indent_mode = mode;
+	pctl->nvprt_indent = start;
+	pctl->nvprt_indentinc = inc;
+}
+
+void
+nvlist_prtctl_doindent(nvlist_prtctl_t pctl, int onemore)
+{
+	indent(pctl, onemore);
+}
+
+
+void
+nvlist_prtctl_setfmt(nvlist_prtctl_t pctl, enum nvlist_prtctl_fmt which,
+    const char *fmt)
+{
+	switch (which) {
+	case NVLIST_FMT_MEMBER_NAME:
+		if (fmt == NULL)
+			fmt = "%s = ";
+		pctl->nvprt_nmfmt = fmt;
+		break;
+
+	case NVLIST_FMT_MEMBER_POSTAMBLE:
+		if (fmt == NULL)
+			fmt = "\n";
+		pctl->nvprt_eomfmt = fmt;
+		break;
+
+	case NVLIST_FMT_BTWN_ARRAY:
+		if (fmt == NULL) {
+			pctl->nvprt_btwnarrfmt = " ";
+			pctl->nvprt_btwnarrfmt_nl = 0;
+		} else {
+			pctl->nvprt_btwnarrfmt = fmt;
+			pctl->nvprt_btwnarrfmt_nl = (strstr(fmt, "\n") != NULL);
+		}
+		break;
+
+	default:
+		break;
+	}
+}
+
+
+void
+nvlist_prtctl_dofmt(nvlist_prtctl_t pctl, enum nvlist_prtctl_fmt which, ...)
+{
+	FILE *fp = pctl->nvprt_fp;
+	va_list ap;
+	char *name;
+
+	va_start(ap, which);
+
+	switch (which) {
+	case NVLIST_FMT_MEMBER_NAME:
+		name = va_arg(ap, char *);
+		(void) fprintf(fp, pctl->nvprt_nmfmt, name);
+		break;
+
+	case NVLIST_FMT_MEMBER_POSTAMBLE:
+		(void) fprintf(fp, pctl->nvprt_eomfmt);
+		break;
+
+	case NVLIST_FMT_BTWN_ARRAY:
+		(void) fprintf(fp, pctl->nvprt_btwnarrfmt); \
+		break;
+
+	default:
+		break;
+	}
+
+	va_end(ap);
 }
 
 /*
+ * ======================================================================
+ * |									|
+ * | Interfaces to allow appointment of replacement rendering functions.|
+ * |									|
+ * ======================================================================
+ */
+
+#define	NVLIST_PRINTCTL_REPLACE(type, vtype) \
+void \
+nvlist_prtctlop_##type(nvlist_prtctl_t pctl, \
+    int (*func)(nvlist_prtctl_t, void *, nvlist_t *, const char *, vtype), \
+    void *private) \
+{ \
+	CUSTPRTOP(pctl, type) = func; \
+	CUSTPRTOPARG(pctl, type) = private; \
+}
+
+NVLIST_PRINTCTL_REPLACE(boolean, int)
+NVLIST_PRINTCTL_REPLACE(boolean_value, boolean_t)
+NVLIST_PRINTCTL_REPLACE(byte, uchar_t)
+NVLIST_PRINTCTL_REPLACE(int8, int8_t)
+NVLIST_PRINTCTL_REPLACE(uint8, uint8_t)
+NVLIST_PRINTCTL_REPLACE(int16, int16_t)
+NVLIST_PRINTCTL_REPLACE(uint16, uint16_t)
+NVLIST_PRINTCTL_REPLACE(int32, int32_t)
+NVLIST_PRINTCTL_REPLACE(uint32, uint32_t)
+NVLIST_PRINTCTL_REPLACE(int64, int64_t)
+NVLIST_PRINTCTL_REPLACE(uint64, uint64_t)
+NVLIST_PRINTCTL_REPLACE(double, double)
+NVLIST_PRINTCTL_REPLACE(string, char *)
+NVLIST_PRINTCTL_REPLACE(hrtime, hrtime_t)
+NVLIST_PRINTCTL_REPLACE(nvlist, nvlist_t *)
+
+#define	NVLIST_PRINTCTL_AREPLACE(type, vtype) \
+void \
+nvlist_prtctlop_##type(nvlist_prtctl_t pctl, \
+    int (*func)(nvlist_prtctl_t, void *, nvlist_t *, const char *, vtype, \
+    uint_t), void *private) \
+{ \
+	CUSTPRTOP(pctl, type) = func; \
+	CUSTPRTOPARG(pctl, type) = private; \
+}
+
+NVLIST_PRINTCTL_AREPLACE(boolean_array, boolean_t *)
+NVLIST_PRINTCTL_AREPLACE(byte_array, uchar_t *)
+NVLIST_PRINTCTL_AREPLACE(int8_array, int8_t *)
+NVLIST_PRINTCTL_AREPLACE(uint8_array, uint8_t *)
+NVLIST_PRINTCTL_AREPLACE(int16_array, int16_t *)
+NVLIST_PRINTCTL_AREPLACE(uint16_array, uint16_t *)
+NVLIST_PRINTCTL_AREPLACE(int32_array, int32_t *)
+NVLIST_PRINTCTL_AREPLACE(uint32_array, uint32_t *)
+NVLIST_PRINTCTL_AREPLACE(int64_array, int64_t *)
+NVLIST_PRINTCTL_AREPLACE(uint64_array, uint64_t *)
+NVLIST_PRINTCTL_AREPLACE(string_array, char **)
+NVLIST_PRINTCTL_AREPLACE(nvlist_array, nvlist_t **)
+
+/*
+ * ======================================================================
+ * |									|
+ * | Interfaces to manage nvlist_prtctl_t cookies.			|
+ * |									|
+ * ======================================================================
+ */
+
+
+static const struct nvlist_printops defprtops = {
+	{ nvprint_boolean, NULL },
+	{ nvprint_boolean_value, NULL },
+	{ nvprint_byte, NULL },
+	{ nvprint_int8, NULL },
+	{ nvprint_uint8, NULL },
+	{ nvprint_int16, NULL },
+	{ nvprint_uint16, NULL },
+	{ nvprint_int32, NULL },
+	{ nvprint_uint32, NULL },
+	{ nvprint_int64, NULL },
+	{ nvprint_uint64, NULL },
+	{ nvprint_double, NULL },
+	{ nvprint_string, NULL },
+	{ nvprint_hrtime, NULL },
+	{ nvprint_nvlist, NULL },
+	{ nvaprint_boolean_array, NULL },
+	{ nvaprint_byte_array, NULL },
+	{ nvaprint_int8_array, NULL },
+	{ nvaprint_uint8_array, NULL },
+	{ nvaprint_int16_array, NULL },
+	{ nvaprint_uint16_array, NULL },
+	{ nvaprint_int32_array, NULL },
+	{ nvaprint_uint32_array, NULL },
+	{ nvaprint_int64_array, NULL },
+	{ nvaprint_uint64_array, NULL },
+	{ nvaprint_string_array, NULL },
+	{ nvaprint_nvlist_array, NULL },
+};
+
+static void
+prtctl_defaults(FILE *fp, struct nvlist_prtctl *pctl,
+    struct nvlist_printops *ops)
+{
+	pctl->nvprt_fp = fp;
+	pctl->nvprt_indent_mode = NVLIST_INDENT_TABBED;
+	pctl->nvprt_indent = 0;
+	pctl->nvprt_indentinc = 1;
+	pctl->nvprt_nmfmt = "%s = ";
+	pctl->nvprt_eomfmt = "\n";
+	pctl->nvprt_btwnarrfmt = " ";
+	pctl->nvprt_btwnarrfmt_nl = 0;
+
+	pctl->nvprt_dfltops = (struct nvlist_printops *)&defprtops;
+	pctl->nvprt_custops = ops;
+}
+
+nvlist_prtctl_t
+nvlist_prtctl_alloc(void)
+{
+	struct nvlist_prtctl *pctl;
+	struct nvlist_printops *ops;
+
+	if ((pctl = malloc(sizeof (*pctl))) == NULL)
+		return (NULL);
+
+	if ((ops = calloc(1, sizeof (*ops))) == NULL) {
+		free(pctl);
+		return (NULL);
+	}
+
+	prtctl_defaults(stdout, pctl, ops);
+
+	return (pctl);
+}
+
+void
+nvlist_prtctl_free(nvlist_prtctl_t pctl)
+{
+	if (pctl != NULL) {
+		free(pctl->nvprt_custops);
+		free(pctl);
+	}
+}
+
+/*
+ * ======================================================================
+ * |									|
+ * | Top-level print request interfaces.				|
+ * |									|
+ * ======================================================================
+ */
+
+/*
  * nvlist_print - Prints elements in an event buffer
  */
-static
-void
-nvlist_print_with_indent(FILE *fp, nvlist_t *nvl, int depth)
+static void
+nvlist_print_with_indent(nvlist_t *nvl, nvlist_prtctl_t pctl)
 {
-	int i;
+	FILE *fp = pctl->nvprt_fp;
 	char *name;
 	uint_t nelem;
 	nvpair_t *nvp;
@@ -60,7 +571,7 @@
 	if (nvl == NULL)
 		return;
 
-	indent(fp, depth);
+	indent(pctl, 0);
 	(void) fprintf(fp, "nvlist version: %d\n", NVL_VERSION(nvl));
 
 	nvp = nvlist_next_nvpair(nvl, NULL);
@@ -68,199 +579,174 @@
 	while (nvp) {
 		data_type_t type = nvpair_type(nvp);
 
-		indent(fp, depth);
 		name = nvpair_name(nvp);
-		(void) fprintf(fp, "\t%s =", name);
 		nelem = 0;
+
 		switch (type) {
 		case DATA_TYPE_BOOLEAN: {
-			(void) fprintf(fp, " 1");
+			RENDER(pctl, boolean, nvl, name, 1);
 			break;
 		}
 		case DATA_TYPE_BOOLEAN_VALUE: {
 			boolean_t val;
 			(void) nvpair_value_boolean_value(nvp, &val);
-			(void) fprintf(fp, " %d", val);
+			RENDER(pctl, boolean_value, nvl, name, val);
 			break;
 		}
 		case DATA_TYPE_BYTE: {
 			uchar_t val;
 			(void) nvpair_value_byte(nvp, &val);
-			(void) fprintf(fp, " 0x%2.2x", val);
+			RENDER(pctl, byte, nvl, name, val);
 			break;
 		}
 		case DATA_TYPE_INT8: {
 			int8_t val;
 			(void) nvpair_value_int8(nvp, &val);
-			(void) fprintf(fp, " %d", val);
+			RENDER(pctl, int8, nvl, name, val);
 			break;
 		}
 		case DATA_TYPE_UINT8: {
 			uint8_t val;
 			(void) nvpair_value_uint8(nvp, &val);
-			(void) fprintf(fp, " 0x%x", val);
+			RENDER(pctl, uint8, nvl, name, val);
 			break;
 		}
 		case DATA_TYPE_INT16: {
 			int16_t val;
 			(void) nvpair_value_int16(nvp, &val);
-			(void) fprintf(fp, " %d", val);
+			RENDER(pctl, int16, nvl, name, val);
 			break;
 		}
 		case DATA_TYPE_UINT16: {
 			uint16_t val;
 			(void) nvpair_value_uint16(nvp, &val);
-			(void) fprintf(fp, " 0x%x", val);
+			RENDER(pctl, uint16, nvl, name, val);
 			break;
 		}
 		case DATA_TYPE_INT32: {
 			int32_t val;
 			(void) nvpair_value_int32(nvp, &val);
-			(void) fprintf(fp, " %d", val);
+			RENDER(pctl, int32, nvl, name, val);
 			break;
 		}
 		case DATA_TYPE_UINT32: {
 			uint32_t val;
 			(void) nvpair_value_uint32(nvp, &val);
-			(void) fprintf(fp, " 0x%x", val);
+			RENDER(pctl, uint32, nvl, name, val);
 			break;
 		}
 		case DATA_TYPE_INT64: {
 			int64_t val;
 			(void) nvpair_value_int64(nvp, &val);
-			(void) fprintf(fp, " %lld", (longlong_t)val);
+			RENDER(pctl, int64, nvl, name, val);
 			break;
 		}
 		case DATA_TYPE_UINT64: {
 			uint64_t val;
 			(void) nvpair_value_uint64(nvp, &val);
-			(void) fprintf(fp, " 0x%llx", (u_longlong_t)val);
+			RENDER(pctl, uint64, nvl, name, val);
 			break;
 		}
 		case DATA_TYPE_DOUBLE: {
 			double val;
 			(void) nvpair_value_double(nvp, &val);
-			(void) fprintf(fp, " 0x%llf", val);
+			RENDER(pctl, double, nvl, name, val);
 			break;
 		}
 		case DATA_TYPE_STRING: {
 			char *val;
 			(void) nvpair_value_string(nvp, &val);
-			(void) fprintf(fp, " %s", val);
+			RENDER(pctl, string, nvl, name, val);
 			break;
 		}
 		case DATA_TYPE_BOOLEAN_ARRAY: {
 			boolean_t *val;
 			(void) nvpair_value_boolean_array(nvp, &val, &nelem);
-			for (i = 0; i < nelem; i++)
-				(void) fprintf(fp, " %d", val[i]);
+			ARENDER(pctl, boolean_array, nvl, name, val, nelem);
 			break;
 		}
 		case DATA_TYPE_BYTE_ARRAY: {
 			uchar_t *val;
 			(void) nvpair_value_byte_array(nvp, &val, &nelem);
-			for (i = 0; i < nelem; i++)
-				(void) fprintf(fp, " 0x%2.2x", val[i]);
+			ARENDER(pctl, byte_array, nvl, name, val, nelem);
 			break;
 		}
 		case DATA_TYPE_INT8_ARRAY: {
 			int8_t *val;
 			(void) nvpair_value_int8_array(nvp, &val, &nelem);
-			for (i = 0; i < nelem; i++)
-				(void) fprintf(fp, " %d", val[i]);
+			ARENDER(pctl, int8_array, nvl, name, val, nelem);
 			break;
 		}
 		case DATA_TYPE_UINT8_ARRAY: {
 			uint8_t *val;
 			(void) nvpair_value_uint8_array(nvp, &val, &nelem);
-			for (i = 0; i < nelem; i++)
-				(void) fprintf(fp, " 0x%x", val[i]);
+			ARENDER(pctl, uint8_array, nvl, name, val, nelem);
 			break;
 		}
 		case DATA_TYPE_INT16_ARRAY: {
 			int16_t *val;
 			(void) nvpair_value_int16_array(nvp, &val, &nelem);
-			for (i = 0; i < nelem; i++)
-				(void) fprintf(fp, " %d", val[i]);
+			ARENDER(pctl, int16_array, nvl, name, val, nelem);
 			break;
 		}
 		case DATA_TYPE_UINT16_ARRAY: {
 			uint16_t *val;
 			(void) nvpair_value_uint16_array(nvp, &val, &nelem);
-			for (i = 0; i < nelem; i++)
-				(void) fprintf(fp, " 0x%x", val[i]);
+			ARENDER(pctl, uint16_array, nvl, name, val, nelem);
 			break;
 		}
 		case DATA_TYPE_INT32_ARRAY: {
 			int32_t *val;
 			(void) nvpair_value_int32_array(nvp, &val, &nelem);
-			for (i = 0; i < nelem; i++)
-				(void) fprintf(fp, " %d", val[i]);
+			ARENDER(pctl, int32_array, nvl, name, val, nelem);
 			break;
 		}
 		case DATA_TYPE_UINT32_ARRAY: {
 			uint32_t *val;
 			(void) nvpair_value_uint32_array(nvp, &val, &nelem);
-			for (i = 0; i < nelem; i++)
-				(void) fprintf(fp, " 0x%x", val[i]);
+			ARENDER(pctl, uint32_array, nvl, name, val, nelem);
 			break;
 		}
 		case DATA_TYPE_INT64_ARRAY: {
 			int64_t *val;
 			(void) nvpair_value_int64_array(nvp, &val, &nelem);
-			for (i = 0; i < nelem; i++)
-				(void) fprintf(fp, " %lld", (longlong_t)val[i]);
+			ARENDER(pctl, int64_array, nvl, name, val, nelem);
 			break;
 		}
 		case DATA_TYPE_UINT64_ARRAY: {
 			uint64_t *val;
 			(void) nvpair_value_uint64_array(nvp, &val, &nelem);
-			for (i = 0; i < nelem; i++)
-				(void) fprintf(fp, " 0x%llx",
-				    (u_longlong_t)val[i]);
+			ARENDER(pctl, uint64_array, nvl, name, val, nelem);
 			break;
 		}
 		case DATA_TYPE_STRING_ARRAY: {
 			char **val;
 			(void) nvpair_value_string_array(nvp, &val, &nelem);
-			for (i = 0; i < nelem; i++)
-				(void) fprintf(fp, " %s", val[i]);
+			ARENDER(pctl, string_array, nvl, name, val, nelem);
 			break;
 		}
 		case DATA_TYPE_HRTIME: {
 			hrtime_t val;
 			(void) nvpair_value_hrtime(nvp, &val);
-			(void) fprintf(fp, " 0x%llx", val);
+			RENDER(pctl, hrtime, nvl, name, val);
 			break;
 		}
 		case DATA_TYPE_NVLIST: {
 			nvlist_t *val;
 			(void) nvpair_value_nvlist(nvp, &val);
-			(void) fprintf(fp, " (embedded nvlist)\n");
-			nvlist_print_with_indent(fp, val, depth + 1);
-			indent(fp, depth + 1);
-			(void) fprintf(fp, "(end %s)\n", name);
+			RENDER(pctl, nvlist, nvl, name, val);
 			break;
 		}
 		case DATA_TYPE_NVLIST_ARRAY: {
 			nvlist_t **val;
 			(void) nvpair_value_nvlist_array(nvp, &val, &nelem);
-			(void) fprintf(fp, " (array of embedded nvlists)\n");
-			for (i = 0; i < nelem; i++) {
-				indent(fp, depth + 1);
-				(void) fprintf(fp,
-				    "(start %s[%d])\n", name, i);
-				nvlist_print_with_indent(fp, val[i], depth + 1);
-				indent(fp, depth + 1);
-				(void) fprintf(fp, "(end %s[%d])\n", name, i);
-			}
+			ARENDER(pctl, nvlist_array, nvl, name, val, nelem);
 			break;
 		}
 		default:
 			(void) fprintf(fp, " unknown data type (%d)", type);
 			break;
 		}
-		(void) fprintf(fp, "\n");
 		nvp = nvlist_next_nvpair(nvl, nvp);
 	}
 }
@@ -268,9 +754,17 @@
 void
 nvlist_print(FILE *fp, nvlist_t *nvl)
 {
-	nvlist_print_with_indent(fp, nvl, 0);
+	struct nvlist_prtctl pc;
+
+	prtctl_defaults(fp, &pc, NULL);
+	nvlist_print_with_indent(nvl, &pc);
 }
 
+void
+nvlist_prt(nvlist_t *nvl, nvlist_prtctl_t pctl)
+{
+	nvlist_print_with_indent(nvl, pctl);
+}
 
 #define	NVP(elem, type, vtype, ptype, format) { \
 	vtype	value; \
@@ -422,6 +916,14 @@
 }
 
 /*
+ * ======================================================================
+ * |									|
+ * | Misc private interface.						|
+ * |									|
+ * ======================================================================
+ */
+
+/*
  * Determine if string 'value' matches 'nvp' value.  The 'value' string is
  * converted, depending on the type of 'nvp', prior to match.  For numeric
  * types, a radix independent sscanf conversion of 'value' is used. If 'nvp'
--- a/usr/src/lib/libnvpair/libnvpair.h	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/lib/libnvpair/libnvpair.h	Fri Jul 30 17:04:17 2010 +1000
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #ifndef	_LIBNVPAIR_H
@@ -35,10 +34,158 @@
 extern "C" {
 #endif
 
-void nvlist_print(FILE *, nvlist_t *);
-int nvpair_value_match(nvpair_t *, int, char *, char **);
-int nvpair_value_match_regex(nvpair_t *, int, char *, regex_t *, char **);
-void dump_nvlist(nvlist_t *, int);
+/*
+ * All interfaces described in this file are private to Solaris, and
+ * are subject to change at any time and without notice.  The public
+ * nvlist/nvpair interfaces, as documented in manpage sections 3NVPAIR,
+ * are all imported from <sys/nvpair.h> included above.
+ */
+
+extern int nvpair_value_match(nvpair_t *, int, char *, char **);
+extern int nvpair_value_match_regex(nvpair_t *, int, char *, regex_t *,
+    char **);
+
+extern void nvlist_print(FILE *, nvlist_t *);
+extern void dump_nvlist(nvlist_t *, int);
+
+/*
+ * Private nvlist printing interface that allows the caller some control
+ * over output rendering (as opposed to nvlist_print and dump_nvlist).
+ *
+ * Obtain an opaque nvlist_prtctl_t cookie using nvlist_prtctl_alloc
+ * (NULL on failure);  on return the cookie is set up for default formatting
+ * and rendering.  Quote the cookie in subsequent customisation functions and
+ * then pass the cookie to nvlist_prt to render the nvlist.  Finally,
+ * use nvlist_prtctl_free to release the cookie.
+ *
+ * For all nvlist_lookup_xxx and nvlist_lookup_xxx_array functions
+ * we have a corresponding brace of functions that appoint replacement
+ * rendering functions:
+ *
+ *	extern void nvlist_prtctl_xxx(nvlist_prtctl_t,
+ *	    void (*)(nvlist_prtctl_t ctl, void *private, const char *name,
+ *	    xxxtype value))
+ *
+ *	and
+ *
+ *	extern void nvlist_prtctl_xxx_array(nvlist_prtctl_t,
+ *	    void (*)(nvlist_prtctl_t ctl, void *private, const char *name,
+ *	    xxxtype value, uint_t count))
+ *
+ * where xxxtype is the C datatype corresponding to xxx, eg int8_t for "int8"
+ * and char * for "string".  The function that is appointed to render the
+ * specified datatype receives as arguments the cookie, the nvlist
+ * member name, the value of that member (or a pointer for array function),
+ * and (for array rendering functions) a count of the number of elements.
+ */
+
+typedef struct nvlist_prtctl *nvlist_prtctl_t;	/* opaque */
+
+enum nvlist_indent_mode {
+	NVLIST_INDENT_ABS,	/* Absolute indentation */
+	NVLIST_INDENT_TABBED	/* Indent with tabstops */
+};
+
+extern nvlist_prtctl_t nvlist_prtctl_alloc(void);
+extern void nvlist_prtctl_free(nvlist_prtctl_t);
+extern void nvlist_prt(nvlist_t *, nvlist_prtctl_t);
+
+/* Output stream */
+extern void nvlist_prtctl_setdest(nvlist_prtctl_t, FILE *);
+extern FILE *nvlist_prtctl_getdest(nvlist_prtctl_t);
+
+/* Indentation mode, start indent, indent increment; default tabbed/0/1 */
+extern void nvlist_prtctl_setindent(nvlist_prtctl_t, enum nvlist_indent_mode,
+    int, int);
+extern void nvlist_prtctl_doindent(nvlist_prtctl_t, int);
+
+enum nvlist_prtctl_fmt {
+	NVLIST_FMT_MEMBER_NAME,		/* name fmt; default "%s = " */
+	NVLIST_FMT_MEMBER_POSTAMBLE,	/* after nvlist member; default "\n" */
+	NVLIST_FMT_BTWN_ARRAY		/* between array members; default " " */
+};
+
+extern void nvlist_prtctl_setfmt(nvlist_prtctl_t, enum nvlist_prtctl_fmt,
+    const char *);
+extern void nvlist_prtctl_dofmt(nvlist_prtctl_t, enum nvlist_prtctl_fmt, ...);
+
+/*
+ * Function prototypes for interfaces that appoint a new rendering function
+ * for single-valued nvlist members.
+ *
+ * A replacement function receives arguments as follows:
+ *
+ *	nvlist_prtctl_t	Print control structure; do not change preferences
+ *			for this object from a print callback function.
+ *
+ *	void *		The function-private cookie argument registered
+ *			when the replacement function was appointed.
+ *
+ *	nvlist_t *	The full nvlist that is being processed.  The
+ *			rendering function is called to render a single
+ *			member (name and value passed as below) but it may
+ *			want to reference or incorporate other aspects of
+ *			the full nvlist.
+ *
+ *	const char *	Member name to render
+ *
+ *	valtype		Value of the member to render
+ *
+ * The function must return non-zero if it has rendered output for this
+ * member, or 0 if it wants to default to standard rendering for this
+ * one member.
+ */
+
+#define	NVLIST_PRINTCTL_SVDECL(funcname, valtype) \
+    extern void funcname(nvlist_prtctl_t, \
+    int (*)(nvlist_prtctl_t, void *, nvlist_t *, const char *, valtype), \
+    void *)
+
+NVLIST_PRINTCTL_SVDECL(nvlist_prtctlop_boolean, int);
+NVLIST_PRINTCTL_SVDECL(nvlist_prtctlop_boolean_value, boolean_t);
+NVLIST_PRINTCTL_SVDECL(nvlist_prtctlop_byte, uchar_t);
+NVLIST_PRINTCTL_SVDECL(nvlist_prtctlop_int8, int8_t);
+NVLIST_PRINTCTL_SVDECL(nvlist_prtctlop_uint8, uint8_t);
+NVLIST_PRINTCTL_SVDECL(nvlist_prtctlop_int16, int16_t);
+NVLIST_PRINTCTL_SVDECL(nvlist_prtctlop_uint16, uint16_t);
+NVLIST_PRINTCTL_SVDECL(nvlist_prtctlop_int32, int32_t);
+NVLIST_PRINTCTL_SVDECL(nvlist_prtctlop_uint32, uint32_t);
+NVLIST_PRINTCTL_SVDECL(nvlist_prtctlop_int64, int64_t);
+NVLIST_PRINTCTL_SVDECL(nvlist_prtctlop_uint64, uint64_t);
+NVLIST_PRINTCTL_SVDECL(nvlist_prtctlop_double, double);
+NVLIST_PRINTCTL_SVDECL(nvlist_prtctlop_string, char *);
+NVLIST_PRINTCTL_SVDECL(nvlist_prtctlop_hrtime, hrtime_t);
+NVLIST_PRINTCTL_SVDECL(nvlist_prtctlop_nvlist, nvlist_t *);
+
+#undef	NVLIST_PRINTCTL_SVDECL	/* was just for "clarity" above */
+
+/*
+ * Function prototypes for interfaces that appoint a new rendering function
+ * for array-valued nvlist members.
+ *
+ * One additional argument is taken: uint_t for the number of array elements
+ *
+ * Return values as above.
+ */
+#define	NVLIST_PRINTCTL_AVDECL(funcname, vtype) \
+    extern void funcname(nvlist_prtctl_t, \
+    int (*)(nvlist_prtctl_t, void *, nvlist_t *, const char *, vtype, uint_t), \
+    void *)
+
+NVLIST_PRINTCTL_AVDECL(nvlist_prtctlop_boolean_array, boolean_t *);
+NVLIST_PRINTCTL_AVDECL(nvlist_prtctlop_byte_array, uchar_t *);
+NVLIST_PRINTCTL_AVDECL(nvlist_prtctlop_int8_array, int8_t *);
+NVLIST_PRINTCTL_AVDECL(nvlist_prtctlop_uint8_array, uint8_t *);
+NVLIST_PRINTCTL_AVDECL(nvlist_prtctlop_int16_array, int16_t *);
+NVLIST_PRINTCTL_AVDECL(nvlist_prtctlop_uint16_array, uint16_t *);
+NVLIST_PRINTCTL_AVDECL(nvlist_prtctlop_int32_array, int32_t *);
+NVLIST_PRINTCTL_AVDECL(nvlist_prtctlop_uint32_array, uint32_t *);
+NVLIST_PRINTCTL_AVDECL(nvlist_prtctlop_int64_array, int64_t *);
+NVLIST_PRINTCTL_AVDECL(nvlist_prtctlop_uint64_array, uint64_t *);
+NVLIST_PRINTCTL_AVDECL(nvlist_prtctlop_string_array, char **);
+NVLIST_PRINTCTL_AVDECL(nvlist_prtctlop_nvlist_array, nvlist_t **);
+
+#undef	NVLIST_PRINTCTL_AVDECL	/* was just for "clarity" above */
 
 #ifdef	__cplusplus
 }
--- a/usr/src/lib/libnvpair/mapfile-vers	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/lib/libnvpair/mapfile-vers	Fri Jul 30 17:04:17 2010 +1000
@@ -43,6 +43,7 @@
 	nvlist_add_double;
 	nvlist_empty;
 	nvlist_lookup_double;
+	nvlist_nvflag;
 	nvlist_prev_nvpair;
 	nvlist_remove_nvpair;
 	nvpair_value_double;
@@ -174,6 +175,42 @@
 	nvlist_add_hrtime;
 	nvlist_lookup_hrtime;
 	nvlist_print;
+	nvlist_prt;
+	nvlist_prtctl_alloc;
+	nvlist_prtctl_free;
+	nvlist_prtctl_getdest;
+	nvlist_prtctl_dofmt;
+	nvlist_prtctl_doindent;
+	nvlist_prtctl_setdest;
+	nvlist_prtctl_setfmt;
+	nvlist_prtctl_setindent;
+	nvlist_prtctlop_byte;
+	nvlist_prtctlop_byte_array;
+	nvlist_prtctlop_boolean;
+	nvlist_prtctlop_boolean_array;
+	nvlist_prtctlop_boolean_value;
+	nvlist_prtctlop_double;
+	nvlist_prtctlop_hrtime;
+	nvlist_prtctlop_int8;
+	nvlist_prtctlop_int8_array;
+	nvlist_prtctlop_int16;
+	nvlist_prtctlop_int16_array;
+	nvlist_prtctlop_int32;
+	nvlist_prtctlop_int32_array;
+	nvlist_prtctlop_int64;
+	nvlist_prtctlop_int64_array;
+	nvlist_prtctlop_nvlist;
+	nvlist_prtctlop_nvlist_array;
+	nvlist_prtctlop_string;
+	nvlist_prtctlop_string_array;
+	nvlist_prtctlop_uint8;
+	nvlist_prtctlop_uint8_array;
+	nvlist_prtctlop_uint16;
+	nvlist_prtctlop_uint16_array;
+	nvlist_prtctlop_uint32;
+	nvlist_prtctlop_uint32_array;
+	nvlist_prtctlop_uint64;
+	nvlist_prtctlop_uint64_array;
 	nvpair_value_hrtime;
 	nvpair_type_is_array;
 	nvlist_lookup_nvpair_embedded_index;
--- a/usr/src/lib/librestart/common/librestart.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/lib/librestart/common/librestart.c	Fri Jul 30 17:04:17 2010 +1000
@@ -20,10 +20,10 @@
  */
 
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
+#include <libintl.h>
 #include <librestart.h>
 #include <librestart_priv.h>
 #include <libscf.h>
@@ -108,6 +108,334 @@
 };
 
 /*
+ * Long reasons must all parse/read correctly in the following contexts:
+ *
+ * "A service instance transitioned state: %s."
+ * "A service failed: %s."
+ * "Reason: %s."
+ * "The service transitioned state (%s) and ..."
+ *
+ * With the exception of restart_str_none they must also fit the following
+ * moulds:
+ *
+ * "An instance transitioned because %s, and ..."
+ * "An instance transitioned to <new-state> because %s, and ..."
+ *
+ * Note that whoever is rendering the long message must provide the
+ * terminal punctuation - don't include it here.  Similarly, do not
+ * provide an initial capital letter in reason-long.
+ *
+ * The long reason strings are Volatile - within the grammatical constraints
+ * above we may improve them as need be.  The intention is that a consumer
+ * may blindly render the string along the lines of the above examples,
+ * but has no other guarantees as to the exact wording.  Long reasons
+ * are localized.
+ *
+ * We define revisions of the set of short reason strings in use.  Within
+ * a given revision, all short reasons are Committed.  Consumers must check
+ * the revision in use before relying on the semantics of the short reason
+ * codes - if the version exceeds that which they are familiar with they should
+ * fail gracefully.  Having checked for version compatability, a consumer
+ * is assured that
+ *
+ *	"short_reason_A iff semantic_A", provided:
+ *
+ *		. the restarter uses this short reason code at all,
+ *		. the short reason is not "none" (which a restarter could
+ *		  specifiy for any transition semantics)
+ *
+ * To split/refine such a Committed semantic_A into further cases,
+ * we are required to bump the revision number.  This should be an
+ * infrequent occurence.  If you bump the revision number you may
+ * need to make corresponding changes in any source that calls
+ * restarter_str_version (e.g., FMA event generation).
+ *
+ * To add additional reasons to the set you must also bump the version
+ * number.
+ */
+
+/*
+ * The following describes revision 0 of the set of transition reasons.
+ * Read the preceding block comment before making any changes.
+ */
+static const struct restarter_state_transition_reason restarter_str[] = {
+	/*
+	 * Any transition for which the restarter has not provided a reason.
+	 */
+	{
+	    restarter_str_none,
+	    "none",
+	    "the restarter gave no reason"
+	},
+
+	/*
+	 * A transition to maintenance state due to a
+	 * 'svcadm mark maintenance <fmri>'.  *Not* used if the libscf
+	 * interface smf_maintain_instance(3SCF) is used to request maintenance.
+	 */
+	{
+	    restarter_str_administrative_request,
+	    "administrative_request",
+	    "maintenance was requested by an administrator"
+	},
+
+	/*
+	 * A transition to maintenance state if a repository inconsistency
+	 * exists when the service/instance state is first read by startd
+	 * into the graph engine (this can also happen during startd restart).
+	 */
+	{
+	    restarter_str_bad_repo_state,
+	    "bad_repo_state",
+	    "an SMF repository inconsistecy exists"
+	},
+
+	/*
+	 * A transition 'maintenance -> uninitialized' resulting always
+	 * from 'svcadm clear <fmri>'.  *Not* used if the libscf interface
+	 * smf_restore_instance(3SCF) is used.
+	 */
+	{
+	    restarter_str_clear_request,
+	    "clear_request",
+	    "maintenance clear was requested by an administrator"
+	},
+
+	/*
+	 * A transition 'online -> offline' due to a process core dump.
+	 */
+	{
+	    restarter_str_ct_ev_core,
+	    "ct_ev_core",
+	    "a process dumped core"
+	},
+
+	/*
+	 * A transition 'online -> offline' due to an empty process contract,
+	 * i.e., the last process in a contract type service has exited.
+	 */
+	{
+	    restarter_str_ct_ev_exit,
+	    "ct_ev_exit",
+	    "all processes in the service have exited"
+	},
+
+	/*
+	 * A transition 'online -> offline' due to a hardware error.
+	 */
+	{
+	    restarter_str_ct_ev_hwerr,
+	    "ct_ev_hwerr",
+	    "a process was killed due to uncorrectable hardware error"
+	},
+
+	/*
+	 * A transition 'online -> offline' due to a process in the service
+	 * having received a fatal signal originating from outside the
+	 * service process contract.
+	 */
+	{
+	    restarter_str_ct_ev_signal,
+	    "ct_ev_signal",
+	    "a process received a fatal signal from outside the service"
+	},
+
+	/*
+	 * A transition 'offline -> online' when all dependencies for the
+	 * service have been met.
+	 */
+	{
+	    restarter_str_dependencies_satisfied,
+	    "dependencies_satisfied",
+	    "all dependencies have been satisfied"
+	},
+
+	/*
+	 * A transition 'online -> offline' because some dependency for the
+	 * service is no-longer met.
+	 */
+	{
+	    restarter_str_dependency_activity,
+	    "dependency_activity",
+	    "a dependency activity required a stop"
+	},
+
+	/*
+	 * A transition to maintenance state due to a cycle in the
+	 * service dependencies.
+	 */
+	{
+	    restarter_str_dependency_cycle,
+	    "dependency_cycle",
+	    "a dependency cycle exists"
+	},
+
+	/*
+	 * A transition 'online -> offline -> disabled' due to a
+	 * 'svcadm disable [-t] <fmri>' or smf_disable_instance(3SCF) call.
+	 */
+	{
+	    restarter_str_disable_request,
+	    "disable_request",
+	    "a disable was requested"
+	},
+
+	/*
+	 * A transition 'disabled -> offline' due to a
+	 * 'svcadm enable [-t] <fmri>' or smf_enable_instance(3SCF) call.
+	 */
+	{
+	    restarter_str_enable_request,
+	    "enable_request",
+	    "an enable was requested"
+	},
+
+	/*
+	 * A transition to maintenance state when a method fails
+	 * repeatedly for a retryable reason.
+	 */
+	{
+	    restarter_str_fault_threshold_reached,
+	    "fault_threshold_reached",
+	    "a method is failing in a retryable manner but too often"
+	},
+
+	/*
+	 * A transition to uninitialized state when startd reads the service
+	 * configuration and inserts it into the graph engine.
+	 */
+	{
+	    restarter_str_insert_in_graph,
+	    "insert_in_graph",
+	    "the instance was inserted in the graph"
+	},
+
+	/*
+	 * A transition to maintenance state due to an invalid dependency
+	 * declared for the service.
+	 */
+	{
+	    restarter_str_invalid_dependency,
+	    "invalid_dependency",
+	    "a service has an invalid dependency"
+	},
+
+	/*
+	 * A transition to maintenance state because the service-declared
+	 * restarter is invalid.
+	 */
+	{
+	    restarter_str_invalid_restarter,
+	    "invalid_restarter",
+	    "the service restarter is invalid"
+	},
+
+	/*
+	 * A transition to maintenance state because a restarter method
+	 * exited with one of SMF_EXIT_ERR_CONFIG, SMF_EXIT_ERR_NOSMF,
+	 * SMF_EXIT_ERR_PERM, or SMF_EXIT_ERR_FATAL.
+	 */
+	{
+	    restarter_str_method_failed,
+	    "method_failed",
+	    "a start, stop or refresh method failed"
+	},
+
+	/*
+	 * A transition 'uninitialized -> {disabled|offline}' after
+	 * "insert_in_graph" to match the state configured in the
+	 * repository.
+	 */
+	{
+	    restarter_str_per_configuration,
+	    "per_configuration",
+	    "the SMF repository configuration specifies this state"
+	},
+
+	/*
+	 * Refresh requested - no state change.
+	 */
+	{
+	    restarter_str_refresh,
+	    NULL,
+	    "a refresh was requested (no change of state)"
+	},
+
+	/*
+	 * A transition 'online -> offline -> online' due to a
+	 * 'svcadm restart <fmri> or equivlaent libscf API call.
+	 * Both the 'online -> offline' and 'offline -> online' transtions
+	 * specify this reason.
+	 */
+	{
+	    restarter_str_restart_request,
+	    "restart_request",
+	    "a restart was requested"
+	},
+
+	/*
+	 * A transition to maintenance state because the start method is
+	 * being executed successfully but too frequently.
+	 */
+	{
+	    restarter_str_restarting_too_quickly,
+	    "restarting_too_quickly",
+	    "the instance is restarting too quickly"
+	},
+
+	/*
+	 * A transition to maintenance state due a service requesting
+	 * 'svcadm mark maintenance <fmri>' or equivalent libscf API call.
+	 * A command line 'svcadm mark maintenance <fmri>' does not produce
+	 * this reason - it produces administrative_request instead.
+	 */
+	{
+	    restarter_str_service_request,
+	    "service_request",
+	    "maintenance was requested by another service"
+	},
+
+	/*
+	 * An instanced inserted into the graph at its existing state
+	 * during a startd restart - no state change.
+	 */
+	{
+	    restarter_str_startd_restart,
+	    NULL,
+	    "the instance was inserted in the graph due to startd restart"
+	}
+};
+
+uint32_t
+restarter_str_version(void)
+{
+	return (RESTARTER_STRING_VERSION);
+}
+
+const char *
+restarter_get_str_short(restarter_str_t key)
+{
+	int i;
+	for (i = 0; i < sizeof (restarter_str) /
+	    sizeof (struct restarter_state_transition_reason); i++)
+		if (key == restarter_str[i].str_key)
+			return (restarter_str[i].str_short);
+	return (NULL);
+}
+
+const char *
+restarter_get_str_long(restarter_str_t key)
+{
+	int i;
+	for (i = 0; i < sizeof (restarter_str) /
+	    sizeof (struct restarter_state_transition_reason); i++)
+		if (key == restarter_str[i].str_key)
+			return (dgettext(TEXT_DOMAIN,
+			    restarter_str[i].str_long));
+	return (NULL);
+}
+
+/*
  * A static no memory error message mc_error_t structure
  * to be used in cases when memory errors are to be returned
  * This avoids the need to attempt to allocate memory for the
@@ -495,8 +823,6 @@
  * Commit the state, next state, and auxiliary state into the repository.
  * Let the graph engine know about the state change and error.  On success,
  * return 0. On error, return
- *   EINVAL - aux has spaces
- *	    - inst is invalid or not an instance FMRI
  *   EPROTO - librestart compiled against different libscf
  *   ENOMEM - out of memory
  *	    - repository server out of resources
@@ -517,27 +843,18 @@
     restarter_instance_state_t new_cur_state,
     restarter_instance_state_t next_state,
     restarter_instance_state_t new_next_state, restarter_error_t e,
-    const char *aux)
+    restarter_str_t aux)
 {
 	nvlist_t *attr;
 	scf_handle_t *scf_h;
 	instance_data_t id;
 	int ret = 0;
-	char *p = (char *)aux;
+	const char *p = restarter_get_str_short(aux);
 
 	assert(h->reh_master_channel != NULL);
 	assert(h->reh_master_channel_name != NULL);
 	assert(h->reh_master_subscriber_id != NULL);
 
-	/* Validate format of auxiliary state: no spaces allowed */
-	if (p != NULL) {
-		while (*p != '\0') {
-			if (isspace(*p))
-				return (EINVAL);
-			p++;
-		}
-	}
-
 	if ((scf_h = scf_handle_create(SCF_VERSION)) == NULL) {
 		switch (scf_error()) {
 		case SCF_ERROR_VERSION_MISMATCH:
@@ -572,7 +889,8 @@
 	    nvlist_add_int32(attr, RESTARTER_NAME_NEXT_STATE, new_next_state)
 	    != 0 ||
 	    nvlist_add_int32(attr, RESTARTER_NAME_ERROR, e) != 0 ||
-	    nvlist_add_string(attr, RESTARTER_NAME_INSTANCE, inst) != 0) {
+	    nvlist_add_string(attr, RESTARTER_NAME_INSTANCE, inst) != 0 ||
+	    nvlist_add_int32(attr, RESTARTER_NAME_REASON, aux) != 0) {
 		ret = ENOMEM;
 	} else {
 		id.i_fmri = inst;
@@ -580,7 +898,7 @@
 		id.i_next_state = next_state;
 
 		ret = _restarter_commit_states(scf_h, &id, new_cur_state,
-		    new_next_state, aux);
+		    new_next_state, p);
 
 		if (ret == 0) {
 			ret = restarter_event_publish_retry(
--- a/usr/src/lib/librestart/common/librestart.h	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/lib/librestart/common/librestart.h	Fri Jul 30 17:04:17 2010 +1000
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #ifndef	_LIBRESTART_H
@@ -74,7 +73,7 @@
  * protocol. In practice, increment RESTARTER_EVENT_VERSION whenever the
  * protocol might have changed.
  */
-#define	RESTARTER_EVENT_VERSION		4
+#define	RESTARTER_EVENT_VERSION		5
 
 #define	RESTARTER_FLAG_DEBUG		1
 
@@ -192,6 +191,43 @@
     restarter_instance_state_t *, restarter_instance_state_t *);
 
 /*
+ * State transition reasons
+ */
+
+typedef enum {
+	restarter_str_none,
+	restarter_str_administrative_request,
+	restarter_str_bad_repo_state,
+	restarter_str_clear_request,
+	restarter_str_ct_ev_core,
+	restarter_str_ct_ev_exit,
+	restarter_str_ct_ev_hwerr,
+	restarter_str_ct_ev_signal,
+	restarter_str_dependencies_satisfied,
+	restarter_str_dependency_activity,
+	restarter_str_dependency_cycle,
+	restarter_str_disable_request,
+	restarter_str_enable_request,
+	restarter_str_fault_threshold_reached,
+	restarter_str_insert_in_graph,
+	restarter_str_invalid_dependency,
+	restarter_str_invalid_restarter,
+	restarter_str_method_failed,
+	restarter_str_per_configuration,
+	restarter_str_refresh,
+	restarter_str_restart_request,
+	restarter_str_restarting_too_quickly,
+	restarter_str_service_request,
+	restarter_str_startd_restart
+} restarter_str_t;
+
+struct restarter_state_transition_reason {
+	restarter_str_t	str_key;
+	const char	*str_short;
+	const char	*str_long;
+};
+
+/*
  * Functions for updating the repository.
  */
 
@@ -207,10 +243,20 @@
 int restarter_set_states(restarter_event_handle_t *, const char *,
     restarter_instance_state_t, restarter_instance_state_t,
     restarter_instance_state_t, restarter_instance_state_t, restarter_error_t,
-    const char *);
+    restarter_str_t);
 int restarter_event_publish_retry(evchan_t *, const char *, const char *,
     const char *, const char *, nvlist_t *, uint32_t);
 
+/*
+ * functions for retrieving the state transition reason messages
+ */
+
+#define	RESTARTER_STRING_VERSION	1
+
+uint32_t restarter_str_version(void);
+const char *restarter_get_str_short(restarter_str_t);
+const char *restarter_get_str_long(restarter_str_t);
+
 int restarter_store_contract(scf_instance_t *, ctid_t,
     restarter_contract_type_t);
 int restarter_remove_contract(scf_instance_t *, ctid_t,
--- a/usr/src/lib/librestart/common/librestart_priv.h	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/lib/librestart/common/librestart_priv.h	Fri Jul 30 17:04:17 2010 +1000
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * 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.
@@ -20,15 +19,12 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #ifndef	_LIBRESTART_PRIV_H
 #define	_LIBRESTART_PRIV_H
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <libscf.h>
 #include <librestart.h>
 
@@ -42,6 +38,7 @@
 #define	RESTARTER_NAME_NEXT_STATE	SCF_PROPERTY_NEXT_STATE
 #define	RESTARTER_NAME_AUX_STATE	SCF_PROPERTY_AUX_STATE
 #define	RESTARTER_NAME_ERROR		"error"
+#define	RESTARTER_NAME_REASON		"reason"
 
 #define	RESTARTER_CHANNEL_MASTER	0
 #define	RESTARTER_CHANNEL_DELEGATE	1
--- a/usr/src/lib/librestart/common/mapfile-vers	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/lib/librestart/common/mapfile-vers	Fri Jul 30 17:04:17 2010 +1000
@@ -63,12 +63,15 @@
 	restarter_state_to_string;
 	restarter_store_contract;
 	restarter_string_to_state;
+	restarter_str_version;
 	restarter_inst_validate_ractions_aux_fmri;
 	restarter_inst_ractions_from_tty;
 	restarter_inst_reset_ractions_aux_fmri;
 	restarter_inst_reset_aux_fmri;
 	restarter_inst_set_aux_fmri;
 	restarter_mc_error_destroy;
+	restarter_get_str_short;
+	restarter_get_str_long;
     local:
 	*;
 };
--- a/usr/src/lib/libscf/Makefile.com	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/lib/libscf/Makefile.com	Fri Jul 30 17:04:17 2010 +1000
@@ -29,6 +29,7 @@
 	error.o			\
 	lowlevel.o		\
 	midlevel.o		\
+	notify_params.o		\
 	highlevel.o		\
 	scf_tmpl.o		\
 	scf_type.o
@@ -43,7 +44,7 @@
 $(NATIVE_BUILD)LIBS = $(DYNLIB)
 
 LDLIBS_i386 += -lsmbios
-LDLIBS +=	-luutil -lc -lgen -lnsl
+LDLIBS +=	-luutil -lc -lgen -lnsl -lnvpair
 LDLIBS +=	$(LDLIBS_$(MACH))
 
 SRCDIR =	../common
@@ -64,7 +65,7 @@
 		-DNATIVE_BUILD $(DTEXTDOM) \
 		-I../inc -I$(COMDIR) -I$(LIBUUTIL)/common -I$(ROOTHDRDIR)
 MY_NATIVE_LDLIBS = -L$(LIBUUTIL)/native -R$(LIBUUTIL)/native -luutil -lc -lgen \
-		   -lnsl
+		   -lnsl -lnvpair
 MY_NATIVE_LDLIBS_i386 = -lsmbios
 MY_NATIVE_LDLIBS += $(MY_NATIVE_LDLIBS_$(MACH))
 
--- a/usr/src/lib/libscf/common/libscf_impl.h	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/lib/libscf/common/libscf_impl.h	Fri Jul 30 17:04:17 2010 +1000
@@ -20,15 +20,12 @@
  */
 
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #ifndef	_LIBSCF_IMPL_H
 #define	_LIBSCF_IMPL_H
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <libscf.h>
 #include <libscf_priv.h>
 
@@ -58,7 +55,10 @@
 	SCF_MSG_PATTERN_LEGACY
 } scf_msg_t;
 
+scf_type_t scf_true_base_type(scf_type_t);
 const char *scf_get_msg(scf_msg_t);
+int ismember(const scf_error_t, const scf_error_t[]);
+int32_t state_from_string(const char *, size_t);
 
 #ifdef	__cplusplus
 }
--- a/usr/src/lib/libscf/common/lowlevel.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/lib/libscf/common/lowlevel.c	Fri Jul 30 17:04:17 2010 +1000
@@ -793,6 +793,38 @@
 	return (ret);
 }
 
+/*
+ * Fails with
+ *   _NO_MEMORY
+ *   _NO_SERVER - server door could not be open()ed
+ *		  door call failed
+ *		  door_info() failed
+ *   _VERSION_MISMATCH - server returned bad file descriptor
+ *			 server claimed bad request
+ *			 server reported version mismatch
+ *			 server refused with unknown reason
+ *   _INVALID_ARGUMENT
+ *   _NO_RESOURCES - server is out of memory
+ *   _PERMISSION_DENIED
+ *   _INTERNAL - could not set up entities or iters
+ *		 server response too big
+ */
+scf_handle_t *
+_scf_handle_create_and_bind(scf_version_t ver)
+{
+	scf_handle_t *h;
+
+	h = scf_handle_create(ver);
+	if (h == NULL)
+		return (NULL);
+
+	if (scf_handle_bind(h) == -1) {
+		scf_handle_destroy(h);
+		return (NULL);
+	}
+	return (h);
+}
+
 int
 scf_handle_decorate(scf_handle_t *handle, const char *name, scf_value_t *v)
 {
@@ -4073,6 +4105,9 @@
 void
 scf_transaction_destroy_children(scf_transaction_t *tran)
 {
+	if (tran == NULL)
+		return;
+
 	scf_transaction_reset_impl(tran, 1, 0);
 }
 
--- a/usr/src/lib/libscf/common/mapfile-vers	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/lib/libscf/common/mapfile-vers	Fri Jul 30 17:04:17 2010 +1000
@@ -92,6 +92,11 @@
 	scf_tmpl_visibility_to_string;
 	scf_type_to_string;
 	scf_values_destroy;
+	smf_notify_del_params;
+	smf_notify_get_params;
+	smf_notify_set_params;
+	smf_state_from_string;
+	smf_state_to_string;
 } SUNW_1.1;
 
 SYMBOL_VERSION SUNW_1.1 {
@@ -275,16 +280,20 @@
 SYMBOL_VERSION SUNWprivate_1.1 {
     global:
 	gen_filenms_from_fmri;
+	ismember;
 	scf_canonify_fmri;
 	scf_cmp_pattern;
 	_scf_create_errors;
         scf_decode32;
 	scf_encode32;
 	scf_general_pg_setup;
+	_scf_get_fma_notify_params;
+	_scf_get_svc_notify_params;
 	_scf_handle_decorations;
 	scf_is_compatible_type;
 	_scf_notify_add_pgname;
 	_scf_notify_add_pgtype;
+	_scf_notify_get_params;
 	_scf_notify_wait;
 	scf_parse_file_fmri;
 	scf_parse_fmri;
@@ -320,6 +329,8 @@
 	scf_is_fastboot_default;
 	scf_fastreboot_default_set_transient;
 	_check_services;
+	_scf_handle_create_and_bind;
+	_smf_refresh_all_instances;
     local:
 	*;
 };
--- a/usr/src/lib/libscf/common/midlevel.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/lib/libscf/common/midlevel.c	Fri Jul 30 17:04:17 2010 +1000
@@ -28,7 +28,6 @@
 #include <assert.h>
 #include <libuutil.h>
 #include <stdio.h>
-#include <strings.h>
 #include <string.h>
 #include <stdlib.h>
 #include <sys/param.h>
@@ -51,25 +50,6 @@
 /* Path to speedy files area must end with a slash */
 #define	SMF_SPEEDY_FILES_PATH		"/etc/svc/volatile/"
 
-/*
- * Internal private function that creates and binds a handle.
- */
-static scf_handle_t *
-handle_create(void)
-{
-	scf_handle_t *h;
-
-	h = scf_handle_create(SCF_VERSION);
-	if (h == NULL)
-		return (NULL);
-
-	if (scf_handle_bind(h) == -1) {
-		scf_handle_destroy(h);
-		return (NULL);
-	}
-	return (h);
-}
-
 void
 scf_simple_handle_destroy(scf_simple_handle_t *simple_h)
 {
@@ -939,7 +919,7 @@
 	scf_instance_t *inst;
 	int ret = -1;
 
-	h = handle_create();
+	h = _scf_handle_create_and_bind(SCF_VERSION);
 	if (h == NULL)
 		return (-1);
 
@@ -1082,7 +1062,7 @@
 		return (ret);
 	}
 
-	if ((h = handle_create()) == NULL)
+	if ((h = _scf_handle_create_and_bind(SCF_VERSION)) == NULL)
 		return (ret);
 
 	if ((inst = scf_instance_create(h)) == NULL) {
@@ -1184,6 +1164,35 @@
 }
 
 int
+_smf_refresh_all_instances(scf_service_t *s)
+{
+	scf_handle_t	*h = scf_service_handle(s);
+	scf_instance_t	*i = scf_instance_create(h);
+	scf_iter_t	*it = scf_iter_create(h);
+	int err, r = -1;
+
+	if (h == NULL || i == NULL || it == NULL)
+		goto error;
+
+	if (scf_iter_service_instances(it, s) != 0)
+		goto error;
+
+	while ((err = scf_iter_next_instance(it, i)) == 1)
+		if (_smf_refresh_instance_i(i) != 0)
+			goto error;
+
+	if (err == -1)
+		goto error;
+
+	r = 0;
+error:
+	scf_instance_destroy(i);
+	scf_iter_destroy(it);
+
+	return (r);
+}
+
+int
 smf_refresh_instance(const char *instance)
 {
 	return (set_inst_action(instance, SCF_PROPERTY_REFRESH));
@@ -1320,7 +1329,7 @@
 		return (NULL);
 	} else {
 
-		ret->h = handle_create();
+		ret->h = _scf_handle_create_and_bind(SCF_VERSION);
 		ret->inst = scf_instance_create(ret->h);
 		ret->snap = scf_snapshot_create(ret->h);
 		ret->running_pg = scf_pg_create(ret->h);
@@ -1564,7 +1573,7 @@
 	int			svc_iter_ret, inst_iter_ret;
 	int			inst_state;
 
-	if ((h = handle_create()) == NULL)
+	if ((h = _scf_handle_create_and_bind(SCF_VERSION)) == NULL)
 		return (ret);
 
 	if (((scope = scf_scope_create(h)) == NULL) ||
@@ -1654,7 +1663,7 @@
 		local_h = B_FALSE;
 	}
 
-	if (local_h && ((h = handle_create()) == NULL))
+	if (local_h && ((h = _scf_handle_create_and_bind(SCF_VERSION)) == NULL))
 		return (NULL);
 
 	if ((fmri_buf = assemble_fmri(h, instance, pgname, propname)) == NULL) {
@@ -1795,7 +1804,7 @@
 		local_h = B_FALSE;
 	}
 
-	if (local_h && ((h = handle_create()) == NULL))
+	if (local_h && ((h = _scf_handle_create_and_bind(SCF_VERSION)) == NULL))
 		return (NULL);
 
 	if (inst_fmri == NULL) {
@@ -2495,7 +2504,7 @@
 	return (0);
 }
 
-static scf_type_t
+scf_type_t
 scf_true_base_type(scf_type_t type)
 {
 	scf_type_t base = type;
@@ -2576,7 +2585,7 @@
 scf_read_propvec(const char *fmri, const char *pgname, boolean_t running,
     scf_propvec_t *properties, scf_propvec_t **badprop)
 {
-	scf_handle_t *h = handle_create();
+	scf_handle_t *h = _scf_handle_create_and_bind(SCF_VERSION);
 	scf_service_t *s = scf_service_create(h);
 	scf_instance_t *i = scf_instance_create(h);
 	scf_snapshot_t *snap = running ? scf_snapshot_create(h) : NULL;
@@ -2742,7 +2751,7 @@
 scf_write_propvec(const char *fmri, const char *pgname,
     scf_propvec_t *properties, scf_propvec_t **badprop)
 {
-	scf_handle_t *h = handle_create();
+	scf_handle_t *h = _scf_handle_create_and_bind(SCF_VERSION);
 	scf_service_t *s = scf_service_create(h);
 	scf_instance_t *inst = scf_instance_create(h);
 	scf_snapshot_t *snap = scf_snapshot_create(h);
@@ -3052,3 +3061,86 @@
 		}
 	}
 }
+
+/*ARGSUSED*/
+static int
+str_compare(const char *s1, const char *s2, size_t n)
+{
+	return (strcmp(s1, s2));
+}
+
+static int
+str_n_compare(const char *s1, const char *s2, size_t n)
+{
+	return (strncmp(s1, s2, n));
+}
+
+int32_t
+state_from_string(const char *state, size_t l)
+{
+	int (*str_cmp)(const char *, const char *, size_t);
+
+	if (l == 0)
+		str_cmp = str_compare;
+	else
+		str_cmp = str_n_compare;
+
+	if (str_cmp(SCF_STATE_STRING_UNINIT, state, l) == 0)
+		return (SCF_STATE_UNINIT);
+	else if (str_cmp(SCF_STATE_STRING_MAINT, state, l) == 0)
+		return (SCF_STATE_MAINT);
+	else if (str_cmp(SCF_STATE_STRING_OFFLINE, state, l) == 0)
+		return (SCF_STATE_OFFLINE);
+	else if (str_cmp(SCF_STATE_STRING_DISABLED, state, l) == 0)
+		return (SCF_STATE_DISABLED);
+	else if (str_cmp(SCF_STATE_STRING_ONLINE, state, l) == 0)
+		return (SCF_STATE_ONLINE);
+	else if (str_cmp(SCF_STATE_STRING_DEGRADED, state, l) == 0)
+		return (SCF_STATE_DEGRADED);
+	else if (str_cmp("all", state, l) == 0)
+		return (SCF_STATE_ALL);
+	else
+		return (-1);
+}
+
+/*
+ * int32_t smf_state_from_string()
+ * return the value of the macro SCF_STATE_* for the corresponding state
+ * it returns SCF_STATE_ALL if "all" is passed. -1 if the string passed doesn't
+ * correspond to any valid state.
+ */
+int32_t
+smf_state_from_string(const char *state)
+{
+	return (state_from_string(state, 0));
+}
+
+/*
+ * smf_state_to_string()
+ * Takes an int32_t representing an SMF state and returns
+ * the corresponding string. The string is read only and need not to be
+ * freed.
+ * returns NULL on invalid input.
+ */
+const char *
+smf_state_to_string(int32_t s)
+{
+	switch (s) {
+	case SCF_STATE_UNINIT:
+		return (SCF_STATE_STRING_UNINIT);
+	case SCF_STATE_MAINT:
+		return (SCF_STATE_STRING_MAINT);
+	case SCF_STATE_OFFLINE:
+		return (SCF_STATE_STRING_OFFLINE);
+	case SCF_STATE_DISABLED:
+		return (SCF_STATE_STRING_DISABLED);
+	case SCF_STATE_ONLINE:
+		return (SCF_STATE_STRING_ONLINE);
+	case SCF_STATE_DEGRADED:
+		return (SCF_STATE_STRING_DEGRADED);
+	case SCF_STATE_ALL:
+		return ("all");
+	default:
+		return (NULL);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libscf/common/notify_params.c	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,1979 @@
+/*
+ * 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 "libscf_impl.h"
+
+#include <assert.h>
+#include <strings.h>
+
+/*
+ * Errors returned by smf_notify_{del|get|set}_params()
+ */
+static const scf_error_t errs_1[] = {
+	SCF_ERROR_BACKEND_ACCESS,
+	SCF_ERROR_BACKEND_READONLY,
+	SCF_ERROR_CONNECTION_BROKEN,
+	SCF_ERROR_DELETED,
+	SCF_ERROR_INTERNAL,
+	SCF_ERROR_INVALID_ARGUMENT,
+	SCF_ERROR_NO_MEMORY,
+	SCF_ERROR_NO_RESOURCES,
+	SCF_ERROR_NOT_FOUND,
+	SCF_ERROR_PERMISSION_DENIED,
+	0
+};
+
+/*
+ * Errors returned by smf_notify_{del|get|set}_params()
+ * Except SCF_ERROR_INVALID_ARGUMENT
+ */
+static const scf_error_t errs_2[] = {
+	SCF_ERROR_BACKEND_ACCESS,
+	SCF_ERROR_BACKEND_READONLY,
+	SCF_ERROR_CONNECTION_BROKEN,
+	SCF_ERROR_DELETED,
+	SCF_ERROR_INTERNAL,
+	SCF_ERROR_NO_MEMORY,
+	SCF_ERROR_NO_RESOURCES,
+	SCF_ERROR_NOT_FOUND,
+	SCF_ERROR_PERMISSION_DENIED,
+	0
+};
+
+/*
+ * Helper function that abort() on unexpected errors.
+ * The expected error set is a zero-terminated array of scf_error_t
+ */
+static int
+check_scf_error(scf_error_t e, const scf_error_t *errs)
+{
+	if (ismember(e, errs))
+		return (1);
+
+	assert(0);
+	abort();
+
+	/*NOTREACHED*/
+}
+
+/*
+ * Mapping of state transition to pgname.
+ */
+static struct st_pgname {
+	const char	*st_pgname;
+	int32_t		st_state;
+} st_pgnames[] = {
+	{ "to-uninitialized", SCF_TRANS(0, SCF_STATE_UNINIT) },
+	{ "from-uninitialized", SCF_TRANS(SCF_STATE_UNINIT, 0) },
+	{ "to-maintenance", SCF_TRANS(0, SCF_STATE_MAINT) },
+	{ "from-maintenance", SCF_TRANS(SCF_STATE_MAINT, 0) },
+	{ "to-offline", SCF_TRANS(0, SCF_STATE_OFFLINE) },
+	{ "from-offline", SCF_TRANS(SCF_STATE_OFFLINE, 0) },
+	{ "to-disabled", SCF_TRANS(0, SCF_STATE_DISABLED) },
+	{ "from-disabled", SCF_TRANS(SCF_STATE_DISABLED, 0) },
+	{ "to-online", SCF_TRANS(0, SCF_STATE_ONLINE) },
+	{ "from-online", SCF_TRANS(SCF_STATE_ONLINE, 0) },
+	{ "to-degraded", SCF_TRANS(0, SCF_STATE_DEGRADED) },
+	{ "from-degraded", SCF_TRANS(SCF_STATE_DEGRADED, 0) },
+	{ NULL, 0 }
+};
+
+/*
+ * Check if class matches or is a subclass of SCF_SVC_TRANSITION_CLASS
+ *
+ * returns 1, otherwise return 0
+ */
+static boolean_t
+is_svc_stn(const char *class)
+{
+	int n = strlen(SCF_SVC_TRANSITION_CLASS);
+
+	if (class && strncmp(class, SCF_SVC_TRANSITION_CLASS, n) == 0)
+		if (class[n] == '\0' || class[n] == '.')
+			return (1);
+	return (0);
+}
+
+/*
+ * Return the len of the base class. For instance, "class.class1.class2.*"
+ * will return the length of "class.class1.class2"
+ * This function does not check if the class or base class is valid.
+ * A class such as "class.class1....****" is not valid but will return the
+ * length of "class.class1....***"
+ */
+static size_t
+base_class_len(const char *c)
+{
+	const char *p;
+	size_t n;
+
+	if ((n = strlen(c)) == 0)
+		return (0);
+
+	p = c + n;
+
+	/* get rid of any trailing asterisk */
+	if (*--p == '*')
+		n--;
+
+	/* make sure the class doesn't end in '.' */
+	while (p >= c && *--p == '.')
+		n--;
+
+	return (n);
+}
+
+/*
+ * Allocates and builds the pgname for an FMA dotted class.
+ * The pgname will be of the form "class.class1.class2,SCF_NOTIFY_PG_POSTFIX"
+ *
+ * NULL on error
+ */
+static char *
+class_to_pgname(const char *class)
+{
+	size_t n;
+	ssize_t sz = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1;
+	char *pgname = NULL;
+
+	n = base_class_len(class);
+
+	if (n == 0) {
+		(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
+		return (NULL);
+	}
+
+	if ((pgname = malloc(sz)) == NULL) {
+		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
+		goto error;
+	}
+
+	if (snprintf(pgname, sz, "%.*s,%s", (int)n, class,
+	    SCF_NOTIFY_PG_POSTFIX) >= sz) {
+		(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
+		goto error;
+	}
+	return (pgname);
+
+error:
+	free(pgname);
+	pgname = NULL;
+
+	return (pgname);
+}
+
+/*
+ * Get the pg from the running snapshot of the instance (composed or not)
+ */
+static int
+get_pg(scf_service_t *s, scf_instance_t *i, const char *n,
+    scf_propertygroup_t *pg, int composed)
+{
+	scf_handle_t	*h = scf_instance_handle(i);
+	scf_error_t	scf_e = scf_error();
+	scf_snapshot_t	*snap = scf_snapshot_create(h);
+	scf_snaplevel_t	*slvl = scf_snaplevel_create(h);
+	int r = -1;
+
+	if (h == NULL) {
+		/*
+		 * Use the error stored in scf_e
+		 */
+		(void) scf_set_error(scf_e);
+		goto out;
+	}
+	if (s == NULL) {
+		if (snap == NULL || slvl == NULL)
+			goto out;
+		if (scf_instance_get_snapshot(i, "running", snap) != 0)
+			goto out;
+
+		if (composed) {
+			if (scf_instance_get_pg_composed(i, snap, n, pg) != 0)
+				goto out;
+		} else {
+			if (scf_snapshot_get_base_snaplevel(snap, slvl) != 0 ||
+			    scf_snaplevel_get_pg(slvl, n, pg) != 0)
+				goto out;
+		}
+	} else {
+		if (scf_service_get_pg(s, n, pg) != 0)
+			goto out;
+	}
+
+	r = 0;
+out:
+	scf_snaplevel_destroy(slvl);
+	scf_snapshot_destroy(snap);
+
+	return (r);
+}
+
+/*
+ * Add a pg if it does not exist, or get it if it exists.
+ * It operates on the instance if the service parameter is NULL.
+ *
+ * returns 0 on success or -1 on failure
+ */
+static int
+get_or_add_pg(scf_service_t *s, scf_instance_t *i, const char *n, const char *t,
+    uint32_t flags, scf_propertygroup_t *pg)
+{
+	int r;
+
+	if (s == NULL)
+		r = scf_instance_add_pg(i, n, t, flags, pg);
+	else
+		r = scf_service_add_pg(s, n, t, flags, pg);
+
+	if (r == 0)
+		return (0);
+	else if (scf_error() != SCF_ERROR_EXISTS)
+		return (-1);
+
+	if (s == NULL)
+		r = scf_instance_get_pg(i, n, pg);
+	else
+		r = scf_service_get_pg(s, n, pg);
+
+	return (r);
+}
+
+/*
+ * Delete the property group form the instance or service.
+ * If service is NULL, use instance, otherwise use only the service.
+ *
+ * Return SCF_SUCCESS or SCF_FAILED on
+ * 	SCF_ERROR_BACKEND_ACCESS
+ * 	SCF_ERROR_BACKEND_READONLY
+ * 	SCF_ERROR_CONNECTION_BROKEN
+ * 	SCF_ERROR_DELETED
+ * 	SCF_ERROR_HANDLE_MISMATCH
+ * 	SCF_ERROR_INTERNAL
+ * 	SCF_ERROR_INVALID_ARGUMENT
+ * 	SCF_ERROR_NO_RESOURCES
+ * 	SCF_ERROR_NOT_BOUND
+ * 	SCF_ERROR_NOT_FOUND
+ * 	SCF_ERROR_NOT_SET
+ * 	SCF_ERROR_PERMISSION_DENIED
+ */
+static int
+del_pg(scf_service_t *s, scf_instance_t *i, const char *n,
+    scf_propertygroup_t *pg)
+{
+	if ((s == NULL ? scf_instance_get_pg(i, n, pg) :
+	    scf_service_get_pg(s, n, pg)) != SCF_SUCCESS)
+		if (scf_error() == SCF_ERROR_NOT_FOUND)
+			return (SCF_SUCCESS);
+		else
+			return (SCF_FAILED);
+
+	if (scf_pg_delete(pg) != SCF_SUCCESS)
+		if (scf_error() == SCF_ERROR_DELETED)
+			return (SCF_SUCCESS);
+		else
+			return (SCF_FAILED);
+
+	return (SCF_SUCCESS);
+}
+
+static scf_type_t
+get_scf_type(nvpair_t *p)
+{
+	switch (nvpair_type(p)) {
+	case DATA_TYPE_BOOLEAN:
+	case DATA_TYPE_BOOLEAN_VALUE:
+	case DATA_TYPE_BOOLEAN_ARRAY:
+		return (SCF_TYPE_BOOLEAN);
+
+	case DATA_TYPE_BYTE:
+	case DATA_TYPE_UINT8:
+	case DATA_TYPE_UINT16:
+	case DATA_TYPE_UINT32:
+	case DATA_TYPE_UINT64:
+	case DATA_TYPE_BYTE_ARRAY:
+	case DATA_TYPE_UINT8_ARRAY:
+	case DATA_TYPE_UINT16_ARRAY:
+	case DATA_TYPE_UINT32_ARRAY:
+	case DATA_TYPE_UINT64_ARRAY:
+		return (SCF_TYPE_COUNT);
+
+	case DATA_TYPE_INT8:
+	case DATA_TYPE_INT16:
+	case DATA_TYPE_INT32:
+	case DATA_TYPE_INT64:
+	case DATA_TYPE_INT8_ARRAY:
+	case DATA_TYPE_INT16_ARRAY:
+	case DATA_TYPE_INT32_ARRAY:
+	case DATA_TYPE_INT64_ARRAY:
+		return (SCF_TYPE_INTEGER);
+
+	case DATA_TYPE_STRING:
+	case DATA_TYPE_STRING_ARRAY:
+		return (SCF_TYPE_ASTRING);
+
+	default:
+		return (SCF_TYPE_INVALID);
+	}
+}
+
+static int
+add_entry(scf_transaction_entry_t *te, scf_value_t *val)
+{
+	if (scf_entry_add_value(te, val) != 0) {
+		scf_value_destroy(val);
+		return (SCF_FAILED);
+	}
+
+	return (SCF_SUCCESS);
+}
+
+static int
+add_boolean_entry(scf_handle_t *h, scf_transaction_entry_t *te, uint8_t v)
+{
+	scf_value_t *val = scf_value_create(h);
+
+	if (val == NULL)
+		return (SCF_FAILED);
+
+	scf_value_set_boolean(val, v);
+
+	return (add_entry(te, val));
+}
+
+static int
+add_count_entry(scf_handle_t *h, scf_transaction_entry_t *te, uint64_t v)
+{
+	scf_value_t *val = scf_value_create(h);
+
+	if (val == NULL)
+		return (SCF_FAILED);
+
+	scf_value_set_count(val, v);
+
+	return (add_entry(te, val));
+}
+
+static int
+add_integer_entry(scf_handle_t *h, scf_transaction_entry_t *te, int64_t v)
+{
+	scf_value_t *val = scf_value_create(h);
+
+	if (val == NULL)
+		return (SCF_FAILED);
+
+	scf_value_set_integer(val, v);
+
+	return (add_entry(te, val));
+}
+
+static int
+add_astring_entry(scf_handle_t *h, scf_transaction_entry_t *te, char *s)
+{
+	scf_value_t *val = scf_value_create(h);
+
+	if (val == NULL)
+		return (SCF_FAILED);
+
+	if (scf_value_set_astring(val, s) != 0) {
+		scf_value_destroy(val);
+		return (SCF_FAILED);
+	}
+
+	return (add_entry(te, val));
+}
+
+static int
+get_nvpair_vals(scf_handle_t *h, scf_transaction_entry_t *te, nvpair_t *p)
+{
+	scf_value_t *val = scf_value_create(h);
+	uint_t n = 1;
+	int i;
+
+	if (val == NULL)
+		return (SCF_FAILED);
+
+	switch (nvpair_type(p)) {
+	case DATA_TYPE_BOOLEAN:
+		return (add_boolean_entry(h, te, 1));
+	case DATA_TYPE_BOOLEAN_VALUE:
+		{
+			boolean_t v;
+
+			(void) nvpair_value_boolean_value(p, &v);
+			return (add_boolean_entry(h, te, (uint8_t)v));
+		}
+	case DATA_TYPE_BOOLEAN_ARRAY:
+		{
+			boolean_t *v;
+
+			(void) nvpair_value_boolean_array(p, &v, &n);
+			for (i = 0; i < n; ++i) {
+				if (add_boolean_entry(h, te, (uint8_t)v[i]) !=
+				    SCF_SUCCESS)
+					return (SCF_FAILED);
+			}
+			return (SCF_SUCCESS);
+		}
+	case DATA_TYPE_BYTE:
+		{
+			uchar_t v;
+
+			(void) nvpair_value_byte(p, &v);
+			return (add_count_entry(h, te, v));
+		}
+	case DATA_TYPE_UINT8:
+		{
+			uint8_t v;
+
+			(void) nvpair_value_uint8(p, &v);
+			return (add_count_entry(h, te, v));
+		}
+	case DATA_TYPE_UINT16:
+		{
+			uint16_t v;
+
+			(void) nvpair_value_uint16(p, &v);
+			return (add_count_entry(h, te, v));
+		}
+	case DATA_TYPE_UINT32:
+		{
+			uint32_t v;
+
+			(void) nvpair_value_uint32(p, &v);
+			return (add_count_entry(h, te, v));
+		}
+	case DATA_TYPE_UINT64:
+		{
+			uint64_t v;
+
+			(void) nvpair_value_uint64(p, &v);
+			return (add_count_entry(h, te, v));
+		}
+	case DATA_TYPE_BYTE_ARRAY:
+		{
+			uchar_t *v;
+
+			(void) nvpair_value_byte_array(p, &v, &n);
+			for (i = 0; i < n; ++i) {
+				if (add_count_entry(h, te, v[i]) != SCF_SUCCESS)
+					return (SCF_FAILED);
+			}
+			return (SCF_SUCCESS);
+		}
+	case DATA_TYPE_UINT8_ARRAY:
+		{
+			uint8_t *v;
+
+			(void) nvpair_value_uint8_array(p, &v, &n);
+			for (i = 0; i < n; ++i) {
+				if (add_count_entry(h, te, v[i]) != SCF_SUCCESS)
+					return (SCF_FAILED);
+			}
+			return (SCF_SUCCESS);
+		}
+	case DATA_TYPE_UINT16_ARRAY:
+		{
+			uint16_t *v;
+
+			(void) nvpair_value_uint16_array(p, &v, &n);
+			for (i = 0; i < n; ++i) {
+				if (add_count_entry(h, te, v[i]) != SCF_SUCCESS)
+					return (SCF_FAILED);
+			}
+			return (SCF_SUCCESS);
+		}
+	case DATA_TYPE_UINT32_ARRAY:
+		{
+			uint32_t *v;
+
+			(void) nvpair_value_uint32_array(p, &v, &n);
+			for (i = 0; i < n; ++i) {
+				if (add_count_entry(h, te, v[i]) != SCF_SUCCESS)
+					return (SCF_FAILED);
+			}
+			return (SCF_SUCCESS);
+		}
+	case DATA_TYPE_UINT64_ARRAY:
+		{
+			uint64_t *v;
+
+			(void) nvpair_value_uint64_array(p, &v, &n);
+			for (i = 0; i < n; ++i) {
+				if (add_count_entry(h, te, v[i]) != SCF_SUCCESS)
+					return (SCF_FAILED);
+			}
+			return (SCF_SUCCESS);
+		}
+	case DATA_TYPE_INT8:
+		{
+			int8_t v;
+
+			(void) nvpair_value_int8(p, &v);
+			return (add_integer_entry(h, te, v));
+		}
+	case DATA_TYPE_INT16:
+		{
+			int16_t v;
+
+			(void) nvpair_value_int16(p, &v);
+			return (add_integer_entry(h, te, v));
+		}
+	case DATA_TYPE_INT32:
+		{
+			int32_t v;
+
+			(void) nvpair_value_int32(p, &v);
+			return (add_integer_entry(h, te, v));
+		}
+	case DATA_TYPE_INT64:
+		{
+			int64_t v;
+
+			(void) nvpair_value_int64(p, &v);
+			return (add_integer_entry(h, te, v));
+		}
+	case DATA_TYPE_INT8_ARRAY:
+		{
+			int8_t *v;
+
+			(void) nvpair_value_int8_array(p, &v, &n);
+			for (i = 0; i < n; ++i) {
+				if (add_integer_entry(h, te, v[i]) !=
+				    SCF_SUCCESS)
+					return (SCF_FAILED);
+			}
+			return (SCF_SUCCESS);
+		}
+	case DATA_TYPE_INT16_ARRAY:
+		{
+			int16_t *v;
+
+			(void) nvpair_value_int16_array(p, &v, &n);
+			for (i = 0; i < n; ++i) {
+				if (add_integer_entry(h, te, v[i]) !=
+				    SCF_SUCCESS)
+					return (SCF_FAILED);
+			}
+			return (SCF_SUCCESS);
+		}
+	case DATA_TYPE_INT32_ARRAY:
+		{
+			int32_t *v;
+
+			(void) nvpair_value_int32_array(p, &v, &n);
+			for (i = 0; i < n; ++i) {
+				if (add_integer_entry(h, te, v[i]) !=
+				    SCF_SUCCESS)
+					return (SCF_FAILED);
+			}
+			return (SCF_SUCCESS);
+		}
+	case DATA_TYPE_INT64_ARRAY:
+		{
+			int64_t *v;
+
+			(void) nvpair_value_int64_array(p, &v, &n);
+			for (i = 0; i < n; ++i) {
+				if (add_integer_entry(h, te, v[i]) !=
+				    SCF_SUCCESS)
+					return (SCF_FAILED);
+			}
+			return (SCF_SUCCESS);
+		}
+	case DATA_TYPE_STRING:
+		{
+			char *str;
+
+			(void) nvpair_value_string(p, &str);
+			return (add_astring_entry(h, te, str));
+		}
+	case DATA_TYPE_STRING_ARRAY:
+		{
+			char **v;
+
+			(void) nvpair_value_string_array(p, &v, &n);
+			for (i = 0; i < n; ++i) {
+				if (add_astring_entry(h, te, v[i]) !=
+				    SCF_SUCCESS)
+					return (SCF_FAILED);
+			}
+			return (SCF_SUCCESS);
+		}
+	default:
+		(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
+		return (SCF_FAILED);
+	}
+
+	/*NOTREACHED*/
+}
+
+/*
+ * Add new transaction entry to scf_transaction_t
+ *
+ * Can fail with
+ *	SCF_ERROR_BACKEND_ACCESS
+ *	SCF_ERROR_CONNECTION_BROKEN
+ *	SCF_ERROR_DELETED
+ *	SCF_ERROR_INTERNAL
+ *	SCF_ERROR_NO_RESOURCES
+ *	SCF_ERROR_NOT_FOUND
+ */
+static int
+prep_transaction(scf_transaction_t *tx, scf_transaction_entry_t *te,
+    const char *prop, scf_type_t type)
+{
+	if (scf_transaction_property_new(tx, te, prop, type) != SCF_SUCCESS &&
+	    (scf_error() != SCF_ERROR_EXISTS ||
+	    scf_transaction_property_change(tx, te, prop, type) !=
+	    SCF_SUCCESS)) {
+		if (check_scf_error(scf_error(), errs_2)) {
+			return (SCF_FAILED);
+		}
+	}
+
+	return (SCF_SUCCESS);
+}
+
+/*
+ * notify_set_params()
+ * returns 0 on success or -1 on failure
+ *	SCF_ERROR_BACKEND_ACCESS
+ *	SCF_ERROR_BACKEND_READONLY
+ *	SCF_ERROR_CONNECTION_BROKEN
+ *	SCF_ERROR_DELETED
+ *	SCF_ERROR_INTERNAL
+ *	SCF_ERROR_INVALID_ARGUMENT
+ *	SCF_ERROR_NO_MEMORY
+ *	SCF_ERROR_NO_RESOURCES
+ *	SCF_ERROR_NOT_FOUND
+ *	SCF_ERROR_PERMISSION_DENIED
+ */
+static int
+notify_set_params(scf_propertygroup_t *pg, nvlist_t *params)
+{
+	scf_handle_t		*h = scf_pg_handle(pg);
+	scf_error_t		scf_e = scf_error();
+	scf_transaction_t	*tx = scf_transaction_create(h);
+	int	bufsz = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1;
+	char	*propname = malloc(bufsz);
+	int	r = -1;
+	int	err;
+
+	if (h == NULL) {
+		/*
+		 * Use the error stored in scf_e
+		 */
+		(void) scf_set_error(scf_e);
+		goto cleanup;
+	}
+	if (tx == NULL)
+		goto cleanup;
+
+	if (propname == NULL) {
+		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
+		goto cleanup;
+	}
+
+	do {
+		nvpair_t *nvp;
+
+		/*
+		 * make sure we have the most recent version of the pg
+		 * start the transaction
+		 */
+		if (scf_pg_update(pg) == SCF_FAILED ||
+		    scf_transaction_start(tx, pg) != SCF_SUCCESS) {
+			if (check_scf_error(scf_error(), errs_2)) {
+				goto cleanup;
+			}
+		}
+
+		for (nvp = nvlist_next_nvpair(params, NULL); nvp != NULL;
+		    nvp = nvlist_next_nvpair(params, nvp)) {
+			nvlist_t	*m;
+			nvpair_t	*p;
+
+			/* we ONLY take nvlists here */
+			if (nvpair_type(nvp) != DATA_TYPE_NVLIST) {
+				char *name = nvpair_name(nvp);
+
+				/*
+				 * if this is output from
+				 * smf_notify_get_params() we want to skip
+				 * the tset value of the nvlist
+				 */
+				if (strcmp(name, SCF_NOTIFY_NAME_TSET) == 0)
+					continue;
+
+				(void) scf_set_error(
+				    SCF_ERROR_INVALID_ARGUMENT);
+				goto cleanup;
+			}
+
+			if (nvpair_value_nvlist(nvp, &m) != 0) {
+				(void) scf_set_error(
+				    SCF_ERROR_INVALID_ARGUMENT);
+				goto cleanup;
+			}
+
+			/*
+			 * Traverse each mechanism list
+			 */
+			for (p = nvlist_next_nvpair(m, NULL); p != NULL;
+			    p = nvlist_next_nvpair(m, p)) {
+				scf_transaction_entry_t *te =
+				    scf_entry_create(h);
+				/* map the nvpair type to scf type */
+				scf_type_t type = get_scf_type(p);
+
+				if (te == NULL) {
+					if (scf_error() !=
+					    SCF_ERROR_INVALID_ARGUMENT) {
+						scf_entry_destroy(te);
+						goto cleanup;
+					} else {
+						assert(0);
+						abort();
+					}
+				}
+
+				if (type == SCF_TYPE_INVALID) {
+					(void) scf_set_error(
+					    SCF_ERROR_INVALID_ARGUMENT);
+					scf_entry_destroy(te);
+					goto cleanup;
+				}
+
+				if (snprintf(propname, bufsz, "%s,%s",
+				    nvpair_name(nvp), nvpair_name(p)) >=
+				    bufsz) {
+					(void) scf_set_error(
+					    SCF_ERROR_INVALID_ARGUMENT);
+					scf_entry_destroy(te);
+					goto cleanup;
+				}
+
+				if (prep_transaction(tx, te, propname, type) !=
+				    SCF_SUCCESS) {
+					scf_entry_destroy(te);
+					goto cleanup;
+				}
+
+				if (get_nvpair_vals(h, te, p) != SCF_SUCCESS) {
+					if (check_scf_error(scf_error(),
+					    errs_2)) {
+						goto cleanup;
+					}
+				}
+			}
+		}
+		err = scf_transaction_commit(tx);
+		scf_transaction_destroy_children(tx);
+	} while (err == 0);
+
+	if (err == -1) {
+		if (check_scf_error(scf_error(), errs_2)) {
+			goto cleanup;
+		}
+	}
+
+	r = 0;
+
+cleanup:
+	scf_transaction_destroy_children(tx);
+	scf_transaction_destroy(tx);
+	free(propname);
+
+	return (r);
+}
+
+/*
+ * Decode fmri. Populates service OR instance depending on which one is an
+ * exact match to the fmri parameter.
+ *
+ * The function destroys and sets the unused entity (service or instance) to
+ * NULL.
+ *
+ * return SCF_SUCCESS or SCF_FAILED on
+ * 	SCF_ERROR_BACKEND_ACCESS
+ * 	SCF_ERROR_CONNECTION_BROKEN
+ * 	SCF_ERROR_CONSTRAINT_VIOLATED
+ * 	SCF_ERROR_DELETED
+ * 	SCF_ERROR_HANDLE_MISMATCH
+ * 	SCF_ERROR_INTERNAL
+ * 	SCF_ERROR_INVALID_ARGUMENT
+ * 	SCF_ERROR_NO_RESOURCES
+ * 	SCF_ERROR_NOT_BOUND
+ * 	SCF_ERROR_NOT_FOUND
+ * 	SCF_ERROR_NOT_SET
+ */
+static int
+decode_fmri(const char *fmri, scf_handle_t *h, scf_service_t **s,
+    scf_instance_t **i)
+{
+	if (scf_handle_decode_fmri(h, fmri, NULL, *s, NULL, NULL, NULL,
+	    SCF_DECODE_FMRI_EXACT) != SCF_SUCCESS) {
+		if (scf_error() == SCF_ERROR_CONSTRAINT_VIOLATED) {
+			scf_service_destroy(*s);
+			*s = NULL;
+		} else {
+			return (SCF_FAILED);
+		}
+	}
+	if (*s == NULL)
+		if (scf_handle_decode_fmri(h, fmri, NULL, NULL, *i,
+		    NULL, NULL, SCF_DECODE_FMRI_EXACT) != SCF_SUCCESS) {
+			return (SCF_FAILED);
+	}
+
+	return (SCF_SUCCESS);
+}
+
+/*
+ * Return size in bytes for an SCF_TYPE_*. Not all libscf types are supported
+ */
+static int
+get_type_size(scf_type_t t)
+{
+	switch (t) {
+	case SCF_TYPE_BOOLEAN:
+		return (sizeof (uint8_t));
+	case SCF_TYPE_COUNT:
+		return (sizeof (uint64_t));
+	case SCF_TYPE_INTEGER:
+		return (sizeof (int64_t));
+	case SCF_TYPE_ASTRING:
+	case SCF_TYPE_USTRING:
+		return (sizeof (void *));
+	default:
+		return (-1);
+	}
+
+	/*NOTREACHED*/
+}
+
+/*
+ * Return a pointer to the array of values according to its type
+ */
+static void **
+get_v_pointer(scf_values_t *v)
+{
+	switch (v->value_type) {
+	case SCF_TYPE_BOOLEAN:
+		return ((void **)&v->values.v_boolean);
+	case SCF_TYPE_COUNT:
+		return ((void **)&v->values.v_count);
+	case SCF_TYPE_INTEGER:
+		return ((void **)&v->values.v_integer);
+	case SCF_TYPE_ASTRING:
+		return ((void **)&v->values.v_astring);
+	case SCF_TYPE_USTRING:
+		return ((void **)&v->values.v_ustring);
+	default:
+		return (NULL);
+	}
+
+	/*NOTREACHED*/
+}
+
+/*
+ * Populate scf_values_t value array at position c.
+ */
+static int
+get_value(scf_value_t *val, scf_values_t *v, int c, char *buf, int sz)
+{
+	switch (v->value_type) {
+	case SCF_TYPE_BOOLEAN:
+		return (scf_value_get_boolean(val, v->values.v_boolean + c));
+	case SCF_TYPE_COUNT:
+		return (scf_value_get_count(val, v->values.v_count + c));
+	case SCF_TYPE_INTEGER:
+		return (scf_value_get_integer(val, v->values.v_integer + c));
+	case SCF_TYPE_ASTRING:
+		if (scf_value_get_astring(val, buf, sz) < 0 ||
+		    (v->values.v_astring[c] = strdup(buf)) == NULL) {
+			(void) scf_set_error(SCF_ERROR_NO_MEMORY);
+			return (-1);
+		}
+		return (0);
+	case SCF_TYPE_USTRING:
+		if (scf_value_get_ustring(val, buf, sz) < 0 ||
+		    (v->values.v_ustring[c] = strdup(buf)) == NULL) {
+			(void) scf_set_error(SCF_ERROR_NO_MEMORY);
+			return (-1);
+		}
+		return (0);
+	default:
+		return (-1);
+	}
+
+	/*NOTREACHED*/
+}
+
+/*
+ * Populate scf_values_t structure with values from prop
+ */
+static int
+values_get(scf_property_t *prop, scf_values_t *v)
+{
+	scf_handle_t	*h = scf_property_handle(prop);
+	scf_error_t	scf_e = scf_error();
+	scf_value_t	*val = scf_value_create(h);
+	scf_iter_t	*it = scf_iter_create(h);
+	scf_type_t	type = SCF_TYPE_INVALID;
+	ssize_t		sz = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH) + 1;
+	char		*buf = malloc(sz);
+	void **p;
+	int err, elem_sz, count, cursz;
+	int r = SCF_FAILED;
+
+	assert(v != NULL);
+	assert(v->reserved == NULL);
+	if (buf == NULL) {
+		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
+		goto cleanup;
+	}
+	if (h == NULL) {
+		/*
+		 * Use the error stored in scf_e
+		 */
+		(void) scf_set_error(scf_e);
+		goto cleanup;
+	}
+	if (val == NULL || it == NULL)
+		goto cleanup;
+
+	if (scf_property_type(prop, &type) != SCF_SUCCESS)
+		goto cleanup;
+	if (scf_property_is_type(prop, v->value_type) != SCF_SUCCESS)
+		goto error;
+
+	elem_sz = get_type_size(type);
+	assert(elem_sz > 0);
+
+	p = get_v_pointer(v);
+	assert(p != NULL);
+
+	cursz = count = v->value_count;
+	if (scf_iter_property_values(it, prop) != 0) {
+		goto error;
+	}
+
+	while ((err = scf_iter_next_value(it, val)) == 1) {
+		if (count + 1 >= cursz) {
+			void *tmp;
+
+			/* set initial size or double it */
+			cursz = cursz ? 2 * cursz : 8;
+			if ((tmp = realloc(*p, cursz * elem_sz)) == NULL) {
+				(void) scf_set_error(SCF_ERROR_NO_MEMORY);
+				goto error;
+			}
+			*p = tmp;
+		}
+
+		if (get_value(val, v, count, buf, sz) != 0)
+			goto error;
+
+		count++;
+	}
+
+	v->value_count = count;
+
+	if (err != 0)
+		goto error;
+
+	r = SCF_SUCCESS;
+	goto cleanup;
+
+error:
+	v->value_count = count;
+	scf_values_destroy(v);
+
+cleanup:
+	free(buf);
+	scf_iter_destroy(it);
+	scf_value_destroy(val);
+	return (r);
+}
+
+/*
+ * Add values from property p to existing nvlist_t nvl. The data type in the
+ * nvlist is inferred from the scf_type_t of the property.
+ *
+ * Returns SCF_SUCCESS or SCF_FAILED on
+ *	SCF_ERROR_CONNECTION_BROKEN
+ *	SCF_ERROR_DELETED
+ *	SCF_ERROR_HANDLE_DESTROYED
+ *	SCF_ERROR_HANDLE_MISMATCH
+ *	SCF_ERROR_INVALID_ARGUMENT
+ *	SCF_ERROR_NO_MEMORY
+ *	SCF_ERROR_NO_RESOURCES
+ *	SCF_ERROR_NOT_BOUND
+ *	SCF_ERROR_NOT_SET
+ *	SCF_ERROR_PERMISSION_DENIED
+ *	SCF_ERROR_TYPE_MISMATCH
+ */
+static int
+add_prop_to_nvlist(scf_property_t *p, const char *pname, nvlist_t *nvl,
+    int array)
+{
+	scf_values_t	vals = { 0 };
+	scf_type_t	type, base_type;
+	int r = SCF_FAILED;
+	int err = 0;
+
+	if (p == NULL || pname == NULL || *pname == '\0' || nvl == NULL) {
+		(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
+		return (r);
+	}
+
+	if (scf_property_type(p, &type) != 0)
+		goto cleanup;
+
+	/*
+	 * scf_values_t does not support subtypes of SCF_TYPE_USTRING,
+	 * mapping them all to SCF_TYPE_USTRING
+	 */
+	base_type = scf_true_base_type(type);
+	if (base_type == SCF_TYPE_ASTRING && type != SCF_TYPE_ASTRING)
+		type = SCF_TYPE_USTRING;
+
+	vals.value_type = type;
+	if (values_get(p, &vals) != SCF_SUCCESS) {
+		if (scf_error() == SCF_ERROR_INVALID_ARGUMENT) {
+			assert(0);
+			abort();
+		}
+		goto cleanup;
+	}
+
+	switch (vals.value_type) {
+	case SCF_TYPE_BOOLEAN:
+		{
+			boolean_t *v;
+			int i;
+			int n = vals.value_count;
+
+			v = calloc(n, sizeof (boolean_t));
+			if (v == NULL) {
+				(void) scf_set_error(SCF_ERROR_NO_MEMORY);
+				goto cleanup;
+			}
+			for (i = 0; i < n; ++i)
+				v[i] = (boolean_t)vals.values.v_boolean[i];
+
+			if (n == 1 && !array)
+				err = nvlist_add_boolean_value(nvl, pname, *v);
+			else
+				err = nvlist_add_boolean_array(nvl, pname,
+				    v, n);
+			if (err != 0) {
+				free(v);
+				goto cleanup;
+			}
+			free(v);
+		}
+		break;
+
+	case SCF_TYPE_COUNT:
+		if (vals.value_count == 1 && !array)
+			err = nvlist_add_uint64(nvl, pname,
+			    *vals.values.v_count);
+		else
+			err = nvlist_add_uint64_array(nvl, pname,
+			    vals.values.v_count, vals.value_count);
+		if (err != 0)
+			goto cleanup;
+
+		break;
+
+	case SCF_TYPE_INTEGER:
+		if (vals.value_count == 1 && !array)
+			err = nvlist_add_int64(nvl, pname,
+			    *vals.values.v_integer);
+		else
+			err = nvlist_add_int64_array(nvl, pname,
+			    vals.values.v_integer, vals.value_count);
+		if (err != 0)
+			goto cleanup;
+
+		break;
+
+	case SCF_TYPE_ASTRING:
+		if (vals.value_count == 1 && !array)
+			err = nvlist_add_string(nvl, pname,
+			    *vals.values.v_astring);
+		else
+			err = nvlist_add_string_array(nvl, pname,
+			    vals.values.v_astring, vals.value_count);
+		if (err != 0)
+			goto cleanup;
+		break;
+
+	default:
+		(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
+		goto cleanup;
+	}
+
+	r = SCF_SUCCESS;
+cleanup:
+	scf_values_destroy(&vals);
+	switch (err) {
+	case 0:
+		break;
+	case EINVAL:
+		(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
+		break;
+	case ENOMEM:
+		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
+		break;
+	default:
+		/* we should *never* get here */
+		abort();
+	}
+
+	return (r);
+}
+
+/*
+ * Parse property name "mechanism,parameter" into separate mechanism
+ * and parameter.  *mech must be freed by caller.  *val points into
+ * *mech and must not be freed.
+ *
+ * Returns SCF_SUCCESS or SCF_FAILED on
+ * 	SCF_ERROR_NO_MEMORY
+ * 	SCF_ERROR_NOT_FOUND
+ */
+static int
+get_mech_name(const char *name, char **mech, char **val)
+{
+	char *p;
+	char *m;
+
+	if ((m = strdup(name)) == NULL) {
+		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
+		return (SCF_FAILED);
+	}
+	if ((p = strchr(m, ',')) == NULL) {
+		free(m);
+		(void) scf_set_error(SCF_ERROR_NOT_FOUND);
+		return (SCF_FAILED);
+	}
+	*p = '\0';
+	*val = p + 1;
+	*mech = m;
+
+	return (SCF_SUCCESS);
+}
+
+/*
+ * Return the number of transitions in a transition set.
+ * If the transition set is invalid, it returns zero.
+ */
+static uint_t
+num_of_transitions(int32_t t)
+{
+	int i;
+	int n = 0;
+
+	if (SCF_TRANS_VALID(t)) {
+		for (i = 0x1; i < SCF_STATE_ALL; i <<= 1) {
+			if (i & t)
+				++n;
+			if (SCF_TRANS_INITIAL_STATE(t) & i)
+				++n;
+		}
+	}
+
+	return (n);
+}
+
+/*
+ * Return the SCF_STATE_* macro value for the state in the FMA classes for
+ * SMF state transitions. They are of type:
+ *     SCF_SVC_TRANSITION_CLASS.<state>
+ *     ireport.os.smf.state-transition.<state>
+ */
+static int32_t
+class_to_transition(const char *c)
+{
+	const char *p;
+	int r = 0;
+	size_t n;
+
+	if (!is_svc_stn(c)) {
+		return (0);
+	}
+
+	/*
+	 * if we get here, c is SCF_SVC_TRANSITION_CLASS or longer
+	 */
+	p = c + strlen(SCF_SVC_TRANSITION_CLASS);
+	if (*p == '.')
+		++p;
+	else
+		return (0);
+
+	if ((n = base_class_len(p)) == 0)
+		return (0);
+
+	if ((r = state_from_string(p, n)) == -1)
+		r = 0;
+
+	return (r);
+}
+
+/*
+ * return SCF_SUCCESS or SCF_FAILED on
+ *	SCF_ERROR_BACKEND_ACCESS
+ *	SCF_ERROR_BACKEND_READONLY
+ *	SCF_ERROR_CONNECTION_BROKEN
+ *	SCF_ERROR_DELETED
+ *	SCF_ERROR_INTERNAL
+ *	SCF_ERROR_INVALID_ARGUMENT
+ *	SCF_ERROR_NO_MEMORY
+ *	SCF_ERROR_NO_RESOURCES
+ *	SCF_ERROR_NOT_FOUND
+ *	SCF_ERROR_PERMISSION_DENIED
+ */
+int
+smf_notify_set_params(const char *class, nvlist_t *attr)
+{
+	uint32_t	ver;
+	int32_t		tset;
+	scf_handle_t		*h = _scf_handle_create_and_bind(SCF_VERSION);
+	scf_error_t		scf_e = scf_error();
+	scf_service_t		*s = scf_service_create(h);
+	scf_instance_t		*i = scf_instance_create(h);
+	scf_propertygroup_t	*pg = scf_pg_create(h);
+	nvlist_t	*params = NULL;
+	char		*fmri = (char *)SCF_NOTIFY_PARAMS_INST;
+	char		*pgname = NULL;
+	int		r = SCF_FAILED;
+	boolean_t	is_stn;
+	int		 j;
+
+	assert(class != NULL);
+	if (h == NULL) {
+		/*
+		 * use saved error if _scf_handle_create_and_bind() fails
+		 */
+		(void) scf_set_error(scf_e);
+		goto cleanup;
+	}
+	if (i == NULL || s == NULL || pg == NULL)
+		goto cleanup;
+
+	/* check version */
+	if (nvlist_lookup_uint32(attr, SCF_NOTIFY_NAME_VERSION, &ver) != 0 ||
+	    ver != SCF_NOTIFY_PARAMS_VERSION) {
+		(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
+		goto cleanup;
+	}
+
+	if (nvlist_lookup_nvlist(attr, SCF_NOTIFY_PARAMS, &params) != 0) {
+		(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
+		goto cleanup;
+	}
+
+	is_stn = is_svc_stn(class);
+	/* special case SMF state transition notification */
+	if (is_stn &&
+	    (nvlist_lookup_string(attr, SCF_NOTIFY_NAME_FMRI, &fmri) != 0 ||
+	    nvlist_lookup_int32(attr, SCF_NOTIFY_NAME_TSET, &tset) != 0 ||
+	    !SCF_TRANS_VALID(tset))) {
+		(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
+		goto cleanup;
+	}
+	if (decode_fmri(fmri, h, &s, &i) != SCF_SUCCESS)
+		if (scf_error() == SCF_ERROR_CONSTRAINT_VIOLATED) {
+			(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
+		} else if (check_scf_error(scf_error(), errs_1)) {
+			goto cleanup;
+		}
+
+	if (is_stn) {
+		tset |= class_to_transition(class);
+
+		if (!SCF_TRANS_VALID(tset)) {
+			(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
+			goto cleanup;
+		}
+
+		for (j = 0; st_pgnames[j].st_pgname != NULL; ++j) {
+			/* if this transition is not in the tset, continue */
+			if (!(tset & st_pgnames[j].st_state))
+				continue;
+
+			if (get_or_add_pg(s, i, st_pgnames[j].st_pgname,
+			    SCF_NOTIFY_PARAMS_PG_TYPE, 0, pg) != 0 &&
+			    check_scf_error(scf_error(), errs_2))
+				goto cleanup;
+
+			if (notify_set_params(pg, params) != 0)
+				goto cleanup;
+		}
+		if (s == NULL) {
+			/* We only need to refresh the instance */
+			if (_smf_refresh_instance_i(i) != 0 &&
+			    check_scf_error(scf_error(), errs_1))
+				goto cleanup;
+		} else {
+			/* We have to refresh all instances in the service */
+			if (_smf_refresh_all_instances(s) != 0 &&
+			    check_scf_error(scf_error(), errs_1))
+				goto cleanup;
+		}
+	} else {
+		if ((pgname = class_to_pgname(class)) == NULL)
+			goto cleanup;
+		if (get_or_add_pg(s, i, pgname, SCF_GROUP_APPLICATION, 0, pg) !=
+		    0) {
+			if (check_scf_error(scf_error(), errs_2)) {
+				goto cleanup;
+			}
+		}
+		if (notify_set_params(pg, params) != 0) {
+			goto cleanup;
+		}
+		if (_smf_refresh_instance_i(i) != 0 &&
+		    check_scf_error(scf_error(), errs_1))
+			goto cleanup;
+	}
+
+	r = SCF_SUCCESS;
+cleanup:
+	scf_instance_destroy(i);
+	scf_service_destroy(s);
+	scf_pg_destroy(pg);
+	scf_handle_destroy(h);
+	free(pgname);
+
+	return (r);
+}
+
+/*
+ * returns SCF_SUCCESS or SCF_FAILED on
+ *	SCF_ERROR_CONNECTION_BROKEN
+ *	SCF_ERROR_DELETED
+ *	SCF_ERROR_HANDLE_DESTROYED
+ *	SCF_ERROR_HANDLE_MISMATCH
+ *	SCF_ERROR_INVALID_ARGUMENT
+ *	SCF_ERROR_NO_MEMORY
+ *	SCF_ERROR_NO_RESOURCES
+ *	SCF_ERROR_NOT_BOUND
+ *	SCF_ERROR_NOT_FOUND
+ *	SCF_ERROR_NOT_SET
+ *	SCF_ERROR_PERMISSION_DENIED
+ */
+int
+_scf_notify_get_params(scf_propertygroup_t *pg, nvlist_t *params)
+{
+	scf_handle_t	*h = scf_pg_handle(pg);
+	scf_error_t	scf_e = scf_error();
+	scf_property_t	*p = scf_property_create(h);
+	scf_iter_t	*it = scf_iter_create(h);
+	int sz = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1;
+	char *name = malloc(sz);
+	int r = SCF_FAILED;
+	int err;
+
+	if (h == NULL) {
+		/*
+		 * Use the error stored in scf_e
+		 */
+		(void) scf_set_error(scf_e);
+		goto cleanup;
+	}
+	if (it == NULL || p == NULL)
+		goto cleanup;
+
+	if (name == NULL) {
+		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
+		goto cleanup;
+	}
+
+	if (scf_iter_pg_properties(it, pg) != SCF_SUCCESS) {
+		if (check_scf_error(scf_error(), errs_1)) {
+			goto cleanup;
+		}
+	}
+
+	while ((err = scf_iter_next_property(it, p)) == 1) {
+		nvlist_t *nvl;
+		int nvl_new = 0;
+		char *mech;
+		char *val;
+
+		if (scf_property_get_name(p, name, sz) == SCF_FAILED) {
+			if (check_scf_error(scf_error(), errs_1)) {
+				goto cleanup;
+			}
+		}
+
+		if (get_mech_name(name, &mech, &val) != SCF_SUCCESS) {
+			if (scf_error() == SCF_ERROR_NOT_FOUND)
+				continue;
+			goto cleanup;
+		}
+
+		if (nvlist_lookup_nvlist(params, mech, &nvl) != 0) {
+			if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) {
+				(void) scf_set_error(SCF_ERROR_NO_MEMORY);
+				free(mech);
+				goto cleanup;
+			}
+			nvl_new = 1;
+		}
+
+		if (add_prop_to_nvlist(p, val, nvl, 1) != SCF_SUCCESS) {
+			if (check_scf_error(scf_error(), errs_2)) {
+				free(mech);
+				nvlist_free(nvl);
+				goto cleanup;
+			}
+		}
+		if (nvl_new) {
+			if (nvlist_add_nvlist(params, mech, nvl) != 0) {
+				(void) scf_set_error(SCF_ERROR_NO_MEMORY);
+				free(mech);
+				nvlist_free(nvl);
+				goto cleanup;
+			}
+			nvlist_free(nvl);
+		}
+
+		free(mech);
+	}
+
+	if (err == 0) {
+		r = SCF_SUCCESS;
+	} else if (check_scf_error(scf_error(), errs_2)) {
+		goto cleanup;
+	}
+
+cleanup:
+	scf_iter_destroy(it);
+	scf_property_destroy(p);
+	free(name);
+
+	return (r);
+}
+
+/*
+ * Look up pg containing an SMF state transition parameters. If it cannot find
+ * the pg in the composed view of the instance, it will look in the global
+ * instance for the system wide parameters.
+ * Instance, service and global instance have to be passed by caller.
+ *
+ * returns SCF_SUCCESS or SCF_FAILED on
+ *	SCF_ERROR_BACKEND_ACCESS
+ *	SCF_ERROR_CONNECTION_BROKEN
+ *	SCF_ERROR_DELETED
+ *	SCF_ERROR_HANDLE_DESTROYED
+ *	SCF_ERROR_HANDLE_MISMATCH
+ *	SCF_ERROR_INTERNAL
+ *	SCF_ERROR_INVALID_ARGUMENT
+ *	SCF_ERROR_NO_MEMORY
+ *	SCF_ERROR_NO_RESOURCES
+ *	SCF_ERROR_NOT_BOUND
+ *	SCF_ERROR_NOT_FOUND
+ *	SCF_ERROR_NOT_SET
+ */
+static int
+get_stn_pg(scf_service_t *s, scf_instance_t *i, scf_instance_t *g,
+    const char *pgname, scf_propertygroup_t *pg)
+{
+	if (get_pg(s, i, pgname, pg, 1) == 0 ||
+	    scf_error() == SCF_ERROR_NOT_FOUND &&
+	    get_pg(NULL, g, pgname, pg, 0) == 0)
+		return (SCF_SUCCESS);
+
+	return (SCF_FAILED);
+}
+
+/*
+ * Populates nvlist_t params with the source fmri for the pg
+ *
+ * return SCF_SUCCESS or SCF_FAILED on
+ * 	SCF_ERROR_DELETED
+ * 	SCF_ERROR_CONNECTION_BROKEN
+ * 	SCF_ERROR_NO_MEMORY
+ */
+static int
+get_pg_source(scf_propertygroup_t *pg, nvlist_t *params)
+{
+	size_t sz = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH) + 1;
+	char *fmri = malloc(sz);
+	char *p;
+	int r = SCF_FAILED;
+
+	if (fmri == NULL) {
+		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
+		goto out;
+	}
+
+	if (scf_pg_to_fmri(pg, fmri, sz) == -1) {
+		if (check_scf_error(scf_error(), errs_1)) {
+			goto out;
+		}
+	}
+
+	/* get rid of the properties part of the pg source */
+	if ((p = strrchr(fmri, ':')) != NULL && p > fmri)
+		*(p - 1) = '\0';
+	if (nvlist_add_string(params, SCF_NOTIFY_PARAMS_SOURCE_NAME, fmri) !=
+	    0) {
+		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
+		goto out;
+	}
+
+	r = SCF_SUCCESS;
+out:
+	free(fmri);
+	return (r);
+}
+
+/*
+ * Specialized function to get SMF state transition notification parameters
+ *
+ * return SCF_SUCCESS or SCF_FAILED on
+ *	SCF_ERROR_BACKEND_ACCESS
+ *	SCF_ERROR_CONNECTION_BROKEN
+ *	SCF_ERROR_DELETED
+ *	SCF_ERROR_INTERNAL
+ *	SCF_ERROR_INVALID_ARGUMENT
+ *	SCF_ERROR_NO_MEMORY
+ *	SCF_ERROR_NO_RESOURCES
+ *	SCF_ERROR_NOT_FOUND
+ *	SCF_ERROR_PERMISSION_DENIED
+ */
+int
+_scf_get_svc_notify_params(const char *fmri, nvlist_t *nvl, int32_t tset,
+    int getsource, int getglobal)
+{
+	scf_handle_t		*h = _scf_handle_create_and_bind(SCF_VERSION);
+	scf_error_t		scf_e = scf_error();
+	scf_service_t		*s = scf_service_create(h);
+	scf_instance_t		*i = scf_instance_create(h);
+	scf_instance_t		*g = scf_instance_create(h);
+	scf_propertygroup_t	*pg = scf_pg_create(h);
+	int r = SCF_FAILED;
+	nvlist_t **params = NULL;
+	uint_t c, nvl_num = 0;
+	int not_found = 1;
+	int j;
+	const char *pgname;
+
+	assert(fmri != NULL && nvl != NULL);
+	if (h == NULL) {
+		/*
+		 * use saved error if _scf_handle_create_and_bind() fails
+		 */
+		(void) scf_set_error(scf_e);
+		goto cleanup;
+	}
+	if (s == NULL || i == NULL || g == NULL || pg == NULL)
+		goto cleanup;
+
+	if (decode_fmri(fmri, h, &s, &i) != SCF_SUCCESS ||
+	    scf_handle_decode_fmri(h, SCF_INSTANCE_GLOBAL, NULL, NULL, g, NULL,
+	    NULL, SCF_DECODE_FMRI_EXACT) != 0) {
+		if (scf_error() == SCF_ERROR_CONSTRAINT_VIOLATED) {
+			(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
+		} else if (check_scf_error(scf_error(), errs_1)) {
+			goto cleanup;
+		}
+	}
+
+	nvl_num = num_of_transitions(tset);
+	if ((params = calloc(nvl_num, sizeof (nvlist_t *))) == NULL) {
+		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
+		goto cleanup;
+	}
+
+	for (c = 0; c < nvl_num; ++c)
+		if (nvlist_alloc(params + c, NV_UNIQUE_NAME, 0) != 0) {
+			(void) scf_set_error(SCF_ERROR_NO_MEMORY);
+			goto cleanup;
+		}
+
+	for (c = 0, j = 0; st_pgnames[j].st_pgname != NULL; ++j) {
+		/* if this transition is not in the tset, continue */
+		if (!(tset & st_pgnames[j].st_state))
+			continue;
+
+		assert(c < nvl_num);
+		pgname = st_pgnames[j].st_pgname;
+
+		if (nvlist_add_int32(params[c], SCF_NOTIFY_NAME_TSET,
+		    st_pgnames[j].st_state) != 0) {
+			(void) scf_set_error(SCF_ERROR_NO_MEMORY);
+			goto cleanup;
+		}
+		if ((getglobal ? get_stn_pg(s, i, g, pgname, pg) :
+		    get_pg(s, i, pgname, pg, 1)) == SCF_SUCCESS) {
+			not_found = 0;
+			if (_scf_notify_get_params(pg, params[c]) !=
+			    SCF_SUCCESS)
+				goto cleanup;
+			if (getsource && get_pg_source(pg, params[c]) !=
+			    SCF_SUCCESS)
+				goto cleanup;
+		} else if (scf_error() == SCF_ERROR_NOT_FOUND ||
+		    scf_error() == SCF_ERROR_DELETED) {
+			/* keep driving */
+			/*EMPTY*/
+		} else if (check_scf_error(scf_error(), errs_1)) {
+			goto cleanup;
+		}
+		++c;
+	}
+
+	if (not_found) {
+		(void) scf_set_error(SCF_ERROR_NOT_FOUND);
+		goto cleanup;
+	}
+
+	assert(c == nvl_num);
+
+	if (nvlist_add_nvlist_array(nvl, SCF_NOTIFY_PARAMS, params, nvl_num) !=
+	    0 || nvlist_add_uint32(nvl, SCF_NOTIFY_NAME_VERSION,
+	    SCF_NOTIFY_PARAMS_VERSION) != 0) {
+		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
+		goto cleanup;
+	}
+
+	r = SCF_SUCCESS;
+
+cleanup:
+	scf_pg_destroy(pg);
+	scf_instance_destroy(i);
+	scf_instance_destroy(g);
+	scf_service_destroy(s);
+	scf_handle_destroy(h);
+	if (params != NULL)
+		for (c = 0; c < nvl_num; ++c)
+			nvlist_free(params[c]);
+	free(params);
+
+	return (r);
+}
+
+/*
+ * Specialized function to get fma notification parameters
+ *
+ * return SCF_SUCCESS or SCF_FAILED on
+ *	SCF_ERROR_BACKEND_ACCESS
+ *	SCF_ERROR_CONNECTION_BROKEN
+ *	SCF_ERROR_DELETED
+ *	SCF_ERROR_INTERNAL
+ *	SCF_ERROR_INVALID_ARGUMENT
+ *	SCF_ERROR_NO_MEMORY
+ *	SCF_ERROR_NO_RESOURCES
+ *	SCF_ERROR_NOT_FOUND
+ *	SCF_ERROR_PERMISSION_DENIED
+ */
+int
+_scf_get_fma_notify_params(const char *class, nvlist_t *nvl, int getsource)
+{
+	scf_handle_t		*h = _scf_handle_create_and_bind(SCF_VERSION);
+	scf_error_t		scf_e = scf_error();
+	scf_instance_t		*i = scf_instance_create(h);
+	scf_propertygroup_t	*pg = scf_pg_create(h);
+	int r = SCF_FAILED;
+	nvlist_t *params = NULL;
+	char *pgname = NULL;
+
+	if (h == NULL) {
+		/*
+		 * use saved error if _scf_handle_create_and_bind() fails
+		 */
+		(void) scf_set_error(scf_e);
+		goto cleanup;
+	}
+	if (i == NULL || pg == NULL)
+		goto cleanup;
+
+	if (scf_handle_decode_fmri(h, SCF_NOTIFY_PARAMS_INST, NULL, NULL, i,
+	    NULL, NULL, SCF_DECODE_FMRI_EXACT) != SCF_SUCCESS) {
+		if (check_scf_error(scf_error(), errs_1)) {
+			goto cleanup;
+		}
+	}
+
+	if ((pgname = class_to_pgname(class)) == NULL)
+		goto cleanup;
+
+	while (get_pg(NULL, i, pgname, pg, 0) != 0) {
+		if (scf_error() == SCF_ERROR_NOT_FOUND) {
+			char *p = strrchr(pgname, '.');
+
+			if (p != NULL) {
+				*p = ',';
+				/*
+				 * since the resulting string is shorter,
+				 * there is no risk of buffer overflow
+				 */
+				(void) strcpy(p + 1, SCF_NOTIFY_PG_POSTFIX);
+				continue;
+			}
+		}
+
+		if (check_scf_error(scf_error(), errs_1)) {
+			goto cleanup;
+		}
+	}
+
+	if (nvlist_alloc(&params, NV_UNIQUE_NAME, 0) != 0) {
+		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
+		goto cleanup;
+	}
+
+	if (_scf_notify_get_params(pg, params) != SCF_SUCCESS)
+		goto cleanup;
+
+	if (getsource && get_pg_source(pg, params) != SCF_SUCCESS)
+		goto cleanup;
+
+	if (nvlist_add_nvlist_array(nvl, SCF_NOTIFY_PARAMS, &params, 1) != 0 ||
+	    nvlist_add_uint32(nvl, SCF_NOTIFY_NAME_VERSION,
+	    SCF_NOTIFY_PARAMS_VERSION) != 0) {
+		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
+		goto cleanup;
+	}
+
+	r = SCF_SUCCESS;
+
+cleanup:
+	if (params)
+		nvlist_free(params);
+	scf_pg_destroy(pg);
+	scf_instance_destroy(i);
+	scf_handle_destroy(h);
+	free(pgname);
+
+	return (r);
+}
+
+/*
+ * Retrieve the notification parameters for the Event described in the
+ * input nvlist_t nvl.
+ * The function will allocate an nvlist_t to store the notification
+ * parameters. The notification parameters in the output nvlist will have
+ * the following format:
+ *
+ *        version (uint32_t)
+ *        SCF_NOTIFY_PARAMS (array of embedded nvlists)
+ *             (start of notify-params[0])
+ *                  tset (int32_t)
+ *                  <mechanism-name> (embedded nvlist)
+ *                       <parameter-name> <parameter-type>
+ *                       ...
+ *                  (end <mechanism-name>)
+ *                  ...
+ *             (end of notify-params[0])
+ *             ...
+ *
+ * return SCF_SUCCESS or SCF_FAILED on
+ *	SCF_ERROR_BACKEND_ACCESS
+ *	SCF_ERROR_CONNECTION_BROKEN
+ *	SCF_ERROR_DELETED
+ *	SCF_ERROR_INTERNAL
+ *	SCF_ERROR_INVALID_ARGUMENT
+ *	SCF_ERROR_NO_MEMORY
+ *	SCF_ERROR_NO_RESOURCES
+ *	SCF_ERROR_NOT_FOUND
+ *	SCF_ERROR_PERMISSION_DENIED
+ */
+int
+smf_notify_get_params(nvlist_t **params, nvlist_t *nvl)
+{
+	char *class;
+	char *from;	/* from state */
+	char *to;	/* to state */
+	nvlist_t *attr;
+	char *fmri;
+	int32_t tset = 0;
+	int r = SCF_FAILED;
+
+	if (params == NULL || nvlist_lookup_string(nvl, "class", &class) != 0) {
+		(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
+		return (r);
+	}
+	if (nvlist_alloc(params, NV_UNIQUE_NAME, 0) != 0) {
+		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
+		return (r);
+	}
+
+	if (is_svc_stn(class)) {
+		if (nvlist_lookup_nvlist(nvl, "attr", &attr) != 0 ||
+		    nvlist_lookup_string(attr, "svc-string", &fmri) != 0 ||
+		    nvlist_lookup_string(attr, "from-state", &from) != 0 ||
+		    nvlist_lookup_string(attr, "to-state", &to) != 0) {
+			(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
+			goto cleanup;
+		}
+
+		tset = SCF_TRANS(smf_state_from_string(from),
+		    smf_state_from_string(to));
+		if (!SCF_TRANS_VALID(tset)) {
+			(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
+			goto cleanup;
+		}
+		tset |= class_to_transition(class);
+
+		r = _scf_get_svc_notify_params(fmri, *params, tset, 0, 1);
+	} else {
+		r = _scf_get_fma_notify_params(class, *params, 0);
+	}
+
+cleanup:
+	if (r == SCF_FAILED) {
+		nvlist_free(*params);
+		*params = NULL;
+	}
+
+	return (r);
+}
+
+/*
+ * return SCF_SUCCESS or SCF_FAILED on
+ *	SCF_ERROR_BACKEND_ACCESS
+ *	SCF_ERROR_BACKEND_READONLY
+ *	SCF_ERROR_CONNECTION_BROKEN
+ *	SCF_ERROR_DELETED
+ *	SCF_ERROR_INTERNAL
+ *	SCF_ERROR_INVALID_ARGUMENT
+ *	SCF_ERROR_NO_MEMORY
+ *	SCF_ERROR_NO_RESOURCES
+ *	SCF_ERROR_NOT_FOUND
+ *	SCF_ERROR_PERMISSION_DENIED
+ */
+int
+smf_notify_del_params(const char *class, const char *fmri, int32_t tset)
+{
+	scf_handle_t		*h = _scf_handle_create_and_bind(SCF_VERSION);
+	scf_error_t		scf_e = scf_error();
+	scf_service_t		*s = scf_service_create(h);
+	scf_instance_t		*i = scf_instance_create(h);
+	scf_propertygroup_t	*pg = scf_pg_create(h);
+	int r = SCF_FAILED;
+	char *pgname = NULL;
+	int j;
+
+	if (class == NULL) {
+		(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
+		goto cleanup;
+	}
+
+	if (h == NULL) {
+		/*
+		 * use saved error if _scf_handle_create_and_bind() fails
+		 */
+		(void) scf_set_error(scf_e);
+		goto cleanup;
+	}
+	if (s == NULL || i == NULL || pg == NULL)
+		goto cleanup;
+
+	if (is_svc_stn(class)) {
+		tset |= class_to_transition(class);
+
+		if (!SCF_TRANS_VALID(tset) || fmri == NULL) {
+			(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
+			goto cleanup;
+		}
+
+		if (decode_fmri(fmri, h, &s, &i) != SCF_SUCCESS) {
+			if (scf_error() == SCF_ERROR_CONSTRAINT_VIOLATED)
+				(void) scf_set_error(
+				    SCF_ERROR_INVALID_ARGUMENT);
+			if (check_scf_error(scf_error(), errs_1)) {
+				goto cleanup;
+			}
+		}
+
+		for (j = 0; st_pgnames[j].st_pgname != NULL; ++j) {
+			/* if this transition is not in the tset, continue */
+			if (!(tset & st_pgnames[j].st_state))
+				continue;
+
+			if (del_pg(s, i, st_pgnames[j].st_pgname, pg) !=
+			    SCF_SUCCESS &&
+			    scf_error() != SCF_ERROR_DELETED &&
+			    scf_error() != SCF_ERROR_NOT_FOUND) {
+				if (check_scf_error(scf_error(),
+				    errs_1)) {
+					goto cleanup;
+				}
+			}
+		}
+		if (s == NULL) {
+			/* We only need to refresh the instance */
+			if (_smf_refresh_instance_i(i) != 0 &&
+			    check_scf_error(scf_error(), errs_1))
+				goto cleanup;
+		} else {
+			/* We have to refresh all instances in the service */
+			if (_smf_refresh_all_instances(s) != 0 &&
+			    check_scf_error(scf_error(), errs_1))
+				goto cleanup;
+		}
+	} else {
+		if ((pgname = class_to_pgname(class)) == NULL)
+			goto cleanup;
+
+		if (scf_handle_decode_fmri(h, SCF_NOTIFY_PARAMS_INST, NULL,
+		    NULL, i, NULL, NULL, SCF_DECODE_FMRI_EXACT) != SCF_SUCCESS)
+			goto cleanup;
+
+		if (del_pg(NULL, i, pgname, pg) != SCF_SUCCESS &&
+		    scf_error() != SCF_ERROR_DELETED &&
+		    scf_error() != SCF_ERROR_NOT_FOUND) {
+			if (check_scf_error(scf_error(), errs_1)) {
+				goto cleanup;
+			}
+		}
+
+		if (_smf_refresh_instance_i(i) != 0 &&
+		    check_scf_error(scf_error(), errs_1))
+			goto cleanup;
+	}
+
+
+	r = SCF_SUCCESS;
+
+cleanup:
+	scf_pg_destroy(pg);
+	scf_instance_destroy(i);
+	scf_service_destroy(s);
+	scf_handle_destroy(h);
+	free(pgname);
+
+	return (r);
+}
--- a/usr/src/lib/libscf/common/scf_tmpl.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/lib/libscf/common/scf_tmpl.c	Fri Jul 30 17:04:17 2010 +1000
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 /*
@@ -152,8 +151,8 @@
  * Returns 1 if the supplied error is a member of the error array, 0
  * if it is not.
  */
-static scf_error_t
-ismember(const int error, const scf_error_t error_array[])
+int
+ismember(const scf_error_t error, const scf_error_t error_array[])
 {
 	int i;
 
@@ -505,7 +504,7 @@
 }
 
 /*
- * char **_append_astrings_values()
+ * static char ** _append_astrings_values()
  *
  * This function reads the values from the property prop_name in pg and
  * appends to an existing scf_values_t *vals.  vals may be empty, but
@@ -4093,11 +4092,13 @@
 {
 	int i;
 	char **items = NULL;
-	char **str = vals->values_as_strings;
+	char **str = NULL;
 
 	if (vals == NULL)
 		return;
 
+	str = vals->values_as_strings;
+
 	/* free values */
 	switch (vals->value_type) {
 	case SCF_TYPE_BOOLEAN:
--- a/usr/src/lib/libscf/inc/libscf.h	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/lib/libscf/inc/libscf.h	Fri Jul 30 17:04:17 2010 +1000
@@ -29,6 +29,7 @@
 
 #include <stddef.h>
 #include <sys/types.h>
+#include <libnvpair.h>
 
 #ifdef	__cplusplus
 extern "C" {
@@ -286,6 +287,7 @@
 /*
  * Standard property names
  */
+#define	SCF_PROPERTY_ACTIVE_POSTFIX	((const char *)"active")
 #define	SCF_PROPERTY_AUX_STATE		((const char *)"auxiliary_state")
 #define	SCF_PROPERTY_AUX_FMRI		((const char *)"auxiliary_fmri")
 #define	SCF_PROPERTY_AUX_TTY		((const char *)"auxiliary_tty")
@@ -401,6 +403,38 @@
 #define	SCF_STATE_DEGRADED		0x00000020
 #define	SCF_STATE_ALL			0x0000003F
 
+/*
+ * software fma svc-transition class
+ */
+#define	SCF_NOTIFY_PARAMS_VERSION	0X0
+#define	SCF_NOTIFY_NAME_FMRI		((const char *)"fmri")
+#define	SCF_NOTIFY_NAME_VERSION		((const char *)"version")
+#define	SCF_NOTIFY_NAME_TSET		((const char *)"tset")
+#define	SCF_NOTIFY_PG_POSTFIX		((const char *)"fmnotify")
+#define	SCF_NOTIFY_PARAMS		((const char *)"notify-params")
+#define	SCF_NOTIFY_PARAMS_INST \
+	((const char *)"svc:/system/fm/notify-params:default")
+#define	SCF_SVC_TRANSITION_CLASS \
+	((const char *)"ireport.os.smf.state-transition")
+#define	SCF_NOTIFY_PARAMS_PG_TYPE	((const char *)"notify_params")
+
+/*
+ * Useful transition macros
+ */
+#define	SCF_TRANS_SHIFT_INITIAL_STATE(s)	((s) << 16)
+#define	SCF_TRANSITION_ALL \
+	(SCF_TRANS_SHIFT_INITIAL_STATE(SCF_STATE_ALL) | SCF_STATE_ALL)
+#define	SCF_TRANS(f, t)	(SCF_TRANS_SHIFT_INITIAL_STATE(f) | (t))
+#define	SCF_TRANS_VALID(t)	(!((t) & ~SCF_TRANSITION_ALL))
+#define	SCF_TRANS_INITIAL_STATE(t)	((t) >> 16 & SCF_STATE_ALL)
+#define	SCF_TRANS_FINAL_STATE(t)	((t) & SCF_STATE_ALL)
+
+/*
+ * Prefixes for states in state transition notification
+ */
+#define	SCF_STN_PREFIX_FROM		((const char *)"from-")
+#define	SCF_STN_PREFIX_TO		((const char *)"to-")
+
 #define	SCF_PG_FLAG_NONPERSISTENT	0x1
 
 #define	SCF_TRACE_LIBRARY		0x1
@@ -775,6 +809,27 @@
 void scf_simple_prop_next_reset(scf_simple_prop_t *);
 
 /*
+ * smf_state_from_string()
+ * return SCF_STATE_* value for the input
+ * -1 on error. String "all" maps to SCF_STATE_ALL macro
+ */
+int32_t smf_state_from_string(const char *);
+
+/*
+ * smf_state_to_string()
+ * return SCF_STATE_STRING* value for the input
+ * NULL on error.
+ */
+const char *smf_state_to_string(int32_t);
+
+/*
+ * Notification interfaces
+ */
+int smf_notify_set_params(const char *, nvlist_t *);
+int smf_notify_get_params(nvlist_t **, nvlist_t *);
+int smf_notify_del_params(const char *, const char *, int32_t);
+
+/*
  * SMF exit status definitions
  */
 #define	SMF_EXIT_OK		  0
--- a/usr/src/lib/libscf/inc/libscf_priv.h	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/lib/libscf/inc/libscf_priv.h	Fri Jul 30 17:04:17 2010 +1000
@@ -552,6 +552,41 @@
  */
 void _check_services(char **);
 
+/*
+ * _scf_handle_create_and_bind()
+ * convenience function that creates and binds a handle
+ */
+scf_handle_t *_scf_handle_create_and_bind(scf_version_t);
+
+/*
+ * _smf_refresh_all_instances()
+ * refresh all intances of a service
+ * return SCF_SUCCESS or SCF_FAILED on _PERMISSION_DENIED, _BACKEND_ACCESS
+ * or _BACKEND_READONLY.
+ */
+int _smf_refresh_all_instances(scf_service_t *);
+
+/*
+ * _scf_get_fma_notify_params()
+ * Specialized fuction to get fma notifitation parameters
+ */
+int _scf_get_fma_notify_params(const char *, nvlist_t *, int);
+
+/*
+ * _scf_get_svc_notify_params()
+ * Specialized function to get SMF state transition notification parameters
+ */
+int _scf_get_svc_notify_params(const char *, nvlist_t *, int32_t, int, int);
+
+/*
+ * _scf_notify_get_params()
+ * Specialized function to get notification parametes from a pg into an
+ * nvlist_t
+ */
+int _scf_notify_get_params(scf_propertygroup_t *, nvlist_t *);
+
+#define	SCF_NOTIFY_PARAMS_SOURCE_NAME	((const char *)"preference_source")
+
 #ifdef	__cplusplus
 }
 #endif
--- a/usr/src/lib/libsecdb/auth_attr.txt	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/lib/libsecdb/auth_attr.txt	Fri Jul 30 17:04:17 2010 +1000
@@ -150,6 +150,8 @@
 solaris.smf.manage.routing:::Manage Routing Service States::help=SmfRoutingStates.html
 solaris.smf.manage.rpc.bind:::Manage RPC Program number mapper::help=SmfRPCBind.html
 solaris.smf.manage.sendmail:::Manage Sendmail Service States::help=SmfSendmailStates.html
+solaris.smf.manage.smtp-notify:::Manage Email Event Notification Agent::
+solaris.smf.manage.snmp-notify:::Manage SNMP Event Notification Agent::
 solaris.smf.manage.ssh:::Manage Secure Shell Service States::help=SmfSshStates.html
 solaris.smf.manage.stmf:::Manage STMF Service States::help=SmfSTMFStates.html
 solaris.smf.manage.system-log:::Manage Syslog Service States::help=SmfSyslogStates.html
@@ -175,6 +177,8 @@
 solaris.smf.value.nwam:::Change Values of SMF Network Auto-Magic Properties::help=SmfValueNWAM.html
 solaris.smf.value.smb:::Change Values of SMB Service Properties::help=SmfValueSMB.html
 solaris.smf.read.smb:::Read permission for protected SMF SMB Service Properties::help=AuthReadSMB.html
+solaris.smf.value.smtp-notify:::Change values of Email Event Notification Agent properties::
+solaris.smf.value.snmp-notify:::Change values of SNMP Event Notification Agent properties::
 solaris.smf.read.stmf:::Read STMF Provider Private Data::help=SmfSTMFRead.html
 solaris.smf.value.routing:::Change Values of SMF Routing Properties::help=SmfValueRouting.html
 solaris.smf.value.tnd:::Change Trusted Network Daemon Service Property Values::help=ValueTND.html
--- a/usr/src/lib/libsecdb/prof_attr.txt	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/lib/libsecdb/prof_attr.txt	Fri Jul 30 17:04:17 2010 +1000
@@ -102,6 +102,7 @@
 Project Management:::Add/Modify/Remove projects:help=RtProjManagement.html
 VSCAN Management:::Manage the VSCAN service:auths=solaris.smf.manage.vscan,solaris.smf.value.vscan,solaris.smf.modify.application;help=RtVscanMngmnt.html
 WUSB Management:::Manage Wireless USB:auths=solaris.admin.wusb.*,solaris.smf.manage.wusb;help=WUSBmgmt.html
+Event Notification Agent Management:::Manage Event Notification Agents:auths=solaris.smf.manage.smtp-notify,solaris.smf.manage.snmp-notify,solaris.smf.value.smtp-notify,solaris.smf.value.snmp-notify
 #
 # Trusted Extensions profiles:
 #
--- a/usr/src/lib/libsysevent/libevchannel.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/lib/libsysevent/libevchannel.c	Fri Jul 30 17:04:17 2010 +1000
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #include <stdio.h>
@@ -435,7 +434,6 @@
 kill_door_servers(evchan_subscr_t *subp)
 {
 	door_arg_t da;
-	int i;
 
 	bzero(&da, sizeof (da));
 	subp->evsub_state = EVCHAN_SUB_STATE_CLOSING;
@@ -603,7 +601,6 @@
     int (*event_handler)(sysevent_t *ev, void *cookie),
     void *cookie, uint32_t flags, sysevent_subattr_t *attr)
 {
-	struct sysevent_subattr_impl sa;
 	struct sysevent_subattr_impl *xsa;
 
 	if (attr != NULL) {
@@ -809,11 +806,13 @@
 		rc = ioctl(EV_FD(scp), SEV_CHAN_CONTROL, (intptr_t)&uargs);
 		*chlenp = uargs.value;
 		break;
+
 	case EVCH_SET_CHAN_LEN:
 		/* Range change will be handled in framework */
 		uargs.value = va_arg(ap, uint32_t);
 		rc = ioctl(EV_FD(scp), SEV_CHAN_CONTROL, (intptr_t)&uargs);
 		break;
+
 	default:
 		rc = EINVAL;
 	}
@@ -828,3 +827,91 @@
 
 	return (errno = rc);
 }
+
+int
+sysevent_evc_setpropnvl(evchan_t *scp, nvlist_t *nvl)
+{
+	sev_propnvl_args_t uargs;
+	char *buf = NULL;
+	size_t nvlsz = 0;
+	int rc;
+
+	if (scp == NULL || misaligned(scp))
+		return (errno = EINVAL);
+
+	if (nvl != NULL &&
+	    nvlist_pack(nvl, &buf, &nvlsz, NV_ENCODE_NATIVE, 0) != 0)
+		return (errno);
+
+	uargs.packednvl.name = (uint64_t)(uintptr_t)buf;
+	uargs.packednvl.len = (uint32_t)nvlsz;
+
+	rc = ioctl(EV_FD(scp), SEV_SETPROPNVL, (intptr_t)&uargs);
+
+	if (buf)
+		free(buf);
+
+	return (rc);
+}
+
+int
+sysevent_evc_getpropnvl(evchan_t *scp, nvlist_t **nvlp)
+{
+	sev_propnvl_args_t uargs;
+	char buf[1024], *bufp = buf;	/* stack buffer */
+	size_t sz = sizeof (buf);
+	char *buf2 = NULL;		/* allocated if stack buf too small */
+	int64_t expgen = -1;
+	int rc;
+
+	if (scp == NULL || misaligned(scp) || nvlp == NULL)
+		return (errno = EINVAL);
+
+	*nvlp = NULL;
+
+again:
+	uargs.packednvl.name = (uint64_t)(uintptr_t)bufp;
+	uargs.packednvl.len = (uint32_t)sz;
+
+	rc = ioctl(EV_FD(scp), SEV_GETPROPNVL, (intptr_t)&uargs);
+
+	if (rc == E2BIG)
+		return (errno = E2BIG);	/* driver refuses to copyout */
+
+	/*
+	 * If the packed nvlist is too big for the buffer size we offered
+	 * then the ioctl returns EOVERFLOW and indicates in the 'len'
+	 * the size required for the current property nvlist generation
+	 * (itself returned in the generation member).
+	 */
+	if (rc == EOVERFLOW &&
+	    (buf2 == NULL || uargs.generation != expgen)) {
+		if (buf2 != NULL)
+			free(buf2);
+
+		if ((sz = uargs.packednvl.len) > 1024 * 1024)
+			return (E2BIG);
+
+		bufp = buf2 = malloc(sz);
+
+		if (buf2 == NULL)
+			return (errno = ENOMEM);
+
+		expgen = uargs.generation;
+		goto again;
+	}
+
+	/*
+	 * The chan prop nvlist can be absent, in which case the ioctl
+	 * returns success and uargs.packednvl.len of 0;  we have already
+	 * set *nvlp to NULL.  Otherwise we must unpack the nvl.
+	 */
+	if (rc == 0 && uargs.packednvl.len != 0 &&
+	    nvlist_unpack(bufp, uargs.packednvl.len, nvlp, 0) != 0)
+		rc = EINVAL;
+
+	if (buf2 != NULL)
+		free(buf2);
+
+	return (rc ? errno = rc : 0);
+}
--- a/usr/src/lib/libsysevent/libsysevent.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/lib/libsysevent/libsysevent.c	Fri Jul 30 17:04:17 2010 +1000
@@ -20,8 +20,7 @@
  */
 
 /*
- * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #include <stdio.h>
@@ -35,6 +34,7 @@
 #include <strings.h>
 #include <synch.h>
 #include <pthread.h>
+#include <signal.h>
 #include <thread.h>
 #include <libnvpair.h>
 #include <assert.h>
@@ -764,6 +764,10 @@
 
 	sub_info = (subscriber_priv_t *)SH_PRIV_DATA(shp);
 
+	/* See hack alert in sysevent_bind_subscriber_cmn */
+	if (sub_info->sp_handler_tid == NULL)
+		sub_info->sp_handler_tid = thr_self();
+
 	(void) mutex_lock(&sub_info->sp_qlock);
 	for (;;) {
 		while (sub_info->sp_evq_head == NULL && SH_BOUND(shp)) {
@@ -2038,18 +2042,72 @@
 	return (-1);
 }
 
-/*
- * sysevent_bind_subscriber - Bind an event receiver to an event channel
- */
-int
-sysevent_bind_subscriber(sysevent_handle_t *shp,
-	void (*event_handler)(sysevent_t *ev))
+static pthread_once_t xdoor_thrattr_once = PTHREAD_ONCE_INIT;
+static pthread_attr_t xdoor_thrattr;
+
+static void
+xdoor_thrattr_init(void)
+{
+	(void) pthread_attr_init(&xdoor_thrattr);
+	(void) pthread_attr_setdetachstate(&xdoor_thrattr,
+	    PTHREAD_CREATE_DETACHED);
+	(void) pthread_attr_setscope(&xdoor_thrattr, PTHREAD_SCOPE_SYSTEM);
+}
+
+static int
+xdoor_server_create(door_info_t *dip, void *(*startf)(void *),
+    void *startfarg, void *cookie)
+{
+	struct sysevent_subattr_impl *xsa = cookie;
+	pthread_attr_t *thrattr;
+	sigset_t oset;
+	int err;
+
+	if (xsa->xs_thrcreate) {
+		return (xsa->xs_thrcreate(dip, startf, startfarg,
+		    xsa->xs_thrcreate_cookie));
+	}
+
+	if (xsa->xs_thrattr == NULL) {
+		(void) pthread_once(&xdoor_thrattr_once, xdoor_thrattr_init);
+		thrattr = &xdoor_thrattr;
+	} else {
+		thrattr = xsa->xs_thrattr;
+	}
+
+	(void) pthread_sigmask(SIG_SETMASK, &xsa->xs_sigmask, &oset);
+	err = pthread_create(NULL, thrattr, startf, startfarg);
+	(void) pthread_sigmask(SIG_SETMASK, &oset, NULL);
+
+	return (err == 0 ? 1 : -1);
+}
+
+static void
+xdoor_server_setup(void *cookie)
+{
+	struct sysevent_subattr_impl *xsa = cookie;
+
+	if (xsa->xs_thrsetup) {
+		xsa->xs_thrsetup(xsa->xs_thrsetup_cookie);
+	} else {
+		(void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
+		(void) pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
+	}
+}
+
+static int
+sysevent_bind_subscriber_cmn(sysevent_handle_t *shp,
+	void (*event_handler)(sysevent_t *ev),
+	sysevent_subattr_t *subattr)
 {
 	int fd = -1;
 	int error = 0;
 	uint32_t sub_id = 0;
 	char door_name[MAXPATHLEN];
 	subscriber_priv_t *sub_info;
+	int created;
+	struct sysevent_subattr_impl *xsa =
+	    (struct sysevent_subattr_impl *)subattr;
 
 	if (shp == NULL || event_handler == NULL) {
 		errno = EINVAL;
@@ -2124,8 +2182,18 @@
 	 * syseventd will use this door service to propagate
 	 * events to the client.
 	 */
-	if ((SH_DOOR_DESC(shp) = door_create(event_deliver_service,
-	    (void *)shp, DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) == -1) {
+	if (subattr == NULL) {
+		SH_DOOR_DESC(shp) = door_create(event_deliver_service,
+		    (void *)shp, DOOR_REFUSE_DESC | DOOR_NO_CANCEL);
+	} else {
+		SH_DOOR_DESC(shp) = door_xcreate(event_deliver_service,
+		    (void *)shp,
+		    DOOR_REFUSE_DESC | DOOR_NO_CANCEL | DOOR_NO_DEPLETION_CB,
+		    xdoor_server_create, xdoor_server_setup,
+		    (void *)subattr, 1);
+	}
+
+	if (SH_DOOR_DESC(shp) == -1) {
 		dprint("sysevent_bind_subscriber: door create failed: "
 		    "%s\n", strerror(errno));
 		error = EFAULT;
@@ -2151,10 +2219,33 @@
 	SH_TYPE(shp) = SUBSCRIBER;
 	SH_PRIV_DATA(shp) = (void *)sub_info;
 
+	/* Create an event handler thread */
+	if (xsa == NULL || xsa->xs_thrcreate == NULL) {
+		created = thr_create(NULL, NULL,
+		    (void *(*)(void *))subscriber_event_handler,
+		    shp, THR_BOUND, &sub_info->sp_handler_tid) == 0;
+	} else {
+		/*
+		 * A terrible hack.  We will use the extended private
+		 * door thread creation function the caller passed in to
+		 * create the event handler thread.  That function will
+		 * be called with our chosen thread start function and arg
+		 * instead of the usual libc-provided ones, but that's ok
+		 * as it is required to use them verbatim anyway.  We will
+		 * pass a NULL door_info_t pointer to the function - so
+		 * callers depending on this hack had better be prepared
+		 * for that.  All this allow the caller to rubberstamp
+		 * the created thread as it wishes.  But we don't get
+		 * the created threadid with this, so we modify the
+		 * thread start function to stash it.
+		 */
 
-	/* Create an event handler thread */
-	if (thr_create(NULL, NULL, (void *(*)(void *))subscriber_event_handler,
-	    shp, THR_BOUND, &sub_info->sp_handler_tid) != 0) {
+		created = xsa->xs_thrcreate(NULL,
+		    (void *(*)(void *))subscriber_event_handler,
+		    shp, xsa->xs_thrcreate_cookie) == 1;
+	}
+
+	if (!created) {
 		error = EFAULT;
 		goto fail;
 	}
@@ -2190,6 +2281,27 @@
 }
 
 /*
+ * sysevent_bind_subscriber - Bind an event receiver to an event channel
+ */
+int
+sysevent_bind_subscriber(sysevent_handle_t *shp,
+	void (*event_handler)(sysevent_t *ev))
+{
+	return (sysevent_bind_subscriber_cmn(shp, event_handler, NULL));
+}
+
+/*
+ * sysevent_bind_xsubscriber - Bind a subscriber using door_xcreate with
+ * attributes specified.
+ */
+int
+sysevent_bind_xsubscriber(sysevent_handle_t *shp,
+	void (*event_handler)(sysevent_t *ev), sysevent_subattr_t *subattr)
+{
+	return (sysevent_bind_subscriber_cmn(shp, event_handler, subattr));
+}
+
+/*
  * sysevent_register_event - register an event class and associated subclasses
  *		for an event subscriber
  */
@@ -2389,7 +2501,6 @@
 	(void) door_revoke(SH_DOOR_DESC(shp));
 	(void) fdetach(SH_DOOR_NAME(shp));
 
-
 	/*
 	 * Release resources and wait for pending event delivery to
 	 * complete.
@@ -2399,7 +2510,8 @@
 	/* Signal event handler and drain the subscriber's event queue */
 	(void) cond_signal(&sub_info->sp_cv);
 	(void) mutex_unlock(&sub_info->sp_qlock);
-	(void) thr_join(sub_info->sp_handler_tid, NULL, NULL);
+	if (sub_info->sp_handler_tid != NULL)
+		(void) thr_join(sub_info->sp_handler_tid, NULL, NULL);
 
 	(void) cond_destroy(&sub_info->sp_cv);
 	(void) mutex_destroy(&sub_info->sp_qlock);
@@ -2447,12 +2559,9 @@
  * Evolving APIs to subscribe to syseventd(1M) system events.
  */
 
-/*
- * sysevent_bind_handle - Bind application event handler for syseventd
- *		subscription.
- */
-sysevent_handle_t *
-sysevent_bind_handle(void (*event_handler)(sysevent_t *ev))
+static sysevent_handle_t *
+sysevent_bind_handle_cmn(void (*event_handler)(sysevent_t *ev),
+    sysevent_subattr_t *subattr)
 {
 	sysevent_handle_t *shp;
 
@@ -2470,8 +2579,7 @@
 		return (NULL);
 	}
 
-	if (sysevent_bind_subscriber(shp, event_handler) != 0) {
-
+	if (sysevent_bind_xsubscriber(shp, event_handler, subattr) != 0) {
 		/*
 		 * Ask syseventd to clean-up any stale subcribers and try to
 		 * to bind again
@@ -2496,7 +2604,8 @@
 			(void) close(pub_fd);
 
 			/* Try to bind again */
-			if (sysevent_bind_subscriber(shp, event_handler) != 0) {
+			if (sysevent_bind_xsubscriber(shp, event_handler,
+			    subattr) != 0) {
 				sysevent_close_channel(shp);
 				return (NULL);
 			}
@@ -2510,6 +2619,27 @@
 }
 
 /*
+ * sysevent_bind_handle - Bind application event handler for syseventd
+ *		subscription.
+ */
+sysevent_handle_t *
+sysevent_bind_handle(void (*event_handler)(sysevent_t *ev))
+{
+	return (sysevent_bind_handle_cmn(event_handler, NULL));
+}
+
+/*
+ * sysevent_bind_xhandle - Bind application event handler for syseventd
+ *		subscription, using door_xcreate and attributes as specified.
+ */
+sysevent_handle_t *
+sysevent_bind_xhandle(void (*event_handler)(sysevent_t *ev),
+    sysevent_subattr_t *subattr)
+{
+	return (sysevent_bind_handle_cmn(event_handler, subattr));
+}
+
+/*
  * sysevent_unbind_handle - Unbind caller from syseventd subscriptions
  */
 void
--- a/usr/src/lib/libsysevent/libsysevent.h	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/lib/libsysevent/libsysevent.h	Fri Jul 30 17:04:17 2010 +1000
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * 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.
@@ -20,15 +19,12 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2000-2003 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #ifndef	_LIBSYSEVENT_H
 #define	_LIBSYSEVENT_H
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <stdio.h>
 #include <thread.h>
 #include <stddef.h>
@@ -83,6 +79,8 @@
 
 /* syseventd subscriber interfaces */
 sysevent_handle_t *sysevent_bind_handle(void (*event_handler)(sysevent_t *ev));
+sysevent_handle_t *sysevent_bind_xhandle(void (*event_handler)(sysevent_t *ev),
+    sysevent_subattr_t *);
 void sysevent_unbind_handle(sysevent_handle_t *sysevent_hdl);
 int sysevent_subscribe_event(sysevent_handle_t *sysevent_hdl,
 	const char *event_class, const char **event_subclass_list,
@@ -99,6 +97,8 @@
 void sysevent_close_channel(sysevent_handle_t *shp);
 int sysevent_bind_subscriber(sysevent_handle_t *shp,
 	void (*event_handler)(sysevent_t *ev));
+int sysevent_bind_xsubscriber(sysevent_handle_t *shp,
+	void (*event_handler)(sysevent_t *ev), sysevent_subattr_t *);
 void sysevent_unbind_subscriber(sysevent_handle_t *shp);
 int sysevent_bind_publisher(sysevent_handle_t *shp);
 void sysevent_unbind_publisher(sysevent_handle_t *shp);
--- a/usr/src/lib/libsysevent/mapfile-vers	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/lib/libsysevent/mapfile-vers	Fri Jul 30 17:04:17 2010 +1000
@@ -75,13 +75,17 @@
 	sysevent_attr_value;
 	sysevent_bind_publisher;
 	sysevent_bind_subscriber;
+	sysevent_bind_xhandle;
+	sysevent_bind_xsubscriber;
 	sysevent_cleanup_publishers;
 	sysevent_cleanup_subscribers;
 	sysevent_close_channel;
 	sysevent_dup;
 	sysevent_evc_bind;
 	sysevent_evc_control;
+	sysevent_evc_getpropnvl;
 	sysevent_evc_publish;
+	sysevent_evc_setpropnvl;
 	sysevent_evc_subscribe;
 	sysevent_evc_unbind;
 	sysevent_evc_unsubscribe;
--- a/usr/src/pkg/manifests/SUNWcs.mf	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/pkg/manifests/SUNWcs.mf	Fri Jul 30 17:04:17 2010 +1000
@@ -47,6 +47,7 @@
 dir path=etc/inet group=sys
 dir path=etc/init.d group=sys
 dir path=etc/lib group=sys
+dir path=etc/logadm.d group=sys
 dir path=etc/mail group=mail
 dir path=etc/net group=sys
 dir path=etc/net/ticlts group=sys
@@ -579,6 +580,7 @@
 file path=lib/svc/manifest/system/identity.xml group=sys mode=0444
 file path=lib/svc/manifest/system/idmap.xml group=sys mode=0444
 file path=lib/svc/manifest/system/keymap.xml group=sys mode=0444
+file path=lib/svc/manifest/system/logadm-upgrade.xml group=sys mode=0444
 file path=lib/svc/manifest/system/manifest-import.xml group=sys mode=0444
 file path=lib/svc/manifest/system/name-service-cache.xml group=sys mode=0444
 file path=lib/svc/manifest/system/pfexecd.xml group=sys mode=0444
@@ -605,6 +607,7 @@
 file path=lib/svc/method/inetd-upgrade mode=0555
 file path=lib/svc/method/keymap mode=0555
 file path=lib/svc/method/ldap-client mode=0555
+file path=lib/svc/method/logadm-upgrade mode=0555
 file path=lib/svc/method/manifest-import mode=0555
 file path=lib/svc/method/mpxio-upgrade mode=0555
 file path=lib/svc/method/net-init mode=0555
--- a/usr/src/pkg/manifests/consolidation-osnet-osnet-message-files.mf	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/pkg/manifests/consolidation-osnet-osnet-message-files.mf	Fri Jul 30 17:04:17 2010 +1000
@@ -296,6 +296,7 @@
 file path=usr/lib/locale/C/LC_MESSAGES/AMD.po
 file path=usr/lib/locale/C/LC_MESSAGES/DISK.po
 file path=usr/lib/locale/C/LC_MESSAGES/FMD.po
+file path=usr/lib/locale/C/LC_MESSAGES/FMNOTIFY.po
 file path=usr/lib/locale/C/LC_MESSAGES/GMCA.po
 file path=usr/lib/locale/C/LC_MESSAGES/INTEL.po
 file path=usr/lib/locale/C/LC_MESSAGES/NXGE.po
@@ -304,6 +305,7 @@
 file path=usr/lib/locale/C/LC_MESSAGES/SCA1000.po
 file path=usr/lib/locale/C/LC_MESSAGES/SCA500.po
 file path=usr/lib/locale/C/LC_MESSAGES/SCF.po
+file path=usr/lib/locale/C/LC_MESSAGES/SMF.po
 file path=usr/lib/locale/C/LC_MESSAGES/SENSOR.po
 file path=usr/lib/locale/C/LC_MESSAGES/STORAGE.po
 file path=usr/lib/locale/C/LC_MESSAGES/SUN4.po
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkg/manifests/service-fault-management-smtp-notify.mf	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,52 @@
+#
+# 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.
+#
+
+set name=pkg.fmri value=pkg:/service/fault-management/smtp-notify@$(PKGVERS)
+set name=pkg.description \
+    value="smtp-notify is a small, lightweight daemon that subscribes to both software and FMA problem lifecycle events.  Upon receipt of an event, it produces an email notification based on a set of notification preferences which are stored in the SMF service configuration repository."
+set name=pkg.summary value="Email Notification Daemon for System Events"
+set name=info.classification \
+    value="org.opensolaris.category.2010:System/Administration and Configuration"
+set name=variant.arch value=$(ARCH)
+set name=variant.opensolaris.zone value=global value=nonglobal
+dir path=lib/svc/manifest/system group=sys
+dir path=lib/svc/manifest/system/fm group=sys
+dir path=usr/lib
+dir path=usr/lib/fm
+dir path=usr/lib/fm/notify
+file path=lib/svc/manifest/system/fm/smtp-notify.xml group=sys mode=0444
+file path=usr/lib/fm/notify/process_msg_template.sh mode=0555
+file path=usr/lib/fm/notify/smtp-notify mode=0555
+license cr_Sun license=cr_Sun
+license lic_CDDL license=lic_CDDL
+#
+# snmp-notify depends on libraries delivered in this package
+#
+depend fmri=service/fault-management type=require
+#
+# snmp-notify requires the sendmail-client service in order to be able to send
+# email notifications.
+#
+depend fmri=service/network/smtp/sendmail type=require
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkg/manifests/service-fault-management-snmp-notify.mf	Fri Jul 30 17:04:17 2010 +1000
@@ -0,0 +1,51 @@
+#
+# 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.
+#
+
+set name=pkg.fmri value=pkg:/service/fault-management/snmp-notify@$(PKGVERS)
+set name=pkg.description \
+    value="snmp-notify is a small, lightweight daemon that subscribes to both software and FMA problem lifecycle events.  Upon receipt of an event, it produces an SNMP notification based on a set of notification preferences which are stored in the SMF service configuration repository."
+set name=pkg.summary value="SNMP Notification Daemon for System Events"
+set name=info.classification \
+    value="org.opensolaris.category.2010:System/Administration and Configuration"
+set name=variant.arch value=$(ARCH)
+set name=variant.opensolaris.zone value=global value=nonglobal
+dir path=lib/svc/manifest/system group=sys
+dir path=lib/svc/manifest/system/fm group=sys
+dir path=usr/lib
+dir path=usr/lib/fm
+dir path=usr/lib/fm/notify
+file path=lib/svc/manifest/system/fm/snmp-notify.xml group=sys mode=0444
+file path=usr/lib/fm/notify/snmp-notify mode=0555
+license cr_Sun license=cr_Sun
+license lic_CDDL license=lic_CDDL
+#
+# snmp-notify depends on libraries delivered in this package
+#
+depend fmri=service/fault-management type=require
+#
+# snmp-notify requires the net-snmp service in order to be able to send
+# SNMP notifications.
+#
+depend fmri=system/management/snmp/net-snmp type=require
--- a/usr/src/pkg/manifests/service-fault-management.mf	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/pkg/manifests/service-fault-management.mf	Fri Jul 30 17:04:17 2010 +1000
@@ -35,33 +35,66 @@
 set name=info.classification \
     value="org.opensolaris.category.2008:System/Administration and Configuration"
 set name=variant.arch value=$(ARCH)
-dir path=etc group=sys
-dir path=etc/fm group=sys
-dir path=etc/fm/fmd group=sys
-dir path=etc/net-snmp
-dir path=etc/net-snmp/snmp
-dir path=etc/net-snmp/snmp/mibs
+#
+# Start by describing our directory structure.
+#
+# The snmp mibs are common to global and non-global zones
+#
+dir path=etc group=sys variant.opensolaris.zone=__NODEFAULT
+dir path=etc/fm group=sys variant.opensolaris.zone=__NODEFAULT
+dir path=etc/fm/fmd group=sys variant.opensolaris.zone=__NODEFAULT
+dir path=etc/net-snmp variant.opensolaris.zone=__NODEFAULT
+dir path=etc/net-snmp/snmp variant.opensolaris.zone=__NODEFAULT
+dir path=etc/net-snmp/snmp/mibs variant.opensolaris.zone=__NODEFAULT
+#
+# Our kernel driver is global zone only
+#
 dir path=kernel group=sys
 dir path=kernel/drv group=sys
 dir path=kernel/drv/$(ARCH64) group=sys
-dir path=usr group=sys
+#
+# Our service manifests are common to global and non-global zones
+#
+dir path=lib/svc/manifest/system group=sys \
+    variant.opensolaris.zone=__NODEFAULT
+dir path=lib/svc/manifest/system/fm group=sys \
+    variant.opensolaris.zone=__NODEFAULT
+#
+# Our additional /etc/logadm.conf entries are common to global and local zones
+#
+dir path=etc/logadm.d group=sys variant.opensolaris.zone=__NODEFAULT
+#
+# usr dirs:
+#	- most are common to both global and non-global zones
+#	- those mentioning 'include' will automatically apply to both
+#	- dictionaries are delivered to both contexts, even if they
+#	  are hardware dictionaries
+#	- eversholt rules (eft) are global zone only
+#	- some plugins and all schemes apply to both contexts
+#	- we don't deliver any topo maps to non-global zones, but we
+#	  create the directory nonetheless; similarly for topo plugins
+#	- paths mentioning mdb will automatically apply to both
+#	- usr/platform is global zone only
+#
+dir path=usr group=sys variant.opensolaris.zone=__NODEFAULT
 dir path=usr/include
 dir path=usr/include/fm
-dir path=usr/lib
-dir path=usr/lib/fm
-dir path=usr/lib/fm/$(ARCH64)
-dir path=usr/lib/fm/dict
+dir path=usr/lib variant.opensolaris.zone=__NODEFAULT
+dir path=usr/lib/fm variant.opensolaris.zone=__NODEFAULT
+dir path=usr/lib/fm/$(ARCH64) variant.opensolaris.zone=__NODEFAULT
+dir path=usr/lib/fm/dict variant.opensolaris.zone=__NODEFAULT
 dir path=usr/lib/fm/eft
-dir path=usr/lib/fm/fmd
-dir path=usr/lib/fm/fmd/plugins
-dir path=usr/lib/fm/fmd/schemes
-dir path=usr/lib/fm/fmd/schemes/$(ARCH64)
-dir path=usr/lib/fm/topo
-dir path=usr/lib/fm/topo/maps
-dir path=usr/lib/fm/topo/plugins
-dir path=usr/lib/locale
-dir path=usr/lib/locale/C
-dir path=usr/lib/locale/C/LC_MESSAGES
+dir path=usr/lib/fm/fmd variant.opensolaris.zone=__NODEFAULT
+dir path=usr/lib/fm/fmd/plugins variant.opensolaris.zone=__NODEFAULT
+dir path=usr/lib/fm/fmd/schemes variant.opensolaris.zone=__NODEFAULT
+dir path=usr/lib/fm/fmd/schemes/$(ARCH64) variant.opensolaris.zone=__NODEFAULT
+dir path=usr/lib/fm/notify variant.opensolaris.zone=__NODEFAULT
+dir path=usr/lib/fm/topo variant.opensolaris.zone=__NODEFAULT
+dir path=usr/lib/fm/topo/maps variant.opensolaris.zone=__NODEFAULT
+dir path=usr/lib/fm/topo/plugins variant.opensolaris.zone=__NODEFAULT
+dir path=usr/lib/locale variant.opensolaris.zone=__NODEFAULT
+dir path=usr/lib/locale/C variant.opensolaris.zone=__NODEFAULT
+dir path=usr/lib/locale/C/LC_MESSAGES variant.opensolaris.zone=__NODEFAULT
 dir path=usr/lib/mdb group=sys
 dir path=usr/lib/mdb/proc group=sys
 dir path=usr/platform group=sys
@@ -186,22 +219,56 @@
 $(sparc_ONLY)dir path=usr/platform/sun4v/lib/fm/topo
 $(sparc_ONLY)dir path=usr/platform/sun4v/lib/fm/topo/maps
 $(sparc_ONLY)dir path=usr/platform/sun4v/lib/fm/topo/plugins
-dir path=usr/sbin
-dir path=usr/share group=sys
-dir path=usr/share/lib group=sys
-dir path=usr/share/lib/xml group=sys
-dir path=usr/share/lib/xml/dtd group=sys
-dir path=var/fm group=sys
-dir path=var/fm/fmd group=sys
-dir path=var/fm/fmd/ckpt group=sys
-dir path=var/fm/fmd/rsrc group=sys
-dir path=var/fm/fmd/xprt group=sys
+#
+# Some directories common to both global and non-global zones:
+#
+dir path=usr/sbin variant.opensolaris.zone=__NODEFAULT
+dir path=usr/share group=sys variant.opensolaris.zone=__NODEFAULT
+dir path=usr/share/lib group=sys variant.opensolaris.zone=__NODEFAULT
+dir path=usr/share/lib/xml group=sys variant.opensolaris.zone=__NODEFAULT
+dir path=usr/share/lib/xml/dtd group=sys variant.opensolaris.zone=__NODEFAULT
+dir path=var/fm group=sys variant.opensolaris.zone=__NODEFAULT
+dir path=var/fm/fmd group=sys variant.opensolaris.zone=__NODEFAULT
+dir path=var/fm/fmd/ckpt group=sys variant.opensolaris.zone=__NODEFAULT
+dir path=var/fm/fmd/rsrc group=sys variant.opensolaris.zone=__NODEFAULT
+dir path=var/fm/fmd/xprt group=sys variant.opensolaris.zone=__NODEFAULT
+#
+# driver is global-zone only
+#
 driver name=fm perms="* 0644 root sys"
-file path=etc/net-snmp/snmp/fmd-trapgen.conf mode=0600
-file path=etc/net-snmp/snmp/mibs/SUN-FM-MIB.mib
+#
+# Now for our file and link payloads
+#
+# snmp MIBs are common to both global and non-global zones
+#
+file path=etc/net-snmp/snmp/mibs/SUN-FM-MIB.mib \
+    variant.opensolaris.zone=__NODEFAULT
+file path=etc/net-snmp/snmp/mibs/SUN-IREPORT-MIB.mib \
+    variant.opensolaris.zone=__NODEFAULT
+#
+# driver is global-zone only
+#
 file path=kernel/drv/$(ARCH64)/fm group=sys
 $(i386_ONLY)file path=kernel/drv/fm group=sys
 file path=kernel/drv/fm.conf group=sys
+#
+# Service manifests are common to both global and non-global zones
+#
+file path=lib/svc/manifest/system/fm/notify-params.xml group=sys mode=0444 \
+    variant.opensolaris.zone=__NODEFAULT
+file path=lib/svc/manifest/system/fmd.xml group=sys mode=0444 \
+    variant.opensolaris.zone=__NODEFAULT
+#
+# Our additional /etc/logadm.conf entries are common to global and local zones
+#
+file path=etc/logadm.d/fmd.logadm.conf group=sys mode=0444 \
+    refresh_fmri='svc:/system/logadm-upgrade:default' \
+    variant.opensolaris.zone=__NODEFAULT
+#
+# Headers are automatically included in both contexts.
+# While some of these are from global-zone-only components
+# there's no harm in including them in both contexts.
+#
 file path=usr/include/fm/diagcode.h
 file path=usr/include/fm/fmd_adm.h
 file path=usr/include/fm/fmd_agent.h
@@ -212,53 +279,110 @@
 file path=usr/include/fm/fmd_snmp.h
 file path=usr/include/fm/libdiskstatus.h
 file path=usr/include/fm/libfmevent.h
+file path=usr/include/fm/libfmevent_ruleset.h
 file path=usr/include/fm/libseslog.h
 file path=usr/include/fm/libtopo.h
 file path=usr/include/fm/topo_hc.h
 file path=usr/include/fm/topo_list.h
 file path=usr/include/fm/topo_method.h
 file path=usr/include/fm/topo_mod.h
-file path=usr/lib/fm/$(ARCH64)/libdiagcode.so.1
-file path=usr/lib/fm/$(ARCH64)/libdiskstatus.so.1
-file path=usr/lib/fm/$(ARCH64)/libfmd_adm.so.1
-file path=usr/lib/fm/$(ARCH64)/libfmd_agent.so.1
-file path=usr/lib/fm/$(ARCH64)/libfmd_log.so.1
-file path=usr/lib/fm/$(ARCH64)/libfmd_msg.so.1
-file path=usr/lib/fm/$(ARCH64)/libfmd_snmp.so.1
-file path=usr/lib/fm/$(ARCH64)/libfmevent.so.1
+#
+#	64-bit .so.1
+#
+file path=usr/lib/fm/$(ARCH64)/libdiagcode.so.1 \
+    variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/$(ARCH64)/libdiskstatus.so.1 \
+    variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/$(ARCH64)/libfmd_adm.so.1 \
+    variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/$(ARCH64)/libfmd_agent.so.1 \
+    variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/$(ARCH64)/libfmd_log.so.1 \
+    variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/$(ARCH64)/libfmd_msg.so.1 \
+    variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/$(ARCH64)/libfmd_snmp.so.1 \
+    variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/$(ARCH64)/libfmevent.so.1 \
+    variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/$(ARCH64)/libfmnotify.so.1 \
+    variant.opensolaris.zone=__NODEFAULT
 $(sparc_ONLY)file path=usr/lib/fm/$(ARCH64)/libldom.so.1
 $(sparc_ONLY)file path=usr/lib/fm/$(ARCH64)/libmdesc.so.1
 file path=usr/lib/fm/$(ARCH64)/libseslog.so.1
-file path=usr/lib/fm/$(ARCH64)/libtopo.so.1
-file path=usr/lib/fm/$(ARCH64)/llib-ldiagcode.ln
-file path=usr/lib/fm/$(ARCH64)/llib-ldiskstatus.ln
-file path=usr/lib/fm/$(ARCH64)/llib-lfmd_adm.ln
-file path=usr/lib/fm/$(ARCH64)/llib-lfmd_agent.ln
-file path=usr/lib/fm/$(ARCH64)/llib-lfmd_log.ln
-file path=usr/lib/fm/$(ARCH64)/llib-lfmd_msg.ln
-file path=usr/lib/fm/$(ARCH64)/llib-lfmd_snmp.ln
-file path=usr/lib/fm/$(ARCH64)/llib-lfmevent.ln
+file path=usr/lib/fm/$(ARCH64)/libtopo.so.1 \
+    variant.opensolaris.zone=__NODEFAULT
+#
+#	64-bit lint libraries
+#
+file path=usr/lib/fm/$(ARCH64)/llib-ldiagcode.ln \
+    variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/$(ARCH64)/llib-ldiskstatus.ln \
+    variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/$(ARCH64)/llib-lfmd_adm.ln \
+    variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/$(ARCH64)/llib-lfmd_agent.ln \
+    variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/$(ARCH64)/llib-lfmd_log.ln \
+    variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/$(ARCH64)/llib-lfmd_msg.ln \
+    variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/$(ARCH64)/llib-lfmd_snmp.ln \
+    variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/$(ARCH64)/llib-lfmevent.ln \
+    variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/$(ARCH64)/llib-lfmnotify.ln \
+    variant.opensolaris.zone=__NODEFAULT
 $(sparc_ONLY)file path=usr/lib/fm/$(ARCH64)/llib-lldom.ln
 $(sparc_ONLY)file path=usr/lib/fm/$(ARCH64)/llib-lmdesc.ln
 file path=usr/lib/fm/$(ARCH64)/llib-lseslog.ln
-file path=usr/lib/fm/$(ARCH64)/llib-ltopo.ln
-$(i386_ONLY)file path=usr/lib/fm/dict/AMD.dict mode=0444
-file path=usr/lib/fm/dict/DISK.dict mode=0444
-file path=usr/lib/fm/dict/FMD.dict mode=0444
-$(i386_ONLY)file path=usr/lib/fm/dict/GMCA.dict mode=0444
-$(i386_ONLY)file path=usr/lib/fm/dict/INTEL.dict mode=0444
-file path=usr/lib/fm/dict/NXGE.dict mode=0444
-file path=usr/lib/fm/dict/PCI.dict mode=0444
-file path=usr/lib/fm/dict/PCIEX.dict mode=0444
-$(sparc_ONLY)file path=usr/lib/fm/dict/SCF.dict mode=0444
-file path=usr/lib/fm/dict/SENSOR.dict mode=0444
-file path=usr/lib/fm/dict/STORAGE.dict mode=0444
-$(sparc_ONLY)file path=usr/lib/fm/dict/SUN4.dict mode=0444
-$(sparc_ONLY)file path=usr/lib/fm/dict/SUN4U.dict mode=0444
-$(sparc_ONLY)file path=usr/lib/fm/dict/SUN4V.dict mode=0444
-file path=usr/lib/fm/dict/SUNOS.dict mode=0444
-file path=usr/lib/fm/dict/ZFS.dict mode=0444
-file path=usr/lib/fm/eft/disk.eft mode=0444
+file path=usr/lib/fm/$(ARCH64)/llib-ltopo.ln \
+    variant.opensolaris.zone=__NODEFAULT
+#
+# Dictionaries, whether they are hardware-specific or not, are
+# common to both global and non-global zones.
+#
+$(i386_ONLY)file path=usr/lib/fm/dict/AMD.dict mode=0444 \
+    variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/dict/DISK.dict mode=0444 \
+    variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/dict/FMD.dict mode=0444 \
+    variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/dict/FMNOTIFY.dict mode=0444 \
+    variant.opensolaris.zone=__NODEFAULT
+$(i386_ONLY)file path=usr/lib/fm/dict/GMCA.dict mode=0444 \
+    variant.opensolaris.zone=__NODEFAULT
+$(i386_ONLY)file path=usr/lib/fm/dict/INTEL.dict mode=0444 \
+    variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/dict/NXGE.dict mode=0444 \
+    variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/dict/PCI.dict mode=0444 \
+    variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/dict/PCIEX.dict mode=0444 \
+    variant.opensolaris.zone=__NODEFAULT
+$(sparc_ONLY)file path=usr/lib/fm/dict/SCF.dict mode=0444 \
+    variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/dict/SENSOR.dict mode=0444 \
+    variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/dict/STORAGE.dict mode=0444 \
+    variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/dict/SMF.dict mode=0444 \
+    variant.opensolaris.zone=__NODEFAULT
+$(sparc_ONLY)file path=usr/lib/fm/dict/SUN4.dict mode=0444 \
+    variant.opensolaris.zone=__NODEFAULT
+$(sparc_ONLY)file path=usr/lib/fm/dict/SUN4U.dict mode=0444 \
+    variant.opensolaris.zone=__NODEFAULT
+$(sparc_ONLY)file path=usr/lib/fm/dict/SUN4V.dict mode=0444 \
+    variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/dict/SUNOS.dict mode=0444 \
+    variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/dict/ZFS.dict mode=0444 \
+    variant.opensolaris.zone=__NODEFAULT
+#
+# Eversholt rules are global zone only
+#
+file path=usr/lib/fm/eft/disk.eft mode=0444 \
+    variant.opensolaris.zone=__NODEFAULT
 file path=usr/lib/fm/eft/neptune_xaui.eft mode=0444
 file path=usr/lib/fm/eft/neptune_xfp.eft mode=0444
 file path=usr/lib/fm/eft/pci.eft mode=0444
@@ -266,11 +390,24 @@
 file path=usr/lib/fm/eft/pciexrc.eft mode=0444
 file path=usr/lib/fm/eft/sensor.eft mode=0444
 file path=usr/lib/fm/eft/storage.eft mode=0444
-file path=usr/lib/fm/fmd/fmd mode=0555
-file path=usr/lib/fm/fmd/fminject mode=0555
-file path=usr/lib/fm/fmd/fmsim mode=0555
-file path=usr/lib/fm/fmd/fmtopo mode=0555
+#
+# usr/lib/fm/fmd executables:
+#	- fmd is common, as are fminject, fmsim and fmtopo
+#	- ipmitopo is global zone only
+#
+file path=usr/lib/fm/fmd/fmd mode=0555 variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/fmd/fminject mode=0555 \
+    variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/fmd/fmsim mode=0555 variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/fmd/fmtopo mode=0555 variant.opensolaris.zone=__NODEFAULT
 file path=usr/lib/fm/fmd/ipmitopo mode=0555
+#
+# fmd plugins:
+#	- most are hardware-specific and so global zone only
+#	- ext-event-transport, ip-transport, syslog-msgs,
+#	  software-diagnosis and software-response
+#	  are common to both contexts
+#
 file path=usr/lib/fm/fmd/plugins/cpumem-retire.conf
 file path=usr/lib/fm/fmd/plugins/cpumem-retire.so mode=0555
 file path=usr/lib/fm/fmd/plugins/disk-monitor.conf
@@ -279,110 +416,197 @@
 file path=usr/lib/fm/fmd/plugins/disk-transport.so mode=0555
 file path=usr/lib/fm/fmd/plugins/eft.conf
 file path=usr/lib/fm/fmd/plugins/eft.so mode=0555
-file path=usr/lib/fm/fmd/plugins/ext-event-transport.conf
-file path=usr/lib/fm/fmd/plugins/ext-event-transport.so mode=0555
+file path=usr/lib/fm/fmd/plugins/ext-event-transport.conf \
+    variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/fmd/plugins/ext-event-transport.so mode=0555 \
+    variant.opensolaris.zone=__NODEFAULT
 file path=usr/lib/fm/fmd/plugins/fabric-xlate.conf
 file path=usr/lib/fm/fmd/plugins/fabric-xlate.so mode=0555
 file path=usr/lib/fm/fmd/plugins/fdd-msg.conf
 file path=usr/lib/fm/fmd/plugins/fdd-msg.so mode=0555
 file path=usr/lib/fm/fmd/plugins/io-retire.conf
 file path=usr/lib/fm/fmd/plugins/io-retire.so mode=0555
-file path=usr/lib/fm/fmd/plugins/ip-transport.conf
-file path=usr/lib/fm/fmd/plugins/ip-transport.so mode=0555
+file path=usr/lib/fm/fmd/plugins/ip-transport.conf \
+    variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/fmd/plugins/ip-transport.so mode=0555 \
+    variant.opensolaris.zone=__NODEFAULT
 file path=usr/lib/fm/fmd/plugins/sensor-transport.conf
 file path=usr/lib/fm/fmd/plugins/sensor-transport.so mode=0555
 file path=usr/lib/fm/fmd/plugins/ses-log-transport.conf
 file path=usr/lib/fm/fmd/plugins/ses-log-transport.so mode=0555
-file path=usr/lib/fm/fmd/plugins/snmp-trapgen.conf
-file path=usr/lib/fm/fmd/plugins/snmp-trapgen.so mode=0555
+file path=usr/lib/fm/fmd/plugins/software-diagnosis.conf \
+    variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/fmd/plugins/software-diagnosis.so mode=0555 \
+    variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/fmd/plugins/software-response.conf \
+    variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/fmd/plugins/software-response.so mode=0555 \
+    variant.opensolaris.zone=__NODEFAULT
 file path=usr/lib/fm/fmd/plugins/sp-monitor.conf
 file path=usr/lib/fm/fmd/plugins/sp-monitor.so mode=0555
-file path=usr/lib/fm/fmd/plugins/syslog-msgs.conf
-file path=usr/lib/fm/fmd/plugins/syslog-msgs.so mode=0555
+file path=usr/lib/fm/fmd/plugins/syslog-msgs.conf \
+    variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/fmd/plugins/syslog-msgs.so mode=0555 \
+    variant.opensolaris.zone=__NODEFAULT
 file path=usr/lib/fm/fmd/plugins/zfs-diagnosis.conf
 file path=usr/lib/fm/fmd/plugins/zfs-diagnosis.so mode=0555
 file path=usr/lib/fm/fmd/plugins/zfs-retire.conf
 file path=usr/lib/fm/fmd/plugins/zfs-retire.so mode=0555
-file path=usr/lib/fm/fmd/schemes/$(ARCH64)/cpu.so mode=0555
-file path=usr/lib/fm/fmd/schemes/$(ARCH64)/dev.so mode=0555
-file path=usr/lib/fm/fmd/schemes/$(ARCH64)/fmd.so mode=0555
-file path=usr/lib/fm/fmd/schemes/$(ARCH64)/hc.so mode=0555
-file path=usr/lib/fm/fmd/schemes/$(ARCH64)/legacy-hc.so mode=0555
-file path=usr/lib/fm/fmd/schemes/$(ARCH64)/mem.so mode=0555
-file path=usr/lib/fm/fmd/schemes/$(ARCH64)/mod.so mode=0555
-file path=usr/lib/fm/fmd/schemes/$(ARCH64)/pkg.so mode=0555
-file path=usr/lib/fm/fmd/schemes/$(ARCH64)/svc.so mode=0555
-file path=usr/lib/fm/fmd/schemes/$(ARCH64)/zfs.so mode=0555
-file path=usr/lib/fm/fmd/schemes/cpu.so mode=0555
-file path=usr/lib/fm/fmd/schemes/dev.so mode=0555
-file path=usr/lib/fm/fmd/schemes/fmd.so mode=0555
-file path=usr/lib/fm/fmd/schemes/hc.so mode=0555
-file path=usr/lib/fm/fmd/schemes/legacy-hc.so mode=0555
-file path=usr/lib/fm/fmd/schemes/mem.so mode=0555
-file path=usr/lib/fm/fmd/schemes/mod.so mode=0555
-file path=usr/lib/fm/fmd/schemes/pkg.so mode=0555
-file path=usr/lib/fm/fmd/schemes/svc.so mode=0555
-file path=usr/lib/fm/fmd/schemes/zfs.so mode=0555
-file path=usr/lib/fm/libdiagcode.so.1
-file path=usr/lib/fm/libdiskstatus.so.1
-file path=usr/lib/fm/libfmd_adm.so.1
-file path=usr/lib/fm/libfmd_agent.so.1
-file path=usr/lib/fm/libfmd_log.so.1
-file path=usr/lib/fm/libfmd_msg.so.1
-file path=usr/lib/fm/libfmd_snmp.so.1
-file path=usr/lib/fm/libfmevent.so.1
+#
+# fmri scheme support: all are common to both global and non-global zones
+#
+file path=usr/lib/fm/fmd/schemes/$(ARCH64)/cpu.so mode=0555 \
+    variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/fmd/schemes/$(ARCH64)/dev.so mode=0555 \
+    variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/fmd/schemes/$(ARCH64)/fmd.so mode=0555 \
+    variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/fmd/schemes/$(ARCH64)/hc.so mode=0555 \
+    variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/fmd/schemes/$(ARCH64)/legacy-hc.so mode=0555 \
+    variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/fmd/schemes/$(ARCH64)/mem.so mode=0555 \
+    variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/fmd/schemes/$(ARCH64)/mod.so mode=0555 \
+    variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/fmd/schemes/$(ARCH64)/pkg.so mode=0555 \
+    variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/fmd/schemes/$(ARCH64)/svc.so mode=0555 \
+    variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/fmd/schemes/$(ARCH64)/sw.so mode=0555 \
+    variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/fmd/schemes/$(ARCH64)/zfs.so mode=0555 \
+    variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/fmd/schemes/cpu.so mode=0555 \
+    variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/fmd/schemes/dev.so mode=0555 \
+    variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/fmd/schemes/fmd.so mode=0555 \
+    variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/fmd/schemes/hc.so mode=0555 \
+    variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/fmd/schemes/legacy-hc.so mode=0555 \
+    variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/fmd/schemes/mem.so mode=0555 \
+    variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/fmd/schemes/mod.so mode=0555 \
+    variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/fmd/schemes/pkg.so mode=0555 \
+    variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/fmd/schemes/svc.so mode=0555 \
+    variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/fmd/schemes/sw.so mode=0555 \
+    variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/fmd/schemes/zfs.so mode=0555 \
+    variant.opensolaris.zone=__NODEFAULT
+#
+# Libraries for usr/lib/fm; we include these in both global and non-global
+# contexts except for the ldom and seslog libraries.
+#
+#
+#	32-bit .so.1
+#
+file path=usr/lib/fm/libdiagcode.so.1 variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/libdiskstatus.so.1 variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/libfmd_adm.so.1 variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/libfmd_agent.so.1 variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/libfmd_log.so.1 variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/libfmd_msg.so.1 variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/libfmd_snmp.so.1 variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/libfmevent.so.1 variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/libfmnotify.so.1 variant.opensolaris.zone=__NODEFAULT
 $(sparc_ONLY)file path=usr/lib/fm/libldom.so.1
 $(sparc_ONLY)file path=usr/lib/fm/libmdesc.so.1
 file path=usr/lib/fm/libseslog.so.1
-file path=usr/lib/fm/libtopo.so.1
-file path=usr/lib/fm/llib-ldiagcode
-file path=usr/lib/fm/llib-ldiagcode.ln
-file path=usr/lib/fm/llib-ldiskstatus
-file path=usr/lib/fm/llib-ldiskstatus.ln
-file path=usr/lib/fm/llib-lfmd_adm
-file path=usr/lib/fm/llib-lfmd_adm.ln
-file path=usr/lib/fm/llib-lfmd_agent
-file path=usr/lib/fm/llib-lfmd_agent.ln
-file path=usr/lib/fm/llib-lfmd_log
-file path=usr/lib/fm/llib-lfmd_log.ln
-file path=usr/lib/fm/llib-lfmd_msg
-file path=usr/lib/fm/llib-lfmd_msg.ln
-file path=usr/lib/fm/llib-lfmd_snmp
-file path=usr/lib/fm/llib-lfmd_snmp.ln
-file path=usr/lib/fm/llib-lfmevent
-file path=usr/lib/fm/llib-lfmevent.ln
+file path=usr/lib/fm/libtopo.so.1 variant.opensolaris.zone=__NODEFAULT
+#
+#	32-bit lint libraries
+#
+file path=usr/lib/fm/llib-ldiagcode variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/llib-ldiagcode.ln variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/llib-ldiskstatus variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/llib-ldiskstatus.ln variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/llib-lfmd_adm variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/llib-lfmd_adm.ln variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/llib-lfmd_agent variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/llib-lfmd_agent.ln variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/llib-lfmd_log variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/llib-lfmd_log.ln variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/llib-lfmd_msg variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/llib-lfmd_msg.ln variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/llib-lfmd_snmp variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/llib-lfmd_snmp.ln variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/llib-lfmevent variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/llib-lfmevent.ln variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/llib-lfmnotify variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/llib-lfmnotify.ln variant.opensolaris.zone=__NODEFAULT
 $(sparc_ONLY)file path=usr/lib/fm/llib-lldom
 $(sparc_ONLY)file path=usr/lib/fm/llib-lldom.ln
 $(sparc_ONLY)file path=usr/lib/fm/llib-lmdesc
 $(sparc_ONLY)file path=usr/lib/fm/llib-lmdesc.ln
 file path=usr/lib/fm/llib-lseslog
 file path=usr/lib/fm/llib-lseslog.ln
-file path=usr/lib/fm/llib-ltopo
-file path=usr/lib/fm/llib-ltopo.ln
+file path=usr/lib/fm/llib-ltopo variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/fm/llib-ltopo.ln variant.opensolaris.zone=__NODEFAULT
+#
+# Topo xml maps are all global-zone only
+#
 file path=usr/lib/fm/topo/maps/xfp-hc-topology.xml mode=0444
 file path=usr/lib/fm/topo/plugins/disk.so mode=0555
 file path=usr/lib/fm/topo/plugins/fac_prov_ipmi.so mode=0555
 file path=usr/lib/fm/topo/plugins/ipmi.so mode=0555
 file path=usr/lib/fm/topo/plugins/ses.so mode=0555
 file path=usr/lib/fm/topo/plugins/xfp.so mode=0555
-$(i386_ONLY)file path=usr/lib/locale/C/LC_MESSAGES/AMD.mo mode=0444
-file path=usr/lib/locale/C/LC_MESSAGES/DISK.mo mode=0444
-file path=usr/lib/locale/C/LC_MESSAGES/FMD.mo mode=0444
-$(i386_ONLY)file path=usr/lib/locale/C/LC_MESSAGES/GMCA.mo mode=0444
-$(i386_ONLY)file path=usr/lib/locale/C/LC_MESSAGES/INTEL.mo mode=0444
-file path=usr/lib/locale/C/LC_MESSAGES/NXGE.mo mode=0444
-file path=usr/lib/locale/C/LC_MESSAGES/PCI.mo mode=0444
-file path=usr/lib/locale/C/LC_MESSAGES/PCIEX.mo mode=0444
-$(sparc_ONLY)file path=usr/lib/locale/C/LC_MESSAGES/SCF.mo mode=0444
-file path=usr/lib/locale/C/LC_MESSAGES/SENSOR.mo mode=0444
-file path=usr/lib/locale/C/LC_MESSAGES/STORAGE.mo mode=0444
-$(sparc_ONLY)file path=usr/lib/locale/C/LC_MESSAGES/SUN4.mo mode=0444
-$(sparc_ONLY)file path=usr/lib/locale/C/LC_MESSAGES/SUN4U.mo mode=0444
-$(sparc_ONLY)file path=usr/lib/locale/C/LC_MESSAGES/SUN4V.mo mode=0444
-file path=usr/lib/locale/C/LC_MESSAGES/SUNOS.mo mode=0444
-file path=usr/lib/locale/C/LC_MESSAGES/ZFS.mo mode=0444
+#
+# Dictionaries, whether they are hardware-specific or not, are
+# common to both global and non-global zones.
+#
+$(i386_ONLY)file path=usr/lib/locale/C/LC_MESSAGES/AMD.mo mode=0444 \
+    variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/locale/C/LC_MESSAGES/DISK.mo mode=0444 \
+    variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/locale/C/LC_MESSAGES/FMD.mo mode=0444 \
+    variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/locale/C/LC_MESSAGES/FMNOTIFY.mo mode=0444 \
+    variant.opensolaris.zone=__NODEFAULT
+$(i386_ONLY)file path=usr/lib/locale/C/LC_MESSAGES/GMCA.mo mode=0444 \
+    variant.opensolaris.zone=__NODEFAULT
+$(i386_ONLY)file path=usr/lib/locale/C/LC_MESSAGES/INTEL.mo mode=0444 \
+    variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/locale/C/LC_MESSAGES/NXGE.mo mode=0444 \
+    variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/locale/C/LC_MESSAGES/PCI.mo mode=0444 \
+    variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/locale/C/LC_MESSAGES/PCIEX.mo mode=0444 \
+    variant.opensolaris.zone=__NODEFAULT
+$(sparc_ONLY)file path=usr/lib/locale/C/LC_MESSAGES/SCF.mo mode=0444 \
+    variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/locale/C/LC_MESSAGES/SENSOR.mo mode=0444 \
+    variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/locale/C/LC_MESSAGES/STORAGE.mo mode=0444 \
+    variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/locale/C/LC_MESSAGES/SMF.mo mode=0444 \
+    variant.opensolaris.zone=__NODEFAULT
+$(sparc_ONLY)file path=usr/lib/locale/C/LC_MESSAGES/SUN4.mo mode=0444 \
+    variant.opensolaris.zone=__NODEFAULT
+$(sparc_ONLY)file path=usr/lib/locale/C/LC_MESSAGES/SUN4U.mo mode=0444 \
+    variant.opensolaris.zone=__NODEFAULT
+$(sparc_ONLY)file path=usr/lib/locale/C/LC_MESSAGES/SUN4V.mo mode=0444 \
+    variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/locale/C/LC_MESSAGES/SUNOS.mo mode=0444 \
+    variant.opensolaris.zone=__NODEFAULT
+file path=usr/lib/locale/C/LC_MESSAGES/ZFS.mo mode=0444 \
+    variant.opensolaris.zone=__NODEFAULT
+#
+# mdb support will automatically be included in both contexts;
+# we allow eft.so to be included despite not delivering
+# the eft plugin in non-global zones
+#
 file path=usr/lib/mdb/proc/eft.so group=sys mode=0555
 file path=usr/lib/mdb/proc/fmd.so group=sys mode=0555
+#
+# Platform-specific fmd plugins are global zone only
+#
 $(sparc_ONLY)file \
     path=usr/platform/SUNW,Netra-CP3060/lib/fm/fmd/plugins/etm.conf
 $(sparc_ONLY)file \
@@ -607,10 +831,20 @@
     mode=0555
 $(sparc_ONLY)file path=usr/platform/sun4v/lib/fm/topo/plugins/zambezi.so \
     mode=0555
-file path=usr/sbin/fmadm mode=0555
-file path=usr/sbin/fmdump mode=0555
-file path=usr/sbin/fmstat mode=0555
-file path=usr/share/lib/xml/dtd/topology.dtd.1
+#
+# Administrative utilities are common to both contexts
+#
+file path=usr/sbin/fmadm mode=0555 variant.opensolaris.zone=__NODEFAULT
+file path=usr/sbin/fmdump mode=0555 variant.opensolaris.zone=__NODEFAULT
+file path=usr/sbin/fmstat mode=0555 variant.opensolaris.zone=__NODEFAULT
+#
+# Topo DTD is also common
+#
+file path=usr/share/lib/xml/dtd/topology.dtd.1 \
+    variant.opensolaris.zone=__NODEFAULT
+#
+# legacy packaging and license
+#
 legacy pkg=SUNWfmd arch=$(ARCH) category=system \
     desc="Fault Management Daemon and Utilities" \
     hotline="Please contact your local service provider" \
@@ -623,30 +857,61 @@
     vendor="Sun Microsystems, Inc." version=11.11,REV=2009.11.11
 license cr_Sun license=cr_Sun
 license lic_CDDL license=lic_CDDL
-link path=usr/lib/fm/$(ARCH64)/libdiagcode.so target=./libdiagcode.so.1
-link path=usr/lib/fm/$(ARCH64)/libdiskstatus.so target=./libdiskstatus.so.1
-link path=usr/lib/fm/$(ARCH64)/libfmd_adm.so target=./libfmd_adm.so.1
-link path=usr/lib/fm/$(ARCH64)/libfmd_agent.so target=./libfmd_agent.so.1
-link path=usr/lib/fm/$(ARCH64)/libfmd_log.so target=./libfmd_log.so.1
-link path=usr/lib/fm/$(ARCH64)/libfmd_msg.so target=./libfmd_msg.so.1
-link path=usr/lib/fm/$(ARCH64)/libfmd_snmp.so target=./libfmd_snmp.so.1
-link path=usr/lib/fm/$(ARCH64)/libfmevent.so target=./libfmevent.so.1
+#
+#	64-bit .so symlinks
+#
+link path=usr/lib/fm/$(ARCH64)/libdiagcode.so target=./libdiagcode.so.1 \
+    variant.opensolaris.zone=__NODEFAULT
+link path=usr/lib/fm/$(ARCH64)/libdiskstatus.so target=./libdiskstatus.so.1 \
+    variant.opensolaris.zone=__NODEFAULT
+link path=usr/lib/fm/$(ARCH64)/libfmd_adm.so target=./libfmd_adm.so.1 \
+    variant.opensolaris.zone=__NODEFAULT
+link path=usr/lib/fm/$(ARCH64)/libfmd_agent.so target=./libfmd_agent.so.1 \
+    variant.opensolaris.zone=__NODEFAULT
+link path=usr/lib/fm/$(ARCH64)/libfmd_log.so target=./libfmd_log.so.1 \
+    variant.opensolaris.zone=__NODEFAULT
+link path=usr/lib/fm/$(ARCH64)/libfmd_msg.so target=./libfmd_msg.so.1 \
+    variant.opensolaris.zone=__NODEFAULT
+link path=usr/lib/fm/$(ARCH64)/libfmd_snmp.so target=./libfmd_snmp.so.1 \
+    variant.opensolaris.zone=__NODEFAULT
+link path=usr/lib/fm/$(ARCH64)/libfmevent.so target=./libfmevent.so.1 \
+    variant.opensolaris.zone=__NODEFAULT
+link path=usr/lib/fm/$(ARCH64)/libfmnotify.so target=./libfmnotify.so.1 \
+    variant.opensolaris.zone=__NODEFAULT
 $(sparc_ONLY)link path=usr/lib/fm/$(ARCH64)/libldom.so target=libldom.so.1
 $(sparc_ONLY)link path=usr/lib/fm/$(ARCH64)/libmdesc.so target=libmdesc.so.1
 link path=usr/lib/fm/$(ARCH64)/libseslog.so target=./libseslog.so.1
-link path=usr/lib/fm/$(ARCH64)/libtopo.so target=libtopo.so.1
-link path=usr/lib/fm/libdiagcode.so target=libdiagcode.so.1
-link path=usr/lib/fm/libdiskstatus.so target=libdiskstatus.so.1
-link path=usr/lib/fm/libfmd_adm.so target=libfmd_adm.so.1
-link path=usr/lib/fm/libfmd_agent.so target=libfmd_agent.so.1
-link path=usr/lib/fm/libfmd_log.so target=libfmd_log.so.1
-link path=usr/lib/fm/libfmd_msg.so target=libfmd_msg.so.1
-link path=usr/lib/fm/libfmd_snmp.so target=libfmd_snmp.so.1
-link path=usr/lib/fm/libfmevent.so target=libfmevent.so.1
+link path=usr/lib/fm/$(ARCH64)/libtopo.so target=libtopo.so.1 \
+    variant.opensolaris.zone=__NODEFAULT
+#
+#	32-bit .so symlinks
+#
+link path=usr/lib/fm/libdiagcode.so target=libdiagcode.so.1 \
+    variant.opensolaris.zone=__NODEFAULT
+link path=usr/lib/fm/libdiskstatus.so target=libdiskstatus.so.1 \
+    variant.opensolaris.zone=__NODEFAULT
+link path=usr/lib/fm/libfmd_adm.so target=libfmd_adm.so.1 \
+    variant.opensolaris.zone=__NODEFAULT
+link path=usr/lib/fm/libfmd_agent.so target=libfmd_agent.so.1 \
+    variant.opensolaris.zone=__NODEFAULT
+link path=usr/lib/fm/libfmd_log.so target=libfmd_log.so.1 \
+    variant.opensolaris.zone=__NODEFAULT
+link path=usr/lib/fm/libfmd_msg.so target=libfmd_msg.so.1 \
+    variant.opensolaris.zone=__NODEFAULT
+link path=usr/lib/fm/libfmd_snmp.so target=libfmd_snmp.so.1 \
+    variant.opensolaris.zone=__NODEFAULT
+link path=usr/lib/fm/libfmevent.so target=libfmevent.so.1 \
+    variant.opensolaris.zone=__NODEFAULT
+link path=usr/lib/fm/libfmnotify.so target=libfmnotify.so.1 \
+    variant.opensolaris.zone=__NODEFAULT
 $(sparc_ONLY)link path=usr/lib/fm/libldom.so target=libldom.so.1
 $(sparc_ONLY)link path=usr/lib/fm/libmdesc.so target=libmdesc.so.1
 link path=usr/lib/fm/libseslog.so target=libseslog.so.1
-link path=usr/lib/fm/libtopo.so target=libtopo.so.1
+link path=usr/lib/fm/libtopo.so target=libtopo.so.1 \
+    variant.opensolaris.zone=__NODEFAULT
+#
+# symlinks for fmd plugins for particular platforms
+#
 $(sparc_ONLY)link \
     path=usr/platform/SUNW,Netra-CP3060/lib/fm/fmd/plugins/etm.so \
     target=../../../../../sun4v/lib/fm/fmd/plugins/etm.so
--- a/usr/src/pkg/manifests/system-kernel.mf	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/pkg/manifests/system-kernel.mf	Fri Jul 30 17:04:17 2010 +1000
@@ -773,7 +773,6 @@
 $(i386_ONLY)file path=kernel/sys/semsys group=sys mode=0755
 $(i386_ONLY)file path=kernel/sys/shmsys group=sys mode=0755
 file path=lib/svc/manifest/system/dumpadm.xml group=sys mode=0444
-file path=lib/svc/manifest/system/fmd.xml group=sys mode=0444
 file path=lib/svc/manifest/system/intrd.xml group=sys mode=0444
 file path=lib/svc/manifest/system/scheduler.xml group=sys mode=0444
 file path=lib/svc/method/svc-dumpadm mode=0555
--- a/usr/src/uts/common/io/dump.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/uts/common/io/dump.c	Fri Jul 30 17:04:17 2010 +1000
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 
@@ -95,6 +94,8 @@
 	uint64_t dumpsize_in_pages;
 	int error = 0;
 	char *pathbuf = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
+	char uuidbuf[36 + 1];
+	size_t len;
 	vnode_t *vp;
 
 	switch (cmd) {
@@ -190,6 +191,23 @@
 		mutex_exit(&dump_lock);
 		break;
 
+	case DIOCSETUUID:
+		if ((error = copyinstr((char *)arg, uuidbuf, sizeof (uuidbuf),
+		    &len)) != 0)
+			break;
+
+		if (len != 37) {
+			error = EINVAL;
+			break;
+		}
+
+		error = dump_set_uuid(uuidbuf);
+		break;
+
+	case DIOCGETUUID:
+		error = copyoutstr(dump_get_uuid(), (void *)arg, 37, NULL);
+		break;
+
 	default:
 		error = ENXIO;
 	}
--- a/usr/src/uts/common/io/sysevent.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/uts/common/io/sysevent.c	Fri Jul 30 17:04:17 2010 +1000
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 
@@ -471,6 +470,116 @@
 	return (rc);
 }
 
+/* ARGSUSED */
+static int
+sysevent_setpropnvl(dev_t dev, int *rvalp, void *arg, int flag, cred_t *cr)
+{
+	sev_propnvl_args_t uargs;
+	nvlist_t *nvl = NULL;
+	evchan_ctl_t *ctl;
+	size_t bufsz;
+	char *buf;
+
+	ctl = ddi_get_soft_state(evchan_ctlp, getminor(dev));
+	if (ctl == NULL || ctl->chp == NULL)
+		return (ENXIO);
+
+	if (copyin(arg, &uargs, sizeof (uargs)) != 0)
+		return (EFAULT);
+
+	if (uargs.packednvl.name != 0) {
+		bufsz = uargs.packednvl.len;
+
+		if (bufsz == 0)
+			return (EINVAL);
+
+		if (bufsz > EVCH_MAX_DATA_SIZE)
+			return (EOVERFLOW);
+
+		buf = kmem_alloc(bufsz, KM_SLEEP);
+
+		if (copyin((void *)(uintptr_t)uargs.packednvl.name, buf,
+		    bufsz) != 0 ||
+		    nvlist_unpack(buf, bufsz, &nvl, KM_SLEEP) != 0) {
+			kmem_free(buf, bufsz);
+			return (EFAULT);
+		}
+
+		kmem_free(buf, bufsz);
+
+		if (nvl == NULL)
+			return (EINVAL);
+	}
+
+	evch_usrsetpropnvl(ctl->chp, nvl);
+	return (0);
+}
+
+/* ARGSUSED */
+static int
+sysevent_getpropnvl(dev_t dev, int *rvalp, void *arg, int flag, cred_t *cr)
+{
+	sev_propnvl_args_t uargs;
+	size_t reqsz, avlsz;
+	evchan_ctl_t *ctl;
+	nvlist_t *nvl;
+	int64_t gen;
+	int rc;
+
+	ctl = ddi_get_soft_state(evchan_ctlp, getminor(dev));
+
+	if (ctl == NULL || ctl->chp == NULL)
+		return (ENXIO);
+
+	if (copyin(arg, &uargs, sizeof (uargs)) != 0)
+		return (EFAULT);
+
+	if ((rc = evch_usrgetpropnvl(ctl->chp, &nvl, &gen)) != 0)
+		return (rc);
+
+	if (nvl != NULL) {
+		avlsz = uargs.packednvl.len;
+
+		if (nvlist_size(nvl, &reqsz, NV_ENCODE_NATIVE) != 0) {
+			nvlist_free(nvl);
+			return (EINVAL);
+		}
+
+		if (reqsz > EVCH_MAX_DATA_SIZE) {
+			nvlist_free(nvl);
+			return (E2BIG);
+		}
+
+		if (reqsz <= avlsz) {
+			char *buf = kmem_alloc(reqsz, KM_SLEEP);
+
+			if (nvlist_pack(nvl, &buf, &reqsz,
+			    NV_ENCODE_NATIVE, 0) != 0 || copyout(buf,
+			    (void *)(uintptr_t)uargs.packednvl.name,
+			    reqsz) != 0) {
+				kmem_free(buf, reqsz);
+				nvlist_free(nvl);
+				return (EFAULT);
+			}
+			kmem_free(buf, reqsz);
+			rc = 0;
+		} else {
+			rc = EOVERFLOW;
+		}
+		uargs.packednvl.len = (uint32_t)reqsz;
+		nvlist_free(nvl);
+	} else {
+		uargs.packednvl.len = 0;
+		rc = 0;
+	}
+
+	uargs.generation = gen;
+	if (copyout((void *)&uargs, arg, sizeof (uargs)) != 0)
+		rc = EFAULT;
+
+	return (rc);
+}
+
 /*ARGSUSED*/
 static int
 sysevent_ioctl(dev_t dev, int cmd, intptr_t arg,
@@ -500,6 +609,12 @@
 	case SEV_CHANDATA:
 		rc = sysevent_chandata(dev, rvalp, (void *)arg, flag, cr);
 		break;
+	case SEV_SETPROPNVL:
+		rc = sysevent_setpropnvl(dev, rvalp, (void *)arg, flag, cr);
+		break;
+	case SEV_GETPROPNVL:
+		rc = sysevent_getpropnvl(dev, rvalp, (void *)arg, flag, cr);
+		break;
 	default:
 		rc = EINVAL;
 	}
--- a/usr/src/uts/common/os/dumpsubr.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/uts/common/os/dumpsubr.c	Fri Jul 30 17:04:17 2010 +1000
@@ -107,6 +107,7 @@
 int		dump_timeleft;		/* portion of dump_timeout remaining */
 int		dump_ioerr;		/* dump i/o error */
 int		dump_check_used;	/* enable check for used pages */
+char	    *dump_stack_scratch; /* scratch area for saving stack summary */
 
 /*
  * Tunables for dump compression and parallelism. These can be set via
@@ -258,6 +259,12 @@
 	int off;			/* byte offset to first pfn */
 };
 
+static char dump_osimage_uuid[36 + 1];
+
+#define	isdigit(ch)	((ch) >= '0' && (ch) <= '9')
+#define	isxdigit(ch)	(isdigit(ch) || ((ch) >= 'a' && (ch) <= 'f') || \
+			((ch) >= 'A' && (ch) <= 'F'))
+
 /*
  * cqueue_t queues: a uni-directional channel for communication
  * from the master to helper tasks or vice-versa using put and
@@ -1103,6 +1110,9 @@
 		dumpcfg.pids = kmem_alloc(v.v_proc * sizeof (pid_t), KM_SLEEP);
 		dumpcfg.helpermap = kmem_zalloc(BT_SIZEOFMAP(NCPU), KM_SLEEP);
 		LOCK_INIT_HELD(&dumpcfg.helper_lock);
+		dump_stack_scratch = kmem_alloc(STACK_BUF_SIZE, KM_SLEEP);
+		(void) strncpy(dumphdr->dump_uuid, dump_get_uuid(),
+		    sizeof (dumphdr->dump_uuid));
 	}
 
 	npages = num_phys_pages();
@@ -1436,6 +1446,48 @@
 	return (0);
 }
 
+/*
+ * The following functions (dump_summary(), dump_ereports(), and
+ * dump_messages()), write data to an uncompressed area within the
+ * crashdump. The layout of these is
+ *
+ * +------------------------------------------------------------+
+ * |     compressed pages       | summary | ereports | messages |
+ * +------------------------------------------------------------+
+ *
+ * With the advent of saving a compressed crash dump by default, we
+ * need to save a little more data to describe the failure mode in
+ * an uncompressed buffer available before savecore uncompresses
+ * the dump. Initially this is a copy of the stack trace. Additional
+ * summary information should be added here.
+ */
+
+void
+dump_summary(void)
+{
+	u_offset_t dumpvp_start;
+	summary_dump_t sd;
+
+	if (dumpvp == NULL || dumphdr == NULL)
+		return;
+
+	dumpbuf.cur = dumpbuf.start;
+
+	dumpbuf.vp_limit = dumpvp_size - (DUMP_OFFSET + DUMP_LOGSIZE +
+	    DUMP_ERPTSIZE);
+	dumpvp_start = dumpbuf.vp_limit - DUMP_SUMMARYSIZE;
+	dumpbuf.vp_off = dumpvp_start;
+
+	sd.sd_magic = SUMMARY_MAGIC;
+	sd.sd_ssum = checksum32(dump_stack_scratch, STACK_BUF_SIZE);
+	dumpvp_write(&sd, sizeof (sd));
+	dumpvp_write(dump_stack_scratch, STACK_BUF_SIZE);
+
+	sd.sd_magic = 0; /* indicate end of summary */
+	dumpvp_write(&sd, sizeof (sd));
+	(void) dumpvp_flush();
+}
+
 void
 dump_ereports(void)
 {
@@ -2859,6 +2911,10 @@
 		size -= datahdr.dump_metrics;
 	}
 
+	/* record in the header whether this is a fault-management panic */
+	if (panicstr)
+		dumphdr->dump_fm_panic = is_fm_panic();
+
 	/* compression info in data header */
 	datahdr.dump_datahdr_magic = DUMP_DATAHDR_MAGIC;
 	datahdr.dump_datahdr_version = DUMP_DATAHDR_VERSION;
@@ -2905,6 +2961,7 @@
 	 * thing we do because the dump process itself emits messages.
 	 */
 	if (panicstr) {
+		dump_summary();
 		dump_ereports();
 		dump_messages();
 	}
@@ -2967,3 +3024,47 @@
 	mutex_exit(&dump_lock);
 	return (0);
 }
+
+int
+dump_set_uuid(const char *uuidstr)
+{
+	const char *ptr;
+	int i;
+
+	if (uuidstr == NULL || strnlen(uuidstr, 36 + 1) != 36)
+		return (EINVAL);
+
+	/* uuid_parse is not common code so check manually */
+	for (i = 0, ptr = uuidstr; i < 36; i++, ptr++) {
+		switch (i) {
+		case 8:
+		case 13:
+		case 18:
+		case 23:
+			if (*ptr != '-')
+				return (EINVAL);
+			break;
+
+		default:
+			if (!isxdigit(*ptr))
+				return (EINVAL);
+			break;
+		}
+	}
+
+	if (dump_osimage_uuid[0] != '\0')
+		return (EALREADY);
+
+	(void) strncpy(dump_osimage_uuid, uuidstr, 36 + 1);
+
+	cmn_err(CE_CONT, "?This Solaris instance has UUID %s",
+	    dump_osimage_uuid);
+
+	return (0);
+}
+
+const char *
+dump_get_uuid(void)
+{
+	return (dump_osimage_uuid[0] != '\0' ? dump_osimage_uuid : "");
+}
--- a/usr/src/uts/common/os/evchannels.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/uts/common/os/evchannels.c	Fri Jul 30 17:04:17 2010 +1000
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 /*
@@ -1165,6 +1164,8 @@
 		mutex_exit(&chp->ch_mutex);
 		evch_dl_del(&eg->evch_list, &chp->ch_link);
 		evch_evq_destroy(chp->ch_queue);
+		if (chp->ch_propnvl)
+			nvlist_free(chp->ch_propnvl);
 		mutex_destroy(&chp->ch_mutex);
 		mutex_destroy(&chp->ch_pubmx);
 		cv_destroy(&chp->ch_pubcv);
@@ -1564,6 +1565,47 @@
 	return (chdlen + buflen);
 }
 
+static void
+evch_chsetpropnvl(evch_bind_t *bp, nvlist_t *nvl)
+{
+	evch_chan_t *chp = bp->bd_channel;
+
+	mutex_enter(&chp->ch_mutex);
+
+	if (chp->ch_propnvl)
+		nvlist_free(chp->ch_propnvl);
+
+	chp->ch_propnvl = nvl;
+	chp->ch_propnvlgen++;
+
+	mutex_exit(&chp->ch_mutex);
+}
+
+static int
+evch_chgetpropnvl(evch_bind_t *bp, nvlist_t **nvlp, int64_t *genp)
+{
+	evch_chan_t *chp = bp->bd_channel;
+	int rc = 0;
+
+	mutex_enter(&chp->ch_mutex);
+
+	if (chp->ch_propnvl != NULL)
+		rc = (nvlist_dup(chp->ch_propnvl, nvlp, 0) == 0) ? 0 : ENOMEM;
+	else
+		*nvlp = NULL;	/* rc still 0 */
+
+	if (genp)
+		*genp = chp->ch_propnvlgen;
+
+	mutex_exit(&chp->ch_mutex);
+
+	if (rc != 0)
+		*nvlp = NULL;
+
+	return (rc);
+
+}
+
 /*
  * Init iteration of all events of a channel. This function creates a new
  * event queue and puts all events from the channel into that queue.
@@ -1718,6 +1760,8 @@
  * sysevent_evc_unsubscribe - Unsubscribe from an event class
  * sysevent_evc_publish	    - Publish an event to an event channel
  * sysevent_evc_control	    - Various control operation on event channel
+ * sysevent_evc_setpropnvl  - Set channel property nvlist
+ * sysevent_evc_getpropnvl  - Get channel property nvlist
  *
  * The function below are for evaluating a sysevent:
  *
@@ -1971,6 +2015,25 @@
 	return (rc);
 }
 
+int
+sysevent_evc_setpropnvl(evchan_t *scp, nvlist_t *nvl)
+{
+	nvlist_t *nvlcp = nvl;
+
+	if (nvl != NULL && nvlist_dup(nvl, &nvlcp, 0) != 0)
+		return (ENOMEM);
+
+	evch_chsetpropnvl((evch_bind_t *)scp, nvlcp);
+
+	return (0);
+}
+
+int
+sysevent_evc_getpropnvl(evchan_t *scp, nvlist_t **nvlp)
+{
+	return (evch_chgetpropnvl((evch_bind_t *)scp, nvlp, NULL));
+}
+
 /*
  * Project private interface to take a snapshot of all events of the
  * specified event channel. Argument subscr may be a subscriber id, the empty
@@ -2130,6 +2193,8 @@
  * evch_usrcontrol_get	- Get channel properties
  * evch_usrgetchnames	- Get list of channel names
  * evch_usrgetchdata	- Get data of an event channel
+ * evch_usrsetpropnvl	- Set channel properties nvlist
+ * evch_usrgetpropnvl	- Get channel properties nvlist
  */
 evchan_t *
 evch_usrchanopen(const char *name, uint32_t flags, int *err)
@@ -2267,3 +2332,15 @@
 {
 	return (evch_chgetchdata(chname, buf, size));
 }
+
+void
+evch_usrsetpropnvl(evchan_t *bp, nvlist_t *nvl)
+{
+	evch_chsetpropnvl((evch_bind_t *)bp, nvl);
+}
+
+int
+evch_usrgetpropnvl(evchan_t *bp, nvlist_t **nvlp, int64_t *genp)
+{
+	return (evch_chgetpropnvl((evch_bind_t *)bp, nvlp, genp));
+}
--- a/usr/src/uts/common/os/fm.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/uts/common/os/fm.c	Fri Jul 30 17:04:17 2010 +1000
@@ -384,6 +384,20 @@
 }
 
 /*
+ * Simply tell the caller if fm_panicstr is set, ie. an fma event has
+ * caused the panic. If so, something other than the default panic
+ * diagnosis method will diagnose the cause of the panic.
+ */
+int
+is_fm_panic()
+{
+	if (fm_panicstr)
+		return (1);
+	else
+		return (0);
+}
+
+/*
  * Print any appropriate FMA banner message before the panic message.  This
  * function is called by panicsys() and prints the message for fm_panic().
  * We print the message here so that it comes after the system is quiesced.
--- a/usr/src/uts/common/os/log_sysevent.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/uts/common/os/log_sysevent.c	Fri Jul 30 17:04:17 2010 +1000
@@ -19,12 +19,9 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <sys/types.h>
 #include <sys/errno.h>
 #include <sys/stropts.h>
@@ -280,11 +277,11 @@
 			}
 			if (retry > 4) {
 				LOG_DEBUG((CE_CONT,
-					"log_event_upcall: ebadf\n"));
+				    "log_event_upcall: ebadf\n"));
 				return (EBADF);
 			}
 			LOG_DEBUG((CE_CONT, "log_event_upcall: "
-				"retrying upcall after lookup\n"));
+			    "retrying upcall after lookup\n"));
 			darg = save_arg;
 			break;
 		default:
@@ -297,14 +294,14 @@
 
 	if (neagain > 0 || neintr > 0) {
 		LOG_DEBUG((CE_CONT, "upcall: eagain=%d eintr=%d nticks=%d\n",
-			neagain, neintr, nticks));
+		    neagain, neintr, nticks));
 	}
 
 	LOG_DEBUG1((CE_CONT, "log_event_upcall:\n\t"
-		"error=%d rptr1=%p rptr2=%p dptr2=%p ret1=%x ret2=%x\n",
-		error, (void *)arg, (void *)darg.rbuf,
-		(void *)darg.data_ptr,
-		*((int *)(darg.rbuf)), *((int *)(darg.data_ptr))));
+	    "error=%d rptr1=%p rptr2=%p dptr2=%p ret1=%x ret2=%x\n",
+	    error, (void *)arg, (void *)darg.rbuf,
+	    (void *)darg.data_ptr,
+	    *((int *)(darg.rbuf)), *((int *)(darg.data_ptr))));
 
 	if (!error) {
 		/*
@@ -334,7 +331,7 @@
 	callb_cpr_t cprinfo;
 
 	CALLB_CPR_INIT(&cprinfo, &eventq_head_mutex, callb_generic_cpr,
-				"logevent");
+	    "logevent");
 
 	/*
 	 * eventq_head_mutex is exited (released) when there are no more
@@ -440,7 +437,7 @@
 			break;
 		default:
 			LOG_DEBUG((CE_CONT, "log_event_deliver: "
-				"upcall err %d\n", upcall_err));
+			    "upcall err %d\n", upcall_err));
 			sysevent_upcall_status = upcall_err;
 			/*
 			 * Signal everyone waiting that transport is down
@@ -521,8 +518,8 @@
 	aligned_pub_sz = SE_ALIGN(pub_sz);
 
 	payload_sz = (aligned_class_sz - sizeof (uint64_t)) +
-		(aligned_subclass_sz - sizeof (uint64_t)) +
-		(aligned_pub_sz - sizeof (uint64_t)) - sizeof (uint64_t);
+	    (aligned_subclass_sz - sizeof (uint64_t)) +
+	    (aligned_pub_sz - sizeof (uint64_t)) - sizeof (uint64_t);
 
 	/*
 	 * Allocate event buffer plus additional sysevent queue
@@ -1611,7 +1608,7 @@
 	/* Unbind old event door */
 	if (logevent_door_upcall_filename) {
 		kmem_free(logevent_door_upcall_filename,
-			logevent_door_upcall_filename_size);
+		    logevent_door_upcall_filename_size);
 		if (event_door) {
 			door_ki_rele(event_door);
 			event_door = NULL;
@@ -1619,7 +1616,7 @@
 	}
 	logevent_door_upcall_filename_size = strlen(file) + 1;
 	logevent_door_upcall_filename = kmem_alloc(
-		logevent_door_upcall_filename_size, KM_SLEEP);
+	    logevent_door_upcall_filename_size, KM_SLEEP);
 	(void) strcpy(logevent_door_upcall_filename, file);
 
 	/*
@@ -1698,7 +1695,7 @@
 
 	/* Time stamp and assign ID */
 	SE_SEQ(ev) = eid->eid_seq = atomic_add_64_nv(&kernel_event_id,
-		(uint64_t)1);
+	    (uint64_t)1);
 	SE_TIME(ev) = eid->eid_ts = gethrtime();
 
 	LOG_DEBUG1((CE_CONT, "log_sysevent: class=%d type=%d id=0x%llx\n",
@@ -1756,7 +1753,7 @@
 	}
 	rval = queue_sysevent(ev_copy, eid, flag);
 	ASSERT(rval == 0 || rval == SE_ENOMEM || rval == SE_EQSIZE ||
-		rval == SE_NO_TRANSPORT);
+	    rval == SE_NO_TRANSPORT);
 	ASSERT(!(flag == SE_SLEEP && (rval == SE_EQSIZE || rval == SE_ENOMEM)));
 	return (rval);
 }
@@ -1775,7 +1772,7 @@
 	log_eventq_t *qcopy;
 
 	copy_sz = ev_size + offsetof(log_eventq_t, arg) +
-		offsetof(log_event_upcall_arg_t, buf);
+	    offsetof(log_event_upcall_arg_t, buf);
 	qcopy = kmem_zalloc(copy_sz, KM_SLEEP);
 	ev_copy = (sysevent_t *)&qcopy->arg.buf;
 
@@ -1825,8 +1822,8 @@
 
 	if (sleep_flag == DDI_SLEEP && servicing_interrupt()) {
 		cmn_err(CE_NOTE, "!ddi_log_syevent: driver %s%d - cannot queue "
-			"event from interrupt context with sleep semantics\n",
-			ddi_driver_name(dip), ddi_get_instance(dip));
+		    "event from interrupt context with sleep semantics\n",
+		    ddi_driver_name(dip), ddi_get_instance(dip));
 		return (DDI_ECONTEXT);
 	}
 
@@ -1836,7 +1833,7 @@
 		publisher = pubstr;
 	} else {
 		publisher = kmem_alloc(n,
-			(sleep_flag == DDI_SLEEP) ? KM_SLEEP : KM_NOSLEEP);
+		    (sleep_flag == DDI_SLEEP) ? KM_SLEEP : KM_NOSLEEP);
 		if (publisher == NULL) {
 			return (DDI_ENOMEM);
 		}
@@ -1880,7 +1877,7 @@
 }
 
 uint64_t
-log_sysevent_new_id()
+log_sysevent_new_id(void)
 {
 	return (atomic_add_64_nv(&kernel_event_id, (uint64_t)1));
 }
--- a/usr/src/uts/common/os/panic.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/uts/common/os/panic.c	Fri Jul 30 17:04:17 2010 +1000
@@ -256,6 +256,9 @@
 		pdp->pd_version = PANICBUFVERS;
 		pdp->pd_msgoff = sizeof (panic_data_t) - sizeof (panic_nv_t);
 
+		(void) strncpy(pdp->pd_uuid, dump_get_uuid(),
+		    sizeof (pdp->pd_uuid));
+
 		if (t->t_panic_trap != NULL)
 			panic_savetrap(pdp, t->t_panic_trap);
 		else
--- a/usr/src/uts/common/sys/dumpadm.h	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/uts/common/sys/dumpadm.h	Fri Jul 30 17:04:17 2010 +1000
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * 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.
@@ -20,15 +19,12 @@
  * CDDL HEADER END
  */
 /*
- * Copyright (c) 1998-2000 by Sun Microsystems, Inc.
- * All rights reserved.
+ * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #ifndef	_SYS_DUMPADM_H
 #define	_SYS_DUMPADM_H
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #ifdef	__cplusplus
 extern "C" {
 #endif
@@ -44,6 +40,8 @@
 #define	DIOCSETDEV	(DDIOC | 0x14)
 #define	DIOCTRYDEV	(DDIOC | 0x15)
 #define	DIOCDUMP	(DDIOC | 0x16)
+#define	DIOCSETUUID	(DDIOC | 0x17)
+#define	DIOCGETUUID	(DDIOC | 0x18)
 
 /*
  * Kernel-controlled dump state flags for dump_conflags
--- a/usr/src/uts/common/sys/dumphdr.h	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/uts/common/sys/dumphdr.h	Fri Jul 30 17:04:17 2010 +1000
@@ -43,7 +43,7 @@
  * overwritten by swap activity.  See dumpadm(1M) for dump configuration.
  */
 #define	DUMP_MAGIC	0xdefec8edU		/* dump magic number */
-#define	DUMP_VERSION	9			/* version of this dumphdr */
+#define	DUMP_VERSION	10			/* version of this dumphdr */
 #define	DUMP_WORDSIZE	(sizeof (long) * NBBY)	/* word size (32 or 64) */
 #define	DUMP_PANICSIZE	200			/* Max panic string copied */
 #define	DUMP_COMPRESS_RATIO	2		/* conservative; usually 2.5+ */
@@ -54,6 +54,10 @@
 	(ERPT_EVCH_MAX +		\
 	ERPT_MAX_ERRS * ERPT_HIWAT),	\
 	DUMP_OFFSET))				/* ereport save area */
+#define	DUMP_SUMMARYSIZE (P2ROUNDUP(    \
+	(STACK_BUF_SIZE +	       \
+	sizeof (summary_dump_t) + 1024), \
+	DUMP_OFFSET))				/* summary save area */
 
 typedef struct dumphdr {
 	uint32_t dump_magic;		/* magic number */
@@ -76,6 +80,8 @@
 	pgcnt_t	dump_npages;		/* number of data pages */
 	size_t	dump_ksyms_size;	/* kernel symbol table size */
 	size_t	dump_ksyms_csize;	/* compressed symbol table size */
+	uint32_t dump_fm_panic;		/* initiated from fm subsystems */
+	char	dump_uuid[36 + 1];	/* os image uuid */
 } dumphdr_t;
 
 /*
@@ -190,6 +196,8 @@
 extern int dump_plat_addr(void);
 extern void dump_plat_pfn(void);
 extern int dump_plat_data(void *);
+extern int dump_set_uuid(const char *);
+extern const char *dump_get_uuid(void);
 
 /*
  * Define a CPU count threshold that determines when to employ
--- a/usr/src/uts/common/sys/fm/protocol.h	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/uts/common/sys/fm/protocol.h	Fri Jul 30 17:04:17 2010 +1000
@@ -43,12 +43,13 @@
 #define	FM_CLASS			"class"
 #define	FM_VERSION			"version"
 
-/* FM event class values */
+/* FM protocol category 1 class names */
 #define	FM_EREPORT_CLASS		"ereport"
 #define	FM_FAULT_CLASS			"fault"
 #define	FM_DEFECT_CLASS			"defect"
 #define	FM_RSRC_CLASS			"resource"
 #define	FM_LIST_EVENT			"list"
+#define	FM_IREPORT_CLASS		"ireport"
 
 /* FM list.* event class values */
 #define	FM_LIST_SUSPECT_CLASS		FM_LIST_EVENT ".suspect"
@@ -72,6 +73,12 @@
 /* list.* event payload member names */
 #define	FM_LIST_EVENT_SIZE		"list-sz"
 
+/* ireport.* event payload member names */
+#define	FM_IREPORT_DETECTOR		"detector"
+#define	FM_IREPORT_UUID			"uuid"
+#define	FM_IREPORT_PRIORITY		"pri"
+#define	FM_IREPORT_ATTRIBUTES		"attr"
+
 /*
  * list.suspect, isolated, updated, repaired and resolved
  * versions/payload member names.
@@ -192,6 +199,7 @@
 #define	FM_FMRI_SCHEME_PKG		"pkg"
 #define	FM_FMRI_SCHEME_LEGACY		"legacy-hc"
 #define	FM_FMRI_SCHEME_ZFS		"zfs"
+#define	FM_FMRI_SCHEME_SW		"sw"
 
 /* Scheme versions */
 #define	FMD_SCHEME_VERSION0		0
@@ -215,6 +223,8 @@
 #define	FM_SVC_SCHEME_VERSION		SVC_SCHEME_VERSION0
 #define	ZFS_SCHEME_VERSION0		0
 #define	FM_ZFS_SCHEME_VERSION		ZFS_SCHEME_VERSION0
+#define	SW_SCHEME_VERSION0		0
+#define	FM_SW_SCHEME_VERSION		SW_SCHEME_VERSION0
 
 /* hc scheme member names */
 #define	FM_FMRI_HC_SERIAL_ID		"serial"
@@ -299,6 +309,25 @@
 #define	FM_FMRI_ZFS_POOL		"pool"
 #define	FM_FMRI_ZFS_VDEV		"vdev"
 
+/* sw scheme member names - extra indentation for members of an nvlist */
+#define	FM_FMRI_SW_OBJ			"object"
+#define	FM_FMRI_SW_OBJ_PATH			"path"
+#define	FM_FMRI_SW_OBJ_ROOT			"root"
+#define	FM_FMRI_SW_OBJ_PKG			"pkg"
+#define	FM_FMRI_SW_SITE			"site"
+#define	FM_FMRI_SW_SITE_TOKEN			"token"
+#define	FM_FMRI_SW_SITE_MODULE			"module"
+#define	FM_FMRI_SW_SITE_FILE			"file"
+#define	FM_FMRI_SW_SITE_LINE			"line"
+#define	FM_FMRI_SW_SITE_FUNC			"func"
+#define	FM_FMRI_SW_CTXT			"context"
+#define	FM_FMRI_SW_CTXT_ORIGIN			"origin"
+#define	FM_FMRI_SW_CTXT_EXECNAME		"execname"
+#define	FM_FMRI_SW_CTXT_PID			"pid"
+#define	FM_FMRI_SW_CTXT_ZONE			"zone"
+#define	FM_FMRI_SW_CTXT_CTID			"ctid"
+#define	FM_FMRI_SW_CTXT_STACK			"stack"
+
 extern nv_alloc_t *fm_nva_xcreate(char *, size_t);
 extern void fm_nva_xdestroy(nv_alloc_t *);
 
--- a/usr/src/uts/common/sys/fm/util.h	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/uts/common/sys/fm/util.h	Fri Jul 30 17:04:17 2010 +1000
@@ -20,15 +20,12 @@
  */
 
 /*
- * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #ifndef	_SYS_FM_UTIL_H
 #define	_SYS_FM_UTIL_H
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #ifdef	__cplusplus
 extern "C" {
 #endif
@@ -96,6 +93,7 @@
 
 extern void fm_payload_stack_add(nvlist_t *, const pc_t *, int);
 
+extern int is_fm_panic();
 #endif  /* _KERNEL */
 
 #ifdef	__cplusplus
--- a/usr/src/uts/common/sys/nvpair.h	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/uts/common/sys/nvpair.h	Fri Jul 30 17:04:17 2010 +1000
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #ifndef	_SYS_NVPAIR_H
@@ -158,6 +157,8 @@
 int nvlist_dup(nvlist_t *, nvlist_t **, int);
 int nvlist_merge(nvlist_t *, nvlist_t *, int);
 
+uint_t nvlist_nvflag(nvlist_t *);
+
 int nvlist_xalloc(nvlist_t **, uint_t, nv_alloc_t *);
 int nvlist_xpack(nvlist_t *, char **, size_t *, int, nv_alloc_t *);
 int nvlist_xunpack(char *, size_t, nvlist_t **, nv_alloc_t *);
--- a/usr/src/uts/common/sys/panic.h	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/uts/common/sys/panic.h	Fri Jul 30 17:04:17 2010 +1000
@@ -19,15 +19,12 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #ifndef	_SYS_PANIC_H
 #define	_SYS_PANIC_H
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #if !defined(_ASM)
 #include <sys/types.h>
 #include <sys/thread.h>
@@ -45,10 +42,13 @@
 #endif
 
 #define	PANICBUFSIZE	8192
-#define	PANICBUFVERS	1
+#define	PANICBUFVERS	2
 
 #define	PANICNVNAMELEN	16
 
+#define	STACK_BUF_SIZE	2048
+#define	SUMMARY_MAGIC	0xdead0d8a
+
 /*
  * Panicbuf Format:
  *
@@ -80,9 +80,19 @@
 typedef struct panic_data {
 	uint32_t pd_version;		/* Version number of panic_data_t */
 	uint32_t pd_msgoff;		/* Message byte offset in panicbuf */
+	char pd_uuid[36 + 1];		/* image uuid */
 	panic_nv_t pd_nvdata[1];	/* Array of named data */
 } panic_data_t;
 
+typedef struct summary_dump {
+	uint32_t sd_magic;		/* magic number */
+	uint32_t sd_ssum;		/* checsksum32(stack buffer) */
+	/*
+	 * stack buffer and other summary data follow here -- see
+	 * dump_summary()
+	 */
+} summary_dump_t;
+
 #if defined(_KERNEL)
 
 /*
--- a/usr/src/uts/common/sys/sysevent.h	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/uts/common/sys/sysevent.h	Fri Jul 30 17:04:17 2010 +1000
@@ -20,8 +20,7 @@
  */
 
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #ifndef	_SYS_SYSEVENT_H
@@ -226,6 +225,8 @@
 extern int sysevent_evc_publish(evchan_t *, const char *, const char *,
     const char *, const char *, nvlist_t *, uint32_t);
 extern int sysevent_evc_control(evchan_t *, int, ...);
+extern int sysevent_evc_setpropnvl(evchan_t *, nvlist_t *);
+extern int sysevent_evc_getpropnvl(evchan_t *, nvlist_t **);
 
 #ifndef	_KERNEL
 
--- a/usr/src/uts/common/sys/sysevent_impl.h	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/uts/common/sys/sysevent_impl.h	Fri Jul 30 17:04:17 2010 +1000
@@ -18,9 +18,9 @@
  *
  * CDDL HEADER END
  */
+
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #ifndef	_SYS_SYSEVENT_IMPL_H
@@ -76,7 +76,7 @@
 
 /*
  * sysevent event header information -
- * 	contained in every event generated.  The header and the event
+ *	contained in every event generated.  The header and the event
  *	must remain 64-bit aligned.  The header, up to the attribute
  *	offset, can be contained in a single cache line.
  */
@@ -98,7 +98,7 @@
 	se_data_t	se_class_name;	/* class string in contig memory */
 	se_data_t	se_subclass_name; /* subclass string in contig memory */
 	se_data_t	se_pub;		/* publisher string in contig mem */
-	se_data_t 	se_attr_buf;	/* contiguous attribute memory	*/
+	se_data_t	se_attr_buf;	/* contiguous attribute memory	*/
 } sysevent_impl_t;
 
 /* Helpful defines */
@@ -237,14 +237,14 @@
 /*
  * log_sysevent private interfaces
  */
-void log_event_init(void);
-void log_sysevent_flushq(int cmd, uint_t flag);
-int log_sysevent_filename(char *file);
-int log_usr_sysevent(sysevent_t *ev, int ev_size, sysevent_id_t *eid);
-int log_sysevent_copyout_data(sysevent_id_t *eid, size_t ubuflen, caddr_t ubuf);
-int log_sysevent_free_data(sysevent_id_t *eid);
-int log_sysevent_register(char *channel_name, char *data, se_pubsub_t *udata);
-uint64_t log_sysevent_new_id();
+extern void log_event_init(void);
+extern void log_sysevent_flushq(int, uint_t);
+extern int log_sysevent_filename(char *);
+extern int log_usr_sysevent(sysevent_t *, int, sysevent_id_t *);
+extern int log_sysevent_copyout_data(sysevent_id_t *, size_t, caddr_t);
+extern int log_sysevent_free_data(sysevent_id_t *);
+extern int log_sysevent_register(char *, char *, se_pubsub_t *);
+extern uint64_t log_sysevent_new_id(void);
 
 /*
  * Structures and definitions for general purpose event channels
@@ -281,8 +281,8 @@
 typedef struct {
 	evch_qelem_t	*sq_head;
 	evch_qelem_t	*sq_tail;
-	uint32_t 	sq_count;
-	uint32_t 	sq_highwm;
+	uint32_t	sq_count;
+	uint32_t	sq_highwm;
 } evch_squeue_t;
 
 /*
@@ -394,6 +394,8 @@
 	int		ch_maxsubscr;	/* Maximum number of subscriptions */
 	int		ch_holdpend;	/* Hold pending events mode if != 0 */
 	time_t		ch_ctime;	/* Channel creation time */
+	nvlist_t	*ch_propnvl;	/* Channel properties nvlist */
+	int64_t		ch_propnvlgen;	/* Properties generation number */
 } evch_chan_t;
 
 /*
@@ -413,26 +415,28 @@
 	sysevent_impl_t	*sn_nxtev;	/* Pointer to find next event */
 } evchanq_t;
 
-/* Project privat interfaces */
-evchan_t *evch_usrchanopen(const char *name, uint32_t flags, int *err);
-void evch_usrchanclose(evchan_t *cbp);
-sysevent_impl_t *evch_usrallocev(size_t evsize, uint32_t flags);
-void evch_usrfreeev(sysevent_impl_t *ev);
-int evch_usrpostevent(evchan_t *bp, sysevent_impl_t *ev, uint32_t flags);
-int evch_usrsubscribe(evchan_t *bp, const char *sid, const char *class,
+/* Project private interfaces */
+extern evchan_t *evch_usrchanopen(const char *name, uint32_t flags, int *err);
+extern void evch_usrchanclose(evchan_t *cbp);
+extern sysevent_impl_t *evch_usrallocev(size_t evsize, uint32_t flags);
+extern void evch_usrfreeev(sysevent_impl_t *ev);
+extern int evch_usrpostevent(evchan_t *bp, sysevent_impl_t *ev, uint32_t flags);
+extern int evch_usrsubscribe(evchan_t *bp, const char *sid, const char *class,
     int d, uint32_t flags);
-int evch_usrcontrol_set(evchan_t *bp, int cmd, uint32_t value);
-int evch_usrcontrol_get(evchan_t *bp, int cmd, uint32_t *value);
-void evch_usrunsubscribe(evchan_t *bp, const char *subid, uint32_t flag);
-int evch_usrgetchnames(char *buf, size_t size);
-int evch_usrgetchdata(char *chname, void *buf, size_t size);
+extern int evch_usrcontrol_set(evchan_t *bp, int cmd, uint32_t value);
+extern int evch_usrcontrol_get(evchan_t *bp, int cmd, uint32_t *value);
+extern void evch_usrunsubscribe(evchan_t *bp, const char *subid, uint32_t flag);
+extern int evch_usrgetchnames(char *buf, size_t size);
+extern int evch_usrgetchdata(char *chname, void *buf, size_t size);
+extern void evch_usrsetpropnvl(evchan_t *bp, nvlist_t *nvl);
+extern int evch_usrgetpropnvl(evchan_t *bp, nvlist_t **nvlp, int64_t *genp);
 
-void sysevent_evc_init();
-void sysevent_evc_thrinit();
-evchanq_t *sysevent_evc_walk_init(evchan_t *, char *);
-sysevent_t *sysevent_evc_walk_step(evchanq_t *);
-void sysevent_evc_walk_fini(evchanq_t *);
-char *sysevent_evc_event_attr(sysevent_t *, size_t *);
+extern void sysevent_evc_init();
+extern void sysevent_evc_thrinit();
+extern evchanq_t *sysevent_evc_walk_init(evchan_t *, char *);
+extern sysevent_t *sysevent_evc_walk_step(evchanq_t *);
+extern void sysevent_evc_walk_fini(evchanq_t *);
+extern char *sysevent_evc_event_attr(sysevent_t *, size_t *);
 
 #endif /* _KERNEL */
 
@@ -508,6 +512,8 @@
 #define	SEV_UNSUBSCRIBE		SEV_BASE | 0x05
 #define	SEV_CHANNAMES		SEV_BASE | 0x06
 #define	SEV_CHANDATA		SEV_BASE | 0x07
+#define	SEV_SETPROPNVL		SEV_BASE | 0x08
+#define	SEV_GETPROPNVL		SEV_BASE | 0x09
 
 #define	DEVSYSEVENT	"/dev/sysevent"
 #define	DEVICESYSEVENT	"/devices/pseudo/sysevent@0:sysevent"
@@ -560,6 +566,11 @@
 	sev_box_t out_data;
 } sev_chandata_args_t;
 
+typedef struct propnvl_args {
+	sev_box_t packednvl;	/* input and output */
+	int64_t generation;	/* output on get operation */
+} sev_propnvl_args_t;
+
 #if _LONG_LONG_ALIGNMENT == 8 && _LONG_LONG_ALIGNMENT_32 == 4
 #pragma pack()
 #endif
--- a/usr/src/uts/intel/ia32/os/archdep.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/uts/intel/ia32/os/archdep.c	Fri Jul 30 17:04:17 2010 +1000
@@ -1129,10 +1129,17 @@
 
 /*
  * Print a stack backtrace using the specified frame pointer.  We delay two
- * seconds before continuing, unless this is the panic traceback.  Note
- * that the frame for the starting stack pointer value is omitted because
+ * seconds before continuing, unless this is the panic traceback.
+ * If we are in the process of panicking, we also attempt to write the
+ * stack backtrace to a staticly assigned buffer, to allow the panic
+ * code to find it and write it in to uncompressed pages within the
+ * system crash dump.
+ * Note that the frame for the starting stack pointer value is omitted because
  * the corresponding %eip is not known.
  */
+
+extern char *dump_stack_scratch;
+
 #if defined(__amd64)
 
 void
@@ -1143,10 +1150,17 @@
 	uintptr_t	pc, nextpc;
 	ulong_t		off;
 	char		args[TR_ARG_MAX * 2 + 16], *sym;
+	uint_t	  offset = 0;
+	uint_t	  next_offset = 0;
+	char	    stack_buffer[1024];
 
 	if (!panicstr)
 		printf("traceback: %%fp = %p\n", (void *)fp);
 
+	if (panicstr && !dump_stack_scratch) {
+		printf("Warning - stack not written to the dump buffer\n");
+	}
+
 	fp = (struct frame *)plat_traceback(fpreg);
 	if ((uintptr_t)fp < KERNELBASE)
 		goto out;
@@ -1177,9 +1191,33 @@
 		if ((sym = kobj_getsymname(pc, &off)) != NULL) {
 			printf("%016lx %s:%s+%lx (%s)\n", (uintptr_t)fp,
 			    mod_containing_pc((caddr_t)pc), sym, off, args);
+			(void) snprintf(stack_buffer, sizeof (stack_buffer),
+			    "%s:%s+%lx (%s) | ",
+			    mod_containing_pc((caddr_t)pc), sym, off, args);
 		} else {
 			printf("%016lx %lx (%s)\n",
 			    (uintptr_t)fp, pc, args);
+			(void) snprintf(stack_buffer, sizeof (stack_buffer),
+			    "%lx (%s) | ", pc, args);
+		}
+
+		if (panicstr && dump_stack_scratch) {
+			next_offset = offset + strlen(stack_buffer);
+			if (next_offset < STACK_BUF_SIZE) {
+				bcopy(stack_buffer, dump_stack_scratch + offset,
+				    strlen(stack_buffer));
+				offset = next_offset;
+			} else {
+				/*
+				 * In attempting to save the panic stack
+				 * to the dumpbuf we have overflowed that area.
+				 * Print a warning and continue to printf the
+				 * stack to the msgbuf
+				 */
+				printf("Warning: stack in the dump buffer"
+				    " may be incomplete\n");
+				offset = next_offset;
+			}
 		}
 
 		pc = nextpc;
@@ -1189,6 +1227,8 @@
 	if (!panicstr) {
 		printf("end of traceback\n");
 		DELAY(2 * MICROSEC);
+	} else if (dump_stack_scratch) {
+		dump_stack_scratch[offset] = '\0';
 	}
 }
 
@@ -1200,6 +1240,9 @@
 	struct frame *fp = (struct frame *)fpreg;
 	struct frame *nextfp, *minfp, *stacktop;
 	uintptr_t pc, nextpc;
+	uint_t	  offset = 0;
+	uint_t	  next_offset = 0;
+	char	    stack_buffer[1024];
 
 	cpu_t *cpu;
 
@@ -1215,6 +1258,10 @@
 	if (!panicstr)
 		printf("traceback: %%fp = %p\n", (void *)fp);
 
+	if (panicstr && !dump_stack_scratch) {
+		printf("Warning - stack not written to the dumpbuf\n");
+	}
+
 	/*
 	 * If we are panicking, all high-level interrupt information in
 	 * CPU was overwritten.  panic_cpu has the correct values.
@@ -1275,9 +1322,35 @@
 		if ((sym = kobj_getsymname(pc, &off)) != NULL) {
 			printf("%08lx %s:%s+%lx (%s)\n", (uintptr_t)fp,
 			    mod_containing_pc((caddr_t)pc), sym, off, args);
+			(void) snprintf(stack_buffer, sizeof (stack_buffer),
+			    "%s:%s+%lx (%s) | ",
+			    mod_containing_pc((caddr_t)pc), sym, off, args);
+
 		} else {
 			printf("%08lx %lx (%s)\n",
 			    (uintptr_t)fp, pc, args);
+			(void) snprintf(stack_buffer, sizeof (stack_buffer),
+			    "%lx (%s) | ", pc, args);
+
+		}
+
+		if (panicstr && dump_stack_scratch) {
+			next_offset = offset + strlen(stack_buffer);
+			if (next_offset < STACK_BUF_SIZE) {
+				bcopy(stack_buffer, dump_stack_scratch + offset,
+				    strlen(stack_buffer));
+				offset = next_offset;
+			} else {
+				/*
+				 * In attempting to save the panic stack
+				 * to the dumpbuf we have overflowed that area.
+				 * Print a warning and continue to printf the
+				 * stack to the msgbuf
+				 */
+				printf("Warning: stack in the dumpbuf"
+				    " may be incomplete\n");
+				offset = next_offset;
+			}
 		}
 
 		minfp = fp;
@@ -1288,7 +1361,10 @@
 	if (!panicstr) {
 		printf("end of traceback\n");
 		DELAY(2 * MICROSEC);
+	} else if (dump_stack_scratch) {
+		dump_stack_scratch[offset] = '\0';
 	}
+
 }
 
 #endif	/* __i386 */
--- a/usr/src/uts/sparc/os/archdep.c	Thu Jul 29 22:45:58 2010 -0700
+++ b/usr/src/uts/sparc/os/archdep.c	Fri Jul 30 17:04:17 2010 +1000
@@ -19,16 +19,13 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
 /*	  All Rights Reserved  	*/
 
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <sys/param.h>
 #include <sys/types.h>
 #include <sys/vmparam.h>
@@ -255,10 +252,18 @@
 
 /*
  * Print a stack backtrace using the specified stack pointer.  We delay two
- * seconds before continuing, unless this is the panic traceback.  Note
- * that the frame for the starting stack pointer value is omitted because
+ * seconds before continuing, unless this is the panic traceback.
+ * If we are in the process of panicking, we also attempt to write the
+ * stack backtrace to a staticly assigned buffer, to allow the panic
+ * code to find it and write it in to uncompressed pages within the
+ * system crash dump.
+ *
+ * Note that the frame for the starting stack pointer value is omitted because
  * the corresponding %pc is not known.
  */
+
+extern char *dump_stack_scratch;
+
 void
 traceback(caddr_t sp)
 {
@@ -268,11 +273,20 @@
 
 	cpu_t *cpu;
 
+	uint_t  offset = 0;
+	uint_t  next_offset = 0;
+	char    stack_buffer[2048];
+	char    local_buffer[1024];
+
 	flush_windows();
 
 	if (!panicstr)
 		printf("traceback: %%sp = %p\n", (void *)sp);
 
+	if (panicstr && !dump_stack_scratch) {
+		printf("Warning - stack not written to the dumpbuf\n");
+	}
+
 	/*
 	 * If we are panicking, the high-level interrupt information in
 	 * CPU was overwritten.  panic_cpu has the correct values.
@@ -321,20 +335,53 @@
 			    nextfp->fr_arg[0], nextfp->fr_arg[1],
 			    nextfp->fr_arg[2], nextfp->fr_arg[3],
 			    nextfp->fr_arg[4], nextfp->fr_arg[5]);
+			(void) snprintf(stack_buffer, sizeof (stack_buffer),
+			    "%s:%s+%lx "
+			    "(%lx, %lx, %lx, %lx, %lx, %lx) | ",
+			    mod_containing_pc((caddr_t)pc), sym, off,
+			    nextfp->fr_arg[0], nextfp->fr_arg[1],
+			    nextfp->fr_arg[2], nextfp->fr_arg[3],
+			    nextfp->fr_arg[4], nextfp->fr_arg[5]);
 		} else {
-			printf("%016lx %p (%lx, %lx, %lx, %lx, %lx, %lx)\n",
+			(void) printf("%016lx %p (%lx, %lx, %lx, "
+			    "%lx, %lx, %lx)\n",
 			    (ulong_t)nextfp, (void *)pc,
 			    nextfp->fr_arg[0], nextfp->fr_arg[1],
 			    nextfp->fr_arg[2], nextfp->fr_arg[3],
 			    nextfp->fr_arg[4], nextfp->fr_arg[5]);
+			(void) snprintf(stack_buffer, sizeof (stack_buffer),
+			    "%p (%lx, %lx, %lx, %lx, %lx, %lx) | ",
+			    (void *)pc,
+			    nextfp->fr_arg[0], nextfp->fr_arg[1],
+			    nextfp->fr_arg[2], nextfp->fr_arg[3],
+			    nextfp->fr_arg[4], nextfp->fr_arg[5]);
 		}
 
-		printf("  %%l0-3: %016lx %016lx %016lx %016lx\n"
+		(void) snprintf(local_buffer, sizeof (local_buffer),
+		    "  %%l0-3: %016lx %016lx %016lx %016lx\n"
 		    "  %%l4-7: %016lx %016lx %016lx %016lx\n",
 		    nextfp->fr_local[0], nextfp->fr_local[1],
 		    nextfp->fr_local[2], nextfp->fr_local[3],
 		    nextfp->fr_local[4], nextfp->fr_local[5],
 		    nextfp->fr_local[6], nextfp->fr_local[7]);
+		if (panicstr && dump_stack_scratch) {
+			next_offset = offset + strlen(stack_buffer);
+			if (next_offset < STACK_BUF_SIZE) {
+				bcopy(stack_buffer, dump_stack_scratch + offset,
+				    strlen(stack_buffer));
+				offset = next_offset;
+			} else {
+				/*
+				 * In attempting to save the panic stack
+				 * to the dumpbuf we have overflowed that area.
+				 * Print a warning and continue to printf the
+				 * stack to the msgbuf
+				 */
+				printf("Warning: stack in the dump buffer"
+				    " may be incomplete\n");
+			}
+		}
+		printf("%s", local_buffer);
 
 		fp = nextfp;
 		minfp = fp;
@@ -343,6 +390,8 @@
 	if (!panicstr) {
 		printf("end of traceback\n");
 		DELAY(2 * MICROSEC);
+	} else if (dump_stack_scratch) {
+		dump_stack_scratch[offset] = '\0';
 	}
 }