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