xref: /illumos-gate/usr/src/cmd/cmd-inet/lib/nwamd/enm.c (revision 0749e8de8370b977962d1cbaa31a8ebbaf755a01)
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 
57 static int
58 enm_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  */
82 void
83 nwamd_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  */
93 void
94 nwamd_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 
101 static boolean_t
102 enm_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 
120 static int64_t
121 enm_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 
145 static void *
146 nwamd_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 
230 err:
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  */
303 boolean_t
304 nwamd_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  */
338 static void
339 nwamd_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. */
445 static void
446 nwamd_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 	}
537 done:
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 */
582 static int
583 nwamd_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 
691 void
692 nwamd_enm_check_conditions(void)
693 {
694 	(void) nwamd_walk_objects(NWAM_OBJECT_TYPE_ENM, nwamd_enm_check, NULL);
695 }
696 
697 int
698 nwamd_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 */
713 void
714 nwamd_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 */
759 void
760 nwamd_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 
787 void
788 nwamd_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 
852 void
853 nwamd_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