diff options
author | Gavin Maltby <gavin.maltby@oracle.com> | 2010-07-30 17:04:17 +1000 |
---|---|---|
committer | Gavin Maltby <gavin.maltby@oracle.com> | 2010-07-30 17:04:17 +1000 |
commit | f6e214c7418f43af38bd8c3a557e3d0a1d311cfa (patch) | |
tree | 0f0e4cee5ead68ee30660107f9eccf7cd9e72c2e /usr/src/lib | |
parent | 265a964d7aa43c47170d21d2f01bcf873d7fd79d (diff) | |
download | illumos-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')
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, ¶ms) != 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(¶ms, 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, ¶ms, 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; |