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