summaryrefslogtreecommitdiff
path: root/usr
diff options
context:
space:
mode:
authorGavin Maltby <gavin.maltby@oracle.com>2010-07-30 17:04:17 +1000
committerGavin Maltby <gavin.maltby@oracle.com>2010-07-30 17:04:17 +1000
commitf6e214c7418f43af38bd8c3a557e3d0a1d311cfa (patch)
tree0f0e4cee5ead68ee30660107f9eccf7cd9e72c2e /usr
parent265a964d7aa43c47170d21d2f01bcf873d7fd79d (diff)
downloadillumos-joyent-f6e214c7418f43af38bd8c3a557e3d0a1d311cfa.tar.gz
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
Diffstat (limited to 'usr')
-rw-r--r--usr/src/Makefile.lint2
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/inetd/inetd.c11
-rw-r--r--usr/src/cmd/dumpadm/Makefile5
-rw-r--r--usr/src/cmd/dumpadm/dconf.c23
-rw-r--r--usr/src/cmd/dumpadm/dconf.h4
-rw-r--r--usr/src/cmd/dumpadm/dumpadm.xml22
-rw-r--r--usr/src/cmd/dumpadm/main.c23
-rw-r--r--usr/src/cmd/dumpadm/svc-dumpadm78
-rw-r--r--usr/src/cmd/fm/Makefile8
-rw-r--r--usr/src/cmd/fm/dicts/FMD.dict8
-rw-r--r--usr/src/cmd/fm/dicts/FMD.po25
-rw-r--r--usr/src/cmd/fm/dicts/FMNOTIFY.dict25
-rw-r--r--usr/src/cmd/fm/dicts/FMNOTIFY.po63
-rw-r--r--usr/src/cmd/fm/dicts/Makefile2
-rw-r--r--usr/src/cmd/fm/dicts/SMF.dict60
-rw-r--r--usr/src/cmd/fm/dicts/SMF.po537
-rw-r--r--usr/src/cmd/fm/dicts/SUNOS.dict5
-rw-r--r--usr/src/cmd/fm/dicts/SUNOS.po34
-rw-r--r--usr/src/cmd/fm/fmd/Makefile.fmd16
-rw-r--r--usr/src/cmd/fm/fmd/common/fmd.c25
-rw-r--r--usr/src/cmd/fm/fmd/common/fmd.h12
-rw-r--r--usr/src/cmd/fm/fmd/common/fmd.logadm.conf27
-rw-r--r--usr/src/cmd/fm/fmd/common/fmd.xml23
-rw-r--r--usr/src/cmd/fm/fmd/common/fmd_api.c216
-rw-r--r--usr/src/cmd/fm/fmd/common/fmd_api.h17
-rw-r--r--usr/src/cmd/fm/fmd/common/fmd_api.map8
-rw-r--r--usr/src/cmd/fm/fmd/common/fmd_builtin.c25
-rw-r--r--usr/src/cmd/fm/fmd/common/fmd_builtin.h17
-rw-r--r--usr/src/cmd/fm/fmd/common/fmd_case.c45
-rw-r--r--usr/src/cmd/fm/fmd/common/fmd_case.h5
-rw-r--r--usr/src/cmd/fm/fmd/common/fmd_log.h11
-rw-r--r--usr/src/cmd/fm/fmd/common/fmd_main.c47
-rw-r--r--usr/src/cmd/fm/fmd/common/fmd_mdb.c7
-rw-r--r--usr/src/cmd/fm/fmd/common/fmd_module.c5
-rw-r--r--usr/src/cmd/fm/fmd/common/fmd_module.h7
-rw-r--r--usr/src/cmd/fm/fmd/common/fmd_rpc.c9
-rw-r--r--usr/src/cmd/fm/fmd/common/fmd_rpc_adm.c22
-rw-r--r--usr/src/cmd/fm/fmd/common/fmd_self.c7
-rw-r--r--usr/src/cmd/fm/fmd/common/fmd_sysevent.c42
-rw-r--r--usr/src/cmd/fm/fmd/common/fmd_thread.c38
-rw-r--r--usr/src/cmd/fm/fmd/common/fmd_thread.h13
-rw-r--r--usr/src/cmd/fm/fmd/common/fmd_xprt.c54
-rw-r--r--usr/src/cmd/fm/fmdump/Makefile.com5
-rw-r--r--usr/src/cmd/fm/fmdump/common/asru.c38
-rw-r--r--usr/src/cmd/fm/fmdump/common/error.c38
-rw-r--r--usr/src/cmd/fm/fmdump/common/fault.c79
-rw-r--r--usr/src/cmd/fm/fmdump/common/fmdump.c934
-rw-r--r--usr/src/cmd/fm/fmdump/common/fmdump.h16
-rw-r--r--usr/src/cmd/fm/fmdump/common/info.c118
-rw-r--r--usr/src/cmd/fm/fmdump/common/nvlrender.c74
-rw-r--r--usr/src/cmd/fm/fmtopo/common/fmtopo.c16
-rw-r--r--usr/src/cmd/fm/modules/Makefile.plugin32
-rw-r--r--usr/src/cmd/fm/modules/common/Makefile6
-rw-r--r--usr/src/cmd/fm/modules/common/eversholt/eft.c10
-rw-r--r--usr/src/cmd/fm/modules/common/eversholt/fme.c4
-rw-r--r--usr/src/cmd/fm/modules/common/eversholt/platform.c6
-rw-r--r--usr/src/cmd/fm/modules/common/ext-event-transport/Makefile14
-rw-r--r--usr/src/cmd/fm/modules/common/ext-event-transport/ext-event-transport.conf6
-rw-r--r--usr/src/cmd/fm/modules/common/ext-event-transport/fmevt.h89
-rw-r--r--usr/src/cmd/fm/modules/common/ext-event-transport/fmevt_inbound.c658
-rw-r--r--usr/src/cmd/fm/modules/common/ext-event-transport/fmevt_inbound_default.c52
-rw-r--r--usr/src/cmd/fm/modules/common/ext-event-transport/fmevt_inbound_on.c76
-rw-r--r--usr/src/cmd/fm/modules/common/ext-event-transport/fmevt_inbound_smf.c194
-rw-r--r--usr/src/cmd/fm/modules/common/ext-event-transport/fmevt_inbound_sunos.c88
-rw-r--r--usr/src/cmd/fm/modules/common/ext-event-transport/fmevt_main.c17
-rw-r--r--usr/src/cmd/fm/modules/common/ext-event-transport/fmevt_outbound.c24
-rw-r--r--usr/src/cmd/fm/modules/common/sensor-transport/sensor_transport.c23
-rw-r--r--usr/src/cmd/fm/modules/common/snmp-trapgen/snmp.c286
-rw-r--r--usr/src/cmd/fm/modules/common/sw-diag-response/Makefile (renamed from usr/src/cmd/fm/modules/common/snmp-trapgen/fmd-trapgen.conf)14
-rw-r--r--usr/src/cmd/fm/modules/common/sw-diag-response/Makefile.com31
-rw-r--r--usr/src/cmd/fm/modules/common/sw-diag-response/common/sw.h255
-rw-r--r--usr/src/cmd/fm/modules/common/sw-diag-response/common/sw_impl.h101
-rw-r--r--usr/src/cmd/fm/modules/common/sw-diag-response/common/sw_main_cmn.c474
-rw-r--r--usr/src/cmd/fm/modules/common/sw-diag-response/software-diagnosis/Makefile51
-rw-r--r--usr/src/cmd/fm/modules/common/sw-diag-response/software-diagnosis/software-diagnosis.conf36
-rw-r--r--usr/src/cmd/fm/modules/common/sw-diag-response/software-diagnosis/swde_case.c368
-rw-r--r--usr/src/cmd/fm/modules/common/sw-diag-response/software-diagnosis/swde_main.c67
-rw-r--r--usr/src/cmd/fm/modules/common/sw-diag-response/software-response/Makefile41
-rw-r--r--usr/src/cmd/fm/modules/common/sw-diag-response/software-response/software-response.conf (renamed from usr/src/cmd/fm/modules/common/snmp-trapgen/snmp-trapgen.conf)10
-rw-r--r--usr/src/cmd/fm/modules/common/sw-diag-response/software-response/swrp_main.c64
-rw-r--r--usr/src/cmd/fm/modules/common/sw-diag-response/subsidiary/panic/panic.h45
-rw-r--r--usr/src/cmd/fm/modules/common/sw-diag-response/subsidiary/panic/panic_diag.c591
-rw-r--r--usr/src/cmd/fm/modules/common/sw-diag-response/subsidiary/smf/smf.h44
-rw-r--r--usr/src/cmd/fm/modules/common/sw-diag-response/subsidiary/smf/smf_diag.c305
-rw-r--r--usr/src/cmd/fm/modules/common/sw-diag-response/subsidiary/smf/smf_response.c543
-rw-r--r--usr/src/cmd/fm/modules/common/sw-diag-response/subsidiary/smf/smf_util.c80
-rw-r--r--usr/src/cmd/fm/modules/common/syslog-msgs/Makefile6
-rw-r--r--usr/src/cmd/fm/modules/common/syslog-msgs/syslog.c118
-rw-r--r--usr/src/cmd/fm/modules/sun4u/fps-transport/fps-transport.c18
-rw-r--r--usr/src/cmd/fm/notify/Makefile31
-rw-r--r--usr/src/cmd/fm/notify/Makefile.subdirs40
-rw-r--r--usr/src/cmd/fm/notify/notify-params.xml103
-rw-r--r--usr/src/cmd/fm/notify/smtp-notify/Makefile28
-rw-r--r--usr/src/cmd/fm/notify/smtp-notify/Makefile.com109
-rw-r--r--usr/src/cmd/fm/notify/smtp-notify/common/process_msg_template.sh68
-rw-r--r--usr/src/cmd/fm/notify/smtp-notify/common/smtp-notify.c906
-rw-r--r--usr/src/cmd/fm/notify/smtp-notify/common/smtp-notify.xml142
-rw-r--r--usr/src/cmd/fm/notify/smtp-notify/i386/Makefile27
-rw-r--r--usr/src/cmd/fm/notify/smtp-notify/sparc/Makefile27
-rw-r--r--usr/src/cmd/fm/notify/snmp-notify/Makefile28
-rw-r--r--usr/src/cmd/fm/notify/snmp-notify/Makefile.com103
-rw-r--r--usr/src/cmd/fm/notify/snmp-notify/common/snmp-notify.c687
-rw-r--r--usr/src/cmd/fm/notify/snmp-notify/common/snmp-notify.xml142
-rw-r--r--usr/src/cmd/fm/notify/snmp-notify/i386/Makefile27
-rw-r--r--usr/src/cmd/fm/notify/snmp-notify/sparc/Makefile27
-rw-r--r--usr/src/cmd/fm/schemes/Makefile4
-rw-r--r--usr/src/cmd/fm/schemes/sw/Makefile31
-rw-r--r--usr/src/cmd/fm/schemes/sw/Makefile.com26
-rw-r--r--usr/src/cmd/fm/schemes/sw/amd64/Makefile34
-rw-r--r--usr/src/cmd/fm/schemes/sw/i386/Makefile (renamed from usr/src/cmd/fm/modules/common/snmp-trapgen/Makefile)27
-rw-r--r--usr/src/cmd/fm/schemes/sw/scheme.c96
-rw-r--r--usr/src/cmd/fm/schemes/sw/sparc/Makefile33
-rw-r--r--usr/src/cmd/fm/schemes/sw/sparcv9/Makefile34
-rw-r--r--usr/src/cmd/fm/scripts/dictck.pl13
-rw-r--r--usr/src/cmd/logadm/Makefile33
-rw-r--r--usr/src/cmd/logadm/logadm-upgrade138
-rw-r--r--usr/src/cmd/logadm/logadm-upgrade.xml84
-rw-r--r--usr/src/cmd/logadm/logadm.conf3
-rw-r--r--usr/src/cmd/mdb/common/kmdb/kmdb_kvm.c15
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_kvm.c12
-rw-r--r--usr/src/cmd/mdb/common/modules/svc.startd/startd.c82
-rw-r--r--usr/src/cmd/savecore/Makefile.com30
-rw-r--r--usr/src/cmd/savecore/amd64/Makefile13
-rw-r--r--usr/src/cmd/savecore/i386/Makefile14
-rw-r--r--usr/src/cmd/savecore/savecore.c630
-rw-r--r--usr/src/cmd/savecore/sparc/Makefile13
-rw-r--r--usr/src/cmd/savecore/sparcv9/Makefile13
-rw-r--r--usr/src/cmd/svc/common/notify_params.c333
-rw-r--r--usr/src/cmd/svc/common/notify_params.h54
-rw-r--r--usr/src/cmd/svc/dtd/service_bundle.dtd.144
-rw-r--r--usr/src/cmd/svc/milestone/fs-usr8
-rw-r--r--usr/src/cmd/svc/milestone/global.xml81
-rw-r--r--usr/src/cmd/svc/milestone/restarter.xml12
-rw-r--r--usr/src/cmd/svc/startd/Makefile8
-rw-r--r--usr/src/cmd/svc/startd/graph.c234
-rw-r--r--usr/src/cmd/svc/startd/libscf.c124
-rw-r--r--usr/src/cmd/svc/startd/method.c24
-rw-r--r--usr/src/cmd/svc/startd/protocol.c21
-rw-r--r--usr/src/cmd/svc/startd/protocol.h14
-rw-r--r--usr/src/cmd/svc/startd/restarter.c131
-rw-r--r--usr/src/cmd/svc/startd/startd.c10
-rw-r--r--usr/src/cmd/svc/startd/startd.h13
-rw-r--r--usr/src/cmd/svc/svccfg/Makefile10
-rw-r--r--usr/src/cmd/svc/svccfg/svccfg.h19
-rw-r--r--usr/src/cmd/svc/svccfg/svccfg.l13
-rw-r--r--usr/src/cmd/svc/svccfg/svccfg.y72
-rw-r--r--usr/src/cmd/svc/svccfg/svccfg_engine.c4
-rw-r--r--usr/src/cmd/svc/svccfg/svccfg_help.c20
-rw-r--r--usr/src/cmd/svc/svccfg/svccfg_libscf.c765
-rw-r--r--usr/src/cmd/svc/svccfg/svccfg_xml.c237
-rw-r--r--usr/src/cmd/svc/svcs/Makefile21
-rw-r--r--usr/src/cmd/svc/svcs/svcs.c254
-rw-r--r--usr/src/cmd/syseventd/sysevent.xml22
-rw-r--r--usr/src/common/nvpair/nvpair.c9
-rw-r--r--usr/src/lib/fm/Makefile2
-rw-r--r--usr/src/lib/fm/libfmd_msg/common/fmd_msg.c186
-rw-r--r--usr/src/lib/fm/libfmd_msg/common/fmd_msg.h7
-rw-r--r--usr/src/lib/fm/libfmd_msg/common/mapfile-vers2
-rw-r--r--usr/src/lib/fm/libfmd_snmp/Makefile5
-rw-r--r--usr/src/lib/fm/libfmd_snmp/common/fmd_snmp.h33
-rw-r--r--usr/src/lib/fm/libfmd_snmp/mibs/SUN-FM-MIB.mib24
-rw-r--r--usr/src/lib/fm/libfmd_snmp/mibs/SUN-IREPORT-MIB.mib204
-rw-r--r--usr/src/lib/fm/libfmevent/Makefile5
-rw-r--r--usr/src/lib/fm/libfmevent/Makefile.com11
-rw-r--r--usr/src/lib/fm/libfmevent/amd64/Makefile5
-rw-r--r--usr/src/lib/fm/libfmevent/common/fmev_channels.h18
-rw-r--r--usr/src/lib/fm/libfmevent/common/fmev_evaccess.c35
-rw-r--r--usr/src/lib/fm/libfmevent/common/fmev_impl.h13
-rw-r--r--usr/src/lib/fm/libfmevent/common/fmev_publish.c536
-rw-r--r--usr/src/lib/fm/libfmevent/common/fmev_subscribe.c181
-rw-r--r--usr/src/lib/fm/libfmevent/common/fmev_util.c57
-rw-r--r--usr/src/lib/fm/libfmevent/common/libfmevent.h305
-rw-r--r--usr/src/lib/fm/libfmevent/common/libfmevent_ruleset.h78
-rw-r--r--usr/src/lib/fm/libfmevent/common/mapfile-vers14
-rw-r--r--usr/src/lib/fm/libfmevent/i386/Makefile5
-rw-r--r--usr/src/lib/fm/libfmevent/sparc/Makefile5
-rw-r--r--usr/src/lib/fm/libfmevent/sparcv9/Makefile5
-rw-r--r--usr/src/lib/fm/libfmnotify/Makefile55
-rw-r--r--usr/src/lib/fm/libfmnotify/Makefile.com63
-rw-r--r--usr/src/lib/fm/libfmnotify/amd64/Makefile31
-rw-r--r--usr/src/lib/fm/libfmnotify/common/libfmnotify.c616
-rw-r--r--usr/src/lib/fm/libfmnotify/common/libfmnotify.h109
-rw-r--r--usr/src/lib/fm/libfmnotify/common/llib-lfmnotify27
-rw-r--r--usr/src/lib/fm/libfmnotify/common/mapfile-vers61
-rw-r--r--usr/src/lib/fm/libfmnotify/i386/Makefile32
-rw-r--r--usr/src/lib/fm/libfmnotify/sparc/Makefile31
-rw-r--r--usr/src/lib/fm/libfmnotify/sparcv9/Makefile32
-rw-r--r--usr/src/lib/fm/topo/libtopo/Makefile.com4
-rw-r--r--usr/src/lib/fm/topo/libtopo/common/dev.c5
-rw-r--r--usr/src/lib/fm/topo/libtopo/common/fmd.c9
-rw-r--r--usr/src/lib/fm/topo/libtopo/common/hc.c5
-rw-r--r--usr/src/lib/fm/topo/libtopo/common/mem.c11
-rw-r--r--usr/src/lib/fm/topo/libtopo/common/mod.c8
-rw-r--r--usr/src/lib/fm/topo/libtopo/common/pkg.c10
-rw-r--r--usr/src/lib/fm/topo/libtopo/common/svc.c305
-rw-r--r--usr/src/lib/fm/topo/libtopo/common/sw.c530
-rw-r--r--usr/src/lib/fm/topo/libtopo/common/sw.h42
-rw-r--r--usr/src/lib/fm/topo/libtopo/common/topo_builtin.c5
-rw-r--r--usr/src/lib/fm/topo/libtopo/common/topo_mod.c67
-rw-r--r--usr/src/lib/fm/topo/libtopo/common/topo_mod.h7
-rw-r--r--usr/src/lib/fm/topo/libtopo/common/topo_node.c6
-rw-r--r--usr/src/lib/fm/topo/libtopo/common/topo_snap.c6
-rw-r--r--usr/src/lib/fm/topo/libtopo/common/zfs.c8
-rw-r--r--usr/src/lib/libnvpair/libnvpair.c630
-rw-r--r--usr/src/lib/libnvpair/libnvpair.h159
-rw-r--r--usr/src/lib/libnvpair/mapfile-vers37
-rw-r--r--usr/src/lib/librestart/common/librestart.c352
-rw-r--r--usr/src/lib/librestart/common/librestart.h54
-rw-r--r--usr/src/lib/librestart/common/librestart_priv.h11
-rw-r--r--usr/src/lib/librestart/common/mapfile-vers3
-rw-r--r--usr/src/lib/libscf/Makefile.com5
-rw-r--r--usr/src/lib/libscf/common/libscf_impl.h8
-rw-r--r--usr/src/lib/libscf/common/lowlevel.c35
-rw-r--r--usr/src/lib/libscf/common/mapfile-vers11
-rw-r--r--usr/src/lib/libscf/common/midlevel.c150
-rw-r--r--usr/src/lib/libscf/common/notify_params.c1979
-rw-r--r--usr/src/lib/libscf/common/scf_tmpl.c13
-rw-r--r--usr/src/lib/libscf/inc/libscf.h55
-rw-r--r--usr/src/lib/libscf/inc/libscf_priv.h35
-rw-r--r--usr/src/lib/libsecdb/auth_attr.txt4
-rw-r--r--usr/src/lib/libsecdb/prof_attr.txt1
-rw-r--r--usr/src/lib/libsysevent/libevchannel.c95
-rw-r--r--usr/src/lib/libsysevent/libsysevent.c178
-rw-r--r--usr/src/lib/libsysevent/libsysevent.h14
-rw-r--r--usr/src/lib/libsysevent/mapfile-vers4
-rw-r--r--usr/src/pkg/manifests/SUNWcs.mf3
-rw-r--r--usr/src/pkg/manifests/consolidation-osnet-osnet-message-files.mf2
-rw-r--r--usr/src/pkg/manifests/service-fault-management-smtp-notify.mf52
-rw-r--r--usr/src/pkg/manifests/service-fault-management-snmp-notify.mf51
-rw-r--r--usr/src/pkg/manifests/service-fault-management.mf595
-rw-r--r--usr/src/pkg/manifests/system-kernel.mf1
-rw-r--r--usr/src/uts/common/io/dump.c22
-rw-r--r--usr/src/uts/common/io/sysevent.c119
-rw-r--r--usr/src/uts/common/os/dumpsubr.c101
-rw-r--r--usr/src/uts/common/os/evchannels.c81
-rw-r--r--usr/src/uts/common/os/fm.c14
-rw-r--r--usr/src/uts/common/os/log_sysevent.c45
-rw-r--r--usr/src/uts/common/os/panic.c3
-rw-r--r--usr/src/uts/common/sys/dumpadm.h12
-rw-r--r--usr/src/uts/common/sys/dumphdr.h10
-rw-r--r--usr/src/uts/common/sys/fm/protocol.h31
-rw-r--r--usr/src/uts/common/sys/fm/util.h6
-rw-r--r--usr/src/uts/common/sys/nvpair.h5
-rw-r--r--usr/src/uts/common/sys/panic.h20
-rw-r--r--usr/src/uts/common/sys/sysevent.h5
-rw-r--r--usr/src/uts/common/sys/sysevent_impl.h77
-rw-r--r--usr/src/uts/intel/ia32/os/archdep.c80
-rw-r--r--usr/src/uts/sparc/os/archdep.c65
248 files changed, 21592 insertions, 1690 deletions
diff --git a/usr/src/Makefile.lint b/usr/src/Makefile.lint
index ad3f41b8ee..7c74fb03f8 100644
--- a/usr/src/Makefile.lint
+++ b/usr/src/Makefile.lint
@@ -263,6 +263,7 @@ COMMON_SUBDIRS = \
cmd/rpcgen \
cmd/rpcsvc/rpc.bootparamd \
cmd/runat \
+ cmd/savecore \
cmd/sbdadm \
cmd/sdpadm \
cmd/setpgrp \
@@ -425,6 +426,7 @@ COMMON_SUBDIRS = \
lib/libsrpt \
lib/libstmf \
lib/libsun_ima \
+ lib/libsysevent \
lib/libthread \
lib/libtsnet \
lib/libtsol \
diff --git a/usr/src/cmd/cmd-inet/usr.lib/inetd/inetd.c b/usr/src/cmd/cmd-inet/usr.lib/inetd/inetd.c
index 8ef243f9ed..f8de58dbc3 100644
--- a/usr/src/cmd/cmd-inet/usr.lib/inetd/inetd.c
+++ b/usr/src/cmd/cmd-inet/usr.lib/inetd/inetd.c
@@ -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 @@ update_instance_states(instance_t *inst, internal_inst_state_t new_cur_state,
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 @@ update_instance_states(instance_t *inst, internal_inst_state_t new_cur_state,
*/
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))
diff --git a/usr/src/cmd/dumpadm/Makefile b/usr/src/cmd/dumpadm/Makefile
index 9ecbf2ed27..7818439eb4 100644
--- a/usr/src/cmd/dumpadm/Makefile
+++ b/usr/src/cmd/dumpadm/Makefile
@@ -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 @@ FILEMODE = 0555
ROOTMANIFESTDIR = $(ROOTSVCSYSTEM)
-LDLIBS += -ldiskmgt -lzfs
+LDLIBS += -ldiskmgt -lzfs -luuid
.KEEP_STATE:
diff --git a/usr/src/cmd/dumpadm/dconf.c b/usr/src/cmd/dumpadm/dconf.c
index f4e59563f9..488b802462 100644
--- a/usr/src/cmd/dumpadm/dconf.c
+++ b/usr/src/cmd/dumpadm/dconf.c
@@ -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 @@ err:
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 @@ dconf_print(dumpconf_t *dcp, FILE *fp)
(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"));
diff --git a/usr/src/cmd/dumpadm/dconf.h b/usr/src/cmd/dumpadm/dconf.h
index a74e0a0c20..1db705dc58 100644
--- a/usr/src/cmd/dumpadm/dconf.h
+++ b/usr/src/cmd/dumpadm/dconf.h
@@ -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_close(dumpconf_t *);
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 *);
diff --git a/usr/src/cmd/dumpadm/dumpadm.xml b/usr/src/cmd/dumpadm/dumpadm.xml
index c5d40e523d..367a92a667 100644
--- a/usr/src/cmd/dumpadm/dumpadm.xml
+++ b/usr/src/cmd/dumpadm/dumpadm.xml
@@ -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'
diff --git a/usr/src/cmd/dumpadm/main.c b/usr/src/cmd/dumpadm/main.c
index 48cf9d692b..52ce3bc756 100644
--- a/usr/src/cmd/dumpadm/main.c
+++ b/usr/src/cmd/dumpadm/main.c
@@ -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 @@ static const char USAGE[] = "\
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 @@ main(int argc, char *argv[])
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 @@ main(int argc, char *argv[])
*/
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 @@ main(int argc, char *argv[])
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 @@ main(int argc, char *argv[])
}
}
+ 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);
diff --git a/usr/src/cmd/dumpadm/svc-dumpadm b/usr/src/cmd/dumpadm/svc-dumpadm
index 022bbab3b7..316e075754 100644
--- a/usr/src/cmd/dumpadm/svc-dumpadm
+++ b/usr/src/cmd/dumpadm/svc-dumpadm
@@ -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 @@ savedev=none
# 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.
#
-if [ -x /usr/bin/savecore ]; then
+# 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.
+#
+
+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 @@ if [ -x /usr/bin/savecore ]; then
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 @@ else
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.
#
-if [ "x$DUMPADM_ENABLE" != xno ]; then
- if /usr/sbin/swap -l 2>/dev/null | grep "^${DUMPADM_DEVICE} " \
+
+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 [ $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 @@ if [ "x$DUMPADM_ENABLE" != xno ]; then
#
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
diff --git a/usr/src/cmd/fm/Makefile b/usr/src/cmd/fm/Makefile
index 58e8be48fe..4243767346 100644
--- a/usr/src/cmd/fm/Makefile
+++ b/usr/src/cmd/fm/Makefile
@@ -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 @@ SUBDIRS = \
scripts \
modules \
dicts \
- eversholt
+ eversholt \
+ notify
include ./Makefile.subdirs
diff --git a/usr/src/cmd/fm/dicts/FMD.dict b/usr/src/cmd/fm/dicts/FMD.dict
index 547f9581c5..bf850ebd7d 100644
--- a/usr/src/cmd/fm/dicts/FMD.dict
+++ b/usr/src/cmd/fm/dicts/FMD.dict
@@ -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
diff --git a/usr/src/cmd/fm/dicts/FMD.po b/usr/src/cmd/fm/dicts/FMD.po
index 34005b4530..1c2b545896 100644
--- a/usr/src/cmd/fm/dicts/FMD.po
+++ b/usr/src/cmd/fm/dicts/FMD.po
@@ -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 @@ msgstr "Defect"
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 @@ msgstr "Minor"
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 @@ msgstr "Minor"
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 @@ msgstr "Minor"
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."
diff --git a/usr/src/cmd/fm/dicts/FMNOTIFY.dict b/usr/src/cmd/fm/dicts/FMNOTIFY.dict
new file mode 100644
index 0000000000..0e7070d75b
--- /dev/null
+++ b/usr/src/cmd/fm/dicts/FMNOTIFY.dict
@@ -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
diff --git a/usr/src/cmd/fm/dicts/FMNOTIFY.po b/usr/src/cmd/fm/dicts/FMNOTIFY.po
new file mode 100644
index 0000000000..29c9bb1176
--- /dev/null
+++ b/usr/src/cmd/fm/dicts/FMNOTIFY.po
@@ -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"
diff --git a/usr/src/cmd/fm/dicts/Makefile b/usr/src/cmd/fm/dicts/Makefile
index 08ad9aec57..8e0bbd1545 100644
--- a/usr/src/cmd/fm/dicts/Makefile
+++ b/usr/src/cmd/fm/dicts/Makefile
@@ -27,7 +27,9 @@ include ../../Makefile.cmd
common_DCNAMES = \
DISK \
FMD \
+ FMNOTIFY \
NXGE \
+ SMF \
SUNOS \
PCI \
PCIEX \
diff --git a/usr/src/cmd/fm/dicts/SMF.dict b/usr/src/cmd/fm/dicts/SMF.dict
new file mode 100644
index 0000000000..10c1fe2abe
--- /dev/null
+++ b/usr/src/cmd/fm/dicts/SMF.dict
@@ -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
diff --git a/usr/src/cmd/fm/dicts/SMF.po b/usr/src/cmd/fm/dicts/SMF.po
new file mode 100644
index 0000000000..974076e0f5
--- /dev/null
+++ b/usr/src/cmd/fm/dicts/SMF.po
@@ -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."
diff --git a/usr/src/cmd/fm/dicts/SUNOS.dict b/usr/src/cmd/fm/dicts/SUNOS.dict
index e53d2c6234..303228720d 100644
--- a/usr/src/cmd/fm/dicts/SUNOS.dict
+++ b/usr/src/cmd/fm/dicts/SUNOS.dict
@@ -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.fme=14
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
diff --git a/usr/src/cmd/fm/dicts/SUNOS.po b/usr/src/cmd/fm/dicts/SUNOS.po
index 143544bd02..5d37f79281 100644
--- a/usr/src/cmd/fm/dicts/SUNOS.po
+++ b/usr/src/cmd/fm/dicts/SUNOS.po
@@ -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 @@ msgstr "Critical"
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 @@ msgstr "Critical"
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 @@ msgstr "Fault"
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."
diff --git a/usr/src/cmd/fm/fmd/Makefile.fmd b/usr/src/cmd/fm/fmd/Makefile.fmd
index 5b26e5d6f8..4180d9c155 100644
--- a/usr/src/cmd/fm/fmd/Makefile.fmd
+++ b/usr/src/cmd/fm/fmd/Makefile.fmd
@@ -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 @@ MANIFEST = ../common/$(PROG).xml
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 @@ lint_prog := LINTFLAGS += -xerroff=E_FUNC_RET_ALWAYS_IGNOR2
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 @@ $(ROOTDMOD): $(ROOT)/usr/lib/mdb/proc $(DMOD)
install_h: $(ROOTHDIR) $(ROOTHDRS)
-install: all install_h $(ROOTPROG) $(ROOTDMOD) \
+install: all install_h $(ROOTPROG) $(ROOTDMOD) $(LOGADMDIR) $(LOGADMENT) \
$(ROOTCDIR) $(ROOTVDIR) $(ROOTVSUB) $(ROOTMANIFEST)
check: $(CHKMANIFEST)
diff --git a/usr/src/cmd/fm/fmd/common/fmd.c b/usr/src/cmd/fm/fmd/common/fmd.c
index 3d121f038f..bdc86a3e7f 100644
--- a/usr/src/cmd/fm/fmd/common/fmd.c
+++ b/usr/src/cmd/fm/fmd/common/fmd.c
@@ -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 @@ static const fmd_conf_formal_t _fmd_conf[] = {
{ "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 @@ static const fmd_conf_formal_t _fmd_conf[] = {
{ "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 @@ fmd_create(fmd_t *dp, const char *arg0, const char *root, const char *conf)
(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 @@ fmd_gc(fmd_t *dp, id_t id, hrtime_t hrt)
(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 @@ fmd_door_server(void *dip)
/*
* 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 @@ fmd_run(fmd_t *dp, int pfd)
(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");
diff --git a/usr/src/cmd/fm/fmd/common/fmd.h b/usr/src/cmd/fm/fmd/common/fmd.h
index bbb1a9f535..c042f78c96 100644
--- a/usr/src/cmd/fm/fmd/common/fmd.h
+++ b/usr/src/cmd/fm/fmd/common/fmd.h
@@ -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 @@ typedef struct fmd {
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;
diff --git a/usr/src/cmd/fm/fmd/common/fmd.logadm.conf b/usr/src/cmd/fm/fmd/common/fmd.logadm.conf
new file mode 100644
index 0000000000..7452c6746b
--- /dev/null
+++ b/usr/src/cmd/fm/fmd/common/fmd.logadm.conf
@@ -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'
+
diff --git a/usr/src/cmd/fm/fmd/common/fmd.xml b/usr/src/cmd/fm/fmd/common/fmd.xml
index b46fd72b19..e03bdc01a1 100644
--- a/usr/src/cmd/fm/fmd/common/fmd.xml
+++ b/usr/src/cmd/fm/fmd/common/fmd.xml
@@ -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
diff --git a/usr/src/cmd/fm/fmd/common/fmd_api.c b/usr/src/cmd/fm/fmd/common/fmd_api.c
index e30a36ee51..008a55c90f 100644
--- a/usr/src/cmd/fm/fmd/common/fmd_api.c
+++ b/usr/src/cmd/fm/fmd/common/fmd_api.c
@@ -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 @@ fmd_api_module(fmd_hdl_t *hdl)
}
/*
- * 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 @@ fmd_hdl_register(fmd_hdl_t *hdl, int version, const fmd_hdl_info_t *mip)
* 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_module_thrcancel(fmd_idspace_t *ids, id_t id, fmd_module_t *mp)
{
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_t *
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_case_uuresolved(fmd_hdl_t *hdl, const char *uuid)
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_thr_checkpoint(fmd_hdl_t *hdl)
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_timer_remove(fmd_hdl_t *hdl, id_t id)
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 @@ fmd_nvl_create_fault(fmd_hdl_t *hdl, const char *class,
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)
{
diff --git a/usr/src/cmd/fm/fmd/common/fmd_api.h b/usr/src/cmd/fm/fmd/common/fmd_api.h
index d2af15cebb..b746484263 100644
--- a/usr/src/cmd/fm/fmd/common/fmd_api.h
+++ b/usr/src/cmd/fm/fmd/common/fmd_api.h
@@ -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 @@ extern "C" {
#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_destroy(fmd_hdl_t *, uint_t, fmd_stat_t *);
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 const char *fmd_case_uuid(fmd_hdl_t *, fmd_case_t *);
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_destroy(fmd_hdl_t *, pthread_t);
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 *);
diff --git a/usr/src/cmd/fm/fmd/common/fmd_api.map b/usr/src/cmd/fm/fmd/common/fmd_api.map
index 1280513880..bb5524185b 100644
--- a/usr/src/cmd/fm/fmd/common/fmd_api.map
+++ b/usr/src/cmd/fm/fmd/common/fmd_api.map
@@ -39,6 +39,7 @@ SYMBOL_SCOPE {
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 @@ SYMBOL_SCOPE {
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 @@ SYMBOL_SCOPE {
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 @@ SYMBOL_SCOPE {
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 };
diff --git a/usr/src/cmd/fm/fmd/common/fmd_builtin.c b/usr/src/cmd/fm/fmd/common/fmd_builtin.c
index ce669465a5..84435216c9 100644
--- a/usr/src/cmd/fm/fmd/common/fmd_builtin.c
+++ b/usr/src/cmd/fm/fmd/common/fmd_builtin.c
@@ -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 @@ int
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;
diff --git a/usr/src/cmd/fm/fmd/common/fmd_builtin.h b/usr/src/cmd/fm/fmd/common/fmd_builtin.h
index 9ef2a54d75..06710dc3ce 100644
--- a/usr/src/cmd/fm/fmd/common/fmd_builtin.h
+++ b/usr/src/cmd/fm/fmd/common/fmd_builtin.h
@@ -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 @@ typedef struct fmd_builtin {
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 */
diff --git a/usr/src/cmd/fm/fmd/common/fmd_case.c b/usr/src/cmd/fm/fmd/common/fmd_case.c
index d65f24601b..9ebcced1f9 100644
--- a/usr/src/cmd/fm/fmd/common/fmd_case.c
+++ b/usr/src/cmd/fm/fmd/common/fmd_case.c
@@ -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_hash_delete(fmd_case_hash_t *chp, fmd_case_impl_t *cip)
}
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 @@ fmd_case_create(fmd_module_t *mp, void *data)
(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);
diff --git a/usr/src/cmd/fm/fmd/common/fmd_case.h b/usr/src/cmd/fm/fmd/common/fmd_case.h
index 891617b190..733826e91c 100644
--- a/usr/src/cmd/fm/fmd/common/fmd_case.h
+++ b/usr/src/cmd/fm/fmd/common/fmd_case.h
@@ -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 fmd_case_t *fmd_case_hash_lookup(fmd_case_hash_t *, const char *);
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);
diff --git a/usr/src/cmd/fm/fmd/common/fmd_log.h b/usr/src/cmd/fm/fmd/common/fmd_log.h
index 11d2b1c399..56caf42bbb 100644
--- a/usr/src/cmd/fm/fmd/common/fmd_log.h
+++ b/usr/src/cmd/fm/fmd/common/fmd_log.h
@@ -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 @@ typedef void fmd_log_f(fmd_log_t *, fmd_event_t *, void *);
#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 *);
diff --git a/usr/src/cmd/fm/fmd/common/fmd_main.c b/usr/src/cmd/fm/fmd/common/fmd_main.c
index f1a8d2df31..d096b8bdbf 100644
--- a/usr/src/cmd/fm/fmd/common/fmd_main.c
+++ b/usr/src/cmd/fm/fmd/common/fmd_main.c
@@ -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 @@ _umem_logging_init(void)
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 @@ daemonize_init(void)
* 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");
/*
diff --git a/usr/src/cmd/fm/fmd/common/fmd_mdb.c b/usr/src/cmd/fm/fmd/common/fmd_mdb.c
index 05b6d25e6c..0d04f80827 100644
--- a/usr/src/cmd/fm/fmd/common/fmd_mdb.c
+++ b/usr/src/cmd/fm/fmd/common/fmd_mdb.c
@@ -483,6 +483,10 @@ module_stat(uintptr_t addr, const void *data, void *wsp)
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 @@ fmd_case(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
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);
}
diff --git a/usr/src/cmd/fm/fmd/common/fmd_module.c b/usr/src/cmd/fm/fmd/common/fmd_module.c
index ea38135c64..79db40e65e 100644
--- a/usr/src/cmd/fm/fmd/common/fmd_module.c
+++ b/usr/src/cmd/fm/fmd/common/fmd_module.c
@@ -79,6 +79,8 @@ static const fmd_modstat_t _fmd_modstat_tmpl = {
{ "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 @@ fmd_module_create(const char *path, const fmd_modops_t *ops)
(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);
diff --git a/usr/src/cmd/fm/fmd/common/fmd_module.h b/usr/src/cmd/fm/fmd/common/fmd_module.h
index ecff784549..04917065b7 100644
--- a/usr/src/cmd/fm/fmd/common/fmd_module.h
+++ b/usr/src/cmd/fm/fmd/common/fmd_module.h
@@ -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 @@ typedef struct fmd_modstat {
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 */
diff --git a/usr/src/cmd/fm/fmd/common/fmd_rpc.c b/usr/src/cmd/fm/fmd/common/fmd_rpc.c
index 9f49de24d6..dd8bfa0635 100644
--- a/usr/src/cmd/fm/fmd/common/fmd_rpc.c
+++ b/usr/src/cmd/fm/fmd/common/fmd_rpc.c
@@ -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 @@ fmd_rpc_fini(void)
/*
* 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 @@ fmd_rpc_deny(struct svc_req *rqp)
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));
}
diff --git a/usr/src/cmd/fm/fmd/common/fmd_rpc_adm.c b/usr/src/cmd/fm/fmd/common/fmd_rpc_adm.c
index 5d56382bb9..5890c6dc99 100644
--- a/usr/src/cmd/fm/fmd/common/fmd_rpc_adm.c
+++ b/usr/src/cmd/fm/fmd/common/fmd_rpc_adm.c
@@ -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_adm_logrotate_1_svc(char *name, int *rvp, struct svc_req *req)
{
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 @@ fmd_adm_logrotate_1_svc(char *name, int *rvp, struct svc_req *req)
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 @@ fmd_adm_logrotate_1_svc(char *name, int *rvp, struct svc_req *req)
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 @@ fmd_adm_logrotate_1_svc(char *name, int *rvp, struct svc_req *req)
*lpp = new;
}
- (void) pthread_rwlock_unlock(&fmd.d_log_lock);
+ (void) pthread_rwlock_unlock(lockp);
} while (new == NULL && errno == EFMD_LOG_ROTBUSY && try++ < trylimit);
diff --git a/usr/src/cmd/fm/fmd/common/fmd_self.c b/usr/src/cmd/fm/fmd/common/fmd_self.c
index 2a11fc3297..3ee514b5f3 100644
--- a/usr/src/cmd/fm/fmd/common/fmd_self.c
+++ b/usr/src/cmd/fm/fmd/common/fmd_self.c
@@ -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 @@ self_recv(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl, const char *class)
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++;
diff --git a/usr/src/cmd/fm/fmd/common/fmd_sysevent.c b/usr/src/cmd/fm/fmd/common/fmd_sysevent.c
index 1893289b00..9f46f13411 100644
--- a/usr/src/cmd/fm/fmd/common/fmd_sysevent.c
+++ b/usr/src/cmd/fm/fmd/common/fmd_sysevent.c
@@ -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 pthread_mutex_t sysev_mutex = PTHREAD_MUTEX_INITIALIZER;
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 @@ sysev_legacy(sysevent_t *sep)
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 @@ sysev_legacy(sysevent_t *sep)
(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 @@ sysev_init(fmd_hdl_t *hdl)
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 @@ sysev_init(fmd_hdl_t *hdl)
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 @@ sysev_init(fmd_hdl_t *hdl)
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 @@ sysev_fini(fmd_hdl_t *hdl)
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.
diff --git a/usr/src/cmd/fm/fmd/common/fmd_thread.c b/usr/src/cmd/fm/fmd/common/fmd_thread.c
index 1da654524c..cb0cb0745d 100644
--- a/usr/src/cmd/fm/fmd/common/fmd_thread.c
+++ b/usr/src/cmd/fm/fmd/common/fmd_thread.c
@@ -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 @@ fmd_thread_xcreate(fmd_module_t *mp, pthread_t tid)
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 @@ fmd_thread_start(void *arg)
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 @@ fmd_thread_create(fmd_module_t *mp, fmd_thread_f *func, void *arg)
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 @@ fmd_thread_create(fmd_module_t *mp, fmd_thread_f *func, void *arg)
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)
{
diff --git a/usr/src/cmd/fm/fmd/common/fmd_thread.h b/usr/src/cmd/fm/fmd/common/fmd_thread.h
index b397b1d36e..d28cccc4bc 100644
--- a/usr/src/cmd/fm/fmd/common/fmd_thread.h
+++ b/usr/src/cmd/fm/fmd/common/fmd_thread.h
@@ -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 @@ typedef struct fmd_thread {
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 */
diff --git a/usr/src/cmd/fm/fmd/common/fmd_xprt.c b/usr/src/cmd/fm/fmd/common/fmd_xprt.c
index 821e92cb5c..89054a1792 100644
--- a/usr/src/cmd/fm/fmd/common/fmd_xprt.c
+++ b/usr/src/cmd/fm/fmd/common/fmd_xprt.c
@@ -1099,7 +1099,7 @@ fmd_xprt_list_suspect_local(fmd_xprt_t *xp, nvlist_t *nvl)
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_xprt_recv(fmd_xprt_t *xp, nvlist_t *nvl, hrtime_t hrt, boolean_t logonly)
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_xprt_recv(fmd_xprt_t *xp, nvlist_t *nvl, hrtime_t hrt, boolean_t logonly)
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 @@ fmd_xprt_recv(fmd_xprt_t *xp, nvlist_t *nvl, hrtime_t hrt, boolean_t logonly)
}
/*
- * 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);
}
/*
diff --git a/usr/src/cmd/fm/fmdump/Makefile.com b/usr/src/cmd/fm/fmdump/Makefile.com
index f7fb96b0b6..4a146eb124 100644
--- a/usr/src/cmd/fm/fmdump/Makefile.com
+++ b/usr/src/cmd/fm/fmdump/Makefile.com
@@ -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)
diff --git a/usr/src/cmd/fm/fmdump/common/asru.c b/usr/src/cmd/fm/fmdump/common/asru.c
index bc17d07c63..46df5a7a12 100644
--- a/usr/src/cmd/fm/fmdump/common/asru.c
+++ b/usr/src/cmd/fm/fmdump/common/asru.c
@@ -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 @@ asru_verb1(fmd_log_t *lp, const fmd_log_record_t *rp, FILE *fp)
/*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 @@ asru_verb2(fmd_log_t *lp, const fmd_log_record_t *rp, FILE *fp)
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 @@ const fmdump_ops_t fmdump_asru_ops = {
"TIME UUID STATE",
(fmd_log_rec_f *)asru_verb2
}, {
+"TIME UUID STATE",
+(fmd_log_rec_f *)asru_pretty
+}, {
NULL, NULL
} }
};
diff --git a/usr/src/cmd/fm/fmdump/common/error.c b/usr/src/cmd/fm/fmdump/common/error.c
index e3c38f2947..62abbcf6ae 100644
--- a/usr/src/cmd/fm/fmdump/common/error.c
+++ b/usr/src/cmd/fm/fmdump/common/error.c
@@ -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 @@ err_verb1(fmd_log_t *lp, const fmd_log_record_t *rp, FILE *fp)
/*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 @@ const fmdump_ops_t fmdump_err_ops = {
"TIME CLASS",
(fmd_log_rec_f *)err_verb2
}, {
+"TIME CLASS",
+(fmd_log_rec_f *)err_pretty
+}, {
NULL, NULL
} }
};
diff --git a/usr/src/cmd/fm/fmdump/common/fault.c b/usr/src/cmd/fm/fmdump/common/fault.c
index aebf444699..268d6773c2 100644
--- a/usr/src/cmd/fm/fmdump/common/fault.c
+++ b/usr/src/cmd/fm/fmdump/common/fault.c
@@ -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 @@ flt_short(fmd_log_t *lp, const fmd_log_record_t *rp, FILE *fp)
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 && strcmp(class, FM_LIST_UPDATED_CLASS) == 0) {
- (void) snprintf(str, sizeof (str), "%s %s", code, "Updated");
- code = str;
+ if (class != NULL) {
+ int i;
+
+ 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 @@ flt_verb1(fmd_log_t *lp, const fmd_log_record_t *rp, FILE *fp)
}
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 @@ flt_verb2(fmd_log_t *lp, const fmd_log_record_t *rp, FILE *fp)
}
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 @@ flt_msg(fmd_log_t *lp, const fmd_log_record_t *rp, FILE *fp)
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
} }
diff --git a/usr/src/cmd/fm/fmdump/common/fmdump.c b/usr/src/cmd/fm/fmdump/common/fmdump.c
index a5b4bf8dd8..c4168f961a 100644
--- a/usr/src/cmd/fm/fmdump/common/fmdump.c
+++ b/usr/src/cmd/fm/fmdump/common/fmdump.c
@@ -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 @@ fmdump_warn(const char *format, ...)
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 @@ fmdump_year(char *buf, size_t len, const fmd_log_record_t *rp)
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 @@ gettimeopt(const char *arg)
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 @@ gettimeopt(const char *arg)
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 @@ gettimeopt(const char *arg)
}
}
- 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 @@ gettimeopt(const char *arg)
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 @@ gettimeopt(const char *arg)
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 @@ setupnamevalue(char *namevalue)
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 @@ setupnamevalue(char *namevalue)
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 @@ addlink(struct loglink **llp, char *dirname, char *logname, long suffix)
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 @@ get_rotated_logs(char *logpath)
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 @@ get_rotated_logs(char *logpath)
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 @@ main(int argc, char *argv[])
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 @@ main(int argc, char *argv[])
allfv[allfc++] = errfv[errfc++];
break;
case 'e':
+ if (opt_i)
+ return (usage(stderr));
opt_e++;
break;
case 'f':
@@ -587,6 +1217,16 @@ main(int argc, char *argv[])
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 @@ main(int argc, char *argv[])
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 @@ main(int argc, char *argv[])
}
}
+ 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 @@ main(int argc, char *argv[])
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 @@ main(int argc, char *argv[])
}
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 @@ main(int argc, char *argv[])
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 @@ main(int argc, char *argv[])
}
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 @@ main(int argc, char *argv[])
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 @@ main(int argc, char *argv[])
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 @@ main(int argc, char *argv[])
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 @@ main(int argc, char *argv[])
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 @@ main(int argc, char *argv[])
} 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 @@ main(int argc, char *argv[])
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);
}
diff --git a/usr/src/cmd/fm/fmdump/common/fmdump.h b/usr/src/cmd/fm/fmdump/common/fmdump.h
index 567b65a7e4..ad3577756f 100644
--- a/usr/src/cmd/fm/fmdump/common/fmdump.h
+++ b/usr/src/cmd/fm/fmdump/common/fmdump.h
@@ -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 @@ extern "C" {
#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 @@ typedef struct fmdump_lyr {
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_date(char *, size_t, const fmd_log_record_t *);
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
diff --git a/usr/src/cmd/fm/fmdump/common/info.c b/usr/src/cmd/fm/fmdump/common/info.c
new file mode 100644
index 0000000000..943dbbc0c1
--- /dev/null
+++ b/usr/src/cmd/fm/fmdump/common/info.c
@@ -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
+} }
+};
diff --git a/usr/src/cmd/fm/fmdump/common/nvlrender.c b/usr/src/cmd/fm/fmdump/common/nvlrender.c
new file mode 100644
index 0000000000..4a55d315c4
--- /dev/null
+++ b/usr/src/cmd/fm/fmdump/common/nvlrender.c
@@ -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);
+}
diff --git a/usr/src/cmd/fm/fmtopo/common/fmtopo.c b/usr/src/cmd/fm/fmtopo/common/fmtopo.c
index 43ec004cf5..b2e26c596f 100644
--- a/usr/src/cmd/fm/fmtopo/common/fmtopo.c
+++ b/usr/src/cmd/fm/fmtopo/common/fmtopo.c
@@ -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 @@ walk_topo(topo_hdl_t *thp, char *uuid)
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 @@ main(int argc, char *argv[])
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) {
diff --git a/usr/src/cmd/fm/modules/Makefile.plugin b/usr/src/cmd/fm/modules/Makefile.plugin
index 3ff34f2d63..a8a0f00c83 100644
--- a/usr/src/cmd/fm/modules/Makefile.plugin
+++ b/usr/src/cmd/fm/modules/Makefile.plugin
@@ -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 @@ OBJS = $(YOBJS) $(SRCS:%.c=%.o)
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 @@ LINTFILES = $(SRCS:%.c=%.ln)
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 @@ all: $(PROG) $(DMODPROG)
.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 @@ $(DMODPROG): $(DMOD_OBJS) $(MAPFILE-DMOD)
$(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 @@ $(ROOTDMOD): $$(@D) $(DMODPROG)
install: $(ROOTPROG) $(ROOTCONF) $(ROOTDMOD)
-include ../../Makefile.rootdirs
+include $(SRC)/cmd/fm/modules/Makefile.rootdirs
diff --git a/usr/src/cmd/fm/modules/common/Makefile b/usr/src/cmd/fm/modules/common/Makefile
index c99aac5034..27c00d9b08 100644
--- a/usr/src/cmd/fm/modules/common/Makefile
+++ b/usr/src/cmd/fm/modules/common/Makefile
@@ -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 @@ SUBDIRS = cpumem-retire \
ip-transport \
sensor-transport \
ses-log-transport \
- snmp-trapgen \
+ sw-diag-response \
sp-monitor \
syslog-msgs \
zfs-diagnosis \
diff --git a/usr/src/cmd/fm/modules/common/eversholt/eft.c b/usr/src/cmd/fm/modules/common/eversholt/eft.c
index 6fde202904..7ef63c0f15 100644
--- a/usr/src/cmd/fm/modules/common/eversholt/eft.c
+++ b/usr/src/cmd/fm/modules/common/eversholt/eft.c
@@ -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 @@ dodiscardprint(struct node *lhs, struct node *rhs, void *arg)
{
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 @@ call_finis(void)
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 @@ _fmd_init(fmd_hdl_t *hdl)
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);
}
diff --git a/usr/src/cmd/fm/modules/common/eversholt/fme.c b/usr/src/cmd/fm/modules/common/eversholt/fme.c
index d1a73c563b..24ee852a4e 100644
--- a/usr/src/cmd/fm/modules/common/eversholt/fme.c
+++ b/usr/src/cmd/fm/modules/common/eversholt/fme.c
@@ -2779,14 +2779,14 @@ publish_suspects(struct fme *fmep, struct rsl *srl)
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;
}
diff --git a/usr/src/cmd/fm/modules/common/eversholt/platform.c b/usr/src/cmd/fm/modules/common/eversholt/platform.c
index e27c1bf127..72238b9da2 100644
--- a/usr/src/cmd/fm/modules/common/eversholt/platform.c
+++ b/usr/src/cmd/fm/modules/common/eversholt/platform.c
@@ -867,7 +867,7 @@ platform_get_files(const char *dirname[], const char *fnstr, int nodups)
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 @@ platform_get_files(const char *dirname[], const char *fnstr, int nodups)
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 @@ platform_get_files(const char *dirname[], const char *fnstr, int nodups)
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);
diff --git a/usr/src/cmd/fm/modules/common/ext-event-transport/Makefile b/usr/src/cmd/fm/modules/common/ext-event-transport/Makefile
index 9639cc5177..8603f409e1 100644
--- a/usr/src/cmd/fm/modules/common/ext-event-transport/Makefile
+++ b/usr/src/cmd/fm/modules/common/ext-event-transport/Makefile
@@ -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
diff --git a/usr/src/cmd/fm/modules/common/ext-event-transport/ext-event-transport.conf b/usr/src/cmd/fm/modules/common/ext-event-transport/ext-event-transport.conf
index c98edaa48e..f17acbbec1 100644
--- a/usr/src/cmd/fm/modules/common/ext-event-transport/ext-event-transport.conf
+++ b/usr/src/cmd/fm/modules/common/ext-event-transport/ext-event-transport.conf
@@ -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 @@ setprop protocol_forward_disable false
# Changing this list may lead to breakage and/or excessive event forwarding.
#
subscribe list.*
-subscribe swevent.*
-
+subscribe ireport.*
diff --git a/usr/src/cmd/fm/modules/common/ext-event-transport/fmevt.h b/usr/src/cmd/fm/modules/common/ext-event-transport/fmevt.h
index 959adb63f5..c8074002c6 100644
--- a/usr/src/cmd/fm/modules/common/ext-event-transport/fmevt.h
+++ b/usr/src/cmd/fm/modules/common/ext-event-transport/fmevt.h
@@ -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 const fmd_prop_t fmevt_props[];
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
diff --git a/usr/src/cmd/fm/modules/common/ext-event-transport/fmevt_inbound.c b/usr/src/cmd/fm/modules/common/ext-event-transport/fmevt_inbound.c
new file mode 100644
index 0000000000..a83d2e6e50
--- /dev/null
+++ b/usr/src/cmd/fm/modules/common/ext-event-transport/fmevt_inbound.c
@@ -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);
+ }
+
+}
diff --git a/usr/src/cmd/fm/modules/common/ext-event-transport/fmevt_inbound_default.c b/usr/src/cmd/fm/modules/common/ext-event-transport/fmevt_inbound_default.c
new file mode 100644
index 0000000000..21ae6cd594
--- /dev/null
+++ b/usr/src/cmd/fm/modules/common/ext-event-transport/fmevt_inbound_default.c
@@ -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);
+}
diff --git a/usr/src/cmd/fm/modules/common/ext-event-transport/fmevt_inbound_on.c b/usr/src/cmd/fm/modules/common/ext-event-transport/fmevt_inbound_on.c
new file mode 100644
index 0000000000..dbf7e5c1b0
--- /dev/null
+++ b/usr/src/cmd/fm/modules/common/ext-event-transport/fmevt_inbound_on.c
@@ -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);
+}
diff --git a/usr/src/cmd/fm/modules/common/ext-event-transport/fmevt_inbound_smf.c b/usr/src/cmd/fm/modules/common/ext-event-transport/fmevt_inbound_smf.c
new file mode 100644
index 0000000000..cf91306d44
--- /dev/null
+++ b/usr/src/cmd/fm/modules/common/ext-event-transport/fmevt_inbound_smf.c
@@ -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);
+}
diff --git a/usr/src/cmd/fm/modules/common/ext-event-transport/fmevt_inbound_sunos.c b/usr/src/cmd/fm/modules/common/ext-event-transport/fmevt_inbound_sunos.c
new file mode 100644
index 0000000000..94b3234896
--- /dev/null
+++ b/usr/src/cmd/fm/modules/common/ext-event-transport/fmevt_inbound_sunos.c
@@ -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);
+}
diff --git a/usr/src/cmd/fm/modules/common/ext-event-transport/fmevt_main.c b/usr/src/cmd/fm/modules/common/ext-event-transport/fmevt_main.c
index 7697468e2c..558fa190b6 100644
--- a/usr/src/cmd/fm/modules/common/ext-event-transport/fmevt_main.c
+++ b/usr/src/cmd/fm/modules/common/ext-event-transport/fmevt_main.c
@@ -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 @@ const fmd_prop_t fmevt_props[] = {
{ "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_ops_t fmd_ops = {
};
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 @@ _fmd_init(fmd_hdl_t *hdl)
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);
}
diff --git a/usr/src/cmd/fm/modules/common/ext-event-transport/fmevt_outbound.c b/usr/src/cmd/fm/modules/common/ext-event-transport/fmevt_outbound.c
index 9ff06eb2e0..46bc458072 100644
--- a/usr/src/cmd/fm/modules/common/ext-event-transport/fmevt_outbound.c
+++ b/usr/src/cmd/fm/modules/common/ext-event-transport/fmevt_outbound.c
@@ -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 evchan_t *fmevt_outbound_chan;
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 @@ static struct fmevt_outbound_stats {
"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 @@ static struct fmevt_outbound_stats {
#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 @@ fmevt_recv(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl, const char *class)
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 @@ fmevt_init_outbound(fmd_hdl_t *hdl)
{
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 @@ fmevt_init_outbound(fmd_hdl_t *hdl)
fmd_hdl_abort(hdl, "Unable to set depth of channel %s to %d",
channel_name, channel_depth);
}
-
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*/
diff --git a/usr/src/cmd/fm/modules/common/sensor-transport/sensor_transport.c b/usr/src/cmd/fm/modules/common/sensor-transport/sensor_transport.c
index b5eafc8367..6bf4da305a 100644
--- a/usr/src/cmd/fm/modules/common/sensor-transport/sensor_transport.c
+++ b/usr/src/cmd/fm/modules/common/sensor-transport/sensor_transport.c
@@ -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 @@ st_stats_t st_stats = {
{ "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 @@ st_check_component(topo_hdl_t *thp, tnode_t *node, void *arg)
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 @@ out:
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 @@ st_timeout(fmd_hdl_t *hdl, id_t id, void *data)
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 @@ st_timeout(fmd_hdl_t *hdl, id_t id, void *data)
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
diff --git a/usr/src/cmd/fm/modules/common/snmp-trapgen/snmp.c b/usr/src/cmd/fm/modules/common/snmp-trapgen/snmp.c
deleted file mode 100644
index 8a4941945b..0000000000
--- a/usr/src/cmd/fm/modules/common/snmp-trapgen/snmp.c
+++ /dev/null
@@ -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();
-}
diff --git a/usr/src/cmd/fm/modules/common/snmp-trapgen/fmd-trapgen.conf b/usr/src/cmd/fm/modules/common/sw-diag-response/Makefile
index 819cf970f5..a481565e89 100644
--- a/usr/src/cmd/fm/modules/common/snmp-trapgen/fmd-trapgen.conf
+++ b/usr/src/cmd/fm/modules/common/sw-diag-response/Makefile
@@ -18,16 +18,10 @@
#
# CDDL HEADER END
#
-
-#
-# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
-# Use is subject to license terms.
#
+# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
#
-#
-# 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).
-#
+SUBDIRS = software-diagnosis software-response
+
+include ../../../Makefile.subdirs
diff --git a/usr/src/cmd/fm/modules/common/sw-diag-response/Makefile.com b/usr/src/cmd/fm/modules/common/sw-diag-response/Makefile.com
new file mode 100644
index 0000000000..64afda3c41
--- /dev/null
+++ b/usr/src/cmd/fm/modules/common/sw-diag-response/Makefile.com
@@ -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
diff --git a/usr/src/cmd/fm/modules/common/sw-diag-response/common/sw.h b/usr/src/cmd/fm/modules/common/sw-diag-response/common/sw.h
new file mode 100644
index 0000000000..14d9bd3642
--- /dev/null
+++ b/usr/src/cmd/fm/modules/common/sw-diag-response/common/sw.h
@@ -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 */
diff --git a/usr/src/cmd/fm/modules/common/sw-diag-response/common/sw_impl.h b/usr/src/cmd/fm/modules/common/sw-diag-response/common/sw_impl.h
new file mode 100644
index 0000000000..11a1f15d00
--- /dev/null
+++ b/usr/src/cmd/fm/modules/common/sw-diag-response/common/sw_impl.h
@@ -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 */
diff --git a/usr/src/cmd/fm/modules/common/sw-diag-response/common/sw_main_cmn.c b/usr/src/cmd/fm/modules/common/sw-diag-response/common/sw_main_cmn.c
new file mode 100644
index 0000000000..964f725c44
--- /dev/null
+++ b/usr/src/cmd/fm/modules/common/sw-diag-response/common/sw_main_cmn.c
@@ -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));
+}
diff --git a/usr/src/cmd/fm/modules/common/sw-diag-response/software-diagnosis/Makefile b/usr/src/cmd/fm/modules/common/sw-diag-response/software-diagnosis/Makefile
new file mode 100644
index 0000000000..816132fe18
--- /dev/null
+++ b/usr/src/cmd/fm/modules/common/sw-diag-response/software-diagnosis/Makefile
@@ -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
diff --git a/usr/src/cmd/fm/modules/common/sw-diag-response/software-diagnosis/software-diagnosis.conf b/usr/src/cmd/fm/modules/common/sw-diag-response/software-diagnosis/software-diagnosis.conf
new file mode 100644
index 0000000000..b2046eaaff
--- /dev/null
+++ b/usr/src/cmd/fm/modules/common/sw-diag-response/software-diagnosis/software-diagnosis.conf
@@ -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
diff --git a/usr/src/cmd/fm/modules/common/sw-diag-response/software-diagnosis/swde_case.c b/usr/src/cmd/fm/modules/common/sw-diag-response/software-diagnosis/swde_case.c
new file mode 100644
index 0000000000..dce11967c5
--- /dev/null
+++ b/usr/src/cmd/fm/modules/common/sw-diag-response/software-diagnosis/swde_case.c
@@ -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)
+{
+}
diff --git a/usr/src/cmd/fm/modules/common/sw-diag-response/software-diagnosis/swde_main.c b/usr/src/cmd/fm/modules/common/sw-diag-response/software-diagnosis/swde_main.c
new file mode 100644
index 0000000000..e0271a4c5a
--- /dev/null
+++ b/usr/src/cmd/fm/modules/common/sw-diag-response/software-diagnosis/swde_main.c
@@ -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);
+}
diff --git a/usr/src/cmd/fm/modules/common/sw-diag-response/software-response/Makefile b/usr/src/cmd/fm/modules/common/sw-diag-response/software-response/Makefile
new file mode 100644
index 0000000000..21369ce36f
--- /dev/null
+++ b/usr/src/cmd/fm/modules/common/sw-diag-response/software-response/Makefile
@@ -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
diff --git a/usr/src/cmd/fm/modules/common/snmp-trapgen/snmp-trapgen.conf b/usr/src/cmd/fm/modules/common/sw-diag-response/software-response/software-response.conf
index 68016a2b58..87dea5f756 100644
--- a/usr/src/cmd/fm/modules/common/snmp-trapgen/snmp-trapgen.conf
+++ b/usr/src/cmd/fm/modules/common/sw-diag-response/software-response/software-response.conf
@@ -18,14 +18,12 @@
#
# CDDL HEADER END
#
-
-#
-# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
-# Use is subject to license terms.
#
-# ident "%Z%%M% %I% %E% SMI"
+# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
#
#
-# snmp-trapgen agent properties:
+# Configuration for the software-diagnosis diagnosis engine.
#
+
+subscribe list.repaired
diff --git a/usr/src/cmd/fm/modules/common/sw-diag-response/software-response/swrp_main.c b/usr/src/cmd/fm/modules/common/sw-diag-response/software-response/swrp_main.c
new file mode 100644
index 0000000000..83a1d461e1
--- /dev/null
+++ b/usr/src/cmd/fm/modules/common/sw-diag-response/software-response/swrp_main.c
@@ -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);
+}
diff --git a/usr/src/cmd/fm/modules/common/sw-diag-response/subsidiary/panic/panic.h b/usr/src/cmd/fm/modules/common/sw-diag-response/subsidiary/panic/panic.h
new file mode 100644
index 0000000000..11115e6f24
--- /dev/null
+++ b/usr/src/cmd/fm/modules/common/sw-diag-response/subsidiary/panic/panic.h
@@ -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 */
diff --git a/usr/src/cmd/fm/modules/common/sw-diag-response/subsidiary/panic/panic_diag.c b/usr/src/cmd/fm/modules/common/sw-diag-response/subsidiary/panic/panic_diag.c
new file mode 100644
index 0000000000..399f3f7676
--- /dev/null
+++ b/usr/src/cmd/fm/modules/common/sw-diag-response/subsidiary/panic/panic_diag.c
@@ -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 */
+};
diff --git a/usr/src/cmd/fm/modules/common/sw-diag-response/subsidiary/smf/smf.h b/usr/src/cmd/fm/modules/common/sw-diag-response/subsidiary/smf/smf.h
new file mode 100644
index 0000000000..56bb6fcdbc
--- /dev/null
+++ b/usr/src/cmd/fm/modules/common/sw-diag-response/subsidiary/smf/smf.h
@@ -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 */
diff --git a/usr/src/cmd/fm/modules/common/sw-diag-response/subsidiary/smf/smf_diag.c b/usr/src/cmd/fm/modules/common/sw-diag-response/subsidiary/smf/smf_diag.c
new file mode 100644
index 0000000000..7fee4b58f5
--- /dev/null
+++ b/usr/src/cmd/fm/modules/common/sw-diag-response/subsidiary/smf/smf_diag.c
@@ -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 */
+};
diff --git a/usr/src/cmd/fm/modules/common/sw-diag-response/subsidiary/smf/smf_response.c b/usr/src/cmd/fm/modules/common/sw-diag-response/subsidiary/smf/smf_response.c
new file mode 100644
index 0000000000..5a2a164c3e
--- /dev/null
+++ b/usr/src/cmd/fm/modules/common/sw-diag-response/subsidiary/smf/smf_response.c
@@ -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 */
+};
diff --git a/usr/src/cmd/fm/modules/common/sw-diag-response/subsidiary/smf/smf_util.c b/usr/src/cmd/fm/modules/common/sw-diag-response/subsidiary/smf/smf_util.c
new file mode 100644
index 0000000000..f033da5765
--- /dev/null
+++ b/usr/src/cmd/fm/modules/common/sw-diag-response/subsidiary/smf/smf_util.c
@@ -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 */
+}
diff --git a/usr/src/cmd/fm/modules/common/syslog-msgs/Makefile b/usr/src/cmd/fm/modules/common/syslog-msgs/Makefile
index 80276353b0..76c75665ee 100644
--- a/usr/src/cmd/fm/modules/common/syslog-msgs/Makefile
+++ b/usr/src/cmd/fm/modules/common/syslog-msgs/Makefile
@@ -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 @@ include ../../Makefile.plugin
LDFLAGS += -L $(ROOT)/usr/lib/fm -R/usr/lib/fm
LINTFLAGS += -L$(ROOT)/usr/lib/fm
-LDLIBS += -lfmd_msg
+LDLIBS += -lfmd_msg -lscf
diff --git a/usr/src/cmd/fm/modules/common/syslog-msgs/syslog.c b/usr/src/cmd/fm/modules/common/syslog-msgs/syslog.c
index 1908417929..30b817b996 100644
--- a/usr/src/cmd/fm/modules/common/syslog-msgs/syslog.c
+++ b/usr/src/cmd/fm/modules/common/syslog-msgs/syslog.c
@@ -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 @@ syslog_emit(fmd_hdl_t *hdl, const char *msg)
}
}
+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 @@ syslog_recv(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl, const char *class)
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_recv(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl, const char *class)
}
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_ops_t fmd_ops = {
};
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 @@ _fmd_init(fmd_hdl_t *hdl)
(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*/
diff --git a/usr/src/cmd/fm/modules/sun4u/fps-transport/fps-transport.c b/usr/src/cmd/fm/modules/sun4u/fps-transport/fps-transport.c
index 9f8b61b530..e085eb2569 100644
--- a/usr/src/cmd/fm/modules/sun4u/fps-transport/fps-transport.c
+++ b/usr/src/cmd/fm/modules/sun4u/fps-transport/fps-transport.c
@@ -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 @@ static struct sysev_stats {
{ "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 @@ _fmd_fini(fmd_hdl_t *handle)
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_init(fmd_hdl_t *hdl)
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);
diff --git a/usr/src/cmd/fm/notify/Makefile b/usr/src/cmd/fm/notify/Makefile
new file mode 100644
index 0000000000..951304e508
--- /dev/null
+++ b/usr/src/cmd/fm/notify/Makefile
@@ -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
diff --git a/usr/src/cmd/fm/notify/Makefile.subdirs b/usr/src/cmd/fm/notify/Makefile.subdirs
new file mode 100644
index 0000000000..ab4a42da0e
--- /dev/null
+++ b/usr/src/cmd/fm/notify/Makefile.subdirs
@@ -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:
diff --git a/usr/src/cmd/fm/notify/notify-params.xml b/usr/src/cmd/fm/notify/notify-params.xml
new file mode 100644
index 0000000000..800383c29b
--- /dev/null
+++ b/usr/src/cmd/fm/notify/notify-params.xml
@@ -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>
diff --git a/usr/src/cmd/fm/notify/smtp-notify/Makefile b/usr/src/cmd/fm/notify/smtp-notify/Makefile
new file mode 100644
index 0000000000..7fb6cb4380
--- /dev/null
+++ b/usr/src/cmd/fm/notify/smtp-notify/Makefile
@@ -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
diff --git a/usr/src/cmd/fm/notify/smtp-notify/Makefile.com b/usr/src/cmd/fm/notify/smtp-notify/Makefile.com
new file mode 100644
index 0000000000..d969986fb4
--- /dev/null
+++ b/usr/src/cmd/fm/notify/smtp-notify/Makefile.com
@@ -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)
diff --git a/usr/src/cmd/fm/notify/smtp-notify/common/process_msg_template.sh b/usr/src/cmd/fm/notify/smtp-notify/common/process_msg_template.sh
new file mode 100644
index 0000000000..eb9bcf82db
--- /dev/null
+++ b/usr/src/cmd/fm/notify/smtp-notify/common/process_msg_template.sh
@@ -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
diff --git a/usr/src/cmd/fm/notify/smtp-notify/common/smtp-notify.c b/usr/src/cmd/fm/notify/smtp-notify/common/smtp-notify.c
new file mode 100644
index 0000000000..a94baffed6
--- /dev/null
+++ b/usr/src/cmd/fm/notify/smtp-notify/common/smtp-notify.c
@@ -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);
+}
diff --git a/usr/src/cmd/fm/notify/smtp-notify/common/smtp-notify.xml b/usr/src/cmd/fm/notify/smtp-notify/common/smtp-notify.xml
new file mode 100644
index 0000000000..4e55e0fd83
--- /dev/null
+++ b/usr/src/cmd/fm/notify/smtp-notify/common/smtp-notify.xml
@@ -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>
diff --git a/usr/src/cmd/fm/notify/smtp-notify/i386/Makefile b/usr/src/cmd/fm/notify/smtp-notify/i386/Makefile
new file mode 100644
index 0000000000..ebcbee3118
--- /dev/null
+++ b/usr/src/cmd/fm/notify/smtp-notify/i386/Makefile
@@ -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
diff --git a/usr/src/cmd/fm/notify/smtp-notify/sparc/Makefile b/usr/src/cmd/fm/notify/smtp-notify/sparc/Makefile
new file mode 100644
index 0000000000..ebcbee3118
--- /dev/null
+++ b/usr/src/cmd/fm/notify/smtp-notify/sparc/Makefile
@@ -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
diff --git a/usr/src/cmd/fm/notify/snmp-notify/Makefile b/usr/src/cmd/fm/notify/snmp-notify/Makefile
new file mode 100644
index 0000000000..7fb6cb4380
--- /dev/null
+++ b/usr/src/cmd/fm/notify/snmp-notify/Makefile
@@ -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
diff --git a/usr/src/cmd/fm/notify/snmp-notify/Makefile.com b/usr/src/cmd/fm/notify/snmp-notify/Makefile.com
new file mode 100644
index 0000000000..ff6b3b5a38
--- /dev/null
+++ b/usr/src/cmd/fm/notify/snmp-notify/Makefile.com
@@ -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)
diff --git a/usr/src/cmd/fm/notify/snmp-notify/common/snmp-notify.c b/usr/src/cmd/fm/notify/snmp-notify/common/snmp-notify.c
new file mode 100644
index 0000000000..76ed1637a0
--- /dev/null
+++ b/usr/src/cmd/fm/notify/snmp-notify/common/snmp-notify.c
@@ -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);
+}
diff --git a/usr/src/cmd/fm/notify/snmp-notify/common/snmp-notify.xml b/usr/src/cmd/fm/notify/snmp-notify/common/snmp-notify.xml
new file mode 100644
index 0000000000..17dc4b2923
--- /dev/null
+++ b/usr/src/cmd/fm/notify/snmp-notify/common/snmp-notify.xml
@@ -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>
diff --git a/usr/src/cmd/fm/notify/snmp-notify/i386/Makefile b/usr/src/cmd/fm/notify/snmp-notify/i386/Makefile
new file mode 100644
index 0000000000..ebcbee3118
--- /dev/null
+++ b/usr/src/cmd/fm/notify/snmp-notify/i386/Makefile
@@ -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
diff --git a/usr/src/cmd/fm/notify/snmp-notify/sparc/Makefile b/usr/src/cmd/fm/notify/snmp-notify/sparc/Makefile
new file mode 100644
index 0000000000..ebcbee3118
--- /dev/null
+++ b/usr/src/cmd/fm/notify/snmp-notify/sparc/Makefile
@@ -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
diff --git a/usr/src/cmd/fm/schemes/Makefile b/usr/src/cmd/fm/schemes/Makefile
index d2ed631f22..6c7c29d9a7 100644
--- a/usr/src/cmd/fm/schemes/Makefile
+++ b/usr/src/cmd/fm/schemes/Makefile
@@ -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 @@ SUBDIRS = \
mem \
mod \
pkg \
+ sw \
svc \
zfs
diff --git a/usr/src/cmd/fm/schemes/sw/Makefile b/usr/src/cmd/fm/schemes/sw/Makefile
new file mode 100644
index 0000000000..836f1e9ad6
--- /dev/null
+++ b/usr/src/cmd/fm/schemes/sw/Makefile
@@ -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
diff --git a/usr/src/cmd/fm/schemes/sw/Makefile.com b/usr/src/cmd/fm/schemes/sw/Makefile.com
new file mode 100644
index 0000000000..fb791402f3
--- /dev/null
+++ b/usr/src/cmd/fm/schemes/sw/Makefile.com
@@ -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
diff --git a/usr/src/cmd/fm/schemes/sw/amd64/Makefile b/usr/src/cmd/fm/schemes/sw/amd64/Makefile
new file mode 100644
index 0000000000..eeb5380cf2
--- /dev/null
+++ b/usr/src/cmd/fm/schemes/sw/amd64/Makefile
@@ -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)
diff --git a/usr/src/cmd/fm/modules/common/snmp-trapgen/Makefile b/usr/src/cmd/fm/schemes/sw/i386/Makefile
index ed7132aef4..b85ad39b94 100644
--- a/usr/src/cmd/fm/modules/common/snmp-trapgen/Makefile
+++ b/usr/src/cmd/fm/schemes/sw/i386/Makefile
@@ -20,31 +20,14 @@
#
#
-# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
-# Use is subject to license terms.
+# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
#
-#
-
-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 =
+include ../Makefile.com
+include ../../Makefile.targ
+LDLIBS += -ltopo
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)
+install: all $(ROOTPROG)
diff --git a/usr/src/cmd/fm/schemes/sw/scheme.c b/usr/src/cmd/fm/schemes/sw/scheme.c
new file mode 100644
index 0000000000..13bfcf37dd
--- /dev/null
+++ b/usr/src/cmd/fm/schemes/sw/scheme.c
@@ -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);
+}
diff --git a/usr/src/cmd/fm/schemes/sw/sparc/Makefile b/usr/src/cmd/fm/schemes/sw/sparc/Makefile
new file mode 100644
index 0000000000..b85ad39b94
--- /dev/null
+++ b/usr/src/cmd/fm/schemes/sw/sparc/Makefile
@@ -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)
diff --git a/usr/src/cmd/fm/schemes/sw/sparcv9/Makefile b/usr/src/cmd/fm/schemes/sw/sparcv9/Makefile
new file mode 100644
index 0000000000..eeb5380cf2
--- /dev/null
+++ b/usr/src/cmd/fm/schemes/sw/sparcv9/Makefile
@@ -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)
diff --git a/usr/src/cmd/fm/scripts/dictck.pl b/usr/src/cmd/fm/scripts/dictck.pl
index f0a3d144d9..cfb07d3d46 100644
--- a/usr/src/cmd/fm/scripts/dictck.pl
+++ b/usr/src/cmd/fm/scripts/dictck.pl
@@ -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 @@ $SIG{HUP} = $SIG{INT} = $SIG{TERM} = $SIG{__DIE__} = sub {
};
#
+# 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 @@ sub dodict {
# 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;
diff --git a/usr/src/cmd/logadm/Makefile b/usr/src/cmd/logadm/Makefile
index 572fa41ba0..818483159e 100644
--- a/usr/src/cmd/logadm/Makefile
+++ b/usr/src/cmd/logadm/Makefile
@@ -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 @@ $(ROOTETC)/$(CONFIGFILE):= FILEMODE= 644
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 @@ $(PROG): $(OBJS)
$(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) $@
diff --git a/usr/src/cmd/logadm/logadm-upgrade b/usr/src/cmd/logadm/logadm-upgrade
new file mode 100644
index 0000000000..e606b32365
--- /dev/null
+++ b/usr/src/cmd/logadm/logadm-upgrade
@@ -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}
+
diff --git a/usr/src/cmd/logadm/logadm-upgrade.xml b/usr/src/cmd/logadm/logadm-upgrade.xml
new file mode 100644
index 0000000000..77bfe3a3d9
--- /dev/null
+++ b/usr/src/cmd/logadm/logadm-upgrade.xml
@@ -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>
+
diff --git a/usr/src/cmd/logadm/logadm.conf b/usr/src/cmd/logadm/logadm.conf
index c5e385305f..7b19473a19 100644
--- a/usr/src/cmd/logadm/logadm.conf
+++ b/usr/src/cmd/logadm/logadm.conf
@@ -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
#
diff --git a/usr/src/cmd/mdb/common/kmdb/kmdb_kvm.c b/usr/src/cmd/mdb/common/kmdb/kmdb_kvm.c
index 93bbece1c8..761b448f4f 100644
--- a/usr/src/cmd/mdb/common/kmdb/kmdb_kvm.c
+++ b/usr/src/cmd/mdb/common/kmdb/kmdb_kvm.c
@@ -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_status_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
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 @@ kmt_status_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
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));
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_kvm.c b/usr/src/cmd/mdb/common/mdb/mdb_kvm.c
index 5d0da621cd..4b368e357b 100644
--- a/usr/src/cmd/mdb/common/mdb/mdb_kvm.c
+++ b/usr/src/cmd/mdb/common/mdb/mdb_kvm.c
@@ -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 @@ kt_status_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
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);
diff --git a/usr/src/cmd/mdb/common/modules/svc.startd/startd.c b/usr/src/cmd/mdb/common/modules/svc.startd/startd.c
index 1f163db4a0..4fae0df7f1 100644
--- a/usr/src/cmd/mdb/common/modules/svc.startd/startd.c
+++ b/usr/src/cmd/mdb/common/modules/svc.startd/startd.c
@@ -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 @@ startd_status(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
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 @@ startd_status(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
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);
}
diff --git a/usr/src/cmd/savecore/Makefile.com b/usr/src/cmd/savecore/Makefile.com
index 32bb2ea7a2..591f33395b 100644
--- a/usr/src/cmd/savecore/Makefile.com
+++ b/usr/src/cmd/savecore/Makefile.com
@@ -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 @@ OBJS= savecore.o compress.o
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 @@ $(PROG): $(OBJS) $(BZIP2OBJS)
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
diff --git a/usr/src/cmd/savecore/amd64/Makefile b/usr/src/cmd/savecore/amd64/Makefile
index 707654ac57..b6c2d55e76 100644
--- a/usr/src/cmd/savecore/amd64/Makefile
+++ b/usr/src/cmd/savecore/amd64/Makefile
@@ -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)
diff --git a/usr/src/cmd/savecore/i386/Makefile b/usr/src/cmd/savecore/i386/Makefile
index 35b465830e..1c19187ee8 100644
--- a/usr/src/cmd/savecore/i386/Makefile
+++ b/usr/src/cmd/savecore/i386/Makefile
@@ -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)
diff --git a/usr/src/cmd/savecore/savecore.c b/usr/src/cmd/savecore/savecore.c
index 5e4eb10239..fdcbd2386f 100644
--- a/usr/src/cmd/savecore/savecore.c
+++ b/usr/src/cmd/savecore/savecore.c
@@ -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 saved; /* count of pages written */
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 @@ usage(void)
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 @@ Open(const char *name, int oflags, mode_t mode)
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 @@ static void
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 @@ static void
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 @@ static void
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 @@ static void
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 @@ Pread(int fd, void *buf, size_t size, offset_t off)
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 @@ static void
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 @@ Zalloc(size_t size)
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 @@ read_dumphdr(void)
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 @@ read_dumphdr(void)
*/
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 @@ check_space(int csave)
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 @@ check_space(int csave)
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 @@ build_dump_map(int corefd, const pfn_t *pfn_table)
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 @@ build_dump_map(int corefd, const pfn_t *pfn_table)
Pwrite(corefd, dmp, dump_mapsize, corehdr.dump_map);
free(dmp);
- fclose(in);
+ (void) fclose(in);
free(inbuf);
}
@@ -369,7 +515,7 @@ Copy(offset_t dumpoff, len_t nb, offset_t *offp, int fd, char *buf,
* 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 @@ CopyPages(offset_t dumpoff, offset_t *offp, int fd, char *buf, size_t sz)
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 @@ CopyPages(offset_t dumpoff, offset_t *offp, int fd, char *buf, size_t sz)
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 @@ CopyPages(offset_t dumpoff, offset_t *offp, int fd, char *buf, size_t sz)
*offp += csize;
np--;
}
- fclose(in);
- fclose(out);
+ (void) fclose(in);
+ (void) fclose(out);
free(outbuf);
free(buf);
}
@@ -418,7 +564,7 @@ copy_crashfile(const char *corefile)
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_crashfile(const char *corefile)
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 @@ copy_crashfile(const char *corefile)
*
* 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 @@ initstreams(int corefd, int nstreams, int maxcsize)
}
/* 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 @@ initstreams(int corefd, int nstreams, int maxcsize)
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 @@ sbarrier()
{
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 @@ stopstreams()
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 @@ getfreeblock()
{
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 @@ iszpage(char *buf)
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 @@ putpage(int corefd, char *buf, pgcnt_t pgnum, pgcnt_t np)
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 @@ lzjbblock(int corefd, stream_t *s, char *block, size_t blocksz)
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 @@ lzjbblock(int corefd, stream_t *s, char *block, size_t blocksz)
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 @@ lzjbblock(int corefd, stream_t *s, char *block, size_t blocksz)
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 @@ bz2decompress(stream_t *s, void *buf, size_t size)
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 @@ bz2block(int corefd, stream_t *s, char *block, size_t blocksz)
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 @@ bz2block(int corefd, stream_t *s, char *block, size_t blocksz)
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 @@ runstreams(void *arg)
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 @@ runstreams(void *arg)
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 @@ runstreams(void *arg)
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 @@ decompress_pages(int corefd)
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 @@ decompress_pages(int corefd)
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 @@ decompress_pages(int corefd)
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 @@ decompress_pages(int corefd)
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 @@ decompress_pages(int corefd)
stopstreams();
if (tracef != NULL)
- fclose(tracef);
- fclose(dumpf);
+ (void) fclose(tracef);
+ (void) fclose(dumpf);
if (inbuf)
free(inbuf);
if (cpage)
@@ -1195,7 +1342,8 @@ build_corefile(const char *namelist, const char *corefile)
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 @@ build_corefile(const char *namelist, const char *corefile)
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 @@ build_corefile(const char *namelist, const char *corefile)
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 @@ message_save(void)
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 @@ message_save(void)
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 @@ getbounds(const char *f)
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 @@ main(int argc, char *argv[])
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 @@ main(int argc, char *argv[])
case 'v':
verbose++;
break;
+ case 'c':
+ cflag++;
+ break;
case 'd':
disregard_valid_flag++;
break;
@@ -1402,13 +1683,16 @@ main(int argc, char *argv[])
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 @@ main(int argc, char *argv[])
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 @@ main(int argc, char *argv[])
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,16 +1758,55 @@ main(int argc, char *argv[])
(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_flags & DF_COMPLETE) == 0)
- logprint(LOG_WARNING, 1, -1, "incomplete dump on dump device");
+ if (dumphdr.dump_fm_panic)
+ fm_panic = B_TRUE;
- logprint(LOG_WARNING, 1, -1, "System dump time: %s",
+ /*
+ * 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));
+ /*
+ * 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(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);
if (filebounds < 0)
@@ -1495,12 +1821,20 @@ main(int argc, char *argv[])
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 @@ main(int argc, char *argv[])
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 @@ main(int argc, char *argv[])
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 @@ main(int argc, char *argv[])
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 @@ main(int argc, char *argv[])
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 @@ main(int argc, char *argv[])
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);
}
}
diff --git a/usr/src/cmd/savecore/sparc/Makefile b/usr/src/cmd/savecore/sparc/Makefile
index 35b465830e..a703dadd67 100644
--- a/usr/src/cmd/savecore/sparc/Makefile
+++ b/usr/src/cmd/savecore/sparc/Makefile
@@ -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)
diff --git a/usr/src/cmd/savecore/sparcv9/Makefile b/usr/src/cmd/savecore/sparcv9/Makefile
index 5e27b45938..aa755b9fca 100644
--- a/usr/src/cmd/savecore/sparcv9/Makefile
+++ b/usr/src/cmd/savecore/sparcv9/Makefile
@@ -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)
diff --git a/usr/src/cmd/svc/common/notify_params.c b/usr/src/cmd/svc/common/notify_params.c
new file mode 100644
index 0000000000..e947b0fa76
--- /dev/null
+++ b/usr/src/cmd/svc/common/notify_params.c
@@ -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);
+ }
+ }
+}
diff --git a/usr/src/cmd/svc/common/notify_params.h b/usr/src/cmd/svc/common/notify_params.h
new file mode 100644
index 0000000000..7f1527ae16
--- /dev/null
+++ b/usr/src/cmd/svc/common/notify_params.h
@@ -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 */
diff --git a/usr/src/cmd/svc/dtd/service_bundle.dtd.1 b/usr/src/cmd/svc/dtd/service_bundle.dtd.1
index 494fd18a6f..e5c23803fc 100644
--- a/usr/src/cmd/svc/dtd/service_bundle.dtd.1
+++ b/usr/src/cmd/svc/dtd/service_bundle.dtd.1
@@ -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
diff --git a/usr/src/cmd/svc/milestone/fs-usr b/usr/src/cmd/svc/milestone/fs-usr
index afcdb24528..b9e643c151 100644
--- a/usr/src/cmd/svc/milestone/fs-usr
+++ b/usr/src/cmd/svc/milestone/fs-usr
@@ -60,6 +60,14 @@ dump_setup()
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
diff --git a/usr/src/cmd/svc/milestone/global.xml b/usr/src/cmd/svc/milestone/global.xml
index 356655f735..b1fca9b3cf 100644
--- a/usr/src/cmd/svc/milestone/global.xml
+++ b/usr/src/cmd/svc/milestone/global.xml
@@ -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 @@ The host and network IPs, network interfaces, and ippools to exempt from the set
</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>
diff --git a/usr/src/cmd/svc/milestone/restarter.xml b/usr/src/cmd/svc/milestone/restarter.xml
index 79dbaca50b..0d10c5881b 100644
--- a/usr/src/cmd/svc/milestone/restarter.xml
+++ b/usr/src/cmd/svc/milestone/restarter.xml
@@ -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 @@ Start no services.
</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'
diff --git a/usr/src/cmd/svc/startd/Makefile b/usr/src/cmd/svc/startd/Makefile
index 800e991082..9c1bc200c8 100644
--- a/usr/src/cmd/svc/startd/Makefile
+++ b/usr/src/cmd/svc/startd/Makefile
@@ -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 @@ LDLIBS += \
-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 @@ lint_SRCS := CPPFLAGS += \
all: $(PROG)
$(PROG): $(ALLOBJS)
- $(LINK.c) -o $@ $(ALLOBJS) $(LDLIBS)
+ $(LINK.c) -o $@ $(ALLOBJS) $(LDLIBS) $(LDFLAGS) $(CTFMERGE_HOOK)
$(POST_PROCESS)
$(POFILE): $(POFILES)
diff --git a/usr/src/cmd/svc/startd/graph.c b/usr/src/cmd/svc/startd/graph.c
index fe7e322f3c..7fbf17a6ec 100644
--- a/usr/src/cmd/svc/startd/graph.c
+++ b/usr/src/cmd/svc/startd/graph.c
@@ -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 @@ vertex_send_event(graph_vertex_t *v, restarter_event_type_t e)
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 @@ configure_vertex(graph_vertex_t *v, scf_instance_t *inst)
int err;
int *path;
int deathrow;
+ int32_t tset;
restarter_fmri[0] = '\0';
@@ -3236,7 +3292,8 @@ configure_vertex(graph_vertex_t *v, scf_instance_t *inst)
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 @@ init_state:
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 @@ init_state:
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 @@ init_state:
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 @@ init_state:
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 @@ dgraph_refresh_instance(graph_vertex_t *v, scf_instance_t *inst)
{
int r;
int enabled;
+ int32_t tset;
assert(MUTEX_HELD(&dgraph_lock));
assert(v->gv_type == GVT_INST);
@@ -4271,6 +4341,16 @@ dgraph_refresh_instance(graph_vertex_t *v, scf_instance_t *inst)
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 @@ recurse:
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 @@ recurse:
*/
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 @@ dgraph_set_instance_state(scf_handle_t *h, const char *inst_name,
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 @@ handle_graph_update_event(scf_handle_t *h, graph_protocol_event_t *e)
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 @@ process_pg_event(scf_handle_t *h, scf_propertygroup_t *pg, scf_instance_t *inst,
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);
diff --git a/usr/src/cmd/svc/startd/libscf.c b/usr/src/cmd/svc/startd/libscf.c
index b9b38442e0..96a7bcef1f 100644
--- a/usr/src/cmd/svc/startd/libscf.c
+++ b/usr/src/cmd/svc/startd/libscf.c
@@ -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 @@ read_single_astring_fail:
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 @@ out:
}
/*
+ * 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 @@ enabled:
return (0);
}
-
/*
* Sets pg to the name property group of s_inst. If it doesn't exist, it is
* added.
@@ -2677,6 +2781,7 @@ out:
return (ret);
}
+extern int32_t stn_global;
/*
* Call dgraph_add_instance() for each instance in the repository.
*/
@@ -2688,7 +2793,6 @@ libscf_populate_graph(scf_handle_t *h)
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 @@ libscf_populate_graph(scf_handle_t *h)
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 @@ add_inst:
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;
diff --git a/usr/src/cmd/svc/startd/method.c b/usr/src/cmd/svc/startd/method.c
index 0febc9d009..a615de643d 100644
--- a/usr/src/cmd/svc/startd/method.c
+++ b/usr/src/cmd/svc/startd/method.c
@@ -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 @@ method_thread(void *arg)
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 @@ rebind_retry:
*/
(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 @@ retry:
*/
(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 @@ retry:
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)
diff --git a/usr/src/cmd/svc/startd/protocol.c b/usr/src/cmd/svc/startd/protocol.c
index e3c9a75e31..009fe8c195 100644
--- a/usr/src/cmd/svc/startd/protocol.c
+++ b/usr/src/cmd/svc/startd/protocol.c
@@ -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 @@ restarter_protocol_init()
* 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 @@ restarter_event_enqueue(const char *inst, restarter_event_type_t event)
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 @@ state_cb(sysevent_t *syse, void *cookie)
{
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 @@ state_cb(sysevent_t *syse, void *cookie)
&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 @@ out:
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 @@ restarter_protocol_send_event(const char *inst, evchan_t *chan,
* 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 @@ restarter_protocol_send_event(const char *inst, evchan_t *chan,
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",
diff --git a/usr/src/cmd/svc/startd/protocol.h b/usr/src/cmd/svc/startd/protocol.h
index f65aa57e72..21642c8a5e 100644
--- a/usr/src/cmd/svc/startd/protocol.h
+++ b/usr/src/cmd/svc/startd/protocol.h
@@ -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 @@ typedef struct protocol_states {
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 graph_update {
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 graph_event_release(graph_protocol_event_t *);
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 *);
diff --git a/usr/src/cmd/svc/startd/restarter.c b/usr/src/cmd/svc/startd/restarter.c
index 5bbee4159b..a088592d14 100644
--- a/usr/src/cmd/svc/startd/restarter.c
+++ b/usr/src/cmd/svc/startd/restarter.c
@@ -324,6 +324,7 @@ restarter_insert_inst(scf_handle_t *h, const char *name)
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 @@ rep_retry:
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 @@ rep_retry:
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 @@ rep_retry:
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 @@ instance_started(restarter_inst_t *inst)
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 @@ restarter_instance_update_states(scf_handle_t *h, restarter_inst_t *ri,
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 @@ retry:
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 @@ stop_instance(scf_handle_t *local_handle, restarter_inst_t *inst,
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 @@ stop_instance(scf_handle_t *local_handle, restarter_inst_t *inst,
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 @@ stop_instance(scf_handle_t *local_handle, restarter_inst_t *inst,
*/
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 @@ stop_instance(scf_handle_t *local_handle, restarter_inst_t *inst,
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 @@ stop_instance(scf_handle_t *local_handle, restarter_inst_t *inst,
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 @@ stop_instance(scf_handle_t *local_handle, restarter_inst_t *inst,
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 @@ unmaintain_instance(scf_handle_t *h, restarter_inst_t *rip,
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 @@ unmaintain_instance(scf_handle_t *h, restarter_inst_t *rip,
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 @@ unmaintain_instance(scf_handle_t *h, restarter_inst_t *rip,
"%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 @@ again:
* 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 @@ enable_inst(scf_handle_t *h, restarter_inst_t *inst, restarter_event_type_t e)
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 @@ enable_inst(scf_handle_t *h, restarter_inst_t *inst, restarter_event_type_t e)
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 @@ enable_inst(scf_handle_t *h, restarter_inst_t *inst, restarter_event_type_t e)
}
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 @@ start_instance(scf_handle_t *local_handle, restarter_inst_t *inst)
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 @@ start_instance(scf_handle_t *local_handle, restarter_inst_t *inst)
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 @@ event_from_tty(scf_handle_t *h, restarter_inst_t *rip)
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 @@ maintain_instance(scf_handle_t *h, restarter_inst_t *rip, int immediate,
}
/*
- * 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 @@ maintain_instance(scf_handle_t *h, restarter_inst_t *rip, int immediate,
(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 @@ maintain_instance(scf_handle_t *h, restarter_inst_t *rip, int immediate,
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 @@ rep_retry:
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 @@ again:
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 @@ again:
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 @@ restarter_queue_event(restarter_inst_t *ri, restarter_protocol_event_t *e)
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);
diff --git a/usr/src/cmd/svc/startd/startd.c b/usr/src/cmd/svc/startd/startd.c
index ea869f94ce..f0c57d37eb 100644
--- a/usr/src/cmd/svc/startd/startd.c
+++ b/usr/src/cmd/svc/startd/startd.c
@@ -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 @@ startd_thread_create(void *(*func)(void *), void *ptr)
return (tid);
}
+extern int info_events_all;
static int
read_startd_config(void)
@@ -393,7 +393,8 @@ read_startd_config(void)
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 @@ timestamp:
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);
diff --git a/usr/src/cmd/svc/startd/startd.h b/usr/src/cmd/svc/startd/startd.h
index 515b9d122c..35a8e1207b 100644
--- a/usr/src/cmd/svc/startd/startd.h
+++ b/usr/src/cmd/svc/startd/startd.h
@@ -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 @@ typedef struct graph_vertex {
* 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 @@ typedef struct graph_edge {
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_list {
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 @@ typedef struct fork_info {
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 @@ void restarter_start(void);
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);
diff --git a/usr/src/cmd/svc/svccfg/Makefile b/usr/src/cmd/svc/svccfg/Makefile
index 07903eed5e..092d3a2253 100644
--- a/usr/src/cmd/svc/svccfg/Makefile
+++ b/usr/src/cmd/svc/svccfg/Makefile
@@ -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 @@ MYOBJS = $(SRCS:%.c=%.o) \
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 @@ YFLAGS = -d
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 @@ LIBUUTIL = $(SRC)/lib/libuutil
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)
diff --git a/usr/src/cmd/svc/svccfg/svccfg.h b/usr/src/cmd/svc/svccfg/svccfg.h
index 5cec384daf..135d12bf33 100644
--- a/usr/src/cmd/svc/svccfg/svccfg.h
+++ b/usr/src/cmd/svc/svccfg/svccfg.h
@@ -99,6 +99,7 @@ typedef enum element {
SC_DOC_LINK,
SC_DOCUMENTATION,
SC_ENABLED,
+ SC_EVENT,
SC_EXEC_METHOD,
SC_FMRI,
SC_HOST,
@@ -117,7 +118,10 @@ typedef enum element {
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 @@ typedef enum element {
SC_STABILITY,
SC_TEMPLATE,
SC_TIME,
+ SC_TYPE,
SC_UNITS,
SC_URI,
SC_USTRING,
@@ -380,6 +385,7 @@ extern const char * const name_attr;
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_retrieve_hash(const char *, unsigned char *);
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_fini(property_t *);
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
diff --git a/usr/src/cmd/svc/svccfg/svccfg.l b/usr/src/cmd/svc/svccfg/svccfg.l
index ca65e79629..453a7e084b 100644
--- a/usr/src/cmd/svc/svccfg/svccfg.l
+++ b/usr/src/cmd/svc/svccfg/svccfg.l
@@ -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 @@ extern int yyerror(const char *);
*/
%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 @@ extern int yyerror(const char *);
<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"));
diff --git a/usr/src/cmd/svc/svccfg/svccfg.y b/usr/src/cmd/svc/svccfg/svccfg.y
index 8e3193bd3b..ec4570c766 100644
--- a/usr/src/cmd/svc/svccfg/svccfg.y
+++ b/usr/src/cmd/svc/svccfg/svccfg.y
@@ -52,6 +52,7 @@ uu_list_pool_t *string_pool;
%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 @@ command : terminator
| 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 @@ revert_cmd: SCC_REVERT opt_word terminator { lscf_revert($2); free ($2); }
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 @@ command_token : SCC_VALIDATE { $$ = SCC_VALIDATE; }
| SCC_REVERT { $$ = SCC_REVERT; }
| SCC_REFRESH { $$ = SCC_REFRESH; }
| SCC_DESCRIBE { $$ = SCC_DESCRIBE; }
+ | SCC_DELNOTIFY { $$ = SCC_DELNOTIFY; }
+ | SCC_LISTNOTIFY { $$ = SCC_LISTNOTIFY; }
+ | SCC_SETNOTIFY { $$ = SCC_SETNOTIFY; }
diff --git a/usr/src/cmd/svc/svccfg/svccfg_engine.c b/usr/src/cmd/svc/svccfg/svccfg_engine.c
index acb8ef4f79..c05100a502 100644
--- a/usr/src/cmd/svc/svccfg/svccfg_engine.c
+++ b/usr/src/cmd/svc/svccfg/svccfg_engine.c
@@ -1074,7 +1074,9 @@ help(int com)
"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;
}
diff --git a/usr/src/cmd/svc/svccfg/svccfg_help.c b/usr/src/cmd/svc/svccfg/svccfg_help.c
index 6a19c2827c..78f82149d5 100644
--- a/usr/src/cmd/svc/svccfg/svccfg_help.c
+++ b/usr/src/cmd/svc/svccfg/svccfg_help.c
@@ -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 @@ struct help_message help_messages[] = {
"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 }
};
diff --git a/usr/src/cmd/svc/svccfg/svccfg_libscf.c b/usr/src/cmd/svc/svccfg/svccfg_libscf.c
index c166aba2ba..3c276c02ab 100644
--- a/usr/src/cmd/svc/svccfg/svccfg_libscf.c
+++ b/usr/src/cmd/svc/svccfg/svccfg_libscf.c
@@ -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 @@ struct entity_elts {
xmlNodePtr dependents;
xmlNodePtr method_context;
xmlNodePtr exec_methods;
+ xmlNodePtr notify_params;
xmlNodePtr property_groups;
xmlNodePtr instances;
xmlNodePtr stability;
@@ -130,6 +133,14 @@ struct template_elts {
};
/*
+ * 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_entity_not_selected;
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 @@ static char *start_method_names[] = {
NULL
};
-static void
-safe_printf(const char *fmt, ...)
+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)
{
- va_list va;
+ int i;
+
+ for (i = 0; uri_scheme[i].protocol != NULL; ++i) {
+ if (strcmp(p, uri_scheme[i].protocol) == 0)
+ return (i);
+ }
- va_start(va, fmt);
- if (vprintf(fmt, va) < 0)
- uu_die(gettext("Error writing to stdout"));
- va_end(va);
+ return (-1);
}
/*
@@ -931,6 +970,8 @@ lscf_init()
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 @@ export_template(scf_propertygroup_t *pg, struct entity_elts *elts,
}
/*
+ * 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 @@ export_instance(scf_instance_t *inst, struct entity_elts *selts, int flags)
} 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 @@ export_instance(scf_instance_t *inst, struct entity_elts *selts, int flags)
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 @@ export_instance(scf_instance_t *inst, struct entity_elts *selts, int flags)
(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 @@ export_service(scf_service_t *svc, int flags)
} 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 @@ export_service(scf_service_t *svc, int flags)
(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 @@ usage:
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
diff --git a/usr/src/cmd/svc/svccfg/svccfg_xml.c b/usr/src/cmd/svc/svccfg/svccfg_xml.c
index 3dfacf53c5..1f37f77b00 100644
--- a/usr/src/cmd/svc/svccfg/svccfg_xml.c
+++ b/usr/src/cmd/svc/svccfg/svccfg_xml.c
@@ -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 uri_attr = "uri";
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 @@ static const char *lxml_elements[] = {
"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 @@ static const char *lxml_elements[] = {
"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 @@ static const char *lxml_elements[] = {
"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 @@ static const char *lxml_prop_types[] = {
"", /* SC_DOC_LINK */
"", /* SC_DOCUMENTATION */
"", /* SC_ENABLED */
+ "", /* SC_EVENT */
"", /* SC_EXEC_METHOD */
"fmri", /* SC_FMRI */
"host", /* SC_HOST */
@@ -199,7 +207,10 @@ static const char *lxml_prop_types[] = {
"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 @@ static const char *lxml_prop_types[] = {
"", /* SC_STABILITY */
"", /* SC_TEMPLATE */
"time", /* SC_TIME */
+ "", /* SC_TYPE */
"", /* SC_UNITS */
"uri", /* SC_URI */
"ustring", /* SC_USTRING */
@@ -1419,6 +1431,223 @@ lxml_get_restarter(entity_t *entity, xmlNodePtr rstr)
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 @@ lxml_get_instance(entity_t *service, xmlNodePtr inst, bundle_type_t bt,
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 @@ lxml_get_service(bundle_t *bundle, xmlNodePtr svc, svccfg_op_t op)
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;
diff --git a/usr/src/cmd/svc/svcs/Makefile b/usr/src/cmd/svc/svcs/Makefile
index 40cac2b232..7472b36563 100644
--- a/usr/src/cmd/svc/svcs/Makefile
+++ b/usr/src/cmd/svc/svcs/Makefile
@@ -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 @@ $(POFILE): $(POFILES)
install: all $(ROOTPROG)
clean:
- $(RM) $(OBJS)
+ $(RM) $(OBJS) $(MYOBJS)
lint: lint_SRCS
diff --git a/usr/src/cmd/svc/svcs/svcs.c b/usr/src/cmd/svc/svcs/svcs.c
index 9cdcc49ad3..e438e8aa4d 100644
--- a/usr/src/cmd/svc/svcs/svcs.c
+++ b/usr/src/cmd/svc/svcs/svcs.c
@@ -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 @@ restarter_common:
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 @@ main(int argc, char **argv)
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 @@ main(int argc, char **argv)
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 @@ main(int argc, char **argv)
case 'd':
case 'D':
case 'l':
+ case 'n':
case 'x':
assert(opt_mode == optopt);
break;
@@ -3268,6 +3501,18 @@ main(int argc, char **argv)
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 @@ main(int argc, char **argv)
free(provider_inst);
break;
+ case 'n':
+ break;
+
default:
assert(0);
abort();
diff --git a/usr/src/cmd/syseventd/sysevent.xml b/usr/src/cmd/syseventd/sysevent.xml
index 36a4674e84..ebb88da564 100644
--- a/usr/src/cmd/syseventd/sysevent.xml
+++ b/usr/src/cmd/syseventd/sysevent.xml
@@ -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'
diff --git a/usr/src/common/nvpair/nvpair.c b/usr/src/common/nvpair/nvpair.c
index 8115091ab9..00d44263cc 100644
--- a/usr/src/common/nvpair/nvpair.c
+++ b/usr/src/common/nvpair/nvpair.c
@@ -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 @@ nvlist_init(nvlist_t *nvl, uint32_t nvflag, nvpriv_t *priv)
nvl->nvl_pad = 0;
}
+uint_t
+nvlist_nvflag(nvlist_t *nvl)
+{
+ return (nvl->nvl_nvflag);
+}
+
/*
* nvlist_alloc - Allocate nvlist.
*/
diff --git a/usr/src/lib/fm/Makefile b/usr/src/lib/fm/Makefile
index 31bb3d9ef4..d0bcbfe8c9 100644
--- a/usr/src/lib/fm/Makefile
+++ b/usr/src/lib/fm/Makefile
@@ -35,6 +35,8 @@ common_SUBDIRS = \
libfmd_msg \
libfmd_snmp \
libfmevent \
+ .WAIT \
+ libfmnotify \
topo
sparc_SUBDIRS = \
diff --git a/usr/src/lib/fm/libfmd_msg/common/fmd_msg.c b/usr/src/lib/fm/libfmd_msg/common/fmd_msg.c
index 7bf4f32947..86cf4ddf75 100644
--- a/usr/src/lib/fm/libfmd_msg/common/fmd_msg.c
+++ b/usr/src/lib/fm/libfmd_msg/common/fmd_msg.c
@@ -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 @@ eos:
}
/*
+ * 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 @@ fmd_msg_gettext_locked(fmd_msg_hdl_t *h,
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 @@ fmd_msg_getitem_id(fmd_msg_hdl_t *h,
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.
diff --git a/usr/src/lib/fm/libfmd_msg/common/fmd_msg.h b/usr/src/lib/fm/libfmd_msg/common/fmd_msg.h
index ac480e1c40..ebf25e0ef9 100644
--- a/usr/src/lib/fm/libfmd_msg/common/fmd_msg.h
+++ b/usr/src/lib/fm/libfmd_msg/common/fmd_msg.h
@@ -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 const char *fmd_msg_url_get(fmd_msg_hdl_t *);
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);
diff --git a/usr/src/lib/fm/libfmd_msg/common/mapfile-vers b/usr/src/lib/fm/libfmd_msg/common/mapfile-vers
index 2c36a033be..15680a7f49 100644
--- a/usr/src/lib/fm/libfmd_msg/common/mapfile-vers
+++ b/usr/src/lib/fm/libfmd_msg/common/mapfile-vers
@@ -38,11 +38,13 @@ $mapfile_version 2
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;
diff --git a/usr/src/lib/fm/libfmd_snmp/Makefile b/usr/src/lib/fm/libfmd_snmp/Makefile
index 703e0020d9..c73060ee5f 100644
--- a/usr/src/lib/fm/libfmd_snmp/Makefile
+++ b/usr/src/lib/fm/libfmd_snmp/Makefile
@@ -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 @@ HDRDIR = common
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)/%)
diff --git a/usr/src/lib/fm/libfmd_snmp/common/fmd_snmp.h b/usr/src/lib/fm/libfmd_snmp/common/fmd_snmp.h
index 5b15f664f3..b6f3a488a3 100644
--- a/usr/src/lib/fm/libfmd_snmp/common/fmd_snmp.h
+++ b/usr/src/lib/fm/libfmd_snmp/common/fmd_snmp.h
@@ -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 @@ extern "C" {
/*
* 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 @@ extern "C" {
#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 */
diff --git a/usr/src/lib/fm/libfmd_snmp/mibs/SUN-FM-MIB.mib b/usr/src/lib/fm/libfmd_snmp/mibs/SUN-FM-MIB.mib
index 1ab18a1d74..b093cbc365 100644
--- a/usr/src/lib/fm/libfmd_snmp/mibs/SUN-FM-MIB.mib
+++ b/usr/src/lib/fm/libfmd_snmp/mibs/SUN-FM-MIB.mib
@@ -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 @@ IMPORTS
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 }
diff --git a/usr/src/lib/fm/libfmd_snmp/mibs/SUN-IREPORT-MIB.mib b/usr/src/lib/fm/libfmd_snmp/mibs/SUN-IREPORT-MIB.mib
new file mode 100644
index 0000000000..21523a2744
--- /dev/null
+++ b/usr/src/lib/fm/libfmd_snmp/mibs/SUN-IREPORT-MIB.mib
@@ -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
+
diff --git a/usr/src/lib/fm/libfmevent/Makefile b/usr/src/lib/fm/libfmevent/Makefile
index bcbca44b61..f68ff800da 100644
--- a/usr/src/lib/fm/libfmevent/Makefile
+++ b/usr/src/lib/fm/libfmevent/Makefile
@@ -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)
diff --git a/usr/src/lib/fm/libfmevent/Makefile.com b/usr/src/lib/fm/libfmevent/Makefile.com
index ed023a34d7..1301bb5a2a 100644
--- a/usr/src/lib/fm/libfmevent/Makefile.com
+++ b/usr/src/lib/fm/libfmevent/Makefile.com
@@ -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 @@ VERS = .1
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 @@ LIBS = $(DYNLIB) $(LINTLIB)
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
diff --git a/usr/src/lib/fm/libfmevent/amd64/Makefile b/usr/src/lib/fm/libfmevent/amd64/Makefile
index e628bce86d..0efa16d7ae 100644
--- a/usr/src/lib/fm/libfmevent/amd64/Makefile
+++ b/usr/src/lib/fm/libfmevent/amd64/Makefile
@@ -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)
diff --git a/usr/src/lib/fm/libfmevent/common/fmev_channels.h b/usr/src/lib/fm/libfmevent/common/fmev_channels.h
index 033e4f52b3..56dea2e5bf 100644
--- a/usr/src/lib/fm/libfmevent/common/fmev_channels.h
+++ b/usr/src/lib/fm/libfmevent/common/fmev_channels.h
@@ -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
diff --git a/usr/src/lib/fm/libfmevent/common/fmev_evaccess.c b/usr/src/lib/fm/libfmevent/common/fmev_evaccess.c
index 442cf0bc01..519f4701d3 100644
--- a/usr/src/lib/fm/libfmevent/common/fmev_evaccess.c
+++ b/usr/src/lib/fm/libfmevent/common/fmev_evaccess.c
@@ -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 @@ fmev_hold(fmev_t ev)
ASSERT(EVENT_VALID(iep));
- API_ENTERV1(iep);
+ (void) FMEV_API_ENTER(iep, 1);
atomic_inc_32(&iep->ei_refcnt);
}
@@ -149,7 +147,7 @@ fmev_rele(fmev_t ev)
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 @@ fmev_dup(fmev_t ev)
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 @@ fmev_attr_list(fmev_t ev)
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 @@ fmev_class(fmev_t ev)
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 @@ fmev_timespec(fmev_t ev, struct timespec *tp)
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 @@ fmev_localtime(fmev_t ev, struct tm *tm)
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);
+}
diff --git a/usr/src/lib/fm/libfmevent/common/fmev_impl.h b/usr/src/lib/fm/libfmevent/common/fmev_impl.h
index 120660a27e..dc9e97cb49 100644
--- a/usr/src/lib/fm/libfmevent/common/fmev_impl.h
+++ b/usr/src/lib/fm/libfmevent/common/fmev_impl.h
@@ -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 @@ extern "C" {
#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 @@ struct fmev_hdl_cmn {
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 fmev_err_t fmev_seterr(fmev_err_t);
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
}
diff --git a/usr/src/lib/fm/libfmevent/common/fmev_publish.c b/usr/src/lib/fm/libfmevent/common/fmev_publish.c
new file mode 100644
index 0000000000..f3dc01ed11
--- /dev/null
+++ b/usr/src/lib/fm/libfmevent/common/fmev_publish.c
@@ -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);
+}
diff --git a/usr/src/lib/fm/libfmevent/common/fmev_subscribe.c b/usr/src/lib/fm/libfmevent/common/fmev_subscribe.c
index a1727f9e58..eebdd4e1e2 100644
--- a/usr/src/lib/fm/libfmevent/common/fmev_subscribe.c
+++ b/usr/src/lib/fm/libfmevent/common/fmev_subscribe.c
@@ -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 @@ typedef struct {
#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_shdlctl_serialize(fmev_shdl_t hdl)
{
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_shdlctl_thrattr(fmev_shdl_t hdl, pthread_attr_t *attr)
{
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_shdlctl_sigmask(fmev_shdl_t hdl, sigset_t *set)
{
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_shdlctl_thrsetup(fmev_shdl_t hdl, door_xcreate_thrsetup_func_t *func,
{
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_shdlctl_thrcreate(fmev_shdl_t hdl, door_xcreate_server_func_t *func,
{
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 @@ fmev_shdl_subscribe(fmev_shdl_t hdl, const char *pat, fmev_cbfunc_t func,
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 @@ fmev_shdl_unsubscribe(fmev_shdl_t hdl, const char *pat)
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 @@ fmev_shdl_unsubscribe(fmev_shdl_t hdl, const char *pat)
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_zalloc(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_zalloc(sz));
}
@@ -429,11 +413,44 @@ fmev_shdl_free(fmev_shdl_t hdl, void *buf, size_t sz)
{
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 @@ error:
}
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 @@ fmev_shdl_fini(fmev_shdl_t hdl)
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);
diff --git a/usr/src/lib/fm/libfmevent/common/fmev_util.c b/usr/src/lib/fm/libfmevent/common/fmev_util.c
index 775cc14bf4..7eec8ad0db 100644
--- a/usr/src/lib/fm/libfmevent/common/fmev_util.c
+++ b/usr/src/lib/fm/libfmevent/common/fmev_util.c
@@ -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 @@ fmev_tsd_destructor(void *data)
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 @@ fmev_api_init(struct fmev_hdl_cmn *hc)
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 @@ fmev_api_enter(struct fmev_hdl_cmn *hc, uint32_t ver_intro)
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 @@ __fmev_errno(void)
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);
+}
diff --git a/usr/src/lib/fm/libfmevent/common/libfmevent.h b/usr/src/lib/fm/libfmevent/common/libfmevent.h
index 8cc06595bc..8fad3511aa 100644
--- a/usr/src/lib/fm/libfmevent/common/libfmevent.h
+++ b/usr/src/lib/fm/libfmevent/common/libfmevent.h
@@ -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 @@ extern "C" {
* 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 @@ typedef enum {
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_subscribe(fmev_shdl_t, const char *, fmev_cbfunc_t,
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 @@ extern fmev_err_t fmev_shdl_unsubscribe(fmev_shdl_t, const char *);
* 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_hold(fmev_t);
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 @@ extern fmev_t fmev_dup(fmev_t);
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
}
diff --git a/usr/src/lib/fm/libfmevent/common/libfmevent_ruleset.h b/usr/src/lib/fm/libfmevent/common/libfmevent_ruleset.h
new file mode 100644
index 0000000000..3fb1fb5769
--- /dev/null
+++ b/usr/src/lib/fm/libfmevent/common/libfmevent_ruleset.h
@@ -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 */
diff --git a/usr/src/lib/fm/libfmevent/common/mapfile-vers b/usr/src/lib/fm/libfmevent/common/mapfile-vers
index 732242af37..ced42890c4 100644
--- a/usr/src/lib/fm/libfmevent/common/mapfile-vers
+++ b/usr/src/lib/fm/libfmevent/common/mapfile-vers
@@ -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 SUNW_1.1 {
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:
*;
diff --git a/usr/src/lib/fm/libfmevent/i386/Makefile b/usr/src/lib/fm/libfmevent/i386/Makefile
index c86be4377c..1b8b367f7c 100644
--- a/usr/src/lib/fm/libfmevent/i386/Makefile
+++ b/usr/src/lib/fm/libfmevent/i386/Makefile
@@ -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)
diff --git a/usr/src/lib/fm/libfmevent/sparc/Makefile b/usr/src/lib/fm/libfmevent/sparc/Makefile
index c86be4377c..1b8b367f7c 100644
--- a/usr/src/lib/fm/libfmevent/sparc/Makefile
+++ b/usr/src/lib/fm/libfmevent/sparc/Makefile
@@ -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)
diff --git a/usr/src/lib/fm/libfmevent/sparcv9/Makefile b/usr/src/lib/fm/libfmevent/sparcv9/Makefile
index e628bce86d..0efa16d7ae 100644
--- a/usr/src/lib/fm/libfmevent/sparcv9/Makefile
+++ b/usr/src/lib/fm/libfmevent/sparcv9/Makefile
@@ -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)
diff --git a/usr/src/lib/fm/libfmnotify/Makefile b/usr/src/lib/fm/libfmnotify/Makefile
new file mode 100644
index 0000000000..645e65ace4
--- /dev/null
+++ b/usr/src/lib/fm/libfmnotify/Makefile
@@ -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
diff --git a/usr/src/lib/fm/libfmnotify/Makefile.com b/usr/src/lib/fm/libfmnotify/Makefile.com
new file mode 100644
index 0000000000..e1ff0e027a
--- /dev/null
+++ b/usr/src/lib/fm/libfmnotify/Makefile.com
@@ -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
diff --git a/usr/src/lib/fm/libfmnotify/amd64/Makefile b/usr/src/lib/fm/libfmnotify/amd64/Makefile
new file mode 100644
index 0000000000..f0d686dced
--- /dev/null
+++ b/usr/src/lib/fm/libfmnotify/amd64/Makefile
@@ -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)
diff --git a/usr/src/lib/fm/libfmnotify/common/libfmnotify.c b/usr/src/lib/fm/libfmnotify/common/libfmnotify.c
new file mode 100644
index 0000000000..cc3ed572fe
--- /dev/null
+++ b/usr/src/lib/fm/libfmnotify/common/libfmnotify.c
@@ -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);
+}
diff --git a/usr/src/lib/fm/libfmnotify/common/libfmnotify.h b/usr/src/lib/fm/libfmnotify/common/libfmnotify.h
new file mode 100644
index 0000000000..839559dd05
--- /dev/null
+++ b/usr/src/lib/fm/libfmnotify/common/libfmnotify.h
@@ -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 */
diff --git a/usr/src/lib/fm/libfmnotify/common/llib-lfmnotify b/usr/src/lib/fm/libfmnotify/common/llib-lfmnotify
new file mode 100644
index 0000000000..230bb3efd8
--- /dev/null
+++ b/usr/src/lib/fm/libfmnotify/common/llib-lfmnotify
@@ -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*/
diff --git a/usr/src/lib/fm/libfmnotify/common/mapfile-vers b/usr/src/lib/fm/libfmnotify/common/mapfile-vers
new file mode 100644
index 0000000000..5c6c048d25
--- /dev/null
+++ b/usr/src/lib/fm/libfmnotify/common/mapfile-vers
@@ -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:
+ *;
+};
diff --git a/usr/src/lib/fm/libfmnotify/i386/Makefile b/usr/src/lib/fm/libfmnotify/i386/Makefile
new file mode 100644
index 0000000000..4e86c1ee6b
--- /dev/null
+++ b/usr/src/lib/fm/libfmnotify/i386/Makefile
@@ -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
+
diff --git a/usr/src/lib/fm/libfmnotify/sparc/Makefile b/usr/src/lib/fm/libfmnotify/sparc/Makefile
new file mode 100644
index 0000000000..686815ee32
--- /dev/null
+++ b/usr/src/lib/fm/libfmnotify/sparc/Makefile
@@ -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
diff --git a/usr/src/lib/fm/libfmnotify/sparcv9/Makefile b/usr/src/lib/fm/libfmnotify/sparcv9/Makefile
new file mode 100644
index 0000000000..c8c3d93095
--- /dev/null
+++ b/usr/src/lib/fm/libfmnotify/sparcv9/Makefile
@@ -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)
diff --git a/usr/src/lib/fm/topo/libtopo/Makefile.com b/usr/src/lib/fm/topo/libtopo/Makefile.com
index 8d56ce0c0d..4e16288540 100644
--- a/usr/src/lib/fm/topo/libtopo/Makefile.com
+++ b/usr/src/lib/fm/topo/libtopo/Makefile.com
@@ -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 @@ BUILTINSRCS = \
mod.c \
pkg.c \
svc.c \
+ sw.c \
zfs.c
LIBSRCS = \
diff --git a/usr/src/lib/fm/topo/libtopo/common/dev.c b/usr/src/lib/fm/topo/libtopo/common/dev.c
index 00abe9fc1c..b991a03e90 100644
--- a/usr/src/lib/fm/topo/libtopo/common/dev.c
+++ b/usr/src/lib/fm/topo/libtopo/common/dev.c
@@ -114,6 +114,11 @@ static int
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);
}
diff --git a/usr/src/lib/fm/topo/libtopo/common/fmd.c b/usr/src/lib/fm/topo/libtopo/common/fmd.c
index 1b8898770e..be48be3e0c 100644
--- a/usr/src/lib/fm/topo/libtopo/common/fmd.c
+++ b/usr/src/lib/fm/topo/libtopo/common/fmd.c
@@ -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 @@ int
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);
}
diff --git a/usr/src/lib/fm/topo/libtopo/common/hc.c b/usr/src/lib/fm/topo/libtopo/common/hc.c
index 2bec088639..352c34c2f6 100644
--- a/usr/src/lib/fm/topo/libtopo/common/hc.c
+++ b/usr/src/lib/fm/topo/libtopo/common/hc.c
@@ -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 @@ int
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 @@ hc_enum(topo_mod_t *mod, tnode_t *pnode, const char *name, topo_instance_t min,
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,
diff --git a/usr/src/lib/fm/topo/libtopo/common/mem.c b/usr/src/lib/fm/topo/libtopo/common/mem.c
index 502fbf994b..994bed3d24 100644
--- a/usr/src/lib/fm/topo/libtopo/common/mem.c
+++ b/usr/src/lib/fm/topo/libtopo/common/mem.c
@@ -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 @@ static int
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 @@ mem_enum(topo_mod_t *mod, tnode_t *pnode, const char *name,
}
}
- 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));
diff --git a/usr/src/lib/fm/topo/libtopo/common/mod.c b/usr/src/lib/fm/topo/libtopo/common/mod.c
index 1b26ccf6e6..5a26378acf 100644
--- a/usr/src/lib/fm/topo/libtopo/common/mod.c
+++ b/usr/src/lib/fm/topo/libtopo/common/mod.c
@@ -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 @@ static int
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);
}
diff --git a/usr/src/lib/fm/topo/libtopo/common/pkg.c b/usr/src/lib/fm/topo/libtopo/common/pkg.c
index 4cf1eddda0..7c5f4ade87 100644
--- a/usr/src/lib/fm/topo/libtopo/common/pkg.c
+++ b/usr/src/lib/fm/topo/libtopo/common/pkg.c
@@ -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 @@ pkg_init(topo_mod_t *mod, topo_version_t version)
{
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 @@ static int
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);
}
diff --git a/usr/src/lib/fm/topo/libtopo/common/svc.c b/usr/src/lib/fm/topo/libtopo/common/svc.c
index 61fed24f5e..7e237661f4 100644
--- a/usr/src/lib/fm/topo/libtopo/common/svc.c
+++ b/usr/src/lib/fm/topo/libtopo/common/svc.c
@@ -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 @@ static int svc_fmri_service_state(topo_mod_t *, tnode_t *, topo_version_t,
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 @@ static const topo_method_t svc_methods[] = {
{ 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 @@ svc_get_handle(topo_mod_t *mod)
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 @@ svc_fini(topo_mod_t *mod)
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 @@ svc_component_valid(const char *str)
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 @@ nomem:
}
/*
- * 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 @@ svc_get_state(topo_mod_t *mod, nvlist_t *fmri, boolean_t presence_only,
}
}
- /*
- * 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 @@ svc_get_state(topo_mod_t *mod, nvlist_t *fmri, boolean_t presence_only,
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:
diff --git a/usr/src/lib/fm/topo/libtopo/common/sw.c b/usr/src/lib/fm/topo/libtopo/common/sw.c
new file mode 100644
index 0000000000..c655627f52
--- /dev/null
+++ b/usr/src/lib/fm/topo/libtopo/common/sw.c
@@ -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);
+}
diff --git a/usr/src/lib/fm/topo/libtopo/common/sw.h b/usr/src/lib/fm/topo/libtopo/common/sw.h
new file mode 100644
index 0000000000..d66ae5bd23
--- /dev/null
+++ b/usr/src/lib/fm/topo/libtopo/common/sw.h
@@ -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 */
diff --git a/usr/src/lib/fm/topo/libtopo/common/topo_builtin.c b/usr/src/lib/fm/topo/libtopo/common/topo_builtin.c
index c3bb83b63c..fe979488bd 100644
--- a/usr/src/lib/fm/topo/libtopo/common/topo_builtin.c
+++ b/usr/src/lib/fm/topo/libtopo/common/topo_builtin.c
@@ -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 @@ static const struct topo_builtin _topo_builtins[] = {
{ "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 */
diff --git a/usr/src/lib/fm/topo/libtopo/common/topo_mod.c b/usr/src/lib/fm/topo/libtopo/common/topo_mod.c
index 00d26891e2..295054dc5e 100644
--- a/usr/src/lib/fm/topo/libtopo/common/topo_mod.c
+++ b/usr/src/lib/fm/topo/libtopo/common/topo_mod.c
@@ -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 @@ topo_mod_modfmri(topo_mod_t *mod, int version, const char *driver)
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)
{
diff --git a/usr/src/lib/fm/topo/libtopo/common/topo_mod.h b/usr/src/lib/fm/topo/libtopo/common/topo_mod.h
index dbb5f3c0f0..2a137ad388 100644
--- a/usr/src/lib/fm/topo/libtopo/common/topo_mod.h
+++ b/usr/src/lib/fm/topo/libtopo/common/topo_mod.h
@@ -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 @@ extern nvlist_t *topo_mod_memfmri(topo_mod_t *, int, uint64_t, uint64_t,
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,
diff --git a/usr/src/lib/fm/topo/libtopo/common/topo_node.c b/usr/src/lib/fm/topo/libtopo/common/topo_node.c
index ab720c1755..553bc851f0 100644
--- a/usr/src/lib/fm/topo/libtopo/common/topo_node.c
+++ b/usr/src/lib/fm/topo/libtopo/common/topo_node.c
@@ -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 @@ topo_node_lookup(tnode_t *pnode, const char *name, topo_instance_t inst)
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)) {
diff --git a/usr/src/lib/fm/topo/libtopo/common/topo_snap.c b/usr/src/lib/fm/topo/libtopo/common/topo_snap.c
index 42ad1cd661..638d8c0677 100644
--- a/usr/src/lib/fm/topo/libtopo/common/topo_snap.c
+++ b/usr/src/lib/fm/topo/libtopo/common/topo_snap.c
@@ -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 @@ topo_snap_hold(topo_hdl_t *thp, const char *uuid, int *errp)
/*
* 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);
diff --git a/usr/src/lib/fm/topo/libtopo/common/zfs.c b/usr/src/lib/fm/topo/libtopo/common/zfs.c
index 6c8a456be3..573115efe3 100644
--- a/usr/src/lib/fm/topo/libtopo/common/zfs.c
+++ b/usr/src/lib/fm/topo/libtopo/common/zfs.c
@@ -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 @@ int
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);
}
diff --git a/usr/src/lib/libnvpair/libnvpair.c b/usr/src/lib/libnvpair/libnvpair.c
index 57915cd737..16bce483be 100644
--- a/usr/src/lib/libnvpair/libnvpair.c
+++ b/usr/src/lib/libnvpair/libnvpair.c
@@ -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)
{
- while (depth-- > 0)
- (void) fprintf(fp, "\t");
+ 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");
+ }
}
/*
- * nvlist_print - Prints elements in an event buffer
+ * ======================================================================
+ * | |
+ * | 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)
+{
+ 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. |
+ * | |
+ * ======================================================================
*/
-static
+
void
-nvlist_print_with_indent(FILE *fp, nvlist_t *nvl, int depth)
+nvlist_prtctl_setdest(nvlist_prtctl_t pctl, FILE *fp)
{
- int i;
+ 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(nvlist_t *nvl, nvlist_prtctl_t pctl)
+{
+ FILE *fp = pctl->nvprt_fp;
char *name;
uint_t nelem;
nvpair_t *nvp;
@@ -60,7 +571,7 @@ nvlist_print_with_indent(FILE *fp, nvlist_t *nvl, int depth)
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 @@ nvlist_print_with_indent(FILE *fp, nvlist_t *nvl, int depth)
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 @@ nvlist_print_with_indent(FILE *fp, nvlist_t *nvl, int depth)
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 @@ dump_nvlist(nvlist_t *list, int indent)
}
/*
+ * ======================================================================
+ * | |
+ * | 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'
diff --git a/usr/src/lib/libnvpair/libnvpair.h b/usr/src/lib/libnvpair/libnvpair.h
index 15c1c78167..4c2615d924 100644
--- a/usr/src/lib/libnvpair/libnvpair.h
+++ b/usr/src/lib/libnvpair/libnvpair.h
@@ -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
}
diff --git a/usr/src/lib/libnvpair/mapfile-vers b/usr/src/lib/libnvpair/mapfile-vers
index 73d6dbd160..079cac5a85 100644
--- a/usr/src/lib/libnvpair/mapfile-vers
+++ b/usr/src/lib/libnvpair/mapfile-vers
@@ -43,6 +43,7 @@ SYMBOL_VERSION SUNW_1.3 {
nvlist_add_double;
nvlist_empty;
nvlist_lookup_double;
+ nvlist_nvflag;
nvlist_prev_nvpair;
nvlist_remove_nvpair;
nvpair_value_double;
@@ -174,6 +175,42 @@ SYMBOL_VERSION SUNWprivate_1.1 {
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;
diff --git a/usr/src/lib/librestart/common/librestart.c b/usr/src/lib/librestart/common/librestart.c
index a455519fe7..7cf1d30e04 100644
--- a/usr/src/lib/librestart/common/librestart.c
+++ b/usr/src/lib/librestart/common/librestart.c
@@ -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 @@ struct restarter_event {
};
/*
+ * 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 @@ restarter_event_publish_retry(evchan_t *scp, const char *class,
* 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_set_states(restarter_event_handle_t *h, const char *inst,
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 @@ restarter_set_states(restarter_event_handle_t *h, const char *inst,
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 @@ restarter_set_states(restarter_event_handle_t *h, const char *inst,
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(
diff --git a/usr/src/lib/librestart/common/librestart.h b/usr/src/lib/librestart/common/librestart.h
index 26fa842867..92f00decfa 100644
--- a/usr/src/lib/librestart/common/librestart.h
+++ b/usr/src/lib/librestart/common/librestart.h
@@ -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 @@ typedef uint32_t restarter_event_type_t;
* 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 @@ int restarter_event_get_current_states(restarter_event_t *,
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_event_get_current_states(restarter_event_t *,
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,
diff --git a/usr/src/lib/librestart/common/librestart_priv.h b/usr/src/lib/librestart/common/librestart_priv.h
index 715a9ee623..520b94d758 100644
--- a/usr/src/lib/librestart/common/librestart_priv.h
+++ b/usr/src/lib/librestart/common/librestart_priv.h
@@ -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 @@ extern "C" {
#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
diff --git a/usr/src/lib/librestart/common/mapfile-vers b/usr/src/lib/librestart/common/mapfile-vers
index 41ba72ca76..181dbe8304 100644
--- a/usr/src/lib/librestart/common/mapfile-vers
+++ b/usr/src/lib/librestart/common/mapfile-vers
@@ -63,12 +63,15 @@ SYMBOL_VERSION SUNWprivate_1.1 {
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:
*;
};
diff --git a/usr/src/lib/libscf/Makefile.com b/usr/src/lib/libscf/Makefile.com
index 784d2ef265..5c53b8803f 100644
--- a/usr/src/lib/libscf/Makefile.com
+++ b/usr/src/lib/libscf/Makefile.com
@@ -29,6 +29,7 @@ OBJECTS = \
error.o \
lowlevel.o \
midlevel.o \
+ notify_params.o \
highlevel.o \
scf_tmpl.o \
scf_type.o
@@ -43,7 +44,7 @@ $(NATIVE_BUILD)VERS =
$(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 @@ MY_NATIVE_CPPFLAGS =\
-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))
diff --git a/usr/src/lib/libscf/common/libscf_impl.h b/usr/src/lib/libscf/common/libscf_impl.h
index 834c23fc53..01fc49f9ae 100644
--- a/usr/src/lib/libscf/common/libscf_impl.h
+++ b/usr/src/lib/libscf/common/libscf_impl.h
@@ -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 @@ typedef enum {
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
}
diff --git a/usr/src/lib/libscf/common/lowlevel.c b/usr/src/lib/libscf/common/lowlevel.c
index 27136167a5..7363c7fc9c 100644
--- a/usr/src/lib/libscf/common/lowlevel.c
+++ b/usr/src/lib/libscf/common/lowlevel.c
@@ -793,6 +793,38 @@ scf_handle_create(scf_version_t v)
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 @@ scf_transaction_destroy(scf_transaction_t *val)
void
scf_transaction_destroy_children(scf_transaction_t *tran)
{
+ if (tran == NULL)
+ return;
+
scf_transaction_reset_impl(tran, 1, 0);
}
diff --git a/usr/src/lib/libscf/common/mapfile-vers b/usr/src/lib/libscf/common/mapfile-vers
index 29d0911937..643f5424f2 100644
--- a/usr/src/lib/libscf/common/mapfile-vers
+++ b/usr/src/lib/libscf/common/mapfile-vers
@@ -92,6 +92,11 @@ SYMBOL_VERSION SUNW_1.2 {
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 SUNW_1.1 {
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 @@ SYMBOL_VERSION SUNWprivate_1.1 {
scf_is_fastboot_default;
scf_fastreboot_default_set_transient;
_check_services;
+ _scf_handle_create_and_bind;
+ _smf_refresh_all_instances;
local:
*;
};
diff --git a/usr/src/lib/libscf/common/midlevel.c b/usr/src/lib/libscf/common/midlevel.c
index c466391761..71b72404f1 100644
--- a/usr/src/lib/libscf/common/midlevel.c
+++ b/usr/src/lib/libscf/common/midlevel.c
@@ -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 @@ set_inst_action(const char *fmri, const char *action)
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 @@ set_inst_enabled_flags(const char *fmri, int flags, uint8_t desired)
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 @@ _smf_refresh_instance_i(scf_instance_t *inst)
}
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 @@ scf_general_pg_setup(const char *fmri, const char *pg_name)
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 @@ scf_simple_walk_instances(uint_t state_flags, void *private,
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 @@ scf_simple_prop_get(scf_handle_t *hin, const char *instance, const char *pgname,
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 @@ scf_simple_app_props_get(scf_handle_t *hin, const char *inst_fmri)
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 @@ gen_filenms_from_fmri(const char *fmri, const char *name, char *filename,
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 @@ int
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 @@ int
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 @@ _check_services(char **svcs)
}
}
}
+
+/*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);
+ }
+}
diff --git a/usr/src/lib/libscf/common/notify_params.c b/usr/src/lib/libscf/common/notify_params.c
new file mode 100644
index 0000000000..3e41d19f8f
--- /dev/null
+++ b/usr/src/lib/libscf/common/notify_params.c
@@ -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);
+}
diff --git a/usr/src/lib/libscf/common/scf_tmpl.c b/usr/src/lib/libscf/common/scf_tmpl.c
index 624fdeb4d7..69062e0eb9 100644
--- a/usr/src/lib/libscf/common/scf_tmpl.c
+++ b/usr/src/lib/libscf/common/scf_tmpl.c
@@ -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 @@ static const scf_error_t errors_server[] = {
* 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 @@ _read_single_boolean_from_pg(scf_propertygroup_t *pg, const char *prop_name,
}
/*
- * 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 @@ scf_values_destroy(scf_values_t *vals)
{
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:
diff --git a/usr/src/lib/libscf/inc/libscf.h b/usr/src/lib/libscf/inc/libscf.h
index b3ded758bd..8e68b4dfcc 100644
--- a/usr/src/lib/libscf/inc/libscf.h
+++ b/usr/src/lib/libscf/inc/libscf.h
@@ -29,6 +29,7 @@
#include <stddef.h>
#include <sys/types.h>
+#include <libnvpair.h>
#ifdef __cplusplus
extern "C" {
@@ -286,6 +287,7 @@ typedef struct scf_tmpl_error scf_tmpl_error_t;
/*
* 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 @@ typedef struct scf_tmpl_error scf_tmpl_error_t;
#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_opaque(scf_simple_prop_t *, size_t *);
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
diff --git a/usr/src/lib/libscf/inc/libscf_priv.h b/usr/src/lib/libscf/inc/libscf_priv.h
index cd058c0f43..ac51cfe453 100644
--- a/usr/src/lib/libscf/inc/libscf_priv.h
+++ b/usr/src/lib/libscf/inc/libscf_priv.h
@@ -552,6 +552,41 @@ int scf_is_compatible_type(scf_type_t, scf_type_t);
*/
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
diff --git a/usr/src/lib/libsecdb/auth_attr.txt b/usr/src/lib/libsecdb/auth_attr.txt
index 39ee87908d..7f30e64320 100644
--- a/usr/src/lib/libsecdb/auth_attr.txt
+++ b/usr/src/lib/libsecdb/auth_attr.txt
@@ -150,6 +150,8 @@ solaris.smf.manage.rmvolmgr:::Manage Rmvolmgr Service States::help=SmfRmvolmgrSt
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.mdns:::Change Values of MDNS Service Properties::help=SmfValue
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
diff --git a/usr/src/lib/libsecdb/prof_attr.txt b/usr/src/lib/libsecdb/prof_attr.txt
index 650a0fd919..65a651ea1b 100644
--- a/usr/src/lib/libsecdb/prof_attr.txt
+++ b/usr/src/lib/libsecdb/prof_attr.txt
@@ -102,6 +102,7 @@ IP Filter Management:::IP Filter Administration:help=RtIPFilterMngmnt.html
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:
#
diff --git a/usr/src/lib/libsysevent/libevchannel.c b/usr/src/lib/libsysevent/libevchannel.c
index 52f9b4641c..8e6743d314 100644
--- a/usr/src/lib/libsysevent/libevchannel.c
+++ b/usr/src/lib/libsysevent/libevchannel.c
@@ -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 @@ static void
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 @@ sysevent_evc_xsubscribe(evchan_t *scp, const char *sid, const char *class,
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 @@ sysevent_evc_control(evchan_t *scp, int cmd, /* arg */ ...)
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 @@ sysevent_evc_control(evchan_t *scp, int cmd, /* arg */ ...)
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);
+}
diff --git a/usr/src/lib/libsysevent/libsysevent.c b/usr/src/lib/libsysevent/libsysevent.c
index 7ad71b5f68..28fdc6ca50 100644
--- a/usr/src/lib/libsysevent/libsysevent.c
+++ b/usr/src/lib/libsysevent/libsysevent.c
@@ -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 @@ subscriber_event_handler(sysevent_handle_t *shp)
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 @@ fail:
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 @@ sysevent_bind_subscriber(sysevent_handle_t *shp,
* 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 @@ sysevent_bind_subscriber(sysevent_handle_t *shp,
SH_TYPE(shp) = SUBSCRIBER;
SH_PRIV_DATA(shp) = (void *)sub_info;
-
/* Create an event handler thread */
- if (thr_create(NULL, NULL, (void *(*)(void *))subscriber_event_handler,
- shp, THR_BOUND, &sub_info->sp_handler_tid) != 0) {
+ 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.
+ */
+
+ 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 @@ fail:
}
/*
+ * 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 @@ sysevent_unbind_subscriber(sysevent_handle_t *shp)
(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 @@ sysevent_unbind_subscriber(sysevent_handle_t *shp)
/* 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 @@ sysevent_unbind_publisher(sysevent_handle_t *shp)
* 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 @@ sysevent_bind_handle(void (*event_handler)(sysevent_t *ev))
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 @@ sysevent_bind_handle(void (*event_handler)(sysevent_t *ev))
(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(void (*event_handler)(sysevent_t *ev))
}
/*
+ * 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
diff --git a/usr/src/lib/libsysevent/libsysevent.h b/usr/src/lib/libsysevent/libsysevent.h
index 35e25d1ecf..54e5ef2a5d 100644
--- a/usr/src/lib/libsysevent/libsysevent.h
+++ b/usr/src/lib/libsysevent/libsysevent.h
@@ -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 @@ size_t sysevent_get_size(sysevent_t *ev);
/* 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 @@ sysevent_handle_t *sysevent_open_channel_alt(const char *channel_path);
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);
diff --git a/usr/src/lib/libsysevent/mapfile-vers b/usr/src/lib/libsysevent/mapfile-vers
index f6d15f8b3f..f72041493e 100644
--- a/usr/src/lib/libsysevent/mapfile-vers
+++ b/usr/src/lib/libsysevent/mapfile-vers
@@ -75,13 +75,17 @@ SYMBOL_VERSION SUNWprivate_1.1 {
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;
diff --git a/usr/src/pkg/manifests/SUNWcs.mf b/usr/src/pkg/manifests/SUNWcs.mf
index c8a0b12784..c300204c16 100644
--- a/usr/src/pkg/manifests/SUNWcs.mf
+++ b/usr/src/pkg/manifests/SUNWcs.mf
@@ -47,6 +47,7 @@ dir path=etc/ftpd group=sys
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/hotplug.xml group=sys mode=0444
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/identity-node mode=0555
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
diff --git a/usr/src/pkg/manifests/consolidation-osnet-osnet-message-files.mf b/usr/src/pkg/manifests/consolidation-osnet-osnet-message-files.mf
index e8f4b7912b..e2b818a162 100644
--- a/usr/src/pkg/manifests/consolidation-osnet-osnet-message-files.mf
+++ b/usr/src/pkg/manifests/consolidation-osnet-osnet-message-files.mf
@@ -296,6 +296,7 @@ file path=usr/lib/help/profiles/locale/RtZoneSecurity.html
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/PCIEX.po
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
diff --git a/usr/src/pkg/manifests/service-fault-management-smtp-notify.mf b/usr/src/pkg/manifests/service-fault-management-smtp-notify.mf
new file mode 100644
index 0000000000..04e9766edf
--- /dev/null
+++ b/usr/src/pkg/manifests/service-fault-management-smtp-notify.mf
@@ -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
diff --git a/usr/src/pkg/manifests/service-fault-management-snmp-notify.mf b/usr/src/pkg/manifests/service-fault-management-snmp-notify.mf
new file mode 100644
index 0000000000..85504febae
--- /dev/null
+++ b/usr/src/pkg/manifests/service-fault-management-snmp-notify.mf
@@ -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
diff --git a/usr/src/pkg/manifests/service-fault-management.mf b/usr/src/pkg/manifests/service-fault-management.mf
index 6bc32ace9d..97a1640729 100644
--- a/usr/src/pkg/manifests/service-fault-management.mf
+++ b/usr/src/pkg/manifests/service-fault-management.mf
@@ -35,33 +35,66 @@ set name=pkg.summary value="Fault Management Daemon and Utilities"
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/fmd/plugins
$(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_msg.h
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/pciex.eft mode=0444
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.conf
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 @@ $(sparc_ONLY)file path=usr/platform/sun4v/lib/fm/topo/plugins/xaui.so \
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 @@ legacy pkg=SUNWfmdr arch=$(ARCH) category=system \
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
diff --git a/usr/src/pkg/manifests/system-kernel.mf b/usr/src/pkg/manifests/system-kernel.mf
index b01a279634..0df926cf16 100644
--- a/usr/src/pkg/manifests/system-kernel.mf
+++ b/usr/src/pkg/manifests/system-kernel.mf
@@ -773,7 +773,6 @@ $(i386_ONLY)file path=kernel/sys/pset group=sys mode=0755
$(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
diff --git a/usr/src/uts/common/io/dump.c b/usr/src/uts/common/io/dump.c
index 18c3c1a3d1..70a61bf9b8 100644
--- a/usr/src/uts/common/io/dump.c
+++ b/usr/src/uts/common/io/dump.c
@@ -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 @@ dump_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cred, int *rvalp)
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 @@ dump_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cred, int *rvalp)
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;
}
diff --git a/usr/src/uts/common/io/sysevent.c b/usr/src/uts/common/io/sysevent.c
index f2cead970e..69065b9506 100644
--- a/usr/src/uts/common/io/sysevent.c
+++ b/usr/src/uts/common/io/sysevent.c
@@ -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 @@ sysevent_chandata(dev_t dev, int *rvalp, void *arg, int flag, cred_t *cr)
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 @@ sysevent_ioctl(dev_t dev, int cmd, intptr_t arg,
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;
}
diff --git a/usr/src/uts/common/os/dumpsubr.c b/usr/src/uts/common/os/dumpsubr.c
index 1c766842de..0ae9c3d390 100644
--- a/usr/src/uts/common/os/dumpsubr.c
+++ b/usr/src/uts/common/os/dumpsubr.c
@@ -107,6 +107,7 @@ int dump_timeout = 120; /* timeout for dumping pages */
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 @@ struct cbuf {
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 @@ dumphdr_init(void)
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 @@ dump_process(pid_t pid)
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 @@ dumpsys(void)
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 @@ dumpsys(void)
* thing we do because the dump process itself emits messages.
*/
if (panicstr) {
+ dump_summary();
dump_ereports();
dump_messages();
}
@@ -2967,3 +3024,47 @@ dumpvp_resize()
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 : "");
+}
diff --git a/usr/src/uts/common/os/evchannels.c b/usr/src/uts/common/os/evchannels.c
index 9ae0565244..e34af700ca 100644
--- a/usr/src/uts/common/os/evchannels.c
+++ b/usr/src/uts/common/os/evchannels.c
@@ -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 @@ evch_chunbind(evch_bind_t *bp)
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 @@ evch_chgetchdata(char *chname, void *buf, size_t size)
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 @@ evch_chgetnextev(evchanq_t *snp)
* 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 @@ sysevent_evc_control(evchan_t *scp, int cmd, ...)
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 @@ sysevent_get_attr_list(sysevent_t *ev, nvlist_t **nvlist)
* 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 @@ evch_usrgetchdata(char *chname, void *buf, size_t size)
{
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));
+}
diff --git a/usr/src/uts/common/os/fm.c b/usr/src/uts/common/os/fm.c
index 4d1754970b..4efcff4f46 100644
--- a/usr/src/uts/common/os/fm.c
+++ b/usr/src/uts/common/os/fm.c
@@ -384,6 +384,20 @@ fm_panic(const char *format, ...)
}
/*
+ * 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.
diff --git a/usr/src/uts/common/os/log_sysevent.c b/usr/src/uts/common/os/log_sysevent.c
index 4f40a30530..8923136f98 100644
--- a/usr/src/uts/common/os/log_sysevent.c
+++ b/usr/src/uts/common/os/log_sysevent.c
@@ -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 @@ log_event_upcall(log_event_upcall_arg_t *arg)
}
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 @@ log_event_upcall(log_event_upcall_arg_t *arg)
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 @@ log_event_deliver()
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 @@ log_event_deliver()
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 @@ sysevent_alloc(char *class, char *subclass, char *pub, int flag)
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 @@ log_sysevent_filename(char *file)
/* 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 @@ log_sysevent_filename(char *file)
}
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 @@ restart:
/* 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 @@ log_sysevent(sysevent_t *ev, int flag, sysevent_id_t *eid)
}
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_usr_sysevent(sysevent_t *ev, int ev_size, sysevent_id_t *eid)
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 @@ ddi_log_sysevent(
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 @@ ddi_log_sysevent(
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 @@ ddi_log_sysevent(
}
uint64_t
-log_sysevent_new_id()
+log_sysevent_new_id(void)
{
return (atomic_add_64_nv(&kernel_event_id, (uint64_t)1));
}
diff --git a/usr/src/uts/common/os/panic.c b/usr/src/uts/common/os/panic.c
index 469d9362ba..3985353ff7 100644
--- a/usr/src/uts/common/os/panic.c
+++ b/usr/src/uts/common/os/panic.c
@@ -256,6 +256,9 @@ panicsys(const char *format, va_list alist, struct regs *rp, int on_panic_stack)
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
diff --git a/usr/src/uts/common/sys/dumpadm.h b/usr/src/uts/common/sys/dumpadm.h
index 679ca04005..4e3704985d 100644
--- a/usr/src/uts/common/sys/dumpadm.h
+++ b/usr/src/uts/common/sys/dumpadm.h
@@ -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 @@ extern "C" {
#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
diff --git a/usr/src/uts/common/sys/dumphdr.h b/usr/src/uts/common/sys/dumphdr.h
index 5fc27bf44e..e0f2b1aebd 100644
--- a/usr/src/uts/common/sys/dumphdr.h
+++ b/usr/src/uts/common/sys/dumphdr.h
@@ -43,7 +43,7 @@ extern "C" {
* 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 @@ extern "C" {
(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 @@ typedef struct dumphdr {
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 dumpvp_resize(void);
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
diff --git a/usr/src/uts/common/sys/fm/protocol.h b/usr/src/uts/common/sys/fm/protocol.h
index c4103c48a4..5eca760dad 100644
--- a/usr/src/uts/common/sys/fm/protocol.h
+++ b/usr/src/uts/common/sys/fm/protocol.h
@@ -43,12 +43,13 @@ extern "C" {
#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 @@ extern "C" {
/* 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 @@ extern "C" {
#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 @@ extern "C" {
#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 @@ extern "C" {
#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 *);
diff --git a/usr/src/uts/common/sys/fm/util.h b/usr/src/uts/common/sys/fm/util.h
index 4934814d86..37334101b3 100644
--- a/usr/src/uts/common/sys/fm/util.h
+++ b/usr/src/uts/common/sys/fm/util.h
@@ -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_ereport_post(nvlist_t *, int);
extern void fm_payload_stack_add(nvlist_t *, const pc_t *, int);
+extern int is_fm_panic();
#endif /* _KERNEL */
#ifdef __cplusplus
diff --git a/usr/src/uts/common/sys/nvpair.h b/usr/src/uts/common/sys/nvpair.h
index 58037b0653..30ff4e0667 100644
--- a/usr/src/uts/common/sys/nvpair.h
+++ b/usr/src/uts/common/sys/nvpair.h
@@ -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_unpack(char *, size_t, nvlist_t **, int);
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 *);
diff --git a/usr/src/uts/common/sys/panic.h b/usr/src/uts/common/sys/panic.h
index 9dd04a0626..1fa00f5f5a 100644
--- a/usr/src/uts/common/sys/panic.h
+++ b/usr/src/uts/common/sys/panic.h
@@ -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 @@ extern "C" {
#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_nv {
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)
/*
diff --git a/usr/src/uts/common/sys/sysevent.h b/usr/src/uts/common/sys/sysevent.h
index 44b5d069fb..2b26441973 100644
--- a/usr/src/uts/common/sys/sysevent.h
+++ b/usr/src/uts/common/sys/sysevent.h
@@ -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_unsubscribe(evchan_t *, const char *);
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
diff --git a/usr/src/uts/common/sys/sysevent_impl.h b/usr/src/uts/common/sys/sysevent_impl.h
index 215862fbdd..117452765f 100644
--- a/usr/src/uts/common/sys/sysevent_impl.h
+++ b/usr/src/uts/common/sys/sysevent_impl.h
@@ -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 @@ typedef struct sysevent_attr_impl {
/*
* 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 @@ typedef struct sys_event_impl {
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 @@ typedef struct sysevent_channel_descriptor {
/*
* 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 {
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 @@ typedef struct {
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 @@ typedef struct {
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);
-
-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 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);
+
+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 @@ typedef struct {
#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 @@ typedef struct chandata {
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
diff --git a/usr/src/uts/intel/ia32/os/archdep.c b/usr/src/uts/intel/ia32/os/archdep.c
index c687a3d9da..506fbaf66e 100644
--- a/usr/src/uts/intel/ia32/os/archdep.c
+++ b/usr/src/uts/intel/ia32/os/archdep.c
@@ -1129,10 +1129,17 @@ argcount(uintptr_t eip)
/*
* 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 @@ traceback(caddr_t fpreg)
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 @@ traceback(caddr_t fpreg)
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 @@ out:
if (!panicstr) {
printf("end of traceback\n");
DELAY(2 * MICROSEC);
+ } else if (dump_stack_scratch) {
+ dump_stack_scratch[offset] = '\0';
}
}
@@ -1200,6 +1240,9 @@ traceback(caddr_t fpreg)
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 @@ traceback(caddr_t fpreg)
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 @@ traceback(caddr_t fpreg)
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 @@ out:
if (!panicstr) {
printf("end of traceback\n");
DELAY(2 * MICROSEC);
+ } else if (dump_stack_scratch) {
+ dump_stack_scratch[offset] = '\0';
}
+
}
#endif /* __i386 */
diff --git a/usr/src/uts/sparc/os/archdep.c b/usr/src/uts/sparc/os/archdep.c
index a1964308ed..e8979ccb66 100644
--- a/usr/src/uts/sparc/os/archdep.c
+++ b/usr/src/uts/sparc/os/archdep.c
@@ -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 @@ __ipltospl(int ipl)
/*
* 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 @@ traceback(caddr_t sp)
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 @@ traceback(caddr_t sp)
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 @@ traceback(caddr_t sp)
if (!panicstr) {
printf("end of traceback\n");
DELAY(2 * MICROSEC);
+ } else if (dump_stack_scratch) {
+ dump_stack_scratch[offset] = '\0';
}
}