xref: /illumos-gate/usr/src/cmd/cmd-inet/lib/nwamd/enm.c (revision 45ede40b2394db7967e59f19288fae9b62efd4aa)
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 
55 static int
56 enm_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  */
80 void
81 nwamd_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  */
91 void
92 nwamd_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 
99 static boolean_t
100 enm_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 
118 static int64_t
119 enm_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 
143 static void *
144 nwamd_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 
257 err:
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 
319 done:
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  */
333 boolean_t
334 nwamd_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  */
368 static void
369 nwamd_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. */
475 static void
476 nwamd_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 	}
566 done:
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 */
611 static int
612 nwamd_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 
719 void
720 nwamd_enm_check_conditions(void)
721 {
722 	(void) nwamd_walk_objects(NWAM_OBJECT_TYPE_ENM, nwamd_enm_check, NULL);
723 }
724 
725 int
726 nwamd_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 */
741 void
742 nwamd_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 */
790 void
791 nwamd_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 
818 void
819 nwamd_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 
883 void
884 nwamd_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