view usr/src/cmd/cmd-inet/lib/nwamd/enm.c @ 12935:eb113dc3db0b

6933909 nwamd has some debug messages logging at the wrong level 6960252 nwamd is too verbose: 1: nwamd_loc_check_conditions: winning loc is NoNet
author Renee Danson Sommerfeld <Renee.Sommerfeld@Oracle.COM>
date Tue, 27 Jul 2010 16:48:51 -0700
parents ab8aacaead3f
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 <arpa/inet.h>
#include <errno.h>
#include <inet/ip.h>
#include <libdladm.h>
#include <libdllink.h>
#include <libdlwlan.h>
#include <libscf.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>

#include <libnwam.h>
#include "conditions.h"
#include "events.h"
#include "objects.h"
#include "util.h"

/*
 * enm.c - contains routines which handle ENM (external network modifier)
 * abstraction.  ENMs represent scripts or services that can be activated either
 * manually or in response to network conditions.
 */

#define	CTRUN	"/usr/bin/ctrun"

static int
enm_create_init_fini_event(nwam_enm_handle_t enmh, void *data)
{
	boolean_t *init = data;
	char *name;
	nwamd_event_t enm_event;

	if (nwam_enm_get_name(enmh, &name) != NWAM_SUCCESS) {
		nlog(LOG_ERR, "enm_init_fini: could not get enm name");
		return (0);
	}

	enm_event = nwamd_event_init(*init ?
	    NWAM_EVENT_TYPE_OBJECT_INIT : NWAM_EVENT_TYPE_OBJECT_FINI,
	    NWAM_OBJECT_TYPE_ENM, 0, name);
	if (enm_event != NULL)
		nwamd_event_enqueue(enm_event);
	free(name);

	return (0);
}

/*
 * Walk all ENMs, creating init events for each.
 */
void
nwamd_init_enms(void)
{
	boolean_t init = B_TRUE;

	(void) nwam_walk_enms(enm_create_init_fini_event, &init, 0, NULL);
}

/*
 * Walk all ENMs, creating fini events for each.
 */
void
nwamd_fini_enms(void)
{
	boolean_t init = B_FALSE;

	(void) nwam_walk_enms(enm_create_init_fini_event, &init, 0, NULL);
}

static boolean_t
enm_is_enabled(nwam_enm_handle_t enmh)
{
	nwam_value_t enabledval;
	boolean_t enabled = B_FALSE;

	if (nwam_enm_get_prop_value(enmh, NWAM_ENM_PROP_ENABLED,
	    &enabledval) != NWAM_SUCCESS) {
		/* It's legal for a conditional ENM to not specify "enabled" */
		return (B_FALSE);
	}
	if (nwam_value_get_boolean(enabledval, &enabled) != NWAM_SUCCESS) {
		nlog(LOG_ERR, "enm_is_enabled: could not retrieve "
		    "enabled value");
	}
	nwam_value_free(enabledval);
	return (enabled);
}

static int64_t
enm_get_activation_mode(nwam_enm_handle_t enmh)
{
	uint64_t activation;
	int64_t ret;
	nwam_value_t activationval;

	if (nwam_enm_get_prop_value(enmh, NWAM_ENM_PROP_ACTIVATION_MODE,
	    &activationval)  != NWAM_SUCCESS) {
		nlog(LOG_ERR, "enm_get_activation_mode: could not retrieve "
		    "activation mode value");
		return (-1);
	}
	if (nwam_value_get_uint64(activationval, &activation) != NWAM_SUCCESS) {
		nlog(LOG_ERR, "enm_get_activation_mode: could not retrieve "
		    "activation mode value");
		ret = -1;
	} else {
		ret = activation;
	}
	nwam_value_free(activationval);

	return (ret);
}

static void *
nwamd_enm_activate_deactivate_thread(void *arg)
{
	char *object_name = arg;
	nwamd_object_t object;
	nwam_enm_handle_t enmh;
	nwam_value_t scriptval = NULL;
	nwam_state_t state;
	nwam_aux_state_t aux_state;
	char *script, *copy = NULL;
	const char **argv = NULL;
	boolean_t going_online, disable_succeeded = B_FALSE;
	int ret;

	object = nwamd_object_find(NWAM_OBJECT_TYPE_ENM, object_name);
	if (object == NULL) {
		nlog(LOG_ERR, "nwamd_enm_activate_deactivate_thread: "
		    "could not find enm %s", object_name);
		goto done;
	}
	enmh = object->nwamd_object_handle;

	going_online =
	    (object->nwamd_object_state == NWAM_STATE_OFFLINE_TO_ONLINE);
	/*
	 * We're starting if current state is offline* and stopping otherwise.
	 */
	if (nwam_enm_get_prop_value(enmh,
	    going_online ? NWAM_ENM_PROP_START : NWAM_ENM_PROP_STOP,
	    &scriptval) != NWAM_SUCCESS ||
	    nwam_value_get_string(scriptval, &script) != NWAM_SUCCESS) {
		/*
		 * If we're stopping, it's not an error for no script to
		 * be specified.
		 */
		nlog(going_online ? LOG_ERR : LOG_DEBUG,
		    "nwamd_enm_activate_deactivate_thread: "
		    "no script specified for enm %s", object_name);
		if (going_online) {
			state = NWAM_STATE_MAINTENANCE;
			aux_state = NWAM_AUX_STATE_METHOD_MISSING;
		} else {
			disable_succeeded = B_TRUE;
		}
	} else {
		char *lasts;
		const char **newargv;
		int i = 0;
		struct timeval now;

		nlog(LOG_DEBUG, "nwamd_enm_activate_deactivate_thread: "
		    "running script %s for enm %s", script, object_name);

		/*
		 * The script may take a number of arguments. We need to
		 * create a string array consisting of the wrapper command
		 * (ctrun), ENM script name, arguments and NULL array
		 * terminator.  Start with an array of size equal to the
		 * string length (since the number of arguments will always
		 * be less than this) and shrink array to the actual number
		 * of arguments when we have parsed the string.
		 */
		if ((copy = strdup(script)) == NULL ||
		    (argv = calloc(strlen(script), sizeof (char *))) == NULL) {
			ret = 1;
			goto err;
		}
		argv[i++] = CTRUN;
		argv[i++] = strtok_r(copy, " ", &lasts);
		if (argv[1] == NULL) {
			ret = 1;
			goto err;
		}

		for (; (argv[i] = strtok_r(NULL, " ", &lasts)) != NULL; i++) {}

		newargv = realloc(argv, (i + 1) * sizeof (char *));
		argv = newargv;

		/* Store the current time as the time the script began */
		(void) gettimeofday(&now, NULL);
		object->nwamd_script_time = now;

		/*
		 * Release the object so that it is not blocked while the
		 * script is running.
		 */
		nwamd_object_release(object);

		ret = nwamd_start_childv(CTRUN, argv);

		/*
		 * Find the object again, now that the script has finished
		 * running.  Check if this ENM was re-read during that time by
		 * comparing the object's script time with the one from above.
		 */
		object = nwamd_object_find(NWAM_OBJECT_TYPE_ENM, object_name);
		if (object == NULL) {
			nlog(LOG_ERR, "nwamd_enm_activate_deactivate_thread: "
			    "could not find enm %s after running script",
			    object_name);
			goto done;
		}

		if (object->nwamd_script_time.tv_sec != now.tv_sec ||
		    object->nwamd_script_time.tv_usec != now.tv_usec) {
			nlog(LOG_INFO, "nwamd_enm_activate_deactivate_thread: "
			    "enm %s has been refreshed, nothing to do",
			    object_name);
			nwamd_object_release(object);
			goto done;
		}
		(void) gettimeofday(&object->nwamd_script_time, NULL);

err:
		/*
		 * If script execution fails and we're not destroying the
		 * object, go to maintenance.
		 */
		if (ret != 0) {
			nlog(LOG_ERR, "nwamd_enm_activate_deactivate_thread: "
			    "execution of '%s' failed for enm %s",
			    script, object_name);
			if (object->nwamd_object_aux_state !=
			    NWAM_AUX_STATE_UNINITIALIZED) {
				state = NWAM_STATE_MAINTENANCE;
				aux_state = NWAM_AUX_STATE_METHOD_FAILED;
			} else {
				state = NWAM_STATE_UNINITIALIZED;
				aux_state = NWAM_AUX_STATE_UNINITIALIZED;
			}
		} else {
			if (going_online) {
				state = NWAM_STATE_ONLINE;
				aux_state = NWAM_AUX_STATE_ACTIVE;
			} else {
				disable_succeeded = B_TRUE;
			}
		}
	}

	if (disable_succeeded) {
		/*
		 * If aux state is "manual disable", we know
		 * this was a disable request, otherwise it was
		 * _fini request or a condition satisfaction
		 * failure.
		 */
		switch (object->nwamd_object_aux_state) {
		case NWAM_AUX_STATE_MANUAL_DISABLE:
			state = NWAM_STATE_DISABLED;
			aux_state = NWAM_AUX_STATE_MANUAL_DISABLE;
			break;
		case NWAM_AUX_STATE_UNINITIALIZED:
			state = NWAM_STATE_UNINITIALIZED;
			aux_state = NWAM_AUX_STATE_UNINITIALIZED;
			break;
		default:
			state = NWAM_STATE_OFFLINE;
			aux_state = NWAM_AUX_STATE_CONDITIONS_NOT_MET;
			break;
		}
	}

	/* If state/aux state are uninitialized/unintialized, destroy the ENM */
	if (state == NWAM_STATE_UNINITIALIZED &&
	    aux_state == NWAM_AUX_STATE_UNINITIALIZED) {
		object->nwamd_object_state = state;
		object->nwamd_object_aux_state = aux_state;
		(void) nwamd_object_release_and_destroy_after_preserve(object);
	} else {
		nwamd_object_set_state(NWAM_OBJECT_TYPE_ENM,
		    object->nwamd_object_name, state, aux_state);
		(void) nwamd_object_release_after_preserve(object);
	}

done:
	/* object_name was malloc() before this thread was created, free() it */
	free(object_name);
	free(argv);
	free(copy);
	nwam_value_free(scriptval);
	return (NULL);
}

/*
 * Run start/stop method for ENM in a separate thread.  The object lock is not
 * held across threads, so we duplicate the object name for the method
 * execution thread.  Returns true if thread is successfully launched.
 */
boolean_t
nwamd_enm_run_method(nwamd_object_t object)
{
	char *name;
	pthread_t script;

	/*
	 * Launch separate thread to wait for execution of script
	 * to complete.  Do not hold object lock across threads.
	 */
	if ((name = strdup(object->nwamd_object_name)) == NULL) {
		nlog(LOG_ERR, "nwamd_enm_run_method: %s: out of memory",
		    object->nwamd_object_name);
		return (B_FALSE);
	}

	if (pthread_create(&script, NULL,
	    nwamd_enm_activate_deactivate_thread, name) != 0) {
		nlog(LOG_ERR, "nwamd_enm_run_method: could not create "
		    "enm script thread for %s", name);
		free(name);
		return (B_FALSE);
	}
	/* "name" will be freed by the newly-created thread. */

	/* detach thread so that it doesn't become a zombie */
	(void) pthread_detach(script);

	return (B_TRUE);
}

/*
 * Activate the ENM, either in response to an enable event or conditions
 * being satisfied.
 */
static void
nwamd_enm_activate(const char *object_name)
{
	nwamd_object_t object;
	nwam_value_t fmrival;
	char *fmri, *smf_state;
	int ret;
	nwam_enm_handle_t enmh;
	nwam_state_t state;
	nwam_aux_state_t aux_state;
	nwam_error_t err;
	boolean_t ran_method = B_FALSE;

	object = nwamd_object_find(NWAM_OBJECT_TYPE_ENM, object_name);
	if (object == NULL) {
		nlog(LOG_ERR, "nwamd_enm_activate: could not find enm %s",
		    object_name);
		return;
	}
	state = object->nwamd_object_state;
	aux_state = object->nwamd_object_aux_state;
	enmh = object->nwamd_object_handle;

	nlog(LOG_DEBUG, "nwamd_enm_activate: activating enm %s",
	    object->nwamd_object_name);

	err = nwam_enm_get_prop_value(enmh, NWAM_ENM_PROP_FMRI, &fmrival);
	switch (err) {
	case NWAM_SUCCESS:

		if (nwam_value_get_string(fmrival, &fmri) != NWAM_SUCCESS) {
			nlog(LOG_ERR, "nwamd_enm_activate: could not retrieve "
			    "fmri string for enm %s",
			    object->nwamd_object_name);
			nwam_value_free(fmrival);
			state = NWAM_STATE_MAINTENANCE;
			aux_state = NWAM_AUX_STATE_INVALID_CONFIG;
			break;
		}

		if ((smf_state = smf_get_state(fmri)) == NULL) {
			nlog(LOG_ERR, "nwamd_enm_activate: invalid fmri %s "
			    "for enm %s", fmri, object->nwamd_object_name);
			nwam_value_free(fmrival);
			state = NWAM_STATE_MAINTENANCE;
			aux_state = NWAM_AUX_STATE_INVALID_CONFIG;
			break;
		}

		nlog(LOG_DEBUG, "nwamd_enm_activate: activating %s for enm %s",
		    fmri, object->nwamd_object_name);

		if (strcmp(smf_state, SCF_STATE_STRING_ONLINE) == 0)
			ret = smf_restart_instance(fmri);
		else if (strcmp(smf_state, SCF_STATE_STRING_OFFLINE) == 0)
			ret = smf_restart_instance(fmri);
		else if (strcmp(smf_state, SCF_STATE_STRING_DISABLED) == 0)
			ret = smf_enable_instance(fmri, SMF_TEMPORARY);
		else
			ret = smf_restore_instance(fmri);

		if (ret == 0) {
			state = NWAM_STATE_ONLINE;
			aux_state = NWAM_AUX_STATE_ACTIVE;
		} else {
			nlog(LOG_ERR, "nwamd_enm_activate: failed to enable "
			    "fmri %s for enm %s", fmri,
			    object->nwamd_object_name);
			state = NWAM_STATE_MAINTENANCE;
			aux_state = NWAM_AUX_STATE_METHOD_FAILED;
		}
		free(smf_state);
		nwam_value_free(fmrival);
		break;
	default:
		/*
		 * Must be a method-based ENM with start (and stop) script(s).
		 */
		if (!nwamd_enm_run_method(object)) {
			/* Could not launch method execution thread */
			state = NWAM_STATE_MAINTENANCE;
			aux_state = NWAM_AUX_STATE_METHOD_FAILED;
		} else {
			ran_method = B_TRUE;
		}
		break;
	}

	if (state != object->nwamd_object_state ||
	    aux_state != object->nwamd_object_aux_state) {
		nwamd_object_set_state(NWAM_OBJECT_TYPE_ENM,
		    object->nwamd_object_name, state, aux_state);
	}

	/*
	 * If the method thread was created, we drop the lock to the ENM
	 * object without decreasing the reference count, ensuring it will not
	 * be destroyed until method execution has completed.
	 */
	if (ran_method) {
		nwamd_object_release_and_preserve(object);
	} else {
		nwamd_object_release(object);
	}
}

/* Deactivates the ENM. */
static void
nwamd_enm_deactivate(const char *object_name)
{
	nwamd_object_t object;
	nwam_enm_handle_t enmh;
	nwam_value_t fmrival;
	char *fmri, *smf_state;
	int ret;
	nwam_state_t state;
	nwam_aux_state_t aux_state;
	boolean_t destroying = B_FALSE;

	object = nwamd_object_find(NWAM_OBJECT_TYPE_ENM, object_name);
	if (object == NULL) {
		nlog(LOG_ERR, "nwamd_enm_deactivate: could not find enm %s",
		    object_name);
		return;
	}

	state = object->nwamd_object_state;
	aux_state = object->nwamd_object_aux_state;
	enmh = object->nwamd_object_handle;
	state = object->nwamd_object_state;
	/* If destroying, we don't care about method failure/config err */
	destroying = (aux_state == NWAM_AUX_STATE_UNINITIALIZED);

	nlog(LOG_DEBUG, "nwamd_enm_deactivate: deactivating enm %s",
	    object->nwamd_object_name);

	if (nwam_enm_get_prop_value(enmh, NWAM_ENM_PROP_FMRI, &fmrival)
	    != NWAM_SUCCESS) {
		/*
		 * Must be a method-based ENM with start (and stop) script(s).
		 * Script execution thread will take care of the rest.
		 * If the method thread was created, we drop the lock to the ENM
		 * object without decreasing the reference count, ensuring it
		 * will not be destroyed until method execution has completed.
		 */
		if (nwamd_enm_run_method(object)) {
			nwamd_object_release_and_preserve(object);
			return;
		}
		/* Could not launch method execution thread */
		if (!destroying) {
			state = NWAM_STATE_MAINTENANCE;
			aux_state = NWAM_AUX_STATE_METHOD_FAILED;
		}
	} else {
		if (nwam_value_get_string(fmrival, &fmri) != NWAM_SUCCESS) {
			nlog(LOG_ERR, "nwamd_enm_deactivate: could not "
			    "retrieve fmri string for enm %s",
			    object->nwamd_object_name);
			if (!destroying) {
				state = NWAM_STATE_MAINTENANCE;
				aux_state = NWAM_AUX_STATE_INVALID_CONFIG;
			}
		} else {
			if ((smf_state = smf_get_state(fmri)) == NULL) {
				nlog(LOG_ERR, "nwamd_enm_deactivate: invalid "
				    "fmri %s for enm %s", fmri,
				    object->nwamd_object_name);
				nwam_value_free(fmrival);
				if (!destroying) {
					state = NWAM_STATE_MAINTENANCE;
					aux_state =
					    NWAM_AUX_STATE_INVALID_CONFIG;
				}
				goto done;
			}
			free(smf_state);

			nlog(LOG_DEBUG, "nwamd_enm_deactivate: deactivating %s "
			    "for enm %s", fmri, object->nwamd_object_name);

			ret = smf_disable_instance(fmri, SMF_TEMPORARY);

			if (ret != 0) {
				nlog(LOG_ERR, "nwamd_enm_deactivate: "
				    "smf_disable_instance(%s) failed for "
				    "enm %s: %s", fmri,
				    object->nwamd_object_name,
				    scf_strerror(scf_error()));
				if (!destroying) {
					state = NWAM_STATE_MAINTENANCE;
					aux_state =
					    NWAM_AUX_STATE_METHOD_FAILED;
				}
			}
		}
		nwam_value_free(fmrival);
	}
done:
	if (state == object->nwamd_object_state &&
	    aux_state == object->nwamd_object_aux_state) {
		/*
		 * If aux state is "manual disable", we know
		 * this was a disable request, otherwise it was
		 * a _fini request or a condition satisfaction
		 * failure.
		 */
		switch (object->nwamd_object_aux_state) {
		case NWAM_AUX_STATE_MANUAL_DISABLE:
			state = NWAM_STATE_DISABLED;
			aux_state = NWAM_AUX_STATE_MANUAL_DISABLE;
			break;
		case NWAM_AUX_STATE_UNINITIALIZED:
			state = NWAM_STATE_UNINITIALIZED;
			aux_state = NWAM_AUX_STATE_UNINITIALIZED;
			break;
		default:
			state = NWAM_STATE_OFFLINE;
			aux_state = NWAM_AUX_STATE_CONDITIONS_NOT_MET;
			break;
		}
	}

	/* Only change state if we aren't destroying the ENM */
	if (!destroying && (state != object->nwamd_object_state ||
	    aux_state != object->nwamd_object_aux_state)) {
		nwamd_object_set_state(NWAM_OBJECT_TYPE_ENM,
		    object->nwamd_object_name, state, aux_state);
	}

	/* If state/aux state are uninitialized/unintialized, destroy the ENM */
	if (state == NWAM_STATE_UNINITIALIZED &&
	    aux_state == NWAM_AUX_STATE_UNINITIALIZED) {
		(void) nwamd_object_release_and_destroy(object);
	} else {
		(void) nwamd_object_release(object);
	}
}

/*
 * Determine whether an ENM should be (de)activated.
 */
/* ARGSUSED1 */
static int
nwamd_enm_check(nwamd_object_t object, void *data)
{
	nwam_enm_handle_t enmh;
	nwam_value_t conditionval;
	int64_t eactivation;
	boolean_t enabled, satisfied;
	char **conditions;
	nwam_state_t state;
	uint_t nelem;

	state = object->nwamd_object_state;

	enmh = object->nwamd_object_handle;

	eactivation = enm_get_activation_mode(enmh);
	if (eactivation == -1)
		return (0);

	switch (eactivation) {
	case NWAM_ACTIVATION_MODE_MANUAL:
		enabled = enm_is_enabled(enmh);

		if (enabled) {
			nlog(LOG_DEBUG, "nwamd_enm_check: %s is enabled",
			    object->nwamd_object_name);
			switch (state) {
			case NWAM_STATE_ONLINE:
			case NWAM_STATE_MAINTENANCE:
				/* Do nothing */
				break;
			default:
				if (nwamd_enm_action(object->nwamd_object_name,
				    NWAM_ACTION_ENABLE) != 0) {
					nlog(LOG_ERR, "nwamd_enm_check: "
					    "enable failed for enm %s",
					    object->nwamd_object_name);
				}
				break;
			}
		} else {
			nlog(LOG_DEBUG, "nwamd_enm_check: %s is disabled",
			    object->nwamd_object_name);
			switch (state) {
			case NWAM_STATE_ONLINE:
				if (nwamd_enm_action(object->nwamd_object_name,
				    NWAM_ACTION_DISABLE) != 0) {
					nlog(LOG_ERR, "nwamd_enm_check: "
					    "disable failed for enm %s",
					    object->nwamd_object_name);
				}
				break;
			case NWAM_STATE_MAINTENANCE:
				/* Do nothing */
				break;
			case NWAM_STATE_DISABLED:
				/* Do nothing */
				break;
			default:
				nwamd_object_set_state(NWAM_OBJECT_TYPE_ENM,
				    object->nwamd_object_name,
				    NWAM_STATE_DISABLED,
				    NWAM_AUX_STATE_MANUAL_DISABLE);
				break;
			}
		}
		break;

	case NWAM_ACTIVATION_MODE_CONDITIONAL_ANY:
	case NWAM_ACTIVATION_MODE_CONDITIONAL_ALL:
		if (nwam_enm_get_prop_value(enmh,
		    NWAM_ENM_PROP_CONDITIONS, &conditionval) != NWAM_SUCCESS) {
			nlog(LOG_ERR, "nwamd_enm_check: could not retrieve "
			    "condition value");
			break;
		}
		if (nwam_value_get_string_array(conditionval,
		    &conditions, &nelem) != NWAM_SUCCESS) {
			nlog(LOG_ERR, "nwamd_enm_check: could not retrieve "
			    "condition value");
			nwam_value_free(conditionval);
			break;
		}
		satisfied = nwamd_check_conditions((uint64_t)eactivation,
		    conditions, nelem);

		nlog(LOG_DEBUG, "nwamd_enm_check: conditions for enm %s "
		    "%s satisfied", object->nwamd_object_name,
		    satisfied ? "is" : "is not");
		if (state != NWAM_STATE_ONLINE && satisfied) {
			nwamd_object_set_state(NWAM_OBJECT_TYPE_ENM,
			    object->nwamd_object_name,
			    NWAM_STATE_OFFLINE_TO_ONLINE,
			    NWAM_AUX_STATE_METHOD_RUNNING);
		}
		if (state == NWAM_STATE_ONLINE && !satisfied) {
			nwamd_object_set_state(NWAM_OBJECT_TYPE_ENM,
			    object->nwamd_object_name,
			    NWAM_STATE_ONLINE_TO_OFFLINE,
			    NWAM_AUX_STATE_CONDITIONS_NOT_MET);
		}
		nwam_value_free(conditionval);
		break;

	}
	return (0);
}

void
nwamd_enm_check_conditions(void)
{
	(void) nwamd_walk_objects(NWAM_OBJECT_TYPE_ENM, nwamd_enm_check, NULL);
}

int
nwamd_enm_action(const char *enm, nwam_action_t action)
{
	nwamd_event_t event = nwamd_event_init_object_action
	    (NWAM_OBJECT_TYPE_ENM, enm, NULL, action);
	if (event == NULL)
		return (1);
	nwamd_event_enqueue(event);
	return (0);
}

/*
 * Event handling functions.
 */

/* Handle ENM initialization/refresh event */
void
nwamd_enm_handle_init_event(nwamd_event_t event)
{
	nwamd_object_t object;
	nwam_enm_handle_t enmh;
	nwam_error_t err;
	boolean_t manual_disabled = B_FALSE;

	if ((err = nwam_enm_read(event->event_object, 0, &enmh))
	    != NWAM_SUCCESS) {
		nlog(LOG_ERR, "nwamd_enm_handle_init_event: could not "
		    "read object '%s': %s", event->event_object,
		    nwam_strerror(err));
		nwamd_event_do_not_send(event);
		return;
	}
	if ((object = nwamd_object_find(NWAM_OBJECT_TYPE_ENM,
	    event->event_object)) != NULL) {
		nwam_enm_free(object->nwamd_object_handle);
		object->nwamd_object_handle = enmh;
	} else {
		object = nwamd_object_init(NWAM_OBJECT_TYPE_ENM,
		    event->event_object, enmh, NULL);
		object->nwamd_object_state = NWAM_STATE_OFFLINE;
		object->nwamd_object_aux_state =
		    NWAM_AUX_STATE_CONDITIONS_NOT_MET;
	}
	/* (Re)set script time to now as the object has just been (re)read */
	(void) gettimeofday(&object->nwamd_script_time, NULL);

	manual_disabled = (enm_get_activation_mode(enmh) ==
	    NWAM_ACTIVATION_MODE_MANUAL && !enm_is_enabled(enmh));

	/*
	 * If this ENM is ONLINE, and not manual and disabled (since in
	 * that case it was online but we've just set enabled = false as part
	 * of a disable action), then it is still active but refreshing.
	 * Change states to re-activate itself.
	 */
	if (!manual_disabled &&
	    object->nwamd_object_state == NWAM_STATE_ONLINE) {
		nwamd_object_set_state(NWAM_OBJECT_TYPE_ENM,
		    event->event_object, NWAM_STATE_OFFLINE_TO_ONLINE,
		    NWAM_AUX_STATE_METHOD_RUNNING);
	}
	nwamd_object_release(object);
}

/* Handle ENM finish event */
void
nwamd_enm_handle_fini_event(nwamd_event_t event)
{
	nwamd_event_t state_event;

	nlog(LOG_DEBUG, "nwamd_enm_handle_fini_event(%s)", event->event_object);

	/*
	 * Simulate a state event so that the state machine can correctly
	 * deactivate the ENM and free up the handle.
	 */
	state_event = nwamd_event_init_object_state(NWAM_OBJECT_TYPE_ENM,
	    event->event_object, NWAM_STATE_ONLINE_TO_OFFLINE,
	    NWAM_AUX_STATE_UNINITIALIZED);
	if (state_event == NULL) {
		nwamd_event_do_not_send(event);
		return;
	}
	nwamd_enm_handle_state_event(state_event);
	nwamd_event_fini(state_event);
	/*
	 * Do not free the handle and object.
	 * nwamd_enm_activate_deactivate_thread() and
	 * nwamd_enm_deactivate() does this after running the stop script
	 * and disabling the FMRI respectively.
	 */
}

void
nwamd_enm_handle_action_event(nwamd_event_t event)
{
	nwamd_object_t object;

	switch (event->event_msg->nwe_data.nwe_object_action.nwe_action) {
	case NWAM_ACTION_ENABLE:
		object = nwamd_object_find(NWAM_OBJECT_TYPE_ENM,
		    event->event_object);
		if (object == NULL) {
			nlog(LOG_ERR, "nwamd_enm_handle_action_event: "
			    "could not find enm %s", event->event_object);
			nwamd_event_do_not_send(event);
			return;
		}
		if (object->nwamd_object_state == NWAM_STATE_ONLINE) {
			nlog(LOG_DEBUG, "nwamd_enm_handle_action_event: "
			    "enm %s already online, nothing to do",
			    event->event_object);
			nwamd_object_release(object);
			return;
		}
		nwamd_object_release(object);

		nwamd_object_set_state(NWAM_OBJECT_TYPE_ENM,
		    event->event_object, NWAM_STATE_OFFLINE_TO_ONLINE,
		    NWAM_AUX_STATE_METHOD_RUNNING);
		break;
	case NWAM_ACTION_DISABLE:
		object = nwamd_object_find(NWAM_OBJECT_TYPE_ENM,
		    event->event_object);
		if (object == NULL) {
			nlog(LOG_ERR, "nwamd_enm_handle_action_event: "
			    "could not find enm %s", event->event_object);
			nwamd_event_do_not_send(event);
			return;
		}
		if (object->nwamd_object_state == NWAM_STATE_DISABLED) {
			nlog(LOG_DEBUG, "nwamd_enm_handle_action_event: "
			    "enm %s already disabled, nothing to do",
			    event->event_object);
			nwamd_object_release(object);
			return;
		}
		nwamd_object_release(object);

		nwamd_object_set_state(NWAM_OBJECT_TYPE_ENM,
		    event->event_object, NWAM_STATE_ONLINE_TO_OFFLINE,
		    NWAM_AUX_STATE_MANUAL_DISABLE);
		break;
	case NWAM_ACTION_ADD:
	case NWAM_ACTION_REFRESH:
		nwamd_enm_handle_init_event(event);
		break;
	case NWAM_ACTION_DESTROY:
		nwamd_enm_handle_fini_event(event);
		break;
	default:
		nlog(LOG_INFO, "nwam_enm_handle_action_event: "
		    "unexpected action");
		nwamd_event_do_not_send(event);
		break;
	}
}

void
nwamd_enm_handle_state_event(nwamd_event_t event)
{
	nwamd_object_t object;
	nwam_state_t new_state;
	nwam_aux_state_t new_aux_state;

	if ((object = nwamd_object_find(NWAM_OBJECT_TYPE_ENM,
	    event->event_object)) == NULL) {
		nlog(LOG_INFO, "nwamd_enm_handle_state_event: "
		    "state event for nonexistent enm %s", event->event_object);
		nwamd_event_do_not_send(event);
		return;
	}
	new_state = event->event_msg->nwe_data.nwe_object_state.nwe_state;
	new_aux_state =
	    event->event_msg->nwe_data.nwe_object_state.nwe_aux_state;

	if (new_state == object->nwamd_object_state &&
	    new_aux_state == object->nwamd_object_aux_state) {
		nlog(LOG_DEBUG, "nwamd_enm_handle_state_event: "
		    "enm %s already in state (%s , %s)",
		    object->nwamd_object_name, nwam_state_to_string(new_state),
		    nwam_aux_state_to_string(new_aux_state));
		nwamd_object_release(object);
		return;
	}

	object->nwamd_object_state = new_state;
	object->nwamd_object_aux_state = new_aux_state;

	nlog(LOG_DEBUG, "nwamd_enm_handle_state_event: changing state for enm "
	    "%s to (%s , %s)", object->nwamd_object_name,
	    nwam_state_to_string(object->nwamd_object_state),
	    nwam_aux_state_to_string(object->nwamd_object_aux_state));

	nwamd_object_release(object);

	/*
	 * State machine for ENMs.
	 */
	switch (new_state) {
	case NWAM_STATE_OFFLINE_TO_ONLINE:
		nwamd_enm_activate(event->event_object);
		break;
	case NWAM_STATE_ONLINE_TO_OFFLINE:
		nwamd_enm_deactivate(event->event_object);
		break;
	case NWAM_STATE_DISABLED:
	case NWAM_STATE_OFFLINE:
	case NWAM_STATE_UNINITIALIZED:
	case NWAM_STATE_MAINTENANCE:
	case NWAM_STATE_DEGRADED:
	default:
		/* do nothing */
		break;
	}
}