summaryrefslogtreecommitdiff
path: root/usr/src/lib
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/src/lib
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/src/lib')
-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
71 files changed, 7320 insertions, 334 deletions
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;