enm.c revision 11994:5cb9130500d6
1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27#include <arpa/inet.h>
28#include <errno.h>
29#include <inet/ip.h>
30#include <inetcfg.h>
31#include <libdladm.h>
32#include <libdllink.h>
33#include <libdlwlan.h>
34#include <libscf.h>
35#include <netinet/in.h>
36#include <netdb.h>
37#include <stdio.h>
38#include <stdlib.h>
39#include <string.h>
40#include <sys/socket.h>
41#include <sys/types.h>
42
43#include <libnwam.h>
44#include "conditions.h"
45#include "events.h"
46#include "objects.h"
47#include "util.h"
48
49/*
50 * enm.c - contains routines which handle ENM (external network modifier)
51 * abstraction.  ENMs represent scripts or services that can be activated either
52 * manually or in response to network conditions.
53 */
54
55#define	CTRUN	"/usr/bin/ctrun"
56
57static int
58enm_create_init_fini_event(nwam_enm_handle_t enmh, void *data)
59{
60	boolean_t *init = data;
61	char *name;
62	nwamd_event_t enm_event;
63
64	if (nwam_enm_get_name(enmh, &name) != NWAM_SUCCESS) {
65		nlog(LOG_ERR, "enm_init_fini: could not get ENM name");
66		return (0);
67	}
68
69	enm_event = nwamd_event_init(*init ?
70	    NWAM_EVENT_TYPE_OBJECT_INIT : NWAM_EVENT_TYPE_OBJECT_FINI,
71	    NWAM_OBJECT_TYPE_ENM, 0, name);
72	if (enm_event != NULL)
73		nwamd_event_enqueue(enm_event);
74	free(name);
75
76	return (0);
77}
78
79/*
80 * Walk all ENMs, creating init events for each.
81 */
82void
83nwamd_init_enms(void)
84{
85	boolean_t init = B_TRUE;
86
87	(void) nwam_walk_enms(enm_create_init_fini_event, &init, 0, NULL);
88}
89
90/*
91 * Walk all ENMs, creating fini events for each.
92 */
93void
94nwamd_fini_enms(void)
95{
96	boolean_t init = B_FALSE;
97
98	(void) nwam_walk_enms(enm_create_init_fini_event, &init, 0, NULL);
99}
100
101static boolean_t
102enm_is_enabled(nwam_enm_handle_t enmh)
103{
104	nwam_value_t enabledval;
105	boolean_t enabled = B_FALSE;
106
107	if (nwam_enm_get_prop_value(enmh, NWAM_ENM_PROP_ENABLED,
108	    &enabledval) != NWAM_SUCCESS) {
109		/* It's legal for a conditional ENM to not specify "enabled" */
110		return (B_FALSE);
111	}
112	if (nwam_value_get_boolean(enabledval, &enabled) != NWAM_SUCCESS) {
113		nlog(LOG_ERR, "enm_is_enabled: could not retrieve "
114		    "enabled value");
115	}
116	nwam_value_free(enabledval);
117	return (enabled);
118}
119
120static int64_t
121enm_get_activation_mode(nwam_enm_handle_t enmh)
122{
123	uint64_t activation;
124	int64_t ret;
125	nwam_value_t activationval;
126
127	if (nwam_enm_get_prop_value(enmh, NWAM_ENM_PROP_ACTIVATION_MODE,
128	    &activationval)  != NWAM_SUCCESS) {
129		nlog(LOG_ERR, "enm_get_activation_mode: could not retrieve "
130		    "activation mode value");
131		return (-1);
132	}
133	if (nwam_value_get_uint64(activationval, &activation) != NWAM_SUCCESS) {
134		nlog(LOG_ERR, "enm_get_activation_mode: could not retrieve "
135		    "activation mode value");
136		ret = -1;
137	} else {
138		ret = activation;
139	}
140	nwam_value_free(activationval);
141
142	return (ret);
143}
144
145static void *
146nwamd_enm_activate_deactivate_thread(void *arg)
147{
148	char *object_name = arg;
149	nwamd_object_t object;
150	nwam_enm_handle_t enmh;
151	nwam_value_t scriptval = NULL;
152	nwam_state_t state;
153	nwam_aux_state_t aux_state;
154	char *script, *copy = NULL;
155	const char **argv = NULL;
156	boolean_t going_online, disable_succeeded = B_FALSE;
157	int ret;
158
159	object = nwamd_object_find(NWAM_OBJECT_TYPE_ENM, object_name);
160	if (object == NULL) {
161		nlog(LOG_ERR, "nwamd_enm_activate_deactivate_thread: "
162		    "could not find ENM %s", object_name);
163		goto done;
164	}
165	enmh = object->nwamd_object_handle;
166
167	going_online =
168	    (object->nwamd_object_state == NWAM_STATE_OFFLINE_TO_ONLINE);
169	/*
170	 * We're starting if current state is offline* and stopping otherwise.
171	 */
172	if (nwam_enm_get_prop_value(enmh,
173	    going_online ? NWAM_ENM_PROP_START : NWAM_ENM_PROP_STOP,
174	    &scriptval) != NWAM_SUCCESS ||
175	    nwam_value_get_string(scriptval, &script) != NWAM_SUCCESS) {
176		/*
177		 * If we're stopping, it's not an error for no script to
178		 * be specified.
179		 */
180		nlog(going_online ? LOG_ERR : LOG_DEBUG,
181		    "nwamd_enm_activate_deactivate_thread: "
182		    "no script specified for enm %s", object_name);
183		if (going_online) {
184			state = NWAM_STATE_MAINTENANCE;
185			aux_state = NWAM_AUX_STATE_METHOD_MISSING;
186		} else {
187			disable_succeeded = B_TRUE;
188		}
189	} else {
190		char *lasts;
191		const char **newargv;
192		int i = 0;
193		struct timeval now;
194
195		nlog(LOG_DEBUG, "nwamd_enm_activate_deactivate_thread: "
196		    "running script %s for ENM %s", script, object_name);
197
198		/*
199		 * The script may take a number of arguments. We need to
200		 * create a string array consisting of the wrapper command
201		 * (ctrun), ENM script name, arguments and NULL array
202		 * terminator.  Start with an array of size equal to the
203		 * string length (since the number of arguments will always
204		 * be less than this) and shrink array to the actual number
205		 * of arguments when we have parsed the string.
206		 */
207		if ((copy = strdup(script)) == NULL ||
208		    (argv = calloc(strlen(script), sizeof (char *))) == NULL) {
209			ret = 1;
210			goto err;
211		}
212		argv[i++] = CTRUN;
213		argv[i++] = strtok_r(copy, " ", &lasts);
214		if (argv[1] == NULL) {
215			ret = 1;
216			goto err;
217		}
218
219		for (; (argv[i] = strtok_r(NULL, " ", &lasts)) != NULL; i++) {}
220
221		newargv = realloc(argv, (i + 1) * sizeof (char *));
222		argv = newargv;
223
224		/* Store the current time as the time the script began */
225		(void) gettimeofday(&now, NULL);
226		object->nwamd_script_time = now;
227
228		/*
229		 * Release the object so that it is not blocked while the
230		 * script is running.
231		 */
232		nwamd_object_release(object);
233
234		ret = nwamd_start_childv(CTRUN, argv);
235
236		/*
237		 * Find the object again, now that the script has finished
238		 * running.  Check if this ENM was re-read during that time by
239		 * comparing the object's script time with the one from above.
240		 */
241		object = nwamd_object_find(NWAM_OBJECT_TYPE_ENM, object_name);
242		if (object == NULL) {
243			nlog(LOG_ERR, "nwamd_enm_activate_deactivate_thread: "
244			    "could not find ENM %s after running script",
245			    object_name);
246			goto done;
247		}
248
249		if (object->nwamd_script_time.tv_sec != now.tv_sec ||
250		    object->nwamd_script_time.tv_usec != now.tv_usec) {
251			nlog(LOG_INFO, "nwamd_enm_activate_deactivate_thread: "
252			    "ENM %s has been refreshed, nothing to do",
253			    object_name);
254			nwamd_object_release(object);
255			goto done;
256		}
257		(void) gettimeofday(&object->nwamd_script_time, NULL);
258
259err:
260		/*
261		 * If script execution fails and we're not destroying the
262		 * object, go to maintenance.
263		 */
264		if (ret != 0) {
265			nlog(LOG_ERR, "nwamd_enm_activate_deactivate_thread: "
266			    "execution of '%s' failed for ENM %s",
267			    script, object_name);
268			if (object->nwamd_object_aux_state !=
269			    NWAM_AUX_STATE_UNINITIALIZED) {
270				state = NWAM_STATE_MAINTENANCE;
271				aux_state = NWAM_AUX_STATE_METHOD_FAILED;
272			} else {
273				state = NWAM_STATE_UNINITIALIZED;
274				aux_state = NWAM_AUX_STATE_UNINITIALIZED;
275			}
276		} else {
277			if (going_online) {
278				state = NWAM_STATE_ONLINE;
279				aux_state = NWAM_AUX_STATE_ACTIVE;
280			} else {
281				disable_succeeded = B_TRUE;
282			}
283		}
284	}
285
286	if (disable_succeeded) {
287		/*
288		 * If aux state is "manual disable", we know
289		 * this was a disable request, otherwise it was
290		 * _fini request or a condition satisfaction
291		 * failure.
292		 */
293		switch (object->nwamd_object_aux_state) {
294		case NWAM_AUX_STATE_MANUAL_DISABLE:
295			state = NWAM_STATE_DISABLED;
296			aux_state = NWAM_AUX_STATE_MANUAL_DISABLE;
297			break;
298		case NWAM_AUX_STATE_UNINITIALIZED:
299			state = NWAM_STATE_UNINITIALIZED;
300			aux_state = NWAM_AUX_STATE_UNINITIALIZED;
301			break;
302		default:
303			state = NWAM_STATE_OFFLINE;
304			aux_state = NWAM_AUX_STATE_CONDITIONS_NOT_MET;
305			break;
306		}
307	}
308
309	/* If state/aux state are uninitialized/unintialized, destroy the ENM */
310	if (state == NWAM_STATE_UNINITIALIZED &&
311	    aux_state == NWAM_AUX_STATE_UNINITIALIZED) {
312		object->nwamd_object_state = state;
313		object->nwamd_object_aux_state = aux_state;
314		(void) nwamd_object_release_and_destroy_after_preserve(object);
315	} else {
316		nwamd_object_set_state(NWAM_OBJECT_TYPE_ENM,
317		    object->nwamd_object_name, state, aux_state);
318		(void) nwamd_object_release_after_preserve(object);
319	}
320
321done:
322	/* object_name was malloc() before this thread was created, free() it */
323	free(object_name);
324	free(argv);
325	free(copy);
326	nwam_value_free(scriptval);
327	return (NULL);
328}
329
330/*
331 * Run start/stop method for ENM in a separate thread.  The object lock is not
332 * held across threads, so we duplicate the object name for the method
333 * execution thread.  Returns true if thread is successfully launched.
334 */
335boolean_t
336nwamd_enm_run_method(nwamd_object_t object)
337{
338	char *name;
339	pthread_t script;
340
341	/*
342	 * Launch separate thread to wait for execution of script
343	 * to complete.  Do not hold object lock across threads.
344	 */
345	if ((name = strdup(object->nwamd_object_name)) == NULL) {
346		nlog(LOG_ERR, "nwamd_enm_run_method: %s: out of memory",
347		    object->nwamd_object_name);
348		return (B_FALSE);
349	}
350
351	if (pthread_create(&script, NULL,
352	    nwamd_enm_activate_deactivate_thread, name) != 0) {
353		nlog(LOG_ERR, "nwamd_enm_run_method: could not create "
354		    "enm script thread for %s", name);
355		free(name);
356		return (B_FALSE);
357	}
358	/* "name" will be freed by the newly-created thread. */
359
360	/* detach thread so that it doesn't become a zombie */
361	(void) pthread_detach(script);
362
363	return (B_TRUE);
364}
365
366/*
367 * Activate the ENM, either in response to an enable event or conditions
368 * being satisfied.
369 */
370static void
371nwamd_enm_activate(const char *object_name)
372{
373	nwamd_object_t object;
374	nwam_value_t fmrival;
375	char *fmri, *smf_state;
376	int ret;
377	nwam_enm_handle_t enmh;
378	nwam_state_t state;
379	nwam_aux_state_t aux_state;
380	nwam_error_t err;
381	boolean_t ran_method = B_FALSE;
382
383	object = nwamd_object_find(NWAM_OBJECT_TYPE_ENM, object_name);
384	if (object == NULL) {
385		nlog(LOG_ERR, "nwamd_enm_activate: could not find ENM %s",
386		    object_name);
387		return;
388	}
389	state = object->nwamd_object_state;
390	aux_state = object->nwamd_object_aux_state;
391	enmh = object->nwamd_object_handle;
392
393	nlog(LOG_DEBUG, "nwamd_enm_activate: activating ENM %s",
394	    object->nwamd_object_name);
395
396	err = nwam_enm_get_prop_value(enmh, NWAM_ENM_PROP_FMRI, &fmrival);
397	switch (err) {
398	case NWAM_SUCCESS:
399
400		if (nwam_value_get_string(fmrival, &fmri) != NWAM_SUCCESS) {
401			nlog(LOG_ERR, "nwamd_enm_activate: could not retrieve "
402			    "FMRI string for ENM %s",
403			    object->nwamd_object_name);
404			nwam_value_free(fmrival);
405			state = NWAM_STATE_MAINTENANCE;
406			aux_state = NWAM_AUX_STATE_INVALID_CONFIG;
407			break;
408		}
409
410		if ((smf_state = smf_get_state(fmri)) == NULL) {
411			nlog(LOG_ERR, "nwamd_enm_activate: invalid FMRI %s "
412			    "for ENM %s", fmri, object->nwamd_object_name);
413			nwam_value_free(fmrival);
414			state = NWAM_STATE_MAINTENANCE;
415			aux_state = NWAM_AUX_STATE_INVALID_CONFIG;
416			break;
417		}
418
419		nlog(LOG_DEBUG, "nwamd_enm_activate: activating %s for ENM %s",
420		    fmri, object->nwamd_object_name);
421
422		if (strcmp(smf_state, SCF_STATE_STRING_ONLINE) == 0)
423			ret = smf_restart_instance(fmri);
424		else if (strcmp(smf_state, SCF_STATE_STRING_OFFLINE) == 0)
425			ret = smf_restart_instance(fmri);
426		else if (strcmp(smf_state, SCF_STATE_STRING_DISABLED) == 0)
427			ret = smf_enable_instance(fmri, SMF_TEMPORARY);
428		else
429			ret = smf_restore_instance(fmri);
430
431		if (ret == 0) {
432			state = NWAM_STATE_ONLINE;
433			aux_state = NWAM_AUX_STATE_ACTIVE;
434		} else {
435			nlog(LOG_ERR, "nwamd_enm_activate: failed to enable "
436			    "FMRI %s for ENM %s", fmri,
437			    object->nwamd_object_name);
438			state = NWAM_STATE_MAINTENANCE;
439			aux_state = NWAM_AUX_STATE_METHOD_FAILED;
440		}
441		free(smf_state);
442		nwam_value_free(fmrival);
443		break;
444	default:
445		/*
446		 * Must be a method-based ENM with start (and stop) script(s).
447		 */
448		if (!nwamd_enm_run_method(object)) {
449			/* Could not launch method execution thread */
450			state = NWAM_STATE_MAINTENANCE;
451			aux_state = NWAM_AUX_STATE_METHOD_FAILED;
452		} else {
453			ran_method = B_TRUE;
454		}
455		break;
456	}
457
458	if (state != object->nwamd_object_state ||
459	    aux_state != object->nwamd_object_aux_state) {
460		nwamd_object_set_state(NWAM_OBJECT_TYPE_ENM,
461		    object->nwamd_object_name, state, aux_state);
462	}
463
464	/*
465	 * If the method thread was created, we drop the lock to the ENM
466	 * object without decreasing the reference count, ensuring it will not
467	 * be destroyed until method execution has completed.
468	 */
469	if (ran_method) {
470		nwamd_object_release_and_preserve(object);
471	} else {
472		nwamd_object_release(object);
473	}
474}
475
476/* Deactivates the ENM. */
477static void
478nwamd_enm_deactivate(const char *object_name)
479{
480	nwamd_object_t object;
481	nwam_enm_handle_t enmh;
482	nwam_value_t fmrival;
483	char *fmri, *smf_state;
484	int ret;
485	nwam_state_t state;
486	nwam_aux_state_t aux_state;
487	boolean_t destroying = B_FALSE;
488
489	object = nwamd_object_find(NWAM_OBJECT_TYPE_ENM, object_name);
490	if (object == NULL) {
491		nlog(LOG_ERR, "nwamd_enm_deactivate: could not find ENM %s",
492		    object_name);
493		return;
494	}
495
496	state = object->nwamd_object_state;
497	aux_state = object->nwamd_object_aux_state;
498	enmh = object->nwamd_object_handle;
499	state = object->nwamd_object_state;
500	/* If destroying, we don't care about method failure/config err */
501	destroying = (aux_state == NWAM_AUX_STATE_UNINITIALIZED);
502
503	nlog(LOG_DEBUG, "nwamd_enm_deactivate: deactivating enm %s",
504	    object->nwamd_object_name);
505
506	if (nwam_enm_get_prop_value(enmh, NWAM_ENM_PROP_FMRI, &fmrival)
507	    != NWAM_SUCCESS) {
508		/*
509		 * Must be a method-based ENM with start (and stop) script(s).
510		 * Script execution thread will take care of the rest.
511		 * If the method thread was created, we drop the lock to the ENM
512		 * object without decreasing the reference count, ensuring it
513		 * will not be destroyed until method execution has completed.
514		 */
515		if (nwamd_enm_run_method(object)) {
516			nwamd_object_release_and_preserve(object);
517			return;
518		}
519		/* Could not launch method execution thread */
520		if (!destroying) {
521			state = NWAM_STATE_MAINTENANCE;
522			aux_state = NWAM_AUX_STATE_METHOD_FAILED;
523		}
524	} else {
525		if (nwam_value_get_string(fmrival, &fmri) != NWAM_SUCCESS) {
526			nlog(LOG_ERR,
527			    "nwamd_enm_deactivate: could not retrieve "
528			    "FMRI string for ENM %s",
529			    object->nwamd_object_name);
530			if (!destroying) {
531				state = NWAM_STATE_MAINTENANCE;
532				aux_state = NWAM_AUX_STATE_INVALID_CONFIG;
533			}
534		} else {
535			if ((smf_state = smf_get_state(fmri)) == NULL) {
536				nlog(LOG_ERR, "nwamd_enm_deactivate: invalid "
537				    "FMRI %s for ENM %s", fmri,
538				    object->nwamd_object_name);
539				nwam_value_free(fmrival);
540				if (!destroying) {
541					state = NWAM_STATE_MAINTENANCE;
542					aux_state =
543					    NWAM_AUX_STATE_INVALID_CONFIG;
544				}
545				goto done;
546			}
547			free(smf_state);
548
549			nlog(LOG_DEBUG, "nwamd_enm_deactivate: deactivating %s "
550			    "for ENM %s", fmri, object->nwamd_object_name);
551
552			ret = smf_disable_instance(fmri, SMF_TEMPORARY);
553
554			if (ret != 0) {
555				nlog(LOG_ERR, "nwamd_enm_deactivate: "
556				    "smf_disable_instance(%s) failed for "
557				    "ENM %s: %s", fmri,
558				    object->nwamd_object_name,
559				    scf_strerror(scf_error()));
560				if (!destroying) {
561					state = NWAM_STATE_MAINTENANCE;
562					aux_state =
563					    NWAM_AUX_STATE_METHOD_FAILED;
564				}
565			}
566		}
567		nwam_value_free(fmrival);
568	}
569done:
570	if (state == object->nwamd_object_state &&
571	    aux_state == object->nwamd_object_aux_state) {
572		/*
573		 * If aux state is "manual disable", we know
574		 * this was a disable request, otherwise it was
575		 * a _fini request or a condition satisfaction
576		 * failure.
577		 */
578		switch (object->nwamd_object_aux_state) {
579		case NWAM_AUX_STATE_MANUAL_DISABLE:
580			state = NWAM_STATE_DISABLED;
581			aux_state = NWAM_AUX_STATE_MANUAL_DISABLE;
582			break;
583		case NWAM_AUX_STATE_UNINITIALIZED:
584			state = NWAM_STATE_UNINITIALIZED;
585			aux_state = NWAM_AUX_STATE_UNINITIALIZED;
586			break;
587		default:
588			state = NWAM_STATE_OFFLINE;
589			aux_state = NWAM_AUX_STATE_CONDITIONS_NOT_MET;
590			break;
591		}
592	}
593
594	/* Only change state if we aren't destroying the ENM */
595	if (!destroying && (state != object->nwamd_object_state ||
596	    aux_state != object->nwamd_object_aux_state)) {
597		nwamd_object_set_state(NWAM_OBJECT_TYPE_ENM,
598		    object->nwamd_object_name, state, aux_state);
599	}
600
601	/* If state/aux state are uninitialized/unintialized, destroy the ENM */
602	if (state == NWAM_STATE_UNINITIALIZED &&
603	    aux_state == NWAM_AUX_STATE_UNINITIALIZED) {
604		(void) nwamd_object_release_and_destroy(object);
605	} else {
606		(void) nwamd_object_release(object);
607	}
608}
609
610/*
611 * Determine whether an ENM should be (de)activated.
612 */
613/* ARGSUSED1 */
614static int
615nwamd_enm_check(nwamd_object_t object, void *data)
616{
617	nwam_enm_handle_t enmh;
618	nwam_value_t conditionval;
619	int64_t eactivation;
620	boolean_t enabled, satisfied;
621	char **conditions;
622	nwam_state_t state;
623	uint_t nelem;
624
625	state = object->nwamd_object_state;
626
627	enmh = object->nwamd_object_handle;
628
629	eactivation = enm_get_activation_mode(enmh);
630	if (eactivation == -1)
631		return (0);
632
633	switch (eactivation) {
634	case NWAM_ACTIVATION_MODE_MANUAL:
635		enabled = enm_is_enabled(enmh);
636
637		if (enabled) {
638			nlog(LOG_DEBUG, "nwamd_enm_check: %s is enabled",
639			    object->nwamd_object_name);
640			switch (state) {
641			case NWAM_STATE_ONLINE:
642			case NWAM_STATE_MAINTENANCE:
643				/* Do nothing */
644				break;
645			default:
646				if (nwamd_enm_action(object->nwamd_object_name,
647				    NWAM_ACTION_ENABLE) != 0) {
648					nlog(LOG_ERR,
649					    "nwamd_enm_check: enable failed "
650					    "for enm %s",
651					    object->nwamd_object_name);
652				}
653				break;
654			}
655		} else {
656			nlog(LOG_DEBUG, "nwamd_enm_check: %s is disabled",
657			    object->nwamd_object_name);
658			switch (state) {
659			case NWAM_STATE_ONLINE:
660				if (nwamd_enm_action(object->nwamd_object_name,
661				    NWAM_ACTION_DISABLE) != 0) {
662					nlog(LOG_ERR, "nwamd_enm_check: "
663					    "disable failed for enm %s",
664					    object->nwamd_object_name);
665				}
666				break;
667			case NWAM_STATE_MAINTENANCE:
668				/* Do nothing */
669				break;
670			case NWAM_STATE_DISABLED:
671				/* Do nothing */
672				break;
673			default:
674				nwamd_object_set_state(NWAM_OBJECT_TYPE_ENM,
675				    object->nwamd_object_name,
676				    NWAM_STATE_DISABLED,
677				    NWAM_AUX_STATE_MANUAL_DISABLE);
678				break;
679			}
680		}
681		break;
682
683	case NWAM_ACTIVATION_MODE_CONDITIONAL_ANY:
684	case NWAM_ACTIVATION_MODE_CONDITIONAL_ALL:
685		if (nwam_enm_get_prop_value(enmh,
686		    NWAM_ENM_PROP_CONDITIONS, &conditionval) != NWAM_SUCCESS) {
687			nlog(LOG_ERR, "nwamd_enm_check: could not retrieve "
688			    "condition value");
689			break;
690		}
691		if (nwam_value_get_string_array(conditionval,
692		    &conditions, &nelem) != NWAM_SUCCESS) {
693			nlog(LOG_ERR, "nwamd_enm_check: could not retrieve "
694			    "condition value");
695			nwam_value_free(conditionval);
696			break;
697		}
698		satisfied = nwamd_check_conditions((uint64_t)eactivation,
699		    conditions, nelem);
700
701		nlog(LOG_DEBUG, "nwamd_enm_check: conditions for enm %s "
702		    "%s satisfied", object->nwamd_object_name,
703		    satisfied ? "is" : "is not");
704		if (state != NWAM_STATE_ONLINE && satisfied) {
705			nwamd_object_set_state(NWAM_OBJECT_TYPE_ENM,
706			    object->nwamd_object_name,
707			    NWAM_STATE_OFFLINE_TO_ONLINE,
708			    NWAM_AUX_STATE_METHOD_RUNNING);
709		}
710		if (state == NWAM_STATE_ONLINE && !satisfied) {
711			nwamd_object_set_state(NWAM_OBJECT_TYPE_ENM,
712			    object->nwamd_object_name,
713			    NWAM_STATE_ONLINE_TO_OFFLINE,
714			    NWAM_AUX_STATE_CONDITIONS_NOT_MET);
715		}
716		nwam_value_free(conditionval);
717		break;
718
719	}
720	return (0);
721}
722
723void
724nwamd_enm_check_conditions(void)
725{
726	(void) nwamd_walk_objects(NWAM_OBJECT_TYPE_ENM, nwamd_enm_check, NULL);
727}
728
729int
730nwamd_enm_action(const char *enm, nwam_action_t action)
731{
732	nwamd_event_t event = nwamd_event_init_object_action
733	    (NWAM_OBJECT_TYPE_ENM, enm, NULL, action);
734	if (event == NULL)
735		return (1);
736	nwamd_event_enqueue(event);
737	return (0);
738}
739
740/*
741 * Event handling functions.
742 */
743
744/* Handle ENM initialization/refresh event */
745void
746nwamd_enm_handle_init_event(nwamd_event_t event)
747{
748	nwamd_object_t object;
749	nwam_enm_handle_t enmh;
750	nwam_error_t err;
751	boolean_t manual_disabled = B_FALSE;
752
753	if ((err = nwam_enm_read(event->event_object, 0, &enmh))
754	    != NWAM_SUCCESS) {
755		nlog(LOG_ERR, "nwamd_enm_handle_init_event: could not "
756		    "read object '%s': %s", event->event_object,
757		    nwam_strerror(err));
758		nwamd_event_do_not_send(event);
759		return;
760	}
761	if ((object = nwamd_object_find(NWAM_OBJECT_TYPE_ENM,
762	    event->event_object)) != NULL) {
763		nwam_enm_free(object->nwamd_object_handle);
764		object->nwamd_object_handle = enmh;
765	} else {
766		object = nwamd_object_init(NWAM_OBJECT_TYPE_ENM,
767		    event->event_object, enmh, NULL);
768		object->nwamd_object_state = NWAM_STATE_OFFLINE;
769		object->nwamd_object_aux_state =
770		    NWAM_AUX_STATE_CONDITIONS_NOT_MET;
771	}
772	/* (Re)set script time to now as the object has just been (re)read */
773	(void) gettimeofday(&object->nwamd_script_time, NULL);
774
775	manual_disabled = (enm_get_activation_mode(enmh) ==
776	    NWAM_ACTIVATION_MODE_MANUAL && !enm_is_enabled(enmh));
777
778	/*
779	 * If this ENM is ONLINE, and not manual and disabled (since in
780	 * that case it was online but we've just set enabled = false as part
781	 * of a disable action), then it is still active but refreshing.
782	 * Change states to re-activate itself.
783	 */
784	if (!manual_disabled &&
785	    object->nwamd_object_state == NWAM_STATE_ONLINE) {
786		nwamd_object_set_state(NWAM_OBJECT_TYPE_ENM,
787		    event->event_object, NWAM_STATE_OFFLINE_TO_ONLINE,
788		    NWAM_AUX_STATE_METHOD_RUNNING);
789	}
790	nwamd_object_release(object);
791}
792
793/* Handle ENM finish event */
794void
795nwamd_enm_handle_fini_event(nwamd_event_t event)
796{
797	nwamd_event_t state_event;
798
799	nlog(LOG_DEBUG, "nwamd_enm_handle_fini_event(%s)", event->event_object);
800
801	/*
802	 * Simulate a state event so that the state machine can correctly
803	 * deactivate the ENM and free up the handle.
804	 */
805	state_event = nwamd_event_init_object_state(NWAM_OBJECT_TYPE_ENM,
806	    event->event_object, NWAM_STATE_ONLINE_TO_OFFLINE,
807	    NWAM_AUX_STATE_UNINITIALIZED);
808	if (state_event == NULL) {
809		nwamd_event_do_not_send(event);
810		return;
811	}
812	nwamd_enm_handle_state_event(state_event);
813	nwamd_event_fini(state_event);
814	/*
815	 * Do not free the handle and object.
816	 * nwamd_enm_activate_deactivate_thread() and
817	 * nwamd_enm_deactivate() does this after running the stop script
818	 * and disabling the FMRI respectively.
819	 */
820}
821
822void
823nwamd_enm_handle_action_event(nwamd_event_t event)
824{
825	nwamd_object_t object;
826
827	switch (event->event_msg->nwe_data.nwe_object_action.nwe_action) {
828	case NWAM_ACTION_ENABLE:
829		object = nwamd_object_find(NWAM_OBJECT_TYPE_ENM,
830		    event->event_object);
831		if (object == NULL) {
832			nlog(LOG_ERR, "nwamd_enm_handle_action_event: "
833			    "could not find enm %s", event->event_object);
834			nwamd_event_do_not_send(event);
835			return;
836		}
837		if (object->nwamd_object_state == NWAM_STATE_ONLINE) {
838			nlog(LOG_DEBUG, "nwamd_enm_handle_action_event: "
839			    "enm %s already online, nothing to do",
840			    event->event_object);
841			nwamd_object_release(object);
842			return;
843		}
844		nwamd_object_release(object);
845
846		nwamd_object_set_state(NWAM_OBJECT_TYPE_ENM,
847		    event->event_object, NWAM_STATE_OFFLINE_TO_ONLINE,
848		    NWAM_AUX_STATE_METHOD_RUNNING);
849		break;
850	case NWAM_ACTION_DISABLE:
851		object = nwamd_object_find(NWAM_OBJECT_TYPE_ENM,
852		    event->event_object);
853		if (object == NULL) {
854			nlog(LOG_ERR, "nwamd_enm_handle_action_event: "
855			    "could not find enm %s", event->event_object);
856			nwamd_event_do_not_send(event);
857			return;
858		}
859		if (object->nwamd_object_state == NWAM_STATE_DISABLED) {
860			nlog(LOG_DEBUG, "nwamd_enm_handle_action_event: "
861			    "enm %s already disabled, nothing to do",
862			    event->event_object);
863			nwamd_object_release(object);
864			return;
865		}
866		nwamd_object_release(object);
867
868		nwamd_object_set_state(NWAM_OBJECT_TYPE_ENM,
869		    event->event_object, NWAM_STATE_ONLINE_TO_OFFLINE,
870		    NWAM_AUX_STATE_MANUAL_DISABLE);
871		break;
872	case NWAM_ACTION_ADD:
873	case NWAM_ACTION_REFRESH:
874		nwamd_enm_handle_init_event(event);
875		break;
876	case NWAM_ACTION_DESTROY:
877		nwamd_enm_handle_fini_event(event);
878		break;
879	default:
880		nlog(LOG_INFO, "nwam_enm_handle_action_event: "
881		    "unexpected action");
882		nwamd_event_do_not_send(event);
883		break;
884	}
885}
886
887void
888nwamd_enm_handle_state_event(nwamd_event_t event)
889{
890	nwamd_object_t object;
891	nwam_state_t new_state;
892	nwam_aux_state_t new_aux_state;
893
894	if ((object = nwamd_object_find(NWAM_OBJECT_TYPE_ENM,
895	    event->event_object)) == NULL) {
896		nlog(LOG_ERR, "nwamd_enm_handle_state_event: "
897		    "state event for nonexistent ENM %s", event->event_object);
898		nwamd_event_do_not_send(event);
899		return;
900	}
901	new_state = event->event_msg->nwe_data.nwe_object_state.nwe_state;
902	new_aux_state =
903	    event->event_msg->nwe_data.nwe_object_state.nwe_aux_state;
904
905	if (new_state == object->nwamd_object_state &&
906	    new_aux_state == object->nwamd_object_aux_state) {
907		nlog(LOG_DEBUG, "nwamd_enm_handle_state_event: "
908		    "ENM %s already in state (%s , %s)",
909		    object->nwamd_object_name, nwam_state_to_string(new_state),
910		    nwam_aux_state_to_string(new_aux_state));
911		nwamd_object_release(object);
912		return;
913	}
914
915	object->nwamd_object_state = new_state;
916	object->nwamd_object_aux_state = new_aux_state;
917
918	nlog(LOG_DEBUG, "nwamd_enm_handle_state_event: changing state for ENM "
919	    "%s to (%s , %s)", object->nwamd_object_name,
920	    nwam_state_to_string(object->nwamd_object_state),
921	    nwam_aux_state_to_string(object->nwamd_object_aux_state));
922
923	nwamd_object_release(object);
924
925	/*
926	 * State machine for ENMs.
927	 */
928	switch (new_state) {
929	case NWAM_STATE_OFFLINE_TO_ONLINE:
930		nwamd_enm_activate(event->event_object);
931		break;
932	case NWAM_STATE_ONLINE_TO_OFFLINE:
933		nwamd_enm_deactivate(event->event_object);
934		break;
935	case NWAM_STATE_DISABLED:
936	case NWAM_STATE_OFFLINE:
937	case NWAM_STATE_UNINITIALIZED:
938	case NWAM_STATE_MAINTENANCE:
939	case NWAM_STATE_DEGRADED:
940	default:
941		/* do nothing */
942		break;
943	}
944}
945