view usr/src/lib/libscf/common/notify_params.c @ 12979:ab9ae749152f

PSARC/2009/617 Software Events Notification Parameters CLI PSARC/2009/618 snmp-notify: SNMP Notification Daemon for Software Events PSARC/2009/619 smtp-notify: Email Notification Daemon for Software Events PSARC/2010/225 fmd for non-global Solaris zones PSARC/2010/226 Solaris Instance UUID PSARC/2010/227 nvlist_nvflag(3NVPAIR) PSARC/2010/228 libfmevent additions PSARC/2010/257 sysevent_evc_setpropnvl and sysevent_evc_getpropnvl PSARC/2010/265 FMRI and FMA Event Stabilty, 'ireport' category 1 event class, and the 'sw' FMRI scheme PSARC/2010/278 FMA/SMF integration: instance state transitions PSARC/2010/279 Modelling panics within FMA PSARC/2010/290 logadm.conf upgrade 6392476 fmdump needs to pretty-print 6393375 userland ereport/ireport event generation interfaces 6445732 Add email notification agent for FMA and software events 6804168 RFE: Allow an efficient means to monitor SMF services status changes 6866661 scf_values_destroy(3SCF) will segfault if is passed NULL 6884709 Add snmp notification agent for FMA and software events 6884712 Add private interface to tap into libfmd_msg macro expansion capabilities 6897919 fmd to run in a non-global zone 6897937 fmd use of non-private doors is not safe 6900081 add a UUID to Solaris kernel image for use in crashdump identification 6914884 model panic events as a defect diagnosis in FMA 6944862 fmd_case_open_uuid, fmd_case_uuisresolved, fmd_nvl_create_defect 6944866 log legacy sysevents in fmd 6944867 enumerate svc scheme in topo 6944868 software-diagnosis and software-response fmd modules 6944870 model SMF maintenance state as a defect diagnosis in FMA 6944876 savecore runs in foreground for systems with zfs root and dedicated dump 6965796 Implement notification parameters for SMF state transitions and FMA events 6968287 SUN-FM-MIB.mib needs to be updated to reflect Oracle information 6972331 logadm.conf upgrade PSARC/2010/290
author Gavin Maltby <gavin.maltby@oracle.com>
date Fri, 30 Jul 2010 17:04:17 +1000
parents
children
line wrap: on
line source

/*
 * 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);
}