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