enm.c revision 11767:8f30d0e611c6
155955Srgrimes/*
255955Srgrimes * CDDL HEADER START
339211Sgibbs *
4119071Sobrien * The contents of this file are subject to the terms of the
5112288Sphk * Common Development and Distribution License (the "License").
6195767Skensmith * You may not use this file except in compliance with the License.
739211Sgibbs *
855955Srgrimes * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
939211Sgibbs * or http://www.opensolaris.org/os/licensing.
10125503Sru * See the License for the specific language governing permissions
11125503Sru * and limitations under the License.
1281133Stmm *
1374870Sru * When distributing Covered Code, include this CDDL HEADER in each
1439211Sgibbs * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1581133Stmm * If applicable, add the following below this CDDL HEADER, with the
1681133Stmm * fields enclosed by brackets "[]" replaced with your own identifying
1781133Stmm * information: Portions Copyright [yyyy] [name of copyright owner]
1881133Stmm *
1981133Stmm * CDDL HEADER END
2081133Stmm */
2181133Stmm
2281133Stmm/*
2381133Stmm * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
2442330Sobrien * Use is subject to license terms.
2542330Sobrien */
2642330Sobrien
2742330Sobrien#include <arpa/inet.h>
2842330Sobrien#include <errno.h>
2942330Sobrien#include <inet/ip.h>
3042330Sobrien#include <inetcfg.h>
3142330Sobrien#include <libdladm.h>
3242330Sobrien#include <libdllink.h>
3342330Sobrien#include <libdlwlan.h>
3476812Sru#include <libscf.h>
3539211Sgibbs#include <netinet/in.h>
36201381Sed#include <netdb.h>
3783868Sken#include <stdio.h>
3839211Sgibbs#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;
155	boolean_t going_online, disable_succeeded = B_FALSE;
156	int ret;
157
158	object = nwamd_object_find(NWAM_OBJECT_TYPE_ENM, object_name);
159	if (object == NULL) {
160		nlog(LOG_ERR, "nwamd_enm_activate_deactivate_thread: "
161		    "could not find ENM %s", object_name);
162		free(object_name);
163		return (NULL);
164	}
165	/* object_name was malloc() before this thread was created, free() it */
166	free(object_name);
167
168	enmh = object->nwamd_object_handle;
169
170	going_online =
171	    (object->nwamd_object_state == NWAM_STATE_OFFLINE_TO_ONLINE);
172	/*
173	 * We're starting if current state is offline* and stopping otherwise.
174	 */
175	if (nwam_enm_get_prop_value(enmh,
176	    going_online ? NWAM_ENM_PROP_START : NWAM_ENM_PROP_STOP,
177	    &scriptval) != NWAM_SUCCESS ||
178	    nwam_value_get_string(scriptval, &script) != NWAM_SUCCESS) {
179		/*
180		 * If we're stopping, it's not an error for no script to
181		 * be specified.
182		 */
183		nlog(going_online ? LOG_ERR : LOG_DEBUG,
184		    "nwamd_enm_activate_deactivate_thread: "
185		    "no script specified for enm %s",
186		    object->nwamd_object_name);
187		if (going_online) {
188			state = NWAM_STATE_MAINTENANCE;
189			aux_state = NWAM_AUX_STATE_METHOD_MISSING;
190		} else {
191			disable_succeeded = B_TRUE;
192		}
193	} else {
194		char *copy = NULL, *lasts;
195		const char **newargv, **argv = NULL;
196		int i = 0;
197
198		nlog(LOG_DEBUG, "nwamd_enm_activate_deactivate_thread: "
199		    "running script %s for ENM %s", script,
200		    object->nwamd_object_name);
201
202		/*
203		 * The script may take a number of arguments. We need to
204		 * create a string array consisting of the wrapper command
205		 * (ctrun), ENM script name, arguments and NULL array
206		 * terminator.  Start with an array of size equal to the
207		 * string length (since the number of arguments will always
208		 * be less than this) and shrink array to the actual number
209		 * of arguments when we have parsed the string.
210		 */
211		if ((copy = strdup(script)) == NULL ||
212		    (argv = calloc(strlen(script), sizeof (char *))) == NULL) {
213			ret = 1;
214			goto err;
215		}
216		argv[i++] = CTRUN;
217		argv[i++] = strtok_r(copy, " ", &lasts);
218		if (argv[1] == NULL) {
219			ret = 1;
220			goto err;
221		}
222
223		for (; (argv[i] = strtok_r(NULL, " ", &lasts)) != NULL; i++) {}
224
225		newargv = realloc(argv, (i + 1) * sizeof (char *));
226		argv = newargv;
227
228		ret = nwamd_start_childv(CTRUN, argv);
229
230err:
231		/*
232		 * If script execution fails and we're not destroying the
233		 * object, go to maintenance.
234		 */
235		if (ret != 0) {
236			nlog(LOG_ERR, "nwamd_enm_activate_deactivate_thread: "
237			    "execution of '%s' failed for ENM %s",
238			    script, object->nwamd_object_name);
239			if (object->nwamd_object_aux_state !=
240			    NWAM_AUX_STATE_UNINITIALIZED) {
241				state = NWAM_STATE_MAINTENANCE;
242				aux_state = NWAM_AUX_STATE_METHOD_FAILED;
243			} else {
244				state = NWAM_STATE_UNINITIALIZED;
245				aux_state = NWAM_AUX_STATE_UNINITIALIZED;
246			}
247		} else {
248			if (going_online) {
249				state = NWAM_STATE_ONLINE;
250				aux_state = NWAM_AUX_STATE_ACTIVE;
251			} else {
252				disable_succeeded = B_TRUE;
253			}
254		}
255		free(argv);
256		free(copy);
257	}
258	nwam_value_free(scriptval);
259
260	if (disable_succeeded) {
261		/*
262		 * If aux state is "manual disable", we know
263		 * this was a disable request, otherwise it was
264		 * _fini request or a condition satisfaction
265		 * failure.
266		 */
267		switch (object->nwamd_object_aux_state) {
268		case NWAM_AUX_STATE_MANUAL_DISABLE:
269			state = NWAM_STATE_DISABLED;
270			aux_state = NWAM_AUX_STATE_MANUAL_DISABLE;
271			break;
272		case NWAM_AUX_STATE_UNINITIALIZED:
273			state = NWAM_STATE_UNINITIALIZED;
274			aux_state = NWAM_AUX_STATE_UNINITIALIZED;
275			break;
276		default:
277			state = NWAM_STATE_OFFLINE;
278			aux_state = NWAM_AUX_STATE_CONDITIONS_NOT_MET;
279			break;
280		}
281	}
282
283	/* If state/aux state are uninitialized/unintialized, destroy the ENM */
284	if (state == NWAM_STATE_UNINITIALIZED &&
285	    aux_state == NWAM_AUX_STATE_UNINITIALIZED) {
286		object->nwamd_object_state = state;
287		object->nwamd_object_aux_state = aux_state;
288		(void) nwamd_object_release_and_destroy_after_preserve(object);
289	} else {
290		nwamd_object_set_state(NWAM_OBJECT_TYPE_ENM,
291		    object->nwamd_object_name, state, aux_state);
292		(void) nwamd_object_release_after_preserve(object);
293	}
294
295	return (NULL);
296}
297
298/*
299 * Run start/stop method for ENM in a separate thread.  The object lock is not
300 * held across threads, so we duplicate the object name for the method
301 * execution thread.  Returns true if thread is successfully launched.
302 */
303boolean_t
304nwamd_enm_run_method(nwamd_object_t object)
305{
306	char *name;
307	pthread_t script;
308
309	/*
310	 * Launch separate thread to wait for execution of script
311	 * to complete.  Do not hold object lock across threads.
312	 */
313	if ((name = strdup(object->nwamd_object_name)) == NULL) {
314		nlog(LOG_ERR, "nwamd_enm_run_method: %s: out of memory",
315		    object->nwamd_object_name);
316		return (B_FALSE);
317	}
318
319	if (pthread_create(&script, NULL,
320	    nwamd_enm_activate_deactivate_thread, name) != 0) {
321		nlog(LOG_ERR, "nwamd_enm_run_method: could not create "
322		    "enm script thread for %s", name);
323		free(name);
324		return (B_FALSE);
325	}
326	/* "name" will be freed by the newly-created thread. */
327
328	/* detach thread so that it doesn't become a zombie */
329	(void) pthread_detach(script);
330
331	return (B_TRUE);
332}
333
334/*
335 * Activate the ENM, either in response to an enable event or conditions
336 * being satisfied.
337 */
338static void
339nwamd_enm_activate(const char *object_name)
340{
341	nwamd_object_t object;
342	nwam_value_t fmrival;
343	char *fmri, *smf_state;
344	int ret;
345	nwam_enm_handle_t enmh;
346	nwam_state_t state;
347	nwam_aux_state_t aux_state;
348	nwam_error_t err;
349	boolean_t ran_method = B_FALSE;
350
351	object = nwamd_object_find(NWAM_OBJECT_TYPE_ENM, object_name);
352	if (object == NULL) {
353		nlog(LOG_ERR, "nwamd_enm_activate: could not find ENM %s",
354		    object_name);
355		return;
356	}
357	state = object->nwamd_object_state;
358	aux_state = object->nwamd_object_aux_state;
359	enmh = object->nwamd_object_handle;
360
361	nlog(LOG_DEBUG, "nwamd_enm_activate: activating ENM %s",
362	    object->nwamd_object_name);
363
364	err = nwam_enm_get_prop_value(enmh, NWAM_ENM_PROP_FMRI, &fmrival);
365	switch (err) {
366	case NWAM_SUCCESS:
367
368		if (nwam_value_get_string(fmrival, &fmri) != NWAM_SUCCESS) {
369			nlog(LOG_ERR, "nwamd_enm_activate: could not retrieve "
370			    "FMRI string for ENM %s",
371			    object->nwamd_object_name);
372			nwam_value_free(fmrival);
373			state = NWAM_STATE_MAINTENANCE;
374			aux_state = NWAM_AUX_STATE_INVALID_CONFIG;
375			break;
376		}
377
378		if ((smf_state = smf_get_state(fmri)) == NULL) {
379			nlog(LOG_ERR, "nwamd_enm_activate: invalid FMRI %s "
380			    "for ENM %s", fmri, object->nwamd_object_name);
381			nwam_value_free(fmrival);
382			state = NWAM_STATE_MAINTENANCE;
383			aux_state = NWAM_AUX_STATE_INVALID_CONFIG;
384			break;
385		}
386
387		nlog(LOG_DEBUG, "nwamd_enm_activate: activating %s for ENM %s",
388		    fmri, object->nwamd_object_name);
389
390		if (strcmp(smf_state, SCF_STATE_STRING_ONLINE) == 0)
391			ret = smf_restart_instance(fmri);
392		else if (strcmp(smf_state, SCF_STATE_STRING_OFFLINE) == 0)
393			ret = smf_restart_instance(fmri);
394		else if (strcmp(smf_state, SCF_STATE_STRING_DISABLED) == 0)
395			ret = smf_enable_instance(fmri, SMF_TEMPORARY);
396		else
397			ret = smf_restore_instance(fmri);
398
399		if (ret == 0) {
400			state = NWAM_STATE_ONLINE;
401			aux_state = NWAM_AUX_STATE_ACTIVE;
402		} else {
403			nlog(LOG_ERR, "nwamd_enm_activate: failed to enable "
404			    "FMRI %s for ENM %s", fmri,
405			    object->nwamd_object_name);
406			state = NWAM_STATE_MAINTENANCE;
407			aux_state = NWAM_AUX_STATE_METHOD_FAILED;
408		}
409		free(smf_state);
410		nwam_value_free(fmrival);
411		break;
412	default:
413		/*
414		 * Must be a method-based ENM with start (and stop) script(s).
415		 */
416		if (!nwamd_enm_run_method(object)) {
417			/* Could not launch method execution thread */
418			state = NWAM_STATE_MAINTENANCE;
419			aux_state = NWAM_AUX_STATE_METHOD_FAILED;
420		} else {
421			ran_method = B_TRUE;
422		}
423		break;
424	}
425
426	if (state != object->nwamd_object_state ||
427	    aux_state != object->nwamd_object_aux_state) {
428		nwamd_object_set_state(NWAM_OBJECT_TYPE_ENM,
429		    object->nwamd_object_name, state, aux_state);
430	}
431
432	/*
433	 * If the method thread was created, we drop the lock to the ENM
434	 * object without decreasing the reference count, ensuring it will not
435	 * be destroyed until method execution has completed.
436	 */
437	if (ran_method) {
438		nwamd_object_release_and_preserve(object);
439	} else {
440		nwamd_object_release(object);
441	}
442}
443
444/* Deactivates the ENM. */
445static void
446nwamd_enm_deactivate(const char *object_name)
447{
448	nwamd_object_t object;
449	nwam_enm_handle_t enmh;
450	nwam_value_t fmrival;
451	char *fmri, *smf_state;
452	int ret;
453	nwam_state_t state;
454	nwam_aux_state_t aux_state;
455	boolean_t destroying = B_FALSE;
456
457	object = nwamd_object_find(NWAM_OBJECT_TYPE_ENM, object_name);
458	if (object == NULL) {
459		nlog(LOG_ERR, "nwamd_enm_deactivate: could not find ENM %s",
460		    object_name);
461		return;
462	}
463
464	state = object->nwamd_object_state;
465	aux_state = object->nwamd_object_aux_state;
466	enmh = object->nwamd_object_handle;
467	state = object->nwamd_object_state;
468	/* If destroying, we don't care about method failure/config err */
469	destroying = (aux_state == NWAM_AUX_STATE_UNINITIALIZED);
470
471	nlog(LOG_DEBUG, "nwamd_enm_deactivate: deactivating enm %s",
472	    object->nwamd_object_name);
473
474	if (nwam_enm_get_prop_value(enmh, NWAM_ENM_PROP_FMRI, &fmrival)
475	    != NWAM_SUCCESS) {
476		/*
477		 * Must be a method-based ENM with start (and stop) script(s).
478		 * Script execution thread will take care of the rest.
479		 * If the method thread was created, we drop the lock to the ENM
480		 * object without decreasing the reference count, ensuring it
481		 * will not be destroyed until method execution has completed.
482		 */
483		if (nwamd_enm_run_method(object)) {
484			nwamd_object_release_and_preserve(object);
485			return;
486		}
487		/* Could not launch method execution thread */
488		if (!destroying) {
489			state = NWAM_STATE_MAINTENANCE;
490			aux_state = NWAM_AUX_STATE_METHOD_FAILED;
491		}
492	} else {
493		if (nwam_value_get_string(fmrival, &fmri) != NWAM_SUCCESS) {
494			nlog(LOG_ERR,
495			    "nwamd_enm_deactivate: could not retrieve "
496			    "FMRI string for ENM %s",
497			    object->nwamd_object_name);
498			if (!destroying) {
499				state = NWAM_STATE_MAINTENANCE;
500				aux_state = NWAM_AUX_STATE_INVALID_CONFIG;
501			}
502		} else {
503			if ((smf_state = smf_get_state(fmri)) == NULL) {
504				nlog(LOG_ERR, "nwamd_enm_deactivate: invalid "
505				    "FMRI %s for ENM %s", fmri,
506				    object->nwamd_object_name);
507				nwam_value_free(fmrival);
508				if (!destroying) {
509					state = NWAM_STATE_MAINTENANCE;
510					aux_state =
511					    NWAM_AUX_STATE_INVALID_CONFIG;
512				}
513				goto done;
514			}
515			free(smf_state);
516
517			nlog(LOG_DEBUG, "nwamd_enm_deactivate: deactivating %s "
518			    "for ENM %s", fmri, object->nwamd_object_name);
519
520			ret = smf_disable_instance(fmri, SMF_TEMPORARY);
521
522			if (ret != 0) {
523				nlog(LOG_ERR, "nwamd_enm_deactivate: "
524				    "smf_disable_instance(%s) failed for "
525				    "ENM %s: %s", fmri,
526				    object->nwamd_object_name,
527				    scf_strerror(scf_error()));
528				if (!destroying) {
529					state = NWAM_STATE_MAINTENANCE;
530					aux_state =
531					    NWAM_AUX_STATE_METHOD_FAILED;
532				}
533			}
534		}
535		nwam_value_free(fmrival);
536	}
537done:
538	if (state == object->nwamd_object_state &&
539	    aux_state == object->nwamd_object_aux_state) {
540		/*
541		 * If aux state is "manual disable", we know
542		 * this was a disable request, otherwise it was
543		 * a _fini request or a condition satisfaction
544		 * failure.
545		 */
546		switch (object->nwamd_object_aux_state) {
547		case NWAM_AUX_STATE_MANUAL_DISABLE:
548			state = NWAM_STATE_DISABLED;
549			aux_state = NWAM_AUX_STATE_MANUAL_DISABLE;
550			break;
551		case NWAM_AUX_STATE_UNINITIALIZED:
552			state = NWAM_STATE_UNINITIALIZED;
553			aux_state = NWAM_AUX_STATE_UNINITIALIZED;
554			break;
555		default:
556			state = NWAM_STATE_OFFLINE;
557			aux_state = NWAM_AUX_STATE_CONDITIONS_NOT_MET;
558			break;
559		}
560	}
561
562	/* Only change state if we aren't destroying the ENM */
563	if (!destroying && (state != object->nwamd_object_state ||
564	    aux_state != object->nwamd_object_aux_state)) {
565		nwamd_object_set_state(NWAM_OBJECT_TYPE_ENM,
566		    object->nwamd_object_name, state, aux_state);
567	}
568
569	/* If state/aux state are uninitialized/unintialized, destroy the ENM */
570	if (state == NWAM_STATE_UNINITIALIZED &&
571	    aux_state == NWAM_AUX_STATE_UNINITIALIZED) {
572		(void) nwamd_object_release_and_destroy(object);
573	} else {
574		(void) nwamd_object_release(object);
575	}
576}
577
578/*
579 * Determine whether an ENM should be (de)activated.
580 */
581/* ARGSUSED1 */
582static int
583nwamd_enm_check(nwamd_object_t object, void *data)
584{
585	nwam_enm_handle_t enmh;
586	nwam_value_t conditionval;
587	int64_t eactivation;
588	boolean_t enabled, satisfied;
589	char **conditions;
590	nwam_state_t state;
591	uint_t nelem;
592
593	state = object->nwamd_object_state;
594
595	enmh = object->nwamd_object_handle;
596
597	eactivation = enm_get_activation_mode(enmh);
598	if (eactivation == -1)
599		return (0);
600
601	switch (eactivation) {
602	case NWAM_ACTIVATION_MODE_MANUAL:
603		enabled = enm_is_enabled(enmh);
604
605		if (enabled) {
606			nlog(LOG_DEBUG, "nwamd_enm_check: %s is enabled",
607			    object->nwamd_object_name);
608			switch (state) {
609			case NWAM_STATE_ONLINE:
610			case NWAM_STATE_MAINTENANCE:
611				/* Do nothing */
612				break;
613			default:
614				if (nwamd_enm_action(object->nwamd_object_name,
615				    NWAM_ACTION_ENABLE) != 0) {
616					nlog(LOG_ERR,
617					    "nwamd_enm_check: enable failed "
618					    "for enm %s",
619					    object->nwamd_object_name);
620				}
621				break;
622			}
623		} else {
624			nlog(LOG_DEBUG, "nwamd_enm_check: %s is disabled",
625			    object->nwamd_object_name);
626			switch (state) {
627			case NWAM_STATE_ONLINE:
628				if (nwamd_enm_action(object->nwamd_object_name,
629				    NWAM_ACTION_DISABLE) != 0) {
630					nlog(LOG_ERR, "nwamd_enm_check: "
631					    "disable failed for enm %s",
632					    object->nwamd_object_name);
633				}
634				break;
635			case NWAM_STATE_MAINTENANCE:
636				/* Do nothing */
637				break;
638			case NWAM_STATE_DISABLED:
639				/* Do nothing */
640				break;
641			default:
642				nwamd_object_set_state(NWAM_OBJECT_TYPE_ENM,
643				    object->nwamd_object_name,
644				    NWAM_STATE_DISABLED,
645				    NWAM_AUX_STATE_MANUAL_DISABLE);
646				break;
647			}
648		}
649		break;
650
651	case NWAM_ACTIVATION_MODE_CONDITIONAL_ANY:
652	case NWAM_ACTIVATION_MODE_CONDITIONAL_ALL:
653		if (nwam_enm_get_prop_value(enmh,
654		    NWAM_ENM_PROP_CONDITIONS, &conditionval) != NWAM_SUCCESS) {
655			nlog(LOG_ERR, "nwamd_enm_check: could not retrieve "
656			    "condition value");
657			break;
658		}
659		if (nwam_value_get_string_array(conditionval,
660		    &conditions, &nelem) != NWAM_SUCCESS) {
661			nlog(LOG_ERR, "nwamd_enm_check: could not retrieve "
662			    "condition value");
663			nwam_value_free(conditionval);
664			break;
665		}
666		satisfied = nwamd_check_conditions((uint64_t)eactivation,
667		    conditions, nelem);
668
669		nlog(LOG_DEBUG, "nwamd_enm_check: conditions for enm %s "
670		    "%s satisfied", object->nwamd_object_name,
671		    satisfied ? "is" : "is not");
672		if (state != NWAM_STATE_ONLINE && satisfied) {
673			nwamd_object_set_state(NWAM_OBJECT_TYPE_ENM,
674			    object->nwamd_object_name,
675			    NWAM_STATE_OFFLINE_TO_ONLINE,
676			    NWAM_AUX_STATE_METHOD_RUNNING);
677		}
678		if (state == NWAM_STATE_ONLINE && !satisfied) {
679			nwamd_object_set_state(NWAM_OBJECT_TYPE_ENM,
680			    object->nwamd_object_name,
681			    NWAM_STATE_ONLINE_TO_OFFLINE,
682			    NWAM_AUX_STATE_CONDITIONS_NOT_MET);
683		}
684		nwam_value_free(conditionval);
685		break;
686
687	}
688	return (0);
689}
690
691void
692nwamd_enm_check_conditions(void)
693{
694	(void) nwamd_walk_objects(NWAM_OBJECT_TYPE_ENM, nwamd_enm_check, NULL);
695}
696
697int
698nwamd_enm_action(const char *enm, nwam_action_t action)
699{
700	nwamd_event_t event = nwamd_event_init_object_action
701	    (NWAM_OBJECT_TYPE_ENM, enm, NULL, action);
702	if (event == NULL)
703		return (1);
704	nwamd_event_enqueue(event);
705	return (0);
706}
707
708/*
709 * Event handling functions.
710 */
711
712/* Handle ENM initialization/refresh event */
713void
714nwamd_enm_handle_init_event(nwamd_event_t event)
715{
716	nwamd_object_t object;
717	nwam_enm_handle_t enmh;
718	nwam_error_t err;
719	boolean_t manual_disabled = B_FALSE;
720
721	if ((err = nwam_enm_read(event->event_object, 0, &enmh))
722	    != NWAM_SUCCESS) {
723		nlog(LOG_ERR, "nwamd_enm_handle_init_event: could not "
724		    "read object '%s': %s", event->event_object,
725		    nwam_strerror(err));
726		nwamd_event_do_not_send(event);
727		return;
728	}
729	if ((object = nwamd_object_find(NWAM_OBJECT_TYPE_ENM,
730	    event->event_object)) != NULL) {
731		nwam_enm_free(object->nwamd_object_handle);
732		object->nwamd_object_handle = enmh;
733	} else {
734		object = nwamd_object_init(NWAM_OBJECT_TYPE_ENM,
735		    event->event_object, enmh, NULL);
736		object->nwamd_object_state = NWAM_STATE_OFFLINE;
737		object->nwamd_object_aux_state =
738		    NWAM_AUX_STATE_CONDITIONS_NOT_MET;
739	}
740	manual_disabled = (enm_get_activation_mode(enmh) ==
741	    NWAM_ACTIVATION_MODE_MANUAL && !enm_is_enabled(enmh));
742
743	/*
744	 * If this ENM is ONLINE, and not manual and disabled (since in
745	 * that case it was online but we've just set enabled = false as part
746	 * of a disable action), then it is still active but refreshing.
747	 * Change states to re-activate itself.
748	 */
749	if (!manual_disabled &&
750	    object->nwamd_object_state == NWAM_STATE_ONLINE) {
751		nwamd_object_set_state(NWAM_OBJECT_TYPE_ENM,
752		    event->event_object, NWAM_STATE_OFFLINE_TO_ONLINE,
753		    NWAM_AUX_STATE_METHOD_RUNNING);
754	}
755	nwamd_object_release(object);
756}
757
758/* Handle ENM finish event */
759void
760nwamd_enm_handle_fini_event(nwamd_event_t event)
761{
762	nwamd_event_t state_event;
763
764	nlog(LOG_DEBUG, "nwamd_enm_handle_fini_event(%s)", event->event_object);
765
766	/*
767	 * Simulate a state event so that the state machine can correctly
768	 * deactivate the ENM and free up the handle.
769	 */
770	state_event = nwamd_event_init_object_state(NWAM_OBJECT_TYPE_ENM,
771	    event->event_object, NWAM_STATE_ONLINE_TO_OFFLINE,
772	    NWAM_AUX_STATE_UNINITIALIZED);
773	if (state_event == NULL) {
774		nwamd_event_do_not_send(event);
775		return;
776	}
777	nwamd_enm_handle_state_event(state_event);
778	nwamd_event_fini(state_event);
779	/*
780	 * Do not free the handle and object.
781	 * nwamd_enm_activate_deactivate_thread() and
782	 * nwamd_enm_deactivate() does this after running the stop script
783	 * and disabling the FMRI respectively.
784	 */
785}
786
787void
788nwamd_enm_handle_action_event(nwamd_event_t event)
789{
790	nwamd_object_t object;
791
792	switch (event->event_msg->nwe_data.nwe_object_action.nwe_action) {
793	case NWAM_ACTION_ENABLE:
794		object = nwamd_object_find(NWAM_OBJECT_TYPE_ENM,
795		    event->event_object);
796		if (object == NULL) {
797			nlog(LOG_ERR, "nwamd_enm_handle_action_event: "
798			    "could not find enm %s", event->event_object);
799			nwamd_event_do_not_send(event);
800			return;
801		}
802		if (object->nwamd_object_state == NWAM_STATE_ONLINE) {
803			nlog(LOG_DEBUG, "nwamd_enm_handle_action_event: "
804			    "enm %s already online, nothing to do",
805			    event->event_object);
806			nwamd_object_release(object);
807			return;
808		}
809		nwamd_object_release(object);
810
811		nwamd_object_set_state(NWAM_OBJECT_TYPE_ENM,
812		    event->event_object, NWAM_STATE_OFFLINE_TO_ONLINE,
813		    NWAM_AUX_STATE_METHOD_RUNNING);
814		break;
815	case NWAM_ACTION_DISABLE:
816		object = nwamd_object_find(NWAM_OBJECT_TYPE_ENM,
817		    event->event_object);
818		if (object == NULL) {
819			nlog(LOG_ERR, "nwamd_enm_handle_action_event: "
820			    "could not find enm %s", event->event_object);
821			nwamd_event_do_not_send(event);
822			return;
823		}
824		if (object->nwamd_object_state == NWAM_STATE_DISABLED) {
825			nlog(LOG_DEBUG, "nwamd_enm_handle_action_event: "
826			    "enm %s already disabled, nothing to do",
827			    event->event_object);
828			nwamd_object_release(object);
829			return;
830		}
831		nwamd_object_release(object);
832
833		nwamd_object_set_state(NWAM_OBJECT_TYPE_ENM,
834		    event->event_object, NWAM_STATE_ONLINE_TO_OFFLINE,
835		    NWAM_AUX_STATE_MANUAL_DISABLE);
836		break;
837	case NWAM_ACTION_ADD:
838	case NWAM_ACTION_REFRESH:
839		nwamd_enm_handle_init_event(event);
840		break;
841	case NWAM_ACTION_DESTROY:
842		nwamd_enm_handle_fini_event(event);
843		break;
844	default:
845		nlog(LOG_INFO, "nwam_enm_handle_action_event: "
846		    "unexpected action");
847		nwamd_event_do_not_send(event);
848		break;
849	}
850}
851
852void
853nwamd_enm_handle_state_event(nwamd_event_t event)
854{
855	nwamd_object_t object;
856	nwam_state_t new_state;
857	nwam_aux_state_t new_aux_state;
858
859	if ((object = nwamd_object_find(NWAM_OBJECT_TYPE_ENM,
860	    event->event_object)) == NULL) {
861		nlog(LOG_ERR, "nwamd_enm_handle_state_event: "
862		    "state event for nonexistent ENM %s", event->event_object);
863		nwamd_event_do_not_send(event);
864		return;
865	}
866	new_state = event->event_msg->nwe_data.nwe_object_state.nwe_state;
867	new_aux_state =
868	    event->event_msg->nwe_data.nwe_object_state.nwe_aux_state;
869
870	if (new_state == object->nwamd_object_state &&
871	    new_aux_state == object->nwamd_object_aux_state) {
872		nlog(LOG_DEBUG, "nwamd_enm_handle_state_event: "
873		    "ENM %s already in state (%s , %s)",
874		    object->nwamd_object_name, nwam_state_to_string(new_state),
875		    nwam_aux_state_to_string(new_aux_state));
876		nwamd_object_release(object);
877		return;
878	}
879
880	object->nwamd_object_state = new_state;
881	object->nwamd_object_aux_state = new_aux_state;
882
883	nlog(LOG_DEBUG, "nwamd_enm_handle_state_event: changing state for ENM "
884	    "%s to (%s , %s)", object->nwamd_object_name,
885	    nwam_state_to_string(object->nwamd_object_state),
886	    nwam_aux_state_to_string(object->nwamd_object_aux_state));
887
888	nwamd_object_release(object);
889
890	/*
891	 * State machine for ENMs.
892	 */
893	switch (new_state) {
894	case NWAM_STATE_OFFLINE_TO_ONLINE:
895		nwamd_enm_activate(event->event_object);
896		break;
897	case NWAM_STATE_ONLINE_TO_OFFLINE:
898		nwamd_enm_deactivate(event->event_object);
899		break;
900	case NWAM_STATE_DISABLED:
901	case NWAM_STATE_OFFLINE:
902	case NWAM_STATE_UNINITIALIZED:
903	case NWAM_STATE_MAINTENANCE:
904	case NWAM_STATE_DEGRADED:
905	default:
906		/* do nothing */
907		break;
908	}
909}
910