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, "nwamd_enm_deactivate: could not "
525			    "retrieve fmri string for enm %s",
526			    object->nwamd_object_name);
527			if (!destroying) {
528				state = NWAM_STATE_MAINTENANCE;
529				aux_state = NWAM_AUX_STATE_INVALID_CONFIG;
530			}
531		} else {
532			if ((smf_state = smf_get_state(fmri)) == NULL) {
533				nlog(LOG_ERR, "nwamd_enm_deactivate: invalid "
534				    "fmri %s for enm %s", fmri,
535				    object->nwamd_object_name);
536				nwam_value_free(fmrival);
537				if (!destroying) {
538					state = NWAM_STATE_MAINTENANCE;
539					aux_state =
540					    NWAM_AUX_STATE_INVALID_CONFIG;
541				}
542				goto done;
543			}
544			free(smf_state);
545
546			nlog(LOG_DEBUG, "nwamd_enm_deactivate: deactivating %s "
547			    "for enm %s", fmri, object->nwamd_object_name);
548
549			ret = smf_disable_instance(fmri, SMF_TEMPORARY);
550
551			if (ret != 0) {
552				nlog(LOG_ERR, "nwamd_enm_deactivate: "
553				    "smf_disable_instance(%s) failed for "
554				    "enm %s: %s", fmri,
555				    object->nwamd_object_name,
556				    scf_strerror(scf_error()));
557				if (!destroying) {
558					state = NWAM_STATE_MAINTENANCE;
559					aux_state =
560					    NWAM_AUX_STATE_METHOD_FAILED;
561				}
562			}
563		}
564		nwam_value_free(fmrival);
565	}
566done:
567	if (state == object->nwamd_object_state &&
568	    aux_state == object->nwamd_object_aux_state) {
569		/*
570		 * If aux state is "manual disable", we know
571		 * this was a disable request, otherwise it was
572		 * a _fini request or a condition satisfaction
573		 * failure.
574		 */
575		switch (object->nwamd_object_aux_state) {
576		case NWAM_AUX_STATE_MANUAL_DISABLE:
577			state = NWAM_STATE_DISABLED;
578			aux_state = NWAM_AUX_STATE_MANUAL_DISABLE;
579			break;
580		case NWAM_AUX_STATE_UNINITIALIZED:
581			state = NWAM_STATE_UNINITIALIZED;
582			aux_state = NWAM_AUX_STATE_UNINITIALIZED;
583			break;
584		default:
585			state = NWAM_STATE_OFFLINE;
586			aux_state = NWAM_AUX_STATE_CONDITIONS_NOT_MET;
587			break;
588		}
589	}
590
591	/* Only change state if we aren't destroying the ENM */
592	if (!destroying && (state != object->nwamd_object_state ||
593	    aux_state != object->nwamd_object_aux_state)) {
594		nwamd_object_set_state(NWAM_OBJECT_TYPE_ENM,
595		    object->nwamd_object_name, state, aux_state);
596	}
597
598	/* If state/aux state are uninitialized/unintialized, destroy the ENM */
599	if (state == NWAM_STATE_UNINITIALIZED &&
600	    aux_state == NWAM_AUX_STATE_UNINITIALIZED) {
601		(void) nwamd_object_release_and_destroy(object);
602	} else {
603		(void) nwamd_object_release(object);
604	}
605}
606
607/*
608 * Determine whether an ENM should be (de)activated.
609 */
610/* ARGSUSED1 */
611static int
612nwamd_enm_check(nwamd_object_t object, void *data)
613{
614	nwam_enm_handle_t enmh;
615	nwam_value_t conditionval;
616	int64_t eactivation;
617	boolean_t enabled, satisfied;
618	char **conditions;
619	nwam_state_t state;
620	uint_t nelem;
621
622	state = object->nwamd_object_state;
623
624	enmh = object->nwamd_object_handle;
625
626	eactivation = enm_get_activation_mode(enmh);
627	if (eactivation == -1)
628		return (0);
629
630	switch (eactivation) {
631	case NWAM_ACTIVATION_MODE_MANUAL:
632		enabled = enm_is_enabled(enmh);
633
634		if (enabled) {
635			nlog(LOG_DEBUG, "nwamd_enm_check: %s is enabled",
636			    object->nwamd_object_name);
637			switch (state) {
638			case NWAM_STATE_ONLINE:
639			case NWAM_STATE_MAINTENANCE:
640				/* Do nothing */
641				break;
642			default:
643				if (nwamd_enm_action(object->nwamd_object_name,
644				    NWAM_ACTION_ENABLE) != 0) {
645					nlog(LOG_ERR, "nwamd_enm_check: "
646					    "enable failed for enm %s",
647					    object->nwamd_object_name);
648				}
649				break;
650			}
651		} else {
652			nlog(LOG_DEBUG, "nwamd_enm_check: %s is disabled",
653			    object->nwamd_object_name);
654			switch (state) {
655			case NWAM_STATE_ONLINE:
656				if (nwamd_enm_action(object->nwamd_object_name,
657				    NWAM_ACTION_DISABLE) != 0) {
658					nlog(LOG_ERR, "nwamd_enm_check: "
659					    "disable failed for enm %s",
660					    object->nwamd_object_name);
661				}
662				break;
663			case NWAM_STATE_MAINTENANCE:
664				/* Do nothing */
665				break;
666			case NWAM_STATE_DISABLED:
667				/* Do nothing */
668				break;
669			default:
670				nwamd_object_set_state(NWAM_OBJECT_TYPE_ENM,
671				    object->nwamd_object_name,
672				    NWAM_STATE_DISABLED,
673				    NWAM_AUX_STATE_MANUAL_DISABLE);
674				break;
675			}
676		}
677		break;
678
679	case NWAM_ACTIVATION_MODE_CONDITIONAL_ANY:
680	case NWAM_ACTIVATION_MODE_CONDITIONAL_ALL:
681		if (nwam_enm_get_prop_value(enmh,
682		    NWAM_ENM_PROP_CONDITIONS, &conditionval) != NWAM_SUCCESS) {
683			nlog(LOG_ERR, "nwamd_enm_check: could not retrieve "
684			    "condition value");
685			break;
686		}
687		if (nwam_value_get_string_array(conditionval,
688		    &conditions, &nelem) != NWAM_SUCCESS) {
689			nlog(LOG_ERR, "nwamd_enm_check: could not retrieve "
690			    "condition value");
691			nwam_value_free(conditionval);
692			break;
693		}
694		satisfied = nwamd_check_conditions((uint64_t)eactivation,
695		    conditions, nelem);
696
697		nlog(LOG_DEBUG, "nwamd_enm_check: conditions for enm %s "
698		    "%s satisfied", object->nwamd_object_name,
699		    satisfied ? "is" : "is not");
700		if (state != NWAM_STATE_ONLINE && satisfied) {
701			nwamd_object_set_state(NWAM_OBJECT_TYPE_ENM,
702			    object->nwamd_object_name,
703			    NWAM_STATE_OFFLINE_TO_ONLINE,
704			    NWAM_AUX_STATE_METHOD_RUNNING);
705		}
706		if (state == NWAM_STATE_ONLINE && !satisfied) {
707			nwamd_object_set_state(NWAM_OBJECT_TYPE_ENM,
708			    object->nwamd_object_name,
709			    NWAM_STATE_ONLINE_TO_OFFLINE,
710			    NWAM_AUX_STATE_CONDITIONS_NOT_MET);
711		}
712		nwam_value_free(conditionval);
713		break;
714
715	}
716	return (0);
717}
718
719void
720nwamd_enm_check_conditions(void)
721{
722	(void) nwamd_walk_objects(NWAM_OBJECT_TYPE_ENM, nwamd_enm_check, NULL);
723}
724
725int
726nwamd_enm_action(const char *enm, nwam_action_t action)
727{
728	nwamd_event_t event = nwamd_event_init_object_action
729	    (NWAM_OBJECT_TYPE_ENM, enm, NULL, action);
730	if (event == NULL)
731		return (1);
732	nwamd_event_enqueue(event);
733	return (0);
734}
735
736/*
737 * Event handling functions.
738 */
739
740/* Handle ENM initialization/refresh event */
741void
742nwamd_enm_handle_init_event(nwamd_event_t event)
743{
744	nwamd_object_t object;
745	nwam_enm_handle_t enmh;
746	nwam_error_t err;
747	boolean_t manual_disabled = B_FALSE;
748
749	if ((err = nwam_enm_read(event->event_object, 0, &enmh))
750	    != NWAM_SUCCESS) {
751		nlog(LOG_ERR, "nwamd_enm_handle_init_event: could not "
752		    "read object '%s': %s", event->event_object,
753		    nwam_strerror(err));
754		nwamd_event_do_not_send(event);
755		return;
756	}
757	if ((object = nwamd_object_find(NWAM_OBJECT_TYPE_ENM,
758	    event->event_object)) != NULL) {
759		nwam_enm_free(object->nwamd_object_handle);
760		object->nwamd_object_handle = enmh;
761	} else {
762		object = nwamd_object_init(NWAM_OBJECT_TYPE_ENM,
763		    event->event_object, enmh, NULL);
764		object->nwamd_object_state = NWAM_STATE_OFFLINE;
765		object->nwamd_object_aux_state =
766		    NWAM_AUX_STATE_CONDITIONS_NOT_MET;
767	}
768	/* (Re)set script time to now as the object has just been (re)read */
769	(void) gettimeofday(&object->nwamd_script_time, NULL);
770
771	manual_disabled = (enm_get_activation_mode(enmh) ==
772	    NWAM_ACTIVATION_MODE_MANUAL && !enm_is_enabled(enmh));
773
774	/*
775	 * If this ENM is ONLINE, and not manual and disabled (since in
776	 * that case it was online but we've just set enabled = false as part
777	 * of a disable action), then it is still active but refreshing.
778	 * Change states to re-activate itself.
779	 */
780	if (!manual_disabled &&
781	    object->nwamd_object_state == NWAM_STATE_ONLINE) {
782		nwamd_object_set_state(NWAM_OBJECT_TYPE_ENM,
783		    event->event_object, NWAM_STATE_OFFLINE_TO_ONLINE,
784		    NWAM_AUX_STATE_METHOD_RUNNING);
785	}
786	nwamd_object_release(object);
787}
788
789/* Handle ENM finish event */
790void
791nwamd_enm_handle_fini_event(nwamd_event_t event)
792{
793	nwamd_event_t state_event;
794
795	nlog(LOG_DEBUG, "nwamd_enm_handle_fini_event(%s)", event->event_object);
796
797	/*
798	 * Simulate a state event so that the state machine can correctly
799	 * deactivate the ENM and free up the handle.
800	 */
801	state_event = nwamd_event_init_object_state(NWAM_OBJECT_TYPE_ENM,
802	    event->event_object, NWAM_STATE_ONLINE_TO_OFFLINE,
803	    NWAM_AUX_STATE_UNINITIALIZED);
804	if (state_event == NULL) {
805		nwamd_event_do_not_send(event);
806		return;
807	}
808	nwamd_enm_handle_state_event(state_event);
809	nwamd_event_fini(state_event);
810	/*
811	 * Do not free the handle and object.
812	 * nwamd_enm_activate_deactivate_thread() and
813	 * nwamd_enm_deactivate() does this after running the stop script
814	 * and disabling the FMRI respectively.
815	 */
816}
817
818void
819nwamd_enm_handle_action_event(nwamd_event_t event)
820{
821	nwamd_object_t object;
822
823	switch (event->event_msg->nwe_data.nwe_object_action.nwe_action) {
824	case NWAM_ACTION_ENABLE:
825		object = nwamd_object_find(NWAM_OBJECT_TYPE_ENM,
826		    event->event_object);
827		if (object == NULL) {
828			nlog(LOG_ERR, "nwamd_enm_handle_action_event: "
829			    "could not find enm %s", event->event_object);
830			nwamd_event_do_not_send(event);
831			return;
832		}
833		if (object->nwamd_object_state == NWAM_STATE_ONLINE) {
834			nlog(LOG_DEBUG, "nwamd_enm_handle_action_event: "
835			    "enm %s already online, nothing to do",
836			    event->event_object);
837			nwamd_object_release(object);
838			return;
839		}
840		nwamd_object_release(object);
841
842		nwamd_object_set_state(NWAM_OBJECT_TYPE_ENM,
843		    event->event_object, NWAM_STATE_OFFLINE_TO_ONLINE,
844		    NWAM_AUX_STATE_METHOD_RUNNING);
845		break;
846	case NWAM_ACTION_DISABLE:
847		object = nwamd_object_find(NWAM_OBJECT_TYPE_ENM,
848		    event->event_object);
849		if (object == NULL) {
850			nlog(LOG_ERR, "nwamd_enm_handle_action_event: "
851			    "could not find enm %s", event->event_object);
852			nwamd_event_do_not_send(event);
853			return;
854		}
855		if (object->nwamd_object_state == NWAM_STATE_DISABLED) {
856			nlog(LOG_DEBUG, "nwamd_enm_handle_action_event: "
857			    "enm %s already disabled, nothing to do",
858			    event->event_object);
859			nwamd_object_release(object);
860			return;
861		}
862		nwamd_object_release(object);
863
864		nwamd_object_set_state(NWAM_OBJECT_TYPE_ENM,
865		    event->event_object, NWAM_STATE_ONLINE_TO_OFFLINE,
866		    NWAM_AUX_STATE_MANUAL_DISABLE);
867		break;
868	case NWAM_ACTION_ADD:
869	case NWAM_ACTION_REFRESH:
870		nwamd_enm_handle_init_event(event);
871		break;
872	case NWAM_ACTION_DESTROY:
873		nwamd_enm_handle_fini_event(event);
874		break;
875	default:
876		nlog(LOG_INFO, "nwam_enm_handle_action_event: "
877		    "unexpected action");
878		nwamd_event_do_not_send(event);
879		break;
880	}
881}
882
883void
884nwamd_enm_handle_state_event(nwamd_event_t event)
885{
886	nwamd_object_t object;
887	nwam_state_t new_state;
888	nwam_aux_state_t new_aux_state;
889
890	if ((object = nwamd_object_find(NWAM_OBJECT_TYPE_ENM,
891	    event->event_object)) == NULL) {
892		nlog(LOG_INFO, "nwamd_enm_handle_state_event: "
893		    "state event for nonexistent enm %s", event->event_object);
894		nwamd_event_do_not_send(event);
895		return;
896	}
897	new_state = event->event_msg->nwe_data.nwe_object_state.nwe_state;
898	new_aux_state =
899	    event->event_msg->nwe_data.nwe_object_state.nwe_aux_state;
900
901	if (new_state == object->nwamd_object_state &&
902	    new_aux_state == object->nwamd_object_aux_state) {
903		nlog(LOG_DEBUG, "nwamd_enm_handle_state_event: "
904		    "enm %s already in state (%s , %s)",
905		    object->nwamd_object_name, nwam_state_to_string(new_state),
906		    nwam_aux_state_to_string(new_aux_state));
907		nwamd_object_release(object);
908		return;
909	}
910
911	object->nwamd_object_state = new_state;
912	object->nwamd_object_aux_state = new_aux_state;
913
914	nlog(LOG_DEBUG, "nwamd_enm_handle_state_event: changing state for enm "
915	    "%s to (%s , %s)", object->nwamd_object_name,
916	    nwam_state_to_string(object->nwamd_object_state),
917	    nwam_aux_state_to_string(object->nwamd_object_aux_state));
918
919	nwamd_object_release(object);
920
921	/*
922	 * State machine for ENMs.
923	 */
924	switch (new_state) {
925	case NWAM_STATE_OFFLINE_TO_ONLINE:
926		nwamd_enm_activate(event->event_object);
927		break;
928	case NWAM_STATE_ONLINE_TO_OFFLINE:
929		nwamd_enm_deactivate(event->event_object);
930		break;
931	case NWAM_STATE_DISABLED:
932	case NWAM_STATE_OFFLINE:
933	case NWAM_STATE_UNINITIALIZED:
934	case NWAM_STATE_MAINTENANCE:
935	case NWAM_STATE_DEGRADED:
936	default:
937		/* do nothing */
938		break;
939	}
940}
941