summaryrefslogtreecommitdiff
path: root/usr/src/lib/libscf/common
diff options
context:
space:
mode:
authorGavin Maltby <gavin.maltby@oracle.com>2010-07-30 17:04:17 +1000
committerGavin Maltby <gavin.maltby@oracle.com>2010-07-30 17:04:17 +1000
commitf6e214c7418f43af38bd8c3a557e3d0a1d311cfa (patch)
tree0f0e4cee5ead68ee30660107f9eccf7cd9e72c2e /usr/src/lib/libscf/common
parent265a964d7aa43c47170d21d2f01bcf873d7fd79d (diff)
downloadillumos-joyent-f6e214c7418f43af38bd8c3a557e3d0a1d311cfa.tar.gz
PSARC/2009/617 Software Events Notification Parameters CLI
PSARC/2009/618 snmp-notify: SNMP Notification Daemon for Software Events PSARC/2009/619 smtp-notify: Email Notification Daemon for Software Events PSARC/2010/225 fmd for non-global Solaris zones PSARC/2010/226 Solaris Instance UUID PSARC/2010/227 nvlist_nvflag(3NVPAIR) PSARC/2010/228 libfmevent additions PSARC/2010/257 sysevent_evc_setpropnvl and sysevent_evc_getpropnvl PSARC/2010/265 FMRI and FMA Event Stabilty, 'ireport' category 1 event class, and the 'sw' FMRI scheme PSARC/2010/278 FMA/SMF integration: instance state transitions PSARC/2010/279 Modelling panics within FMA PSARC/2010/290 logadm.conf upgrade 6392476 fmdump needs to pretty-print 6393375 userland ereport/ireport event generation interfaces 6445732 Add email notification agent for FMA and software events 6804168 RFE: Allow an efficient means to monitor SMF services status changes 6866661 scf_values_destroy(3SCF) will segfault if is passed NULL 6884709 Add snmp notification agent for FMA and software events 6884712 Add private interface to tap into libfmd_msg macro expansion capabilities 6897919 fmd to run in a non-global zone 6897937 fmd use of non-private doors is not safe 6900081 add a UUID to Solaris kernel image for use in crashdump identification 6914884 model panic events as a defect diagnosis in FMA 6944862 fmd_case_open_uuid, fmd_case_uuisresolved, fmd_nvl_create_defect 6944866 log legacy sysevents in fmd 6944867 enumerate svc scheme in topo 6944868 software-diagnosis and software-response fmd modules 6944870 model SMF maintenance state as a defect diagnosis in FMA 6944876 savecore runs in foreground for systems with zfs root and dedicated dump 6965796 Implement notification parameters for SMF state transitions and FMA events 6968287 SUN-FM-MIB.mib needs to be updated to reflect Oracle information 6972331 logadm.conf upgrade PSARC/2010/290
Diffstat (limited to 'usr/src/lib/libscf/common')
-rw-r--r--usr/src/lib/libscf/common/libscf_impl.h8
-rw-r--r--usr/src/lib/libscf/common/lowlevel.c35
-rw-r--r--usr/src/lib/libscf/common/mapfile-vers11
-rw-r--r--usr/src/lib/libscf/common/midlevel.c150
-rw-r--r--usr/src/lib/libscf/common/notify_params.c1979
-rw-r--r--usr/src/lib/libscf/common/scf_tmpl.c13
6 files changed, 2157 insertions, 39 deletions
diff --git a/usr/src/lib/libscf/common/libscf_impl.h b/usr/src/lib/libscf/common/libscf_impl.h
index 834c23fc53..01fc49f9ae 100644
--- a/usr/src/lib/libscf/common/libscf_impl.h
+++ b/usr/src/lib/libscf/common/libscf_impl.h
@@ -20,15 +20,12 @@
*/
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
*/
#ifndef _LIBSCF_IMPL_H
#define _LIBSCF_IMPL_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <libscf.h>
#include <libscf_priv.h>
@@ -58,7 +55,10 @@ typedef enum {
SCF_MSG_PATTERN_LEGACY
} scf_msg_t;
+scf_type_t scf_true_base_type(scf_type_t);
const char *scf_get_msg(scf_msg_t);
+int ismember(const scf_error_t, const scf_error_t[]);
+int32_t state_from_string(const char *, size_t);
#ifdef __cplusplus
}
diff --git a/usr/src/lib/libscf/common/lowlevel.c b/usr/src/lib/libscf/common/lowlevel.c
index 27136167a5..7363c7fc9c 100644
--- a/usr/src/lib/libscf/common/lowlevel.c
+++ b/usr/src/lib/libscf/common/lowlevel.c
@@ -793,6 +793,38 @@ scf_handle_create(scf_version_t v)
return (ret);
}
+/*
+ * Fails with
+ * _NO_MEMORY
+ * _NO_SERVER - server door could not be open()ed
+ * door call failed
+ * door_info() failed
+ * _VERSION_MISMATCH - server returned bad file descriptor
+ * server claimed bad request
+ * server reported version mismatch
+ * server refused with unknown reason
+ * _INVALID_ARGUMENT
+ * _NO_RESOURCES - server is out of memory
+ * _PERMISSION_DENIED
+ * _INTERNAL - could not set up entities or iters
+ * server response too big
+ */
+scf_handle_t *
+_scf_handle_create_and_bind(scf_version_t ver)
+{
+ scf_handle_t *h;
+
+ h = scf_handle_create(ver);
+ if (h == NULL)
+ return (NULL);
+
+ if (scf_handle_bind(h) == -1) {
+ scf_handle_destroy(h);
+ return (NULL);
+ }
+ return (h);
+}
+
int
scf_handle_decorate(scf_handle_t *handle, const char *name, scf_value_t *v)
{
@@ -4073,6 +4105,9 @@ scf_transaction_destroy(scf_transaction_t *val)
void
scf_transaction_destroy_children(scf_transaction_t *tran)
{
+ if (tran == NULL)
+ return;
+
scf_transaction_reset_impl(tran, 1, 0);
}
diff --git a/usr/src/lib/libscf/common/mapfile-vers b/usr/src/lib/libscf/common/mapfile-vers
index 29d0911937..643f5424f2 100644
--- a/usr/src/lib/libscf/common/mapfile-vers
+++ b/usr/src/lib/libscf/common/mapfile-vers
@@ -92,6 +92,11 @@ SYMBOL_VERSION SUNW_1.2 {
scf_tmpl_visibility_to_string;
scf_type_to_string;
scf_values_destroy;
+ smf_notify_del_params;
+ smf_notify_get_params;
+ smf_notify_set_params;
+ smf_state_from_string;
+ smf_state_to_string;
} SUNW_1.1;
SYMBOL_VERSION SUNW_1.1 {
@@ -275,16 +280,20 @@ SYMBOL_VERSION SUNW_1.1 {
SYMBOL_VERSION SUNWprivate_1.1 {
global:
gen_filenms_from_fmri;
+ ismember;
scf_canonify_fmri;
scf_cmp_pattern;
_scf_create_errors;
scf_decode32;
scf_encode32;
scf_general_pg_setup;
+ _scf_get_fma_notify_params;
+ _scf_get_svc_notify_params;
_scf_handle_decorations;
scf_is_compatible_type;
_scf_notify_add_pgname;
_scf_notify_add_pgtype;
+ _scf_notify_get_params;
_scf_notify_wait;
scf_parse_file_fmri;
scf_parse_fmri;
@@ -320,6 +329,8 @@ SYMBOL_VERSION SUNWprivate_1.1 {
scf_is_fastboot_default;
scf_fastreboot_default_set_transient;
_check_services;
+ _scf_handle_create_and_bind;
+ _smf_refresh_all_instances;
local:
*;
};
diff --git a/usr/src/lib/libscf/common/midlevel.c b/usr/src/lib/libscf/common/midlevel.c
index c466391761..71b72404f1 100644
--- a/usr/src/lib/libscf/common/midlevel.c
+++ b/usr/src/lib/libscf/common/midlevel.c
@@ -28,7 +28,6 @@
#include <assert.h>
#include <libuutil.h>
#include <stdio.h>
-#include <strings.h>
#include <string.h>
#include <stdlib.h>
#include <sys/param.h>
@@ -51,25 +50,6 @@
/* Path to speedy files area must end with a slash */
#define SMF_SPEEDY_FILES_PATH "/etc/svc/volatile/"
-/*
- * Internal private function that creates and binds a handle.
- */
-static scf_handle_t *
-handle_create(void)
-{
- scf_handle_t *h;
-
- h = scf_handle_create(SCF_VERSION);
- if (h == NULL)
- return (NULL);
-
- if (scf_handle_bind(h) == -1) {
- scf_handle_destroy(h);
- return (NULL);
- }
- return (h);
-}
-
void
scf_simple_handle_destroy(scf_simple_handle_t *simple_h)
{
@@ -939,7 +919,7 @@ set_inst_action(const char *fmri, const char *action)
scf_instance_t *inst;
int ret = -1;
- h = handle_create();
+ h = _scf_handle_create_and_bind(SCF_VERSION);
if (h == NULL)
return (-1);
@@ -1082,7 +1062,7 @@ set_inst_enabled_flags(const char *fmri, int flags, uint8_t desired)
return (ret);
}
- if ((h = handle_create()) == NULL)
+ if ((h = _scf_handle_create_and_bind(SCF_VERSION)) == NULL)
return (ret);
if ((inst = scf_instance_create(h)) == NULL) {
@@ -1184,6 +1164,35 @@ _smf_refresh_instance_i(scf_instance_t *inst)
}
int
+_smf_refresh_all_instances(scf_service_t *s)
+{
+ scf_handle_t *h = scf_service_handle(s);
+ scf_instance_t *i = scf_instance_create(h);
+ scf_iter_t *it = scf_iter_create(h);
+ int err, r = -1;
+
+ if (h == NULL || i == NULL || it == NULL)
+ goto error;
+
+ if (scf_iter_service_instances(it, s) != 0)
+ goto error;
+
+ while ((err = scf_iter_next_instance(it, i)) == 1)
+ if (_smf_refresh_instance_i(i) != 0)
+ goto error;
+
+ if (err == -1)
+ goto error;
+
+ r = 0;
+error:
+ scf_instance_destroy(i);
+ scf_iter_destroy(it);
+
+ return (r);
+}
+
+int
smf_refresh_instance(const char *instance)
{
return (set_inst_action(instance, SCF_PROPERTY_REFRESH));
@@ -1320,7 +1329,7 @@ scf_general_pg_setup(const char *fmri, const char *pg_name)
return (NULL);
} else {
- ret->h = handle_create();
+ ret->h = _scf_handle_create_and_bind(SCF_VERSION);
ret->inst = scf_instance_create(ret->h);
ret->snap = scf_snapshot_create(ret->h);
ret->running_pg = scf_pg_create(ret->h);
@@ -1564,7 +1573,7 @@ scf_simple_walk_instances(uint_t state_flags, void *private,
int svc_iter_ret, inst_iter_ret;
int inst_state;
- if ((h = handle_create()) == NULL)
+ if ((h = _scf_handle_create_and_bind(SCF_VERSION)) == NULL)
return (ret);
if (((scope = scf_scope_create(h)) == NULL) ||
@@ -1654,7 +1663,7 @@ scf_simple_prop_get(scf_handle_t *hin, const char *instance, const char *pgname,
local_h = B_FALSE;
}
- if (local_h && ((h = handle_create()) == NULL))
+ if (local_h && ((h = _scf_handle_create_and_bind(SCF_VERSION)) == NULL))
return (NULL);
if ((fmri_buf = assemble_fmri(h, instance, pgname, propname)) == NULL) {
@@ -1795,7 +1804,7 @@ scf_simple_app_props_get(scf_handle_t *hin, const char *inst_fmri)
local_h = B_FALSE;
}
- if (local_h && ((h = handle_create()) == NULL))
+ if (local_h && ((h = _scf_handle_create_and_bind(SCF_VERSION)) == NULL))
return (NULL);
if (inst_fmri == NULL) {
@@ -2495,7 +2504,7 @@ gen_filenms_from_fmri(const char *fmri, const char *name, char *filename,
return (0);
}
-static scf_type_t
+scf_type_t
scf_true_base_type(scf_type_t type)
{
scf_type_t base = type;
@@ -2576,7 +2585,7 @@ int
scf_read_propvec(const char *fmri, const char *pgname, boolean_t running,
scf_propvec_t *properties, scf_propvec_t **badprop)
{
- scf_handle_t *h = handle_create();
+ scf_handle_t *h = _scf_handle_create_and_bind(SCF_VERSION);
scf_service_t *s = scf_service_create(h);
scf_instance_t *i = scf_instance_create(h);
scf_snapshot_t *snap = running ? scf_snapshot_create(h) : NULL;
@@ -2742,7 +2751,7 @@ int
scf_write_propvec(const char *fmri, const char *pgname,
scf_propvec_t *properties, scf_propvec_t **badprop)
{
- scf_handle_t *h = handle_create();
+ scf_handle_t *h = _scf_handle_create_and_bind(SCF_VERSION);
scf_service_t *s = scf_service_create(h);
scf_instance_t *inst = scf_instance_create(h);
scf_snapshot_t *snap = scf_snapshot_create(h);
@@ -3052,3 +3061,86 @@ _check_services(char **svcs)
}
}
}
+
+/*ARGSUSED*/
+static int
+str_compare(const char *s1, const char *s2, size_t n)
+{
+ return (strcmp(s1, s2));
+}
+
+static int
+str_n_compare(const char *s1, const char *s2, size_t n)
+{
+ return (strncmp(s1, s2, n));
+}
+
+int32_t
+state_from_string(const char *state, size_t l)
+{
+ int (*str_cmp)(const char *, const char *, size_t);
+
+ if (l == 0)
+ str_cmp = str_compare;
+ else
+ str_cmp = str_n_compare;
+
+ if (str_cmp(SCF_STATE_STRING_UNINIT, state, l) == 0)
+ return (SCF_STATE_UNINIT);
+ else if (str_cmp(SCF_STATE_STRING_MAINT, state, l) == 0)
+ return (SCF_STATE_MAINT);
+ else if (str_cmp(SCF_STATE_STRING_OFFLINE, state, l) == 0)
+ return (SCF_STATE_OFFLINE);
+ else if (str_cmp(SCF_STATE_STRING_DISABLED, state, l) == 0)
+ return (SCF_STATE_DISABLED);
+ else if (str_cmp(SCF_STATE_STRING_ONLINE, state, l) == 0)
+ return (SCF_STATE_ONLINE);
+ else if (str_cmp(SCF_STATE_STRING_DEGRADED, state, l) == 0)
+ return (SCF_STATE_DEGRADED);
+ else if (str_cmp("all", state, l) == 0)
+ return (SCF_STATE_ALL);
+ else
+ return (-1);
+}
+
+/*
+ * int32_t smf_state_from_string()
+ * return the value of the macro SCF_STATE_* for the corresponding state
+ * it returns SCF_STATE_ALL if "all" is passed. -1 if the string passed doesn't
+ * correspond to any valid state.
+ */
+int32_t
+smf_state_from_string(const char *state)
+{
+ return (state_from_string(state, 0));
+}
+
+/*
+ * smf_state_to_string()
+ * Takes an int32_t representing an SMF state and returns
+ * the corresponding string. The string is read only and need not to be
+ * freed.
+ * returns NULL on invalid input.
+ */
+const char *
+smf_state_to_string(int32_t s)
+{
+ switch (s) {
+ case SCF_STATE_UNINIT:
+ return (SCF_STATE_STRING_UNINIT);
+ case SCF_STATE_MAINT:
+ return (SCF_STATE_STRING_MAINT);
+ case SCF_STATE_OFFLINE:
+ return (SCF_STATE_STRING_OFFLINE);
+ case SCF_STATE_DISABLED:
+ return (SCF_STATE_STRING_DISABLED);
+ case SCF_STATE_ONLINE:
+ return (SCF_STATE_STRING_ONLINE);
+ case SCF_STATE_DEGRADED:
+ return (SCF_STATE_STRING_DEGRADED);
+ case SCF_STATE_ALL:
+ return ("all");
+ default:
+ return (NULL);
+ }
+}
diff --git a/usr/src/lib/libscf/common/notify_params.c b/usr/src/lib/libscf/common/notify_params.c
new file mode 100644
index 0000000000..3e41d19f8f
--- /dev/null
+++ b/usr/src/lib/libscf/common/notify_params.c
@@ -0,0 +1,1979 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ */
+
+#include "libscf_impl.h"
+
+#include <assert.h>
+#include <strings.h>
+
+/*
+ * Errors returned by smf_notify_{del|get|set}_params()
+ */
+static const scf_error_t errs_1[] = {
+ SCF_ERROR_BACKEND_ACCESS,
+ SCF_ERROR_BACKEND_READONLY,
+ SCF_ERROR_CONNECTION_BROKEN,
+ SCF_ERROR_DELETED,
+ SCF_ERROR_INTERNAL,
+ SCF_ERROR_INVALID_ARGUMENT,
+ SCF_ERROR_NO_MEMORY,
+ SCF_ERROR_NO_RESOURCES,
+ SCF_ERROR_NOT_FOUND,
+ SCF_ERROR_PERMISSION_DENIED,
+ 0
+};
+
+/*
+ * Errors returned by smf_notify_{del|get|set}_params()
+ * Except SCF_ERROR_INVALID_ARGUMENT
+ */
+static const scf_error_t errs_2[] = {
+ SCF_ERROR_BACKEND_ACCESS,
+ SCF_ERROR_BACKEND_READONLY,
+ SCF_ERROR_CONNECTION_BROKEN,
+ SCF_ERROR_DELETED,
+ SCF_ERROR_INTERNAL,
+ SCF_ERROR_NO_MEMORY,
+ SCF_ERROR_NO_RESOURCES,
+ SCF_ERROR_NOT_FOUND,
+ SCF_ERROR_PERMISSION_DENIED,
+ 0
+};
+
+/*
+ * Helper function that abort() on unexpected errors.
+ * The expected error set is a zero-terminated array of scf_error_t
+ */
+static int
+check_scf_error(scf_error_t e, const scf_error_t *errs)
+{
+ if (ismember(e, errs))
+ return (1);
+
+ assert(0);
+ abort();
+
+ /*NOTREACHED*/
+}
+
+/*
+ * Mapping of state transition to pgname.
+ */
+static struct st_pgname {
+ const char *st_pgname;
+ int32_t st_state;
+} st_pgnames[] = {
+ { "to-uninitialized", SCF_TRANS(0, SCF_STATE_UNINIT) },
+ { "from-uninitialized", SCF_TRANS(SCF_STATE_UNINIT, 0) },
+ { "to-maintenance", SCF_TRANS(0, SCF_STATE_MAINT) },
+ { "from-maintenance", SCF_TRANS(SCF_STATE_MAINT, 0) },
+ { "to-offline", SCF_TRANS(0, SCF_STATE_OFFLINE) },
+ { "from-offline", SCF_TRANS(SCF_STATE_OFFLINE, 0) },
+ { "to-disabled", SCF_TRANS(0, SCF_STATE_DISABLED) },
+ { "from-disabled", SCF_TRANS(SCF_STATE_DISABLED, 0) },
+ { "to-online", SCF_TRANS(0, SCF_STATE_ONLINE) },
+ { "from-online", SCF_TRANS(SCF_STATE_ONLINE, 0) },
+ { "to-degraded", SCF_TRANS(0, SCF_STATE_DEGRADED) },
+ { "from-degraded", SCF_TRANS(SCF_STATE_DEGRADED, 0) },
+ { NULL, 0 }
+};
+
+/*
+ * Check if class matches or is a subclass of SCF_SVC_TRANSITION_CLASS
+ *
+ * returns 1, otherwise return 0
+ */
+static boolean_t
+is_svc_stn(const char *class)
+{
+ int n = strlen(SCF_SVC_TRANSITION_CLASS);
+
+ if (class && strncmp(class, SCF_SVC_TRANSITION_CLASS, n) == 0)
+ if (class[n] == '\0' || class[n] == '.')
+ return (1);
+ return (0);
+}
+
+/*
+ * Return the len of the base class. For instance, "class.class1.class2.*"
+ * will return the length of "class.class1.class2"
+ * This function does not check if the class or base class is valid.
+ * A class such as "class.class1....****" is not valid but will return the
+ * length of "class.class1....***"
+ */
+static size_t
+base_class_len(const char *c)
+{
+ const char *p;
+ size_t n;
+
+ if ((n = strlen(c)) == 0)
+ return (0);
+
+ p = c + n;
+
+ /* get rid of any trailing asterisk */
+ if (*--p == '*')
+ n--;
+
+ /* make sure the class doesn't end in '.' */
+ while (p >= c && *--p == '.')
+ n--;
+
+ return (n);
+}
+
+/*
+ * Allocates and builds the pgname for an FMA dotted class.
+ * The pgname will be of the form "class.class1.class2,SCF_NOTIFY_PG_POSTFIX"
+ *
+ * NULL on error
+ */
+static char *
+class_to_pgname(const char *class)
+{
+ size_t n;
+ ssize_t sz = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1;
+ char *pgname = NULL;
+
+ n = base_class_len(class);
+
+ if (n == 0) {
+ (void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
+ return (NULL);
+ }
+
+ if ((pgname = malloc(sz)) == NULL) {
+ (void) scf_set_error(SCF_ERROR_NO_MEMORY);
+ goto error;
+ }
+
+ if (snprintf(pgname, sz, "%.*s,%s", (int)n, class,
+ SCF_NOTIFY_PG_POSTFIX) >= sz) {
+ (void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
+ goto error;
+ }
+ return (pgname);
+
+error:
+ free(pgname);
+ pgname = NULL;
+
+ return (pgname);
+}
+
+/*
+ * Get the pg from the running snapshot of the instance (composed or not)
+ */
+static int
+get_pg(scf_service_t *s, scf_instance_t *i, const char *n,
+ scf_propertygroup_t *pg, int composed)
+{
+ scf_handle_t *h = scf_instance_handle(i);
+ scf_error_t scf_e = scf_error();
+ scf_snapshot_t *snap = scf_snapshot_create(h);
+ scf_snaplevel_t *slvl = scf_snaplevel_create(h);
+ int r = -1;
+
+ if (h == NULL) {
+ /*
+ * Use the error stored in scf_e
+ */
+ (void) scf_set_error(scf_e);
+ goto out;
+ }
+ if (s == NULL) {
+ if (snap == NULL || slvl == NULL)
+ goto out;
+ if (scf_instance_get_snapshot(i, "running", snap) != 0)
+ goto out;
+
+ if (composed) {
+ if (scf_instance_get_pg_composed(i, snap, n, pg) != 0)
+ goto out;
+ } else {
+ if (scf_snapshot_get_base_snaplevel(snap, slvl) != 0 ||
+ scf_snaplevel_get_pg(slvl, n, pg) != 0)
+ goto out;
+ }
+ } else {
+ if (scf_service_get_pg(s, n, pg) != 0)
+ goto out;
+ }
+
+ r = 0;
+out:
+ scf_snaplevel_destroy(slvl);
+ scf_snapshot_destroy(snap);
+
+ return (r);
+}
+
+/*
+ * Add a pg if it does not exist, or get it if it exists.
+ * It operates on the instance if the service parameter is NULL.
+ *
+ * returns 0 on success or -1 on failure
+ */
+static int
+get_or_add_pg(scf_service_t *s, scf_instance_t *i, const char *n, const char *t,
+ uint32_t flags, scf_propertygroup_t *pg)
+{
+ int r;
+
+ if (s == NULL)
+ r = scf_instance_add_pg(i, n, t, flags, pg);
+ else
+ r = scf_service_add_pg(s, n, t, flags, pg);
+
+ if (r == 0)
+ return (0);
+ else if (scf_error() != SCF_ERROR_EXISTS)
+ return (-1);
+
+ if (s == NULL)
+ r = scf_instance_get_pg(i, n, pg);
+ else
+ r = scf_service_get_pg(s, n, pg);
+
+ return (r);
+}
+
+/*
+ * Delete the property group form the instance or service.
+ * If service is NULL, use instance, otherwise use only the service.
+ *
+ * Return SCF_SUCCESS or SCF_FAILED on
+ * SCF_ERROR_BACKEND_ACCESS
+ * SCF_ERROR_BACKEND_READONLY
+ * SCF_ERROR_CONNECTION_BROKEN
+ * SCF_ERROR_DELETED
+ * SCF_ERROR_HANDLE_MISMATCH
+ * SCF_ERROR_INTERNAL
+ * SCF_ERROR_INVALID_ARGUMENT
+ * SCF_ERROR_NO_RESOURCES
+ * SCF_ERROR_NOT_BOUND
+ * SCF_ERROR_NOT_FOUND
+ * SCF_ERROR_NOT_SET
+ * SCF_ERROR_PERMISSION_DENIED
+ */
+static int
+del_pg(scf_service_t *s, scf_instance_t *i, const char *n,
+ scf_propertygroup_t *pg)
+{
+ if ((s == NULL ? scf_instance_get_pg(i, n, pg) :
+ scf_service_get_pg(s, n, pg)) != SCF_SUCCESS)
+ if (scf_error() == SCF_ERROR_NOT_FOUND)
+ return (SCF_SUCCESS);
+ else
+ return (SCF_FAILED);
+
+ if (scf_pg_delete(pg) != SCF_SUCCESS)
+ if (scf_error() == SCF_ERROR_DELETED)
+ return (SCF_SUCCESS);
+ else
+ return (SCF_FAILED);
+
+ return (SCF_SUCCESS);
+}
+
+static scf_type_t
+get_scf_type(nvpair_t *p)
+{
+ switch (nvpair_type(p)) {
+ case DATA_TYPE_BOOLEAN:
+ case DATA_TYPE_BOOLEAN_VALUE:
+ case DATA_TYPE_BOOLEAN_ARRAY:
+ return (SCF_TYPE_BOOLEAN);
+
+ case DATA_TYPE_BYTE:
+ case DATA_TYPE_UINT8:
+ case DATA_TYPE_UINT16:
+ case DATA_TYPE_UINT32:
+ case DATA_TYPE_UINT64:
+ case DATA_TYPE_BYTE_ARRAY:
+ case DATA_TYPE_UINT8_ARRAY:
+ case DATA_TYPE_UINT16_ARRAY:
+ case DATA_TYPE_UINT32_ARRAY:
+ case DATA_TYPE_UINT64_ARRAY:
+ return (SCF_TYPE_COUNT);
+
+ case DATA_TYPE_INT8:
+ case DATA_TYPE_INT16:
+ case DATA_TYPE_INT32:
+ case DATA_TYPE_INT64:
+ case DATA_TYPE_INT8_ARRAY:
+ case DATA_TYPE_INT16_ARRAY:
+ case DATA_TYPE_INT32_ARRAY:
+ case DATA_TYPE_INT64_ARRAY:
+ return (SCF_TYPE_INTEGER);
+
+ case DATA_TYPE_STRING:
+ case DATA_TYPE_STRING_ARRAY:
+ return (SCF_TYPE_ASTRING);
+
+ default:
+ return (SCF_TYPE_INVALID);
+ }
+}
+
+static int
+add_entry(scf_transaction_entry_t *te, scf_value_t *val)
+{
+ if (scf_entry_add_value(te, val) != 0) {
+ scf_value_destroy(val);
+ return (SCF_FAILED);
+ }
+
+ return (SCF_SUCCESS);
+}
+
+static int
+add_boolean_entry(scf_handle_t *h, scf_transaction_entry_t *te, uint8_t v)
+{
+ scf_value_t *val = scf_value_create(h);
+
+ if (val == NULL)
+ return (SCF_FAILED);
+
+ scf_value_set_boolean(val, v);
+
+ return (add_entry(te, val));
+}
+
+static int
+add_count_entry(scf_handle_t *h, scf_transaction_entry_t *te, uint64_t v)
+{
+ scf_value_t *val = scf_value_create(h);
+
+ if (val == NULL)
+ return (SCF_FAILED);
+
+ scf_value_set_count(val, v);
+
+ return (add_entry(te, val));
+}
+
+static int
+add_integer_entry(scf_handle_t *h, scf_transaction_entry_t *te, int64_t v)
+{
+ scf_value_t *val = scf_value_create(h);
+
+ if (val == NULL)
+ return (SCF_FAILED);
+
+ scf_value_set_integer(val, v);
+
+ return (add_entry(te, val));
+}
+
+static int
+add_astring_entry(scf_handle_t *h, scf_transaction_entry_t *te, char *s)
+{
+ scf_value_t *val = scf_value_create(h);
+
+ if (val == NULL)
+ return (SCF_FAILED);
+
+ if (scf_value_set_astring(val, s) != 0) {
+ scf_value_destroy(val);
+ return (SCF_FAILED);
+ }
+
+ return (add_entry(te, val));
+}
+
+static int
+get_nvpair_vals(scf_handle_t *h, scf_transaction_entry_t *te, nvpair_t *p)
+{
+ scf_value_t *val = scf_value_create(h);
+ uint_t n = 1;
+ int i;
+
+ if (val == NULL)
+ return (SCF_FAILED);
+
+ switch (nvpair_type(p)) {
+ case DATA_TYPE_BOOLEAN:
+ return (add_boolean_entry(h, te, 1));
+ case DATA_TYPE_BOOLEAN_VALUE:
+ {
+ boolean_t v;
+
+ (void) nvpair_value_boolean_value(p, &v);
+ return (add_boolean_entry(h, te, (uint8_t)v));
+ }
+ case DATA_TYPE_BOOLEAN_ARRAY:
+ {
+ boolean_t *v;
+
+ (void) nvpair_value_boolean_array(p, &v, &n);
+ for (i = 0; i < n; ++i) {
+ if (add_boolean_entry(h, te, (uint8_t)v[i]) !=
+ SCF_SUCCESS)
+ return (SCF_FAILED);
+ }
+ return (SCF_SUCCESS);
+ }
+ case DATA_TYPE_BYTE:
+ {
+ uchar_t v;
+
+ (void) nvpair_value_byte(p, &v);
+ return (add_count_entry(h, te, v));
+ }
+ case DATA_TYPE_UINT8:
+ {
+ uint8_t v;
+
+ (void) nvpair_value_uint8(p, &v);
+ return (add_count_entry(h, te, v));
+ }
+ case DATA_TYPE_UINT16:
+ {
+ uint16_t v;
+
+ (void) nvpair_value_uint16(p, &v);
+ return (add_count_entry(h, te, v));
+ }
+ case DATA_TYPE_UINT32:
+ {
+ uint32_t v;
+
+ (void) nvpair_value_uint32(p, &v);
+ return (add_count_entry(h, te, v));
+ }
+ case DATA_TYPE_UINT64:
+ {
+ uint64_t v;
+
+ (void) nvpair_value_uint64(p, &v);
+ return (add_count_entry(h, te, v));
+ }
+ case DATA_TYPE_BYTE_ARRAY:
+ {
+ uchar_t *v;
+
+ (void) nvpair_value_byte_array(p, &v, &n);
+ for (i = 0; i < n; ++i) {
+ if (add_count_entry(h, te, v[i]) != SCF_SUCCESS)
+ return (SCF_FAILED);
+ }
+ return (SCF_SUCCESS);
+ }
+ case DATA_TYPE_UINT8_ARRAY:
+ {
+ uint8_t *v;
+
+ (void) nvpair_value_uint8_array(p, &v, &n);
+ for (i = 0; i < n; ++i) {
+ if (add_count_entry(h, te, v[i]) != SCF_SUCCESS)
+ return (SCF_FAILED);
+ }
+ return (SCF_SUCCESS);
+ }
+ case DATA_TYPE_UINT16_ARRAY:
+ {
+ uint16_t *v;
+
+ (void) nvpair_value_uint16_array(p, &v, &n);
+ for (i = 0; i < n; ++i) {
+ if (add_count_entry(h, te, v[i]) != SCF_SUCCESS)
+ return (SCF_FAILED);
+ }
+ return (SCF_SUCCESS);
+ }
+ case DATA_TYPE_UINT32_ARRAY:
+ {
+ uint32_t *v;
+
+ (void) nvpair_value_uint32_array(p, &v, &n);
+ for (i = 0; i < n; ++i) {
+ if (add_count_entry(h, te, v[i]) != SCF_SUCCESS)
+ return (SCF_FAILED);
+ }
+ return (SCF_SUCCESS);
+ }
+ case DATA_TYPE_UINT64_ARRAY:
+ {
+ uint64_t *v;
+
+ (void) nvpair_value_uint64_array(p, &v, &n);
+ for (i = 0; i < n; ++i) {
+ if (add_count_entry(h, te, v[i]) != SCF_SUCCESS)
+ return (SCF_FAILED);
+ }
+ return (SCF_SUCCESS);
+ }
+ case DATA_TYPE_INT8:
+ {
+ int8_t v;
+
+ (void) nvpair_value_int8(p, &v);
+ return (add_integer_entry(h, te, v));
+ }
+ case DATA_TYPE_INT16:
+ {
+ int16_t v;
+
+ (void) nvpair_value_int16(p, &v);
+ return (add_integer_entry(h, te, v));
+ }
+ case DATA_TYPE_INT32:
+ {
+ int32_t v;
+
+ (void) nvpair_value_int32(p, &v);
+ return (add_integer_entry(h, te, v));
+ }
+ case DATA_TYPE_INT64:
+ {
+ int64_t v;
+
+ (void) nvpair_value_int64(p, &v);
+ return (add_integer_entry(h, te, v));
+ }
+ case DATA_TYPE_INT8_ARRAY:
+ {
+ int8_t *v;
+
+ (void) nvpair_value_int8_array(p, &v, &n);
+ for (i = 0; i < n; ++i) {
+ if (add_integer_entry(h, te, v[i]) !=
+ SCF_SUCCESS)
+ return (SCF_FAILED);
+ }
+ return (SCF_SUCCESS);
+ }
+ case DATA_TYPE_INT16_ARRAY:
+ {
+ int16_t *v;
+
+ (void) nvpair_value_int16_array(p, &v, &n);
+ for (i = 0; i < n; ++i) {
+ if (add_integer_entry(h, te, v[i]) !=
+ SCF_SUCCESS)
+ return (SCF_FAILED);
+ }
+ return (SCF_SUCCESS);
+ }
+ case DATA_TYPE_INT32_ARRAY:
+ {
+ int32_t *v;
+
+ (void) nvpair_value_int32_array(p, &v, &n);
+ for (i = 0; i < n; ++i) {
+ if (add_integer_entry(h, te, v[i]) !=
+ SCF_SUCCESS)
+ return (SCF_FAILED);
+ }
+ return (SCF_SUCCESS);
+ }
+ case DATA_TYPE_INT64_ARRAY:
+ {
+ int64_t *v;
+
+ (void) nvpair_value_int64_array(p, &v, &n);
+ for (i = 0; i < n; ++i) {
+ if (add_integer_entry(h, te, v[i]) !=
+ SCF_SUCCESS)
+ return (SCF_FAILED);
+ }
+ return (SCF_SUCCESS);
+ }
+ case DATA_TYPE_STRING:
+ {
+ char *str;
+
+ (void) nvpair_value_string(p, &str);
+ return (add_astring_entry(h, te, str));
+ }
+ case DATA_TYPE_STRING_ARRAY:
+ {
+ char **v;
+
+ (void) nvpair_value_string_array(p, &v, &n);
+ for (i = 0; i < n; ++i) {
+ if (add_astring_entry(h, te, v[i]) !=
+ SCF_SUCCESS)
+ return (SCF_FAILED);
+ }
+ return (SCF_SUCCESS);
+ }
+ default:
+ (void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
+ return (SCF_FAILED);
+ }
+
+ /*NOTREACHED*/
+}
+
+/*
+ * Add new transaction entry to scf_transaction_t
+ *
+ * Can fail with
+ * SCF_ERROR_BACKEND_ACCESS
+ * SCF_ERROR_CONNECTION_BROKEN
+ * SCF_ERROR_DELETED
+ * SCF_ERROR_INTERNAL
+ * SCF_ERROR_NO_RESOURCES
+ * SCF_ERROR_NOT_FOUND
+ */
+static int
+prep_transaction(scf_transaction_t *tx, scf_transaction_entry_t *te,
+ const char *prop, scf_type_t type)
+{
+ if (scf_transaction_property_new(tx, te, prop, type) != SCF_SUCCESS &&
+ (scf_error() != SCF_ERROR_EXISTS ||
+ scf_transaction_property_change(tx, te, prop, type) !=
+ SCF_SUCCESS)) {
+ if (check_scf_error(scf_error(), errs_2)) {
+ return (SCF_FAILED);
+ }
+ }
+
+ return (SCF_SUCCESS);
+}
+
+/*
+ * notify_set_params()
+ * returns 0 on success or -1 on failure
+ * SCF_ERROR_BACKEND_ACCESS
+ * SCF_ERROR_BACKEND_READONLY
+ * SCF_ERROR_CONNECTION_BROKEN
+ * SCF_ERROR_DELETED
+ * SCF_ERROR_INTERNAL
+ * SCF_ERROR_INVALID_ARGUMENT
+ * SCF_ERROR_NO_MEMORY
+ * SCF_ERROR_NO_RESOURCES
+ * SCF_ERROR_NOT_FOUND
+ * SCF_ERROR_PERMISSION_DENIED
+ */
+static int
+notify_set_params(scf_propertygroup_t *pg, nvlist_t *params)
+{
+ scf_handle_t *h = scf_pg_handle(pg);
+ scf_error_t scf_e = scf_error();
+ scf_transaction_t *tx = scf_transaction_create(h);
+ int bufsz = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1;
+ char *propname = malloc(bufsz);
+ int r = -1;
+ int err;
+
+ if (h == NULL) {
+ /*
+ * Use the error stored in scf_e
+ */
+ (void) scf_set_error(scf_e);
+ goto cleanup;
+ }
+ if (tx == NULL)
+ goto cleanup;
+
+ if (propname == NULL) {
+ (void) scf_set_error(SCF_ERROR_NO_MEMORY);
+ goto cleanup;
+ }
+
+ do {
+ nvpair_t *nvp;
+
+ /*
+ * make sure we have the most recent version of the pg
+ * start the transaction
+ */
+ if (scf_pg_update(pg) == SCF_FAILED ||
+ scf_transaction_start(tx, pg) != SCF_SUCCESS) {
+ if (check_scf_error(scf_error(), errs_2)) {
+ goto cleanup;
+ }
+ }
+
+ for (nvp = nvlist_next_nvpair(params, NULL); nvp != NULL;
+ nvp = nvlist_next_nvpair(params, nvp)) {
+ nvlist_t *m;
+ nvpair_t *p;
+
+ /* we ONLY take nvlists here */
+ if (nvpair_type(nvp) != DATA_TYPE_NVLIST) {
+ char *name = nvpair_name(nvp);
+
+ /*
+ * if this is output from
+ * smf_notify_get_params() we want to skip
+ * the tset value of the nvlist
+ */
+ if (strcmp(name, SCF_NOTIFY_NAME_TSET) == 0)
+ continue;
+
+ (void) scf_set_error(
+ SCF_ERROR_INVALID_ARGUMENT);
+ goto cleanup;
+ }
+
+ if (nvpair_value_nvlist(nvp, &m) != 0) {
+ (void) scf_set_error(
+ SCF_ERROR_INVALID_ARGUMENT);
+ goto cleanup;
+ }
+
+ /*
+ * Traverse each mechanism list
+ */
+ for (p = nvlist_next_nvpair(m, NULL); p != NULL;
+ p = nvlist_next_nvpair(m, p)) {
+ scf_transaction_entry_t *te =
+ scf_entry_create(h);
+ /* map the nvpair type to scf type */
+ scf_type_t type = get_scf_type(p);
+
+ if (te == NULL) {
+ if (scf_error() !=
+ SCF_ERROR_INVALID_ARGUMENT) {
+ scf_entry_destroy(te);
+ goto cleanup;
+ } else {
+ assert(0);
+ abort();
+ }
+ }
+
+ if (type == SCF_TYPE_INVALID) {
+ (void) scf_set_error(
+ SCF_ERROR_INVALID_ARGUMENT);
+ scf_entry_destroy(te);
+ goto cleanup;
+ }
+
+ if (snprintf(propname, bufsz, "%s,%s",
+ nvpair_name(nvp), nvpair_name(p)) >=
+ bufsz) {
+ (void) scf_set_error(
+ SCF_ERROR_INVALID_ARGUMENT);
+ scf_entry_destroy(te);
+ goto cleanup;
+ }
+
+ if (prep_transaction(tx, te, propname, type) !=
+ SCF_SUCCESS) {
+ scf_entry_destroy(te);
+ goto cleanup;
+ }
+
+ if (get_nvpair_vals(h, te, p) != SCF_SUCCESS) {
+ if (check_scf_error(scf_error(),
+ errs_2)) {
+ goto cleanup;
+ }
+ }
+ }
+ }
+ err = scf_transaction_commit(tx);
+ scf_transaction_destroy_children(tx);
+ } while (err == 0);
+
+ if (err == -1) {
+ if (check_scf_error(scf_error(), errs_2)) {
+ goto cleanup;
+ }
+ }
+
+ r = 0;
+
+cleanup:
+ scf_transaction_destroy_children(tx);
+ scf_transaction_destroy(tx);
+ free(propname);
+
+ return (r);
+}
+
+/*
+ * Decode fmri. Populates service OR instance depending on which one is an
+ * exact match to the fmri parameter.
+ *
+ * The function destroys and sets the unused entity (service or instance) to
+ * NULL.
+ *
+ * return SCF_SUCCESS or SCF_FAILED on
+ * SCF_ERROR_BACKEND_ACCESS
+ * SCF_ERROR_CONNECTION_BROKEN
+ * SCF_ERROR_CONSTRAINT_VIOLATED
+ * SCF_ERROR_DELETED
+ * SCF_ERROR_HANDLE_MISMATCH
+ * SCF_ERROR_INTERNAL
+ * SCF_ERROR_INVALID_ARGUMENT
+ * SCF_ERROR_NO_RESOURCES
+ * SCF_ERROR_NOT_BOUND
+ * SCF_ERROR_NOT_FOUND
+ * SCF_ERROR_NOT_SET
+ */
+static int
+decode_fmri(const char *fmri, scf_handle_t *h, scf_service_t **s,
+ scf_instance_t **i)
+{
+ if (scf_handle_decode_fmri(h, fmri, NULL, *s, NULL, NULL, NULL,
+ SCF_DECODE_FMRI_EXACT) != SCF_SUCCESS) {
+ if (scf_error() == SCF_ERROR_CONSTRAINT_VIOLATED) {
+ scf_service_destroy(*s);
+ *s = NULL;
+ } else {
+ return (SCF_FAILED);
+ }
+ }
+ if (*s == NULL)
+ if (scf_handle_decode_fmri(h, fmri, NULL, NULL, *i,
+ NULL, NULL, SCF_DECODE_FMRI_EXACT) != SCF_SUCCESS) {
+ return (SCF_FAILED);
+ }
+
+ return (SCF_SUCCESS);
+}
+
+/*
+ * Return size in bytes for an SCF_TYPE_*. Not all libscf types are supported
+ */
+static int
+get_type_size(scf_type_t t)
+{
+ switch (t) {
+ case SCF_TYPE_BOOLEAN:
+ return (sizeof (uint8_t));
+ case SCF_TYPE_COUNT:
+ return (sizeof (uint64_t));
+ case SCF_TYPE_INTEGER:
+ return (sizeof (int64_t));
+ case SCF_TYPE_ASTRING:
+ case SCF_TYPE_USTRING:
+ return (sizeof (void *));
+ default:
+ return (-1);
+ }
+
+ /*NOTREACHED*/
+}
+
+/*
+ * Return a pointer to the array of values according to its type
+ */
+static void **
+get_v_pointer(scf_values_t *v)
+{
+ switch (v->value_type) {
+ case SCF_TYPE_BOOLEAN:
+ return ((void **)&v->values.v_boolean);
+ case SCF_TYPE_COUNT:
+ return ((void **)&v->values.v_count);
+ case SCF_TYPE_INTEGER:
+ return ((void **)&v->values.v_integer);
+ case SCF_TYPE_ASTRING:
+ return ((void **)&v->values.v_astring);
+ case SCF_TYPE_USTRING:
+ return ((void **)&v->values.v_ustring);
+ default:
+ return (NULL);
+ }
+
+ /*NOTREACHED*/
+}
+
+/*
+ * Populate scf_values_t value array at position c.
+ */
+static int
+get_value(scf_value_t *val, scf_values_t *v, int c, char *buf, int sz)
+{
+ switch (v->value_type) {
+ case SCF_TYPE_BOOLEAN:
+ return (scf_value_get_boolean(val, v->values.v_boolean + c));
+ case SCF_TYPE_COUNT:
+ return (scf_value_get_count(val, v->values.v_count + c));
+ case SCF_TYPE_INTEGER:
+ return (scf_value_get_integer(val, v->values.v_integer + c));
+ case SCF_TYPE_ASTRING:
+ if (scf_value_get_astring(val, buf, sz) < 0 ||
+ (v->values.v_astring[c] = strdup(buf)) == NULL) {
+ (void) scf_set_error(SCF_ERROR_NO_MEMORY);
+ return (-1);
+ }
+ return (0);
+ case SCF_TYPE_USTRING:
+ if (scf_value_get_ustring(val, buf, sz) < 0 ||
+ (v->values.v_ustring[c] = strdup(buf)) == NULL) {
+ (void) scf_set_error(SCF_ERROR_NO_MEMORY);
+ return (-1);
+ }
+ return (0);
+ default:
+ return (-1);
+ }
+
+ /*NOTREACHED*/
+}
+
+/*
+ * Populate scf_values_t structure with values from prop
+ */
+static int
+values_get(scf_property_t *prop, scf_values_t *v)
+{
+ scf_handle_t *h = scf_property_handle(prop);
+ scf_error_t scf_e = scf_error();
+ scf_value_t *val = scf_value_create(h);
+ scf_iter_t *it = scf_iter_create(h);
+ scf_type_t type = SCF_TYPE_INVALID;
+ ssize_t sz = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH) + 1;
+ char *buf = malloc(sz);
+ void **p;
+ int err, elem_sz, count, cursz;
+ int r = SCF_FAILED;
+
+ assert(v != NULL);
+ assert(v->reserved == NULL);
+ if (buf == NULL) {
+ (void) scf_set_error(SCF_ERROR_NO_MEMORY);
+ goto cleanup;
+ }
+ if (h == NULL) {
+ /*
+ * Use the error stored in scf_e
+ */
+ (void) scf_set_error(scf_e);
+ goto cleanup;
+ }
+ if (val == NULL || it == NULL)
+ goto cleanup;
+
+ if (scf_property_type(prop, &type) != SCF_SUCCESS)
+ goto cleanup;
+ if (scf_property_is_type(prop, v->value_type) != SCF_SUCCESS)
+ goto error;
+
+ elem_sz = get_type_size(type);
+ assert(elem_sz > 0);
+
+ p = get_v_pointer(v);
+ assert(p != NULL);
+
+ cursz = count = v->value_count;
+ if (scf_iter_property_values(it, prop) != 0) {
+ goto error;
+ }
+
+ while ((err = scf_iter_next_value(it, val)) == 1) {
+ if (count + 1 >= cursz) {
+ void *tmp;
+
+ /* set initial size or double it */
+ cursz = cursz ? 2 * cursz : 8;
+ if ((tmp = realloc(*p, cursz * elem_sz)) == NULL) {
+ (void) scf_set_error(SCF_ERROR_NO_MEMORY);
+ goto error;
+ }
+ *p = tmp;
+ }
+
+ if (get_value(val, v, count, buf, sz) != 0)
+ goto error;
+
+ count++;
+ }
+
+ v->value_count = count;
+
+ if (err != 0)
+ goto error;
+
+ r = SCF_SUCCESS;
+ goto cleanup;
+
+error:
+ v->value_count = count;
+ scf_values_destroy(v);
+
+cleanup:
+ free(buf);
+ scf_iter_destroy(it);
+ scf_value_destroy(val);
+ return (r);
+}
+
+/*
+ * Add values from property p to existing nvlist_t nvl. The data type in the
+ * nvlist is inferred from the scf_type_t of the property.
+ *
+ * Returns SCF_SUCCESS or SCF_FAILED on
+ * SCF_ERROR_CONNECTION_BROKEN
+ * SCF_ERROR_DELETED
+ * SCF_ERROR_HANDLE_DESTROYED
+ * SCF_ERROR_HANDLE_MISMATCH
+ * SCF_ERROR_INVALID_ARGUMENT
+ * SCF_ERROR_NO_MEMORY
+ * SCF_ERROR_NO_RESOURCES
+ * SCF_ERROR_NOT_BOUND
+ * SCF_ERROR_NOT_SET
+ * SCF_ERROR_PERMISSION_DENIED
+ * SCF_ERROR_TYPE_MISMATCH
+ */
+static int
+add_prop_to_nvlist(scf_property_t *p, const char *pname, nvlist_t *nvl,
+ int array)
+{
+ scf_values_t vals = { 0 };
+ scf_type_t type, base_type;
+ int r = SCF_FAILED;
+ int err = 0;
+
+ if (p == NULL || pname == NULL || *pname == '\0' || nvl == NULL) {
+ (void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
+ return (r);
+ }
+
+ if (scf_property_type(p, &type) != 0)
+ goto cleanup;
+
+ /*
+ * scf_values_t does not support subtypes of SCF_TYPE_USTRING,
+ * mapping them all to SCF_TYPE_USTRING
+ */
+ base_type = scf_true_base_type(type);
+ if (base_type == SCF_TYPE_ASTRING && type != SCF_TYPE_ASTRING)
+ type = SCF_TYPE_USTRING;
+
+ vals.value_type = type;
+ if (values_get(p, &vals) != SCF_SUCCESS) {
+ if (scf_error() == SCF_ERROR_INVALID_ARGUMENT) {
+ assert(0);
+ abort();
+ }
+ goto cleanup;
+ }
+
+ switch (vals.value_type) {
+ case SCF_TYPE_BOOLEAN:
+ {
+ boolean_t *v;
+ int i;
+ int n = vals.value_count;
+
+ v = calloc(n, sizeof (boolean_t));
+ if (v == NULL) {
+ (void) scf_set_error(SCF_ERROR_NO_MEMORY);
+ goto cleanup;
+ }
+ for (i = 0; i < n; ++i)
+ v[i] = (boolean_t)vals.values.v_boolean[i];
+
+ if (n == 1 && !array)
+ err = nvlist_add_boolean_value(nvl, pname, *v);
+ else
+ err = nvlist_add_boolean_array(nvl, pname,
+ v, n);
+ if (err != 0) {
+ free(v);
+ goto cleanup;
+ }
+ free(v);
+ }
+ break;
+
+ case SCF_TYPE_COUNT:
+ if (vals.value_count == 1 && !array)
+ err = nvlist_add_uint64(nvl, pname,
+ *vals.values.v_count);
+ else
+ err = nvlist_add_uint64_array(nvl, pname,
+ vals.values.v_count, vals.value_count);
+ if (err != 0)
+ goto cleanup;
+
+ break;
+
+ case SCF_TYPE_INTEGER:
+ if (vals.value_count == 1 && !array)
+ err = nvlist_add_int64(nvl, pname,
+ *vals.values.v_integer);
+ else
+ err = nvlist_add_int64_array(nvl, pname,
+ vals.values.v_integer, vals.value_count);
+ if (err != 0)
+ goto cleanup;
+
+ break;
+
+ case SCF_TYPE_ASTRING:
+ if (vals.value_count == 1 && !array)
+ err = nvlist_add_string(nvl, pname,
+ *vals.values.v_astring);
+ else
+ err = nvlist_add_string_array(nvl, pname,
+ vals.values.v_astring, vals.value_count);
+ if (err != 0)
+ goto cleanup;
+ break;
+
+ default:
+ (void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
+ goto cleanup;
+ }
+
+ r = SCF_SUCCESS;
+cleanup:
+ scf_values_destroy(&vals);
+ switch (err) {
+ case 0:
+ break;
+ case EINVAL:
+ (void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
+ break;
+ case ENOMEM:
+ (void) scf_set_error(SCF_ERROR_NO_MEMORY);
+ break;
+ default:
+ /* we should *never* get here */
+ abort();
+ }
+
+ return (r);
+}
+
+/*
+ * Parse property name "mechanism,parameter" into separate mechanism
+ * and parameter. *mech must be freed by caller. *val points into
+ * *mech and must not be freed.
+ *
+ * Returns SCF_SUCCESS or SCF_FAILED on
+ * SCF_ERROR_NO_MEMORY
+ * SCF_ERROR_NOT_FOUND
+ */
+static int
+get_mech_name(const char *name, char **mech, char **val)
+{
+ char *p;
+ char *m;
+
+ if ((m = strdup(name)) == NULL) {
+ (void) scf_set_error(SCF_ERROR_NO_MEMORY);
+ return (SCF_FAILED);
+ }
+ if ((p = strchr(m, ',')) == NULL) {
+ free(m);
+ (void) scf_set_error(SCF_ERROR_NOT_FOUND);
+ return (SCF_FAILED);
+ }
+ *p = '\0';
+ *val = p + 1;
+ *mech = m;
+
+ return (SCF_SUCCESS);
+}
+
+/*
+ * Return the number of transitions in a transition set.
+ * If the transition set is invalid, it returns zero.
+ */
+static uint_t
+num_of_transitions(int32_t t)
+{
+ int i;
+ int n = 0;
+
+ if (SCF_TRANS_VALID(t)) {
+ for (i = 0x1; i < SCF_STATE_ALL; i <<= 1) {
+ if (i & t)
+ ++n;
+ if (SCF_TRANS_INITIAL_STATE(t) & i)
+ ++n;
+ }
+ }
+
+ return (n);
+}
+
+/*
+ * Return the SCF_STATE_* macro value for the state in the FMA classes for
+ * SMF state transitions. They are of type:
+ * SCF_SVC_TRANSITION_CLASS.<state>
+ * ireport.os.smf.state-transition.<state>
+ */
+static int32_t
+class_to_transition(const char *c)
+{
+ const char *p;
+ int r = 0;
+ size_t n;
+
+ if (!is_svc_stn(c)) {
+ return (0);
+ }
+
+ /*
+ * if we get here, c is SCF_SVC_TRANSITION_CLASS or longer
+ */
+ p = c + strlen(SCF_SVC_TRANSITION_CLASS);
+ if (*p == '.')
+ ++p;
+ else
+ return (0);
+
+ if ((n = base_class_len(p)) == 0)
+ return (0);
+
+ if ((r = state_from_string(p, n)) == -1)
+ r = 0;
+
+ return (r);
+}
+
+/*
+ * return SCF_SUCCESS or SCF_FAILED on
+ * SCF_ERROR_BACKEND_ACCESS
+ * SCF_ERROR_BACKEND_READONLY
+ * SCF_ERROR_CONNECTION_BROKEN
+ * SCF_ERROR_DELETED
+ * SCF_ERROR_INTERNAL
+ * SCF_ERROR_INVALID_ARGUMENT
+ * SCF_ERROR_NO_MEMORY
+ * SCF_ERROR_NO_RESOURCES
+ * SCF_ERROR_NOT_FOUND
+ * SCF_ERROR_PERMISSION_DENIED
+ */
+int
+smf_notify_set_params(const char *class, nvlist_t *attr)
+{
+ uint32_t ver;
+ int32_t tset;
+ scf_handle_t *h = _scf_handle_create_and_bind(SCF_VERSION);
+ scf_error_t scf_e = scf_error();
+ scf_service_t *s = scf_service_create(h);
+ scf_instance_t *i = scf_instance_create(h);
+ scf_propertygroup_t *pg = scf_pg_create(h);
+ nvlist_t *params = NULL;
+ char *fmri = (char *)SCF_NOTIFY_PARAMS_INST;
+ char *pgname = NULL;
+ int r = SCF_FAILED;
+ boolean_t is_stn;
+ int j;
+
+ assert(class != NULL);
+ if (h == NULL) {
+ /*
+ * use saved error if _scf_handle_create_and_bind() fails
+ */
+ (void) scf_set_error(scf_e);
+ goto cleanup;
+ }
+ if (i == NULL || s == NULL || pg == NULL)
+ goto cleanup;
+
+ /* check version */
+ if (nvlist_lookup_uint32(attr, SCF_NOTIFY_NAME_VERSION, &ver) != 0 ||
+ ver != SCF_NOTIFY_PARAMS_VERSION) {
+ (void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
+ goto cleanup;
+ }
+
+ if (nvlist_lookup_nvlist(attr, SCF_NOTIFY_PARAMS, &params) != 0) {
+ (void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
+ goto cleanup;
+ }
+
+ is_stn = is_svc_stn(class);
+ /* special case SMF state transition notification */
+ if (is_stn &&
+ (nvlist_lookup_string(attr, SCF_NOTIFY_NAME_FMRI, &fmri) != 0 ||
+ nvlist_lookup_int32(attr, SCF_NOTIFY_NAME_TSET, &tset) != 0 ||
+ !SCF_TRANS_VALID(tset))) {
+ (void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
+ goto cleanup;
+ }
+ if (decode_fmri(fmri, h, &s, &i) != SCF_SUCCESS)
+ if (scf_error() == SCF_ERROR_CONSTRAINT_VIOLATED) {
+ (void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
+ } else if (check_scf_error(scf_error(), errs_1)) {
+ goto cleanup;
+ }
+
+ if (is_stn) {
+ tset |= class_to_transition(class);
+
+ if (!SCF_TRANS_VALID(tset)) {
+ (void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
+ goto cleanup;
+ }
+
+ for (j = 0; st_pgnames[j].st_pgname != NULL; ++j) {
+ /* if this transition is not in the tset, continue */
+ if (!(tset & st_pgnames[j].st_state))
+ continue;
+
+ if (get_or_add_pg(s, i, st_pgnames[j].st_pgname,
+ SCF_NOTIFY_PARAMS_PG_TYPE, 0, pg) != 0 &&
+ check_scf_error(scf_error(), errs_2))
+ goto cleanup;
+
+ if (notify_set_params(pg, params) != 0)
+ goto cleanup;
+ }
+ if (s == NULL) {
+ /* We only need to refresh the instance */
+ if (_smf_refresh_instance_i(i) != 0 &&
+ check_scf_error(scf_error(), errs_1))
+ goto cleanup;
+ } else {
+ /* We have to refresh all instances in the service */
+ if (_smf_refresh_all_instances(s) != 0 &&
+ check_scf_error(scf_error(), errs_1))
+ goto cleanup;
+ }
+ } else {
+ if ((pgname = class_to_pgname(class)) == NULL)
+ goto cleanup;
+ if (get_or_add_pg(s, i, pgname, SCF_GROUP_APPLICATION, 0, pg) !=
+ 0) {
+ if (check_scf_error(scf_error(), errs_2)) {
+ goto cleanup;
+ }
+ }
+ if (notify_set_params(pg, params) != 0) {
+ goto cleanup;
+ }
+ if (_smf_refresh_instance_i(i) != 0 &&
+ check_scf_error(scf_error(), errs_1))
+ goto cleanup;
+ }
+
+ r = SCF_SUCCESS;
+cleanup:
+ scf_instance_destroy(i);
+ scf_service_destroy(s);
+ scf_pg_destroy(pg);
+ scf_handle_destroy(h);
+ free(pgname);
+
+ return (r);
+}
+
+/*
+ * returns SCF_SUCCESS or SCF_FAILED on
+ * SCF_ERROR_CONNECTION_BROKEN
+ * SCF_ERROR_DELETED
+ * SCF_ERROR_HANDLE_DESTROYED
+ * SCF_ERROR_HANDLE_MISMATCH
+ * SCF_ERROR_INVALID_ARGUMENT
+ * SCF_ERROR_NO_MEMORY
+ * SCF_ERROR_NO_RESOURCES
+ * SCF_ERROR_NOT_BOUND
+ * SCF_ERROR_NOT_FOUND
+ * SCF_ERROR_NOT_SET
+ * SCF_ERROR_PERMISSION_DENIED
+ */
+int
+_scf_notify_get_params(scf_propertygroup_t *pg, nvlist_t *params)
+{
+ scf_handle_t *h = scf_pg_handle(pg);
+ scf_error_t scf_e = scf_error();
+ scf_property_t *p = scf_property_create(h);
+ scf_iter_t *it = scf_iter_create(h);
+ int sz = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1;
+ char *name = malloc(sz);
+ int r = SCF_FAILED;
+ int err;
+
+ if (h == NULL) {
+ /*
+ * Use the error stored in scf_e
+ */
+ (void) scf_set_error(scf_e);
+ goto cleanup;
+ }
+ if (it == NULL || p == NULL)
+ goto cleanup;
+
+ if (name == NULL) {
+ (void) scf_set_error(SCF_ERROR_NO_MEMORY);
+ goto cleanup;
+ }
+
+ if (scf_iter_pg_properties(it, pg) != SCF_SUCCESS) {
+ if (check_scf_error(scf_error(), errs_1)) {
+ goto cleanup;
+ }
+ }
+
+ while ((err = scf_iter_next_property(it, p)) == 1) {
+ nvlist_t *nvl;
+ int nvl_new = 0;
+ char *mech;
+ char *val;
+
+ if (scf_property_get_name(p, name, sz) == SCF_FAILED) {
+ if (check_scf_error(scf_error(), errs_1)) {
+ goto cleanup;
+ }
+ }
+
+ if (get_mech_name(name, &mech, &val) != SCF_SUCCESS) {
+ if (scf_error() == SCF_ERROR_NOT_FOUND)
+ continue;
+ goto cleanup;
+ }
+
+ if (nvlist_lookup_nvlist(params, mech, &nvl) != 0) {
+ if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) {
+ (void) scf_set_error(SCF_ERROR_NO_MEMORY);
+ free(mech);
+ goto cleanup;
+ }
+ nvl_new = 1;
+ }
+
+ if (add_prop_to_nvlist(p, val, nvl, 1) != SCF_SUCCESS) {
+ if (check_scf_error(scf_error(), errs_2)) {
+ free(mech);
+ nvlist_free(nvl);
+ goto cleanup;
+ }
+ }
+ if (nvl_new) {
+ if (nvlist_add_nvlist(params, mech, nvl) != 0) {
+ (void) scf_set_error(SCF_ERROR_NO_MEMORY);
+ free(mech);
+ nvlist_free(nvl);
+ goto cleanup;
+ }
+ nvlist_free(nvl);
+ }
+
+ free(mech);
+ }
+
+ if (err == 0) {
+ r = SCF_SUCCESS;
+ } else if (check_scf_error(scf_error(), errs_2)) {
+ goto cleanup;
+ }
+
+cleanup:
+ scf_iter_destroy(it);
+ scf_property_destroy(p);
+ free(name);
+
+ return (r);
+}
+
+/*
+ * Look up pg containing an SMF state transition parameters. If it cannot find
+ * the pg in the composed view of the instance, it will look in the global
+ * instance for the system wide parameters.
+ * Instance, service and global instance have to be passed by caller.
+ *
+ * returns SCF_SUCCESS or SCF_FAILED on
+ * SCF_ERROR_BACKEND_ACCESS
+ * SCF_ERROR_CONNECTION_BROKEN
+ * SCF_ERROR_DELETED
+ * SCF_ERROR_HANDLE_DESTROYED
+ * SCF_ERROR_HANDLE_MISMATCH
+ * SCF_ERROR_INTERNAL
+ * SCF_ERROR_INVALID_ARGUMENT
+ * SCF_ERROR_NO_MEMORY
+ * SCF_ERROR_NO_RESOURCES
+ * SCF_ERROR_NOT_BOUND
+ * SCF_ERROR_NOT_FOUND
+ * SCF_ERROR_NOT_SET
+ */
+static int
+get_stn_pg(scf_service_t *s, scf_instance_t *i, scf_instance_t *g,
+ const char *pgname, scf_propertygroup_t *pg)
+{
+ if (get_pg(s, i, pgname, pg, 1) == 0 ||
+ scf_error() == SCF_ERROR_NOT_FOUND &&
+ get_pg(NULL, g, pgname, pg, 0) == 0)
+ return (SCF_SUCCESS);
+
+ return (SCF_FAILED);
+}
+
+/*
+ * Populates nvlist_t params with the source fmri for the pg
+ *
+ * return SCF_SUCCESS or SCF_FAILED on
+ * SCF_ERROR_DELETED
+ * SCF_ERROR_CONNECTION_BROKEN
+ * SCF_ERROR_NO_MEMORY
+ */
+static int
+get_pg_source(scf_propertygroup_t *pg, nvlist_t *params)
+{
+ size_t sz = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH) + 1;
+ char *fmri = malloc(sz);
+ char *p;
+ int r = SCF_FAILED;
+
+ if (fmri == NULL) {
+ (void) scf_set_error(SCF_ERROR_NO_MEMORY);
+ goto out;
+ }
+
+ if (scf_pg_to_fmri(pg, fmri, sz) == -1) {
+ if (check_scf_error(scf_error(), errs_1)) {
+ goto out;
+ }
+ }
+
+ /* get rid of the properties part of the pg source */
+ if ((p = strrchr(fmri, ':')) != NULL && p > fmri)
+ *(p - 1) = '\0';
+ if (nvlist_add_string(params, SCF_NOTIFY_PARAMS_SOURCE_NAME, fmri) !=
+ 0) {
+ (void) scf_set_error(SCF_ERROR_NO_MEMORY);
+ goto out;
+ }
+
+ r = SCF_SUCCESS;
+out:
+ free(fmri);
+ return (r);
+}
+
+/*
+ * Specialized function to get SMF state transition notification parameters
+ *
+ * return SCF_SUCCESS or SCF_FAILED on
+ * SCF_ERROR_BACKEND_ACCESS
+ * SCF_ERROR_CONNECTION_BROKEN
+ * SCF_ERROR_DELETED
+ * SCF_ERROR_INTERNAL
+ * SCF_ERROR_INVALID_ARGUMENT
+ * SCF_ERROR_NO_MEMORY
+ * SCF_ERROR_NO_RESOURCES
+ * SCF_ERROR_NOT_FOUND
+ * SCF_ERROR_PERMISSION_DENIED
+ */
+int
+_scf_get_svc_notify_params(const char *fmri, nvlist_t *nvl, int32_t tset,
+ int getsource, int getglobal)
+{
+ scf_handle_t *h = _scf_handle_create_and_bind(SCF_VERSION);
+ scf_error_t scf_e = scf_error();
+ scf_service_t *s = scf_service_create(h);
+ scf_instance_t *i = scf_instance_create(h);
+ scf_instance_t *g = scf_instance_create(h);
+ scf_propertygroup_t *pg = scf_pg_create(h);
+ int r = SCF_FAILED;
+ nvlist_t **params = NULL;
+ uint_t c, nvl_num = 0;
+ int not_found = 1;
+ int j;
+ const char *pgname;
+
+ assert(fmri != NULL && nvl != NULL);
+ if (h == NULL) {
+ /*
+ * use saved error if _scf_handle_create_and_bind() fails
+ */
+ (void) scf_set_error(scf_e);
+ goto cleanup;
+ }
+ if (s == NULL || i == NULL || g == NULL || pg == NULL)
+ goto cleanup;
+
+ if (decode_fmri(fmri, h, &s, &i) != SCF_SUCCESS ||
+ scf_handle_decode_fmri(h, SCF_INSTANCE_GLOBAL, NULL, NULL, g, NULL,
+ NULL, SCF_DECODE_FMRI_EXACT) != 0) {
+ if (scf_error() == SCF_ERROR_CONSTRAINT_VIOLATED) {
+ (void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
+ } else if (check_scf_error(scf_error(), errs_1)) {
+ goto cleanup;
+ }
+ }
+
+ nvl_num = num_of_transitions(tset);
+ if ((params = calloc(nvl_num, sizeof (nvlist_t *))) == NULL) {
+ (void) scf_set_error(SCF_ERROR_NO_MEMORY);
+ goto cleanup;
+ }
+
+ for (c = 0; c < nvl_num; ++c)
+ if (nvlist_alloc(params + c, NV_UNIQUE_NAME, 0) != 0) {
+ (void) scf_set_error(SCF_ERROR_NO_MEMORY);
+ goto cleanup;
+ }
+
+ for (c = 0, j = 0; st_pgnames[j].st_pgname != NULL; ++j) {
+ /* if this transition is not in the tset, continue */
+ if (!(tset & st_pgnames[j].st_state))
+ continue;
+
+ assert(c < nvl_num);
+ pgname = st_pgnames[j].st_pgname;
+
+ if (nvlist_add_int32(params[c], SCF_NOTIFY_NAME_TSET,
+ st_pgnames[j].st_state) != 0) {
+ (void) scf_set_error(SCF_ERROR_NO_MEMORY);
+ goto cleanup;
+ }
+ if ((getglobal ? get_stn_pg(s, i, g, pgname, pg) :
+ get_pg(s, i, pgname, pg, 1)) == SCF_SUCCESS) {
+ not_found = 0;
+ if (_scf_notify_get_params(pg, params[c]) !=
+ SCF_SUCCESS)
+ goto cleanup;
+ if (getsource && get_pg_source(pg, params[c]) !=
+ SCF_SUCCESS)
+ goto cleanup;
+ } else if (scf_error() == SCF_ERROR_NOT_FOUND ||
+ scf_error() == SCF_ERROR_DELETED) {
+ /* keep driving */
+ /*EMPTY*/
+ } else if (check_scf_error(scf_error(), errs_1)) {
+ goto cleanup;
+ }
+ ++c;
+ }
+
+ if (not_found) {
+ (void) scf_set_error(SCF_ERROR_NOT_FOUND);
+ goto cleanup;
+ }
+
+ assert(c == nvl_num);
+
+ if (nvlist_add_nvlist_array(nvl, SCF_NOTIFY_PARAMS, params, nvl_num) !=
+ 0 || nvlist_add_uint32(nvl, SCF_NOTIFY_NAME_VERSION,
+ SCF_NOTIFY_PARAMS_VERSION) != 0) {
+ (void) scf_set_error(SCF_ERROR_NO_MEMORY);
+ goto cleanup;
+ }
+
+ r = SCF_SUCCESS;
+
+cleanup:
+ scf_pg_destroy(pg);
+ scf_instance_destroy(i);
+ scf_instance_destroy(g);
+ scf_service_destroy(s);
+ scf_handle_destroy(h);
+ if (params != NULL)
+ for (c = 0; c < nvl_num; ++c)
+ nvlist_free(params[c]);
+ free(params);
+
+ return (r);
+}
+
+/*
+ * Specialized function to get fma notification parameters
+ *
+ * return SCF_SUCCESS or SCF_FAILED on
+ * SCF_ERROR_BACKEND_ACCESS
+ * SCF_ERROR_CONNECTION_BROKEN
+ * SCF_ERROR_DELETED
+ * SCF_ERROR_INTERNAL
+ * SCF_ERROR_INVALID_ARGUMENT
+ * SCF_ERROR_NO_MEMORY
+ * SCF_ERROR_NO_RESOURCES
+ * SCF_ERROR_NOT_FOUND
+ * SCF_ERROR_PERMISSION_DENIED
+ */
+int
+_scf_get_fma_notify_params(const char *class, nvlist_t *nvl, int getsource)
+{
+ scf_handle_t *h = _scf_handle_create_and_bind(SCF_VERSION);
+ scf_error_t scf_e = scf_error();
+ scf_instance_t *i = scf_instance_create(h);
+ scf_propertygroup_t *pg = scf_pg_create(h);
+ int r = SCF_FAILED;
+ nvlist_t *params = NULL;
+ char *pgname = NULL;
+
+ if (h == NULL) {
+ /*
+ * use saved error if _scf_handle_create_and_bind() fails
+ */
+ (void) scf_set_error(scf_e);
+ goto cleanup;
+ }
+ if (i == NULL || pg == NULL)
+ goto cleanup;
+
+ if (scf_handle_decode_fmri(h, SCF_NOTIFY_PARAMS_INST, NULL, NULL, i,
+ NULL, NULL, SCF_DECODE_FMRI_EXACT) != SCF_SUCCESS) {
+ if (check_scf_error(scf_error(), errs_1)) {
+ goto cleanup;
+ }
+ }
+
+ if ((pgname = class_to_pgname(class)) == NULL)
+ goto cleanup;
+
+ while (get_pg(NULL, i, pgname, pg, 0) != 0) {
+ if (scf_error() == SCF_ERROR_NOT_FOUND) {
+ char *p = strrchr(pgname, '.');
+
+ if (p != NULL) {
+ *p = ',';
+ /*
+ * since the resulting string is shorter,
+ * there is no risk of buffer overflow
+ */
+ (void) strcpy(p + 1, SCF_NOTIFY_PG_POSTFIX);
+ continue;
+ }
+ }
+
+ if (check_scf_error(scf_error(), errs_1)) {
+ goto cleanup;
+ }
+ }
+
+ if (nvlist_alloc(&params, NV_UNIQUE_NAME, 0) != 0) {
+ (void) scf_set_error(SCF_ERROR_NO_MEMORY);
+ goto cleanup;
+ }
+
+ if (_scf_notify_get_params(pg, params) != SCF_SUCCESS)
+ goto cleanup;
+
+ if (getsource && get_pg_source(pg, params) != SCF_SUCCESS)
+ goto cleanup;
+
+ if (nvlist_add_nvlist_array(nvl, SCF_NOTIFY_PARAMS, &params, 1) != 0 ||
+ nvlist_add_uint32(nvl, SCF_NOTIFY_NAME_VERSION,
+ SCF_NOTIFY_PARAMS_VERSION) != 0) {
+ (void) scf_set_error(SCF_ERROR_NO_MEMORY);
+ goto cleanup;
+ }
+
+ r = SCF_SUCCESS;
+
+cleanup:
+ if (params)
+ nvlist_free(params);
+ scf_pg_destroy(pg);
+ scf_instance_destroy(i);
+ scf_handle_destroy(h);
+ free(pgname);
+
+ return (r);
+}
+
+/*
+ * Retrieve the notification parameters for the Event described in the
+ * input nvlist_t nvl.
+ * The function will allocate an nvlist_t to store the notification
+ * parameters. The notification parameters in the output nvlist will have
+ * the following format:
+ *
+ * version (uint32_t)
+ * SCF_NOTIFY_PARAMS (array of embedded nvlists)
+ * (start of notify-params[0])
+ * tset (int32_t)
+ * <mechanism-name> (embedded nvlist)
+ * <parameter-name> <parameter-type>
+ * ...
+ * (end <mechanism-name>)
+ * ...
+ * (end of notify-params[0])
+ * ...
+ *
+ * return SCF_SUCCESS or SCF_FAILED on
+ * SCF_ERROR_BACKEND_ACCESS
+ * SCF_ERROR_CONNECTION_BROKEN
+ * SCF_ERROR_DELETED
+ * SCF_ERROR_INTERNAL
+ * SCF_ERROR_INVALID_ARGUMENT
+ * SCF_ERROR_NO_MEMORY
+ * SCF_ERROR_NO_RESOURCES
+ * SCF_ERROR_NOT_FOUND
+ * SCF_ERROR_PERMISSION_DENIED
+ */
+int
+smf_notify_get_params(nvlist_t **params, nvlist_t *nvl)
+{
+ char *class;
+ char *from; /* from state */
+ char *to; /* to state */
+ nvlist_t *attr;
+ char *fmri;
+ int32_t tset = 0;
+ int r = SCF_FAILED;
+
+ if (params == NULL || nvlist_lookup_string(nvl, "class", &class) != 0) {
+ (void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
+ return (r);
+ }
+ if (nvlist_alloc(params, NV_UNIQUE_NAME, 0) != 0) {
+ (void) scf_set_error(SCF_ERROR_NO_MEMORY);
+ return (r);
+ }
+
+ if (is_svc_stn(class)) {
+ if (nvlist_lookup_nvlist(nvl, "attr", &attr) != 0 ||
+ nvlist_lookup_string(attr, "svc-string", &fmri) != 0 ||
+ nvlist_lookup_string(attr, "from-state", &from) != 0 ||
+ nvlist_lookup_string(attr, "to-state", &to) != 0) {
+ (void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
+ goto cleanup;
+ }
+
+ tset = SCF_TRANS(smf_state_from_string(from),
+ smf_state_from_string(to));
+ if (!SCF_TRANS_VALID(tset)) {
+ (void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
+ goto cleanup;
+ }
+ tset |= class_to_transition(class);
+
+ r = _scf_get_svc_notify_params(fmri, *params, tset, 0, 1);
+ } else {
+ r = _scf_get_fma_notify_params(class, *params, 0);
+ }
+
+cleanup:
+ if (r == SCF_FAILED) {
+ nvlist_free(*params);
+ *params = NULL;
+ }
+
+ return (r);
+}
+
+/*
+ * return SCF_SUCCESS or SCF_FAILED on
+ * SCF_ERROR_BACKEND_ACCESS
+ * SCF_ERROR_BACKEND_READONLY
+ * SCF_ERROR_CONNECTION_BROKEN
+ * SCF_ERROR_DELETED
+ * SCF_ERROR_INTERNAL
+ * SCF_ERROR_INVALID_ARGUMENT
+ * SCF_ERROR_NO_MEMORY
+ * SCF_ERROR_NO_RESOURCES
+ * SCF_ERROR_NOT_FOUND
+ * SCF_ERROR_PERMISSION_DENIED
+ */
+int
+smf_notify_del_params(const char *class, const char *fmri, int32_t tset)
+{
+ scf_handle_t *h = _scf_handle_create_and_bind(SCF_VERSION);
+ scf_error_t scf_e = scf_error();
+ scf_service_t *s = scf_service_create(h);
+ scf_instance_t *i = scf_instance_create(h);
+ scf_propertygroup_t *pg = scf_pg_create(h);
+ int r = SCF_FAILED;
+ char *pgname = NULL;
+ int j;
+
+ if (class == NULL) {
+ (void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
+ goto cleanup;
+ }
+
+ if (h == NULL) {
+ /*
+ * use saved error if _scf_handle_create_and_bind() fails
+ */
+ (void) scf_set_error(scf_e);
+ goto cleanup;
+ }
+ if (s == NULL || i == NULL || pg == NULL)
+ goto cleanup;
+
+ if (is_svc_stn(class)) {
+ tset |= class_to_transition(class);
+
+ if (!SCF_TRANS_VALID(tset) || fmri == NULL) {
+ (void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
+ goto cleanup;
+ }
+
+ if (decode_fmri(fmri, h, &s, &i) != SCF_SUCCESS) {
+ if (scf_error() == SCF_ERROR_CONSTRAINT_VIOLATED)
+ (void) scf_set_error(
+ SCF_ERROR_INVALID_ARGUMENT);
+ if (check_scf_error(scf_error(), errs_1)) {
+ goto cleanup;
+ }
+ }
+
+ for (j = 0; st_pgnames[j].st_pgname != NULL; ++j) {
+ /* if this transition is not in the tset, continue */
+ if (!(tset & st_pgnames[j].st_state))
+ continue;
+
+ if (del_pg(s, i, st_pgnames[j].st_pgname, pg) !=
+ SCF_SUCCESS &&
+ scf_error() != SCF_ERROR_DELETED &&
+ scf_error() != SCF_ERROR_NOT_FOUND) {
+ if (check_scf_error(scf_error(),
+ errs_1)) {
+ goto cleanup;
+ }
+ }
+ }
+ if (s == NULL) {
+ /* We only need to refresh the instance */
+ if (_smf_refresh_instance_i(i) != 0 &&
+ check_scf_error(scf_error(), errs_1))
+ goto cleanup;
+ } else {
+ /* We have to refresh all instances in the service */
+ if (_smf_refresh_all_instances(s) != 0 &&
+ check_scf_error(scf_error(), errs_1))
+ goto cleanup;
+ }
+ } else {
+ if ((pgname = class_to_pgname(class)) == NULL)
+ goto cleanup;
+
+ if (scf_handle_decode_fmri(h, SCF_NOTIFY_PARAMS_INST, NULL,
+ NULL, i, NULL, NULL, SCF_DECODE_FMRI_EXACT) != SCF_SUCCESS)
+ goto cleanup;
+
+ if (del_pg(NULL, i, pgname, pg) != SCF_SUCCESS &&
+ scf_error() != SCF_ERROR_DELETED &&
+ scf_error() != SCF_ERROR_NOT_FOUND) {
+ if (check_scf_error(scf_error(), errs_1)) {
+ goto cleanup;
+ }
+ }
+
+ if (_smf_refresh_instance_i(i) != 0 &&
+ check_scf_error(scf_error(), errs_1))
+ goto cleanup;
+ }
+
+
+ r = SCF_SUCCESS;
+
+cleanup:
+ scf_pg_destroy(pg);
+ scf_instance_destroy(i);
+ scf_service_destroy(s);
+ scf_handle_destroy(h);
+ free(pgname);
+
+ return (r);
+}
diff --git a/usr/src/lib/libscf/common/scf_tmpl.c b/usr/src/lib/libscf/common/scf_tmpl.c
index 624fdeb4d7..69062e0eb9 100644
--- a/usr/src/lib/libscf/common/scf_tmpl.c
+++ b/usr/src/lib/libscf/common/scf_tmpl.c
@@ -19,8 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
*/
/*
@@ -152,8 +151,8 @@ static const scf_error_t errors_server[] = {
* Returns 1 if the supplied error is a member of the error array, 0
* if it is not.
*/
-static scf_error_t
-ismember(const int error, const scf_error_t error_array[])
+int
+ismember(const scf_error_t error, const scf_error_t error_array[])
{
int i;
@@ -505,7 +504,7 @@ _read_single_boolean_from_pg(scf_propertygroup_t *pg, const char *prop_name,
}
/*
- * char **_append_astrings_values()
+ * static char ** _append_astrings_values()
*
* This function reads the values from the property prop_name in pg and
* appends to an existing scf_values_t *vals. vals may be empty, but
@@ -4093,11 +4092,13 @@ scf_values_destroy(scf_values_t *vals)
{
int i;
char **items = NULL;
- char **str = vals->values_as_strings;
+ char **str = NULL;
if (vals == NULL)
return;
+ str = vals->values_as_strings;
+
/* free values */
switch (vals->value_type) {
case SCF_TYPE_BOOLEAN: