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