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
enm_create_init_fini_event(nwam_enm_handle_t enmh,void * data)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
nwamd_init_enms(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
nwamd_fini_enms(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
enm_is_enabled(nwam_enm_handle_t enmh)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
enm_get_activation_mode(nwam_enm_handle_t enmh)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 *
nwamd_enm_activate_deactivate_thread(void * arg)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
nwamd_enm_run_method(nwamd_object_t object)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
nwamd_enm_activate(const char * object_name)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
nwamd_enm_deactivate(const char * object_name)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
nwamd_enm_check(nwamd_object_t object,void * data)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
nwamd_enm_check_conditions(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
nwamd_enm_action(const char * enm,nwam_action_t action)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
nwamd_enm_handle_init_event(nwamd_event_t event)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
nwamd_enm_handle_fini_event(nwamd_event_t event)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
nwamd_enm_handle_action_event(nwamd_event_t event)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
nwamd_enm_handle_state_event(nwamd_event_t event)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