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