1/* 2 * Copyright (c) 2007-2014 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24/* 25 * Modification History 26 * 27 * October 24, 2007 Allan Nathanson <ajn@apple.com> 28 * - initial revision 29 */ 30 31#include <stdio.h> 32#include <stdlib.h> 33#include <unistd.h> 34#include <asl.h> 35#include <CoreFoundation/CoreFoundation.h> 36#include <SystemConfiguration/SystemConfiguration.h> 37#include <SystemConfiguration/SCPrivate.h> 38#include <IOKit/IOKitLib.h> 39#include <IOKit/IOMessage.h> 40#include <ApplicationServices/ApplicationServices.h> 41#include "UserEventAgentInterface.h" 42 43#define MY_BUNDLE_ID "com.apple.SystemConfiguration.SCMonitor" 44#define MY_ICON_PATH "/System/Library/PreferencePanes/Network.prefPane/Contents/Resources/Network.icns" 45 46#define NETWORK_PREF_APP "/System/Library/PreferencePanes/Network.prefPane" 47#define NETWORK_PREF_CMD "New Interface" 48 49/* 50 * The following keys/values control the actions taken when a new interface 51 * has been detected and whether we interact with the user. 52 * 53 * The keys/values can be provided globally (in SCMonitor.plugin's Info.plist 54 * file) or per-inteface in the IORegistry. 55 * 56 * For the "New Interface Detected Action" key we define the following [CFString] 57 * values : 58 * 59 * "None" No action, ignore this interface. 60 * "Prompt" Post a "new interface detected" notification to the user. 61 * "Configure" Automatically configure this interface without any 62 * intervention. 63 * 64 * Note: automatic configuration may not be possible if the logged in user 65 * is NOT "root" (eUID==0) or if the authorization right that governs 66 * SCHelper write operations (kSCPreferencesAuthorizationRight_write) 67 * is not currently available. 68 * 69 * An [older] "User Intervention" key is also supported. That CFBoolean 70 * key, if present and TRUE, implies "Configure" configuration of the 71 * interface without intervention. 72 */ 73 74typedef struct { 75 UserEventAgentInterfaceStruct *_UserEventAgentInterface; 76 CFUUIDRef _factoryID; 77 UInt32 _refCount; 78 79 Boolean debug; 80 81 asl_object_t log_msg; 82 83 CFStringRef configuration_action; 84 85 CFRunLoopSourceRef monitorRls; 86 87 IONotificationPortRef notifyPort; 88 io_iterator_t notifyIterator; 89 CFMutableArrayRef notifyNodes; 90 91 // interfaces that we already know about 92 CFMutableSetRef interfaces_known; 93 94 // interfaces that should be auto-configured (no user notification) 95 CFMutableArrayRef interfaces_configure; 96 97 // interfaces that require user notification 98 CFMutableArrayRef interfaces_prompt; 99 100 CFUserNotificationRef userNotification; 101 CFRunLoopSourceRef userRls; 102 103 AuthorizationRef authorization; 104} MyType; 105 106static CFMutableDictionaryRef notify_to_instance = NULL; 107 108 109#pragma mark - 110#pragma mark Authorization 111 112 113static AuthorizationRef 114getAuthorization(MyType *myInstance) 115{ 116 if (myInstance->authorization == NULL) { 117 AuthorizationFlags flags = kAuthorizationFlagDefaults; 118 OSStatus status; 119 120 status = AuthorizationCreate(NULL, 121 kAuthorizationEmptyEnvironment, 122 flags, 123 &myInstance->authorization); 124 if (status != errAuthorizationSuccess) { 125 SCLOG(NULL, myInstance->log_msg, ASL_LEVEL_ERR, 126 CFSTR("AuthorizationCreate() failed: status = %d"), 127 (int)status); 128 } 129 } 130 131 return myInstance->authorization; 132} 133 134 135static Boolean 136hasAuthorization(MyType *myInstance) 137{ 138 AuthorizationRef authorization; 139 Boolean isAdmin = FALSE; 140 141 authorization = getAuthorization(myInstance); 142 if (authorization != NULL) { 143 AuthorizationFlags flags = kAuthorizationFlagDefaults; 144 AuthorizationItem items[1]; 145 AuthorizationRights rights; 146 OSStatus status; 147 148 items[0].name = kSCPreferencesAuthorizationRight_write; 149 items[0].value = NULL; 150 items[0].valueLength = 0; 151 items[0].flags = 0; 152 153 rights.count = sizeof(items) / sizeof(items[0]); 154 rights.items = items; 155 156 status = AuthorizationCopyRights(authorization, 157 &rights, 158 kAuthorizationEmptyEnvironment, 159 flags, 160 NULL); 161 isAdmin = (status == errAuthorizationSuccess); 162 } 163 164 return isAdmin; 165} 166 167 168static void 169freeAuthorization(MyType *myInstance) 170{ 171 if (myInstance->authorization != NULL) { 172 AuthorizationFree(myInstance->authorization, kAuthorizationFlagDefaults); 173// AuthorizationFree(myInstance->authorization, kAuthorizationFlagDestroyRights); 174 myInstance->authorization = NULL; 175 } 176 177 return; 178} 179 180 181#pragma mark - 182#pragma mark New interface notification / configuration 183 184 185static void 186open_NetworkPrefPane(MyType *myInstance) 187{ 188 AEDesc aeDesc = { typeNull, NULL }; 189 CFArrayRef prefArray; 190 CFURLRef prefURL; 191 LSLaunchURLSpec prefSpec; 192 OSStatus status; 193 194 prefURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, 195 CFSTR(NETWORK_PREF_APP), 196 kCFURLPOSIXPathStyle, 197 FALSE); 198 prefArray = CFArrayCreate(NULL, (const void **)&prefURL, 1, &kCFTypeArrayCallBacks); 199 CFRelease(prefURL); 200 201 status = AECreateDesc('ptru', 202 (const void *)NETWORK_PREF_CMD, 203 strlen(NETWORK_PREF_CMD), 204 &aeDesc); 205 if (status != noErr) { 206 SCLOG(NULL, myInstance->log_msg, ASL_LEVEL_ERR, CFSTR("SCMonitor: AECreateDesc() failed: %d"), (int)status); 207 } 208 209 prefSpec.appURL = NULL; 210 prefSpec.itemURLs = prefArray; 211 prefSpec.passThruParams = &aeDesc; 212 prefSpec.launchFlags = kLSLaunchAsync | kLSLaunchDontAddToRecents; 213 prefSpec.asyncRefCon = NULL; 214 215 status = LSOpenFromURLSpec(&prefSpec, NULL); 216 if (status != noErr) { 217 SCLOG(NULL, myInstance->log_msg, ASL_LEVEL_ERR, CFSTR("SCMonitor: LSOpenFromURLSpec() failed: %d"), (int)status); 218 } 219 220 CFRelease(prefArray); 221 if (aeDesc.descriptorType != typeNull) AEDisposeDesc(&aeDesc); 222 return; 223} 224 225 226static void 227notify_remove(MyType *myInstance, Boolean cancel) 228{ 229 if (myInstance->interfaces_configure != NULL) { 230 CFRelease(myInstance->interfaces_configure); 231 myInstance->interfaces_configure = NULL; 232 } 233 234 if (myInstance->interfaces_prompt != NULL) { 235 CFRelease(myInstance->interfaces_prompt); 236 myInstance->interfaces_prompt = NULL; 237 } 238 239 if (myInstance->userRls != NULL) { 240 CFRunLoopSourceInvalidate(myInstance->userRls); 241 CFRelease(myInstance->userRls); 242 myInstance->userRls = NULL; 243 } 244 245 if (myInstance->userNotification != NULL) { 246 if (cancel) { 247 SInt32 status; 248 249 status = CFUserNotificationCancel(myInstance->userNotification); 250 if (status != 0) { 251 SCLOG(NULL, myInstance->log_msg, ASL_LEVEL_ERR, 252 CFSTR("SCMonitor: CFUserNotificationCancel() failed, status=%d"), 253 (int)status); 254 } 255 } 256 CFRelease(myInstance->userNotification); 257 myInstance->userNotification = NULL; 258 } 259 260 return; 261} 262 263 264static void 265notify_reply(CFUserNotificationRef userNotification, CFOptionFlags response_flags) 266{ 267 MyType *myInstance = NULL; 268 269 // get instance for notification 270 if (notify_to_instance != NULL) { 271 myInstance = (MyType *)CFDictionaryGetValue(notify_to_instance, userNotification); 272 if (myInstance != NULL) { 273 CFDictionaryRemoveValue(notify_to_instance, userNotification); 274 if (CFDictionaryGetCount(notify_to_instance) == 0) { 275 CFRelease(notify_to_instance); 276 notify_to_instance = NULL; 277 } 278 } 279 } 280 if (myInstance == NULL) { 281 SCLOG(NULL, NULL, ASL_LEVEL_ERR, CFSTR("SCMonitor: can't find user notification")); 282 return; 283 } 284 285 // process response 286 switch (response_flags & 0x3) { 287 case kCFUserNotificationDefaultResponse: 288 // user asked to configure interface 289 open_NetworkPrefPane(myInstance); 290 break; 291 default: 292 // user cancelled 293 break; 294 } 295 296 notify_remove(myInstance, FALSE); 297 return; 298} 299 300 301static void 302notify_add(MyType *myInstance) 303{ 304 CFBundleRef bundle; 305 CFMutableDictionaryRef dict = NULL; 306 SInt32 error = 0; 307 CFIndex i; 308 CFIndex n = CFArrayGetCount(myInstance->interfaces_prompt); 309 CFURLRef url = NULL; 310 311 if (myInstance->userNotification != NULL) { 312 CFMutableArrayRef save = NULL; 313 314 if (n > 0) { 315 CFRetain(myInstance->interfaces_prompt); 316 save = myInstance->interfaces_prompt; 317 } 318 notify_remove(myInstance, TRUE); 319 myInstance->interfaces_prompt = save; 320 if (n == 0) { 321 return; 322 } 323 } 324 325 dict = CFDictionaryCreateMutable(NULL, 326 0, 327 &kCFTypeDictionaryKeyCallBacks, 328 &kCFTypeDictionaryValueCallBacks); 329 330 // set localization URL 331 bundle = CFBundleGetBundleWithIdentifier(CFSTR(MY_BUNDLE_ID)); 332 if (bundle != NULL) { 333 url = CFBundleCopyBundleURL(bundle); 334 } 335#ifdef MAIN 336 if (url == NULL) { 337 url = CFURLCreateFromFileSystemRepresentation(NULL, 338 (const UInt8 *)"/System/Library/UserEventPlugins/SCMonitor.plugin", 339 strlen("/System/Library/UserEventPlugins/SCMonitor.plugin"), 340 FALSE); 341 if (bundle == NULL) { 342 bundle = CFBundleCreate(NULL, url); 343 } 344 } 345#endif // MAIN 346 if (url != NULL) { 347 // set URL 348 CFDictionarySetValue(dict, kCFUserNotificationLocalizationURLKey, url); 349 CFRelease(url); 350 } else { 351 SCLOG(NULL, myInstance->log_msg, ASL_LEVEL_ERR, CFSTR("SCMonitor: can't find bundle")); 352 goto done; 353 } 354 355 // set icon URL 356 url = CFURLCreateFromFileSystemRepresentation(NULL, 357 (const UInt8 *)MY_ICON_PATH, 358 strlen(MY_ICON_PATH), 359 FALSE); 360 if (url != NULL) { 361 CFDictionarySetValue(dict, kCFUserNotificationIconURLKey, url); 362 CFRelease(url); 363 } 364 365 // header 366 CFDictionarySetValue(dict, 367 kCFUserNotificationAlertHeaderKey, 368 (n == 1) ? CFSTR("HEADER_1") : CFSTR("HEADER_N")); 369 370 // message 371 if (n == 1) { 372 CFStringRef format; 373 SCNetworkInterfaceRef interface; 374 CFStringRef message; 375 CFStringRef name; 376 377#define MESSAGE_1 "The \"%@\" network interface has not been set up. To set up this interface, use Network Preferences." 378 379 format = CFBundleCopyLocalizedString(bundle, 380 CFSTR("MESSAGE_1"), 381 CFSTR(MESSAGE_1), 382 NULL); 383 interface = CFArrayGetValueAtIndex(myInstance->interfaces_prompt, 0); 384 name = SCNetworkInterfaceGetLocalizedDisplayName(interface); 385 message = CFStringCreateWithFormat(NULL, NULL, format, name); 386 CFDictionarySetValue(dict, kCFUserNotificationAlertMessageKey, message); 387 CFRelease(message); 388 CFRelease(format); 389 } else { 390 CFMutableArrayRef message; 391 392 message = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 393 CFArrayAppendValue(message, CFSTR("MESSAGE_SN")); 394 for (i = 0; i < n; i++) { 395 SCNetworkInterfaceRef interface; 396 CFStringRef name; 397 CFStringRef str; 398 399 interface = CFArrayGetValueAtIndex(myInstance->interfaces_prompt, i); 400 name = SCNetworkInterfaceGetLocalizedDisplayName(interface); 401 str = CFStringCreateWithFormat(NULL, NULL, CFSTR("\r\t%@"), name); 402 CFArrayAppendValue(message, str); 403 CFRelease(str); 404 } 405 CFArrayAppendValue(message, CFSTR("MESSAGE_EN")); 406 CFDictionarySetValue(dict, kCFUserNotificationAlertMessageKey, message); 407 CFRelease(message); 408 } 409 410 // button titles 411 CFDictionaryAddValue(dict, kCFUserNotificationDefaultButtonTitleKey, CFSTR("OPEN_NP")); 412 CFDictionaryAddValue(dict, kCFUserNotificationAlternateButtonTitleKey, CFSTR("CANCEL")); 413 414 // create and post notification 415 myInstance->userNotification = CFUserNotificationCreate(NULL, 416 0, 417 kCFUserNotificationNoteAlertLevel, 418 &error, 419 dict); 420 if (myInstance->userNotification == NULL) { 421 SCLOG(NULL, myInstance->log_msg, ASL_LEVEL_ERR, CFSTR("SCMonitor: CFUserNotificationCreate() failed, %d"), (int)error); 422 goto done; 423 } 424 425 // establish callback 426 myInstance->userRls = CFUserNotificationCreateRunLoopSource(NULL, 427 myInstance->userNotification, 428 notify_reply, 429 0); 430 if (myInstance->userRls == NULL) { 431 SCLOG(NULL, myInstance->log_msg, ASL_LEVEL_ERR, CFSTR("SCMonitor: CFUserNotificationCreateRunLoopSource() failed")); 432 CFRelease(myInstance->userNotification); 433 myInstance->userNotification = NULL; 434 goto done; 435 } 436 CFRunLoopAddSource(CFRunLoopGetCurrent(), myInstance->userRls, kCFRunLoopDefaultMode); 437 438 // add instance for notification 439 if (notify_to_instance == NULL) { 440 notify_to_instance = CFDictionaryCreateMutable(NULL, 441 0, 442 &kCFTypeDictionaryKeyCallBacks, 443 NULL); // no retain/release/... for values 444 } 445 CFDictionarySetValue(notify_to_instance, myInstance->userNotification, myInstance); 446 447 done : 448 449 if (dict != NULL) CFRelease(dict); 450 return; 451} 452 453 454static void 455notify_configure(MyType *myInstance) 456{ 457 CFIndex i; 458 CFIndex n = CFArrayGetCount(myInstance->interfaces_configure); 459 Boolean ok; 460 SCPreferencesRef prefs = NULL; 461 SCNetworkSetRef set = NULL; 462 463 if (geteuid() == 0) { 464 prefs = SCPreferencesCreate(NULL, CFSTR("SCMonitor"), NULL); 465 } else { 466 AuthorizationRef authorization; 467 468 authorization = getAuthorization(myInstance); 469 if (authorization == NULL) { 470 return; 471 } 472 473 prefs = SCPreferencesCreateWithAuthorization(NULL, CFSTR("SCMonitor"), NULL, authorization); 474 } 475 476 set = SCNetworkSetCopyCurrent(prefs); 477 if (set == NULL) { 478 set = SCNetworkSetCreate(prefs); 479 if (set == NULL) { 480 goto done; 481 } 482 } 483 484 for (i = 0; i < n; i++) { 485 SCNetworkInterfaceRef interface; 486 487 interface = CFArrayGetValueAtIndex(myInstance->interfaces_configure, i); 488 ok = SCNetworkSetEstablishDefaultInterfaceConfiguration(set, interface); 489 if (ok) { 490 CFStringRef name; 491 492 name = SCNetworkInterfaceGetLocalizedDisplayName(interface); 493 SCLOG(NULL, myInstance->log_msg, ASL_LEVEL_NOTICE, CFSTR("add/update service for %@"), name); 494 } 495 } 496 497 ok = SCPreferencesCommitChanges(prefs); 498 if (!ok) { 499 SCLOG(NULL, myInstance->log_msg, ASL_LEVEL_ERR, 500 CFSTR("SCPreferencesCommitChanges() failed: %s"), 501 SCErrorString(SCError())); 502 goto done; 503 } 504 505 ok = SCPreferencesApplyChanges(prefs); 506 if (!ok) { 507 SCLOG(NULL, myInstance->log_msg, ASL_LEVEL_ERR, 508 CFSTR("SCPreferencesApplyChanges() failed: %s"), 509 SCErrorString(SCError())); 510 goto done; 511 } 512 513 done : 514 515 if (set != NULL) { 516 CFRelease(set); 517 set = NULL; 518 } 519 520 if (prefs != NULL) { 521 CFRelease(prefs); 522 prefs = NULL; 523 } 524 525 CFRelease(myInstance->interfaces_configure); 526 myInstance->interfaces_configure = NULL; 527 528 return; 529} 530 531 532#pragma mark - 533 534 535// configure ONLY IF authorized 536#define kSCNetworkInterfaceConfigurationActionValueConfigureAuthorized CFSTR("Configure-Authorized") 537 538 539static void 540updateInterfaceList(MyType *myInstance) 541{ 542 Boolean changed = FALSE; 543 CFIndex i; 544 CFArrayRef interfaces; 545 CFMutableSetRef interfaces_old = NULL; 546 CFIndex n; 547 SCPreferencesRef prefs; 548 SCNetworkSetRef set = NULL; 549 550 prefs = SCPreferencesCreate(NULL, CFSTR("SCMonitor"), NULL); 551 if (prefs == NULL) { 552 return; 553 } 554 555 set = SCNetworkSetCopyCurrent(prefs); 556 if (set == NULL) { 557 set = SCNetworkSetCreate(prefs); 558 if (set == NULL) { 559 goto done; 560 } 561 } 562 563 interfaces_old = CFSetCreateMutableCopy(NULL, 0, myInstance->interfaces_known); 564 565 interfaces = _SCNetworkInterfaceCopyAllWithPreferences(prefs); 566 if (interfaces != NULL) { 567 n = CFArrayGetCount(interfaces); 568 for (i = 0; i < n; i++) { 569 SCNetworkInterfaceRef interface; 570 Boolean ok; 571 572 interface = CFArrayGetValueAtIndex(interfaces, i); 573 574 // track new vs. old (removed) interfaces 575 CFSetRemoveValue(interfaces_old, interface); 576 if (CFSetContainsValue(myInstance->interfaces_known, interface)) { 577 // if we already know about this interface 578 continue; 579 } 580 CFSetAddValue(myInstance->interfaces_known, interface); 581 changed = TRUE; 582 583 ok = SCNetworkSetEstablishDefaultInterfaceConfiguration(set, interface); 584 if (ok) { 585 CFStringRef action; 586 587 // this is a *new* interface 588 589 action = _SCNetworkInterfaceGetConfigurationAction(interface); 590 if (action == NULL) { 591 // if no per-interface action, use [global] default 592 action = myInstance->configuration_action; 593 } 594 if ((action == NULL) || 595 (!CFEqual(action, kSCNetworkInterfaceConfigurationActionValueNone) && 596 !CFEqual(action, kSCNetworkInterfaceConfigurationActionValueConfigure))) { 597 if (_SCNetworkInterfaceIsBuiltin(interface)) { 598 // if built-in interface 599 action = kSCNetworkInterfaceConfigurationActionValueConfigureAuthorized; 600 } else { 601 action = kSCNetworkInterfaceConfigurationActionValuePrompt; 602 } 603 } 604 605 if (CFEqual(action, kSCNetworkInterfaceConfigurationActionValueNone)) { 606 continue; 607 } else if (CFEqual(action, kSCNetworkInterfaceConfigurationActionValueConfigure)) { 608 // configure automatically (without user intervention) 609 if (myInstance->interfaces_configure == NULL) { 610 myInstance->interfaces_configure = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 611 } 612 CFArrayAppendValue(myInstance->interfaces_configure, interface); 613 } else if (hasAuthorization(myInstance)) { 614 // if we already have the "admin" (kSCPreferencesAuthorizationRight_write) 615 // right, configure automatically (without user intervention) 616 if (myInstance->interfaces_configure == NULL) { 617 myInstance->interfaces_configure = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 618 } 619 CFArrayAppendValue(myInstance->interfaces_configure, interface); 620 } else if (!CFEqual(action, kSCNetworkInterfaceConfigurationActionValueConfigureAuthorized)) { 621 // notify user 622 if (myInstance->interfaces_prompt == NULL) { 623 myInstance->interfaces_prompt = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 624 } 625 CFArrayAppendValue(myInstance->interfaces_prompt, interface); 626 } 627 } 628 } 629 630 CFRelease(interfaces); 631 } 632 633 // remove any posted notifications for network interfaces that have been removed 634 n = CFSetGetCount(interfaces_old); 635 if (n > 0) { 636 const void * paths_q[32]; 637 const void ** paths = paths_q; 638 639 if (n > (CFIndex)(sizeof(paths_q) / sizeof(CFTypeRef))) 640 paths = CFAllocatorAllocate(NULL, n * sizeof(CFTypeRef), 0); 641 CFSetGetValues(interfaces_old, paths); 642 for (i = 0; i < n; i++) { 643 if (myInstance->interfaces_prompt != NULL) { 644 CFIndex j; 645 646 j = CFArrayGetCount(myInstance->interfaces_prompt); 647 while (j > 0) { 648 SCNetworkInterfaceRef interface; 649 650 j--; 651 interface = CFArrayGetValueAtIndex(myInstance->interfaces_prompt, j); 652 if (CFEqual(interface, paths[i])) { 653 // if we have previously posted a notification 654 // for this no-longer-present interface 655 CFArrayRemoveValueAtIndex(myInstance->interfaces_prompt, j); 656 changed = TRUE; 657 } 658 } 659 } 660 661 CFSetRemoveValue(myInstance->interfaces_known, paths[i]); 662 } 663 if (paths != paths_q) CFAllocatorDeallocate(NULL, paths); 664 } 665 666 done : 667 668 if (changed) { 669 if (myInstance->interfaces_configure != NULL) { 670 // if we have network services to configure automatically 671 notify_configure(myInstance); 672 } 673 674 if (myInstance->interfaces_prompt != NULL) { 675 // if we have network services that require user intervention 676 // post notification for new interfaces 677 notify_add(myInstance); 678 } 679 } 680 681 if (interfaces_old != NULL) CFRelease(interfaces_old); 682 if (set != NULL) CFRelease(set); 683 CFRelease(prefs); 684 return; 685} 686 687 688#pragma mark - 689#pragma mark Watch for new [network] interfaces 690 691 692static void 693update_lan(SCDynamicStoreRef store, CFArrayRef changes, void * arg) 694{ 695 MyType *myInstance = (MyType *)arg; 696 697 updateInterfaceList(myInstance); 698 return; 699} 700 701 702static void 703watcher_add_lan(MyType *myInstance) 704{ 705 SCDynamicStoreContext context = { 0, (void *)myInstance, NULL, NULL, NULL }; 706 CFStringRef key; 707 CFArrayRef keys; 708 SCDynamicStoreRef store; 709 710 store = SCDynamicStoreCreate(NULL, CFSTR("SCMonitor"), update_lan, &context); 711 if (store == NULL) { 712 SCLOG(NULL, myInstance->log_msg, ASL_LEVEL_ERR, 713 CFSTR("SCMonitor: SCDynamicStoreCreate() failed: %s"), 714 SCErrorString(SCError())); 715 return; 716 } 717 718 key = SCDynamicStoreKeyCreateNetworkInterface(NULL, kSCDynamicStoreDomainState); 719 720 // watch for changes to the list of network interfaces 721 keys = CFArrayCreate(NULL, (const void **)&key, 1, &kCFTypeArrayCallBacks); 722 SCDynamicStoreSetNotificationKeys(store, NULL, keys); 723 CFRelease(keys); 724 myInstance->monitorRls = SCDynamicStoreCreateRunLoopSource(NULL, store, 0); 725 CFRunLoopAddSource(CFRunLoopGetCurrent(), 726 myInstance->monitorRls, 727 kCFRunLoopDefaultMode); 728 729 // check if we already have the "admin" (kSCPreferencesAuthorizationRight_write) 730 // right. If so, we can automatically configure (without user intervention) any 731 // "new" network interfaces that are present at login (e.g. a USB ethernet 732 // dongle that was plugged in before login). 733 if (!hasAuthorization(myInstance)) { 734 CFDictionaryRef dict; 735 736 // ... and if we don't have the right then we populate the list of 737 // known interfaces with those already named so that we avoid any 738 // login prompts (that the user might have no choice but to dismiss) 739 dict = SCDynamicStoreCopyValue(store, key); 740 if (dict != NULL) { 741 if (isA_CFDictionary(dict)) { 742 CFIndex i; 743 CFArrayRef interfaces; 744 CFIndex n; 745 746 interfaces = CFDictionaryGetValue(dict, kSCPropNetInterfaces); 747 n = isA_CFArray(interfaces) ? CFArrayGetCount(interfaces) : 0; 748 for (i = 0; i < n; i++) { 749 CFStringRef bsdName; 750 751 bsdName = CFArrayGetValueAtIndex(interfaces, i); 752 if (isA_CFString(bsdName)) { 753 SCNetworkInterfaceRef interface; 754 755 interface = _SCNetworkInterfaceCreateWithBSDName(NULL, bsdName, kIncludeNoVirtualInterfaces); 756 if (interface != NULL) { 757 CFSetAddValue(myInstance->interfaces_known, interface); 758 CFRelease(interface); 759 } 760 } 761 } 762 } 763 764 CFRelease(dict); 765 } 766 } 767 768 CFRelease(key); 769 CFRelease(store); 770 return; 771} 772 773 774static void 775watcher_remove_lan(MyType *myInstance) 776{ 777 if (myInstance->monitorRls != NULL) { 778 CFRunLoopSourceInvalidate(myInstance->monitorRls); 779 CFRelease(myInstance->monitorRls); 780 myInstance->monitorRls = NULL; 781 } 782 783 return; 784} 785 786 787#pragma mark - 788 789 790typedef struct { 791 io_registry_entry_t interface; 792 io_registry_entry_t interface_node; 793 MyType *myInstance; 794 io_object_t notification; 795} MyNode; 796 797 798static void 799add_node_watcher(MyType *myInstance, io_registry_entry_t node, io_registry_entry_t interface); 800 801 802static void 803update_node(void *refCon, io_service_t service, natural_t messageType, void *messageArgument) 804{ 805 CFIndex i; 806 CFDataRef myData = (CFDataRef)refCon; 807 MyType *myInstance; 808 MyNode *myNode; 809 810 /* ALIGN: CF aligns to at least >8 bytes */ 811 myNode = (MyNode *)(void *)CFDataGetBytePtr(myData); 812 myInstance = myNode->myInstance; 813 814 switch (messageType) { 815 case kIOMessageServicePropertyChange : { 816 Boolean initializing = FALSE; 817 SCNetworkInterfaceRef interface; 818 CFTypeRef val; 819 820 if (myNode->interface == MACH_PORT_NULL) { 821 // if we are not watching the "Initializing" property 822 return; 823 } 824 825 val = IORegistryEntryCreateCFProperty(service, CFSTR("Initializing"), NULL, 0); 826 if (val != NULL) { 827 initializing = (isA_CFBoolean(val) && CFBooleanGetValue(val)); 828 CFRelease(val); 829 if (initializing) { 830 // if initialization not complete, keep watching 831 return; 832 } 833 } 834 835 // node is ready 836 interface = _SCNetworkInterfaceCreateWithIONetworkInterfaceObject(myNode->interface); 837 if (interface != NULL) { 838 CFRelease(interface); 839 840 // watch interface (to see when/if it's removed) 841 add_node_watcher(myInstance, myNode->interface, MACH_PORT_NULL); 842 } 843 break; 844 } 845 846 case kIOMessageServiceIsTerminated : 847 break; 848 849 default : 850 return; 851 } 852 853 // remove no-longer-needed notification 854 if (myNode->interface != myNode->interface_node) { 855 IOObjectRelease(myNode->interface_node); 856 } 857 if (myNode->interface != MACH_PORT_NULL) { 858 IOObjectRelease(myNode->interface); 859 } 860 IOObjectRelease(myNode->notification); 861 i = CFArrayGetFirstIndexOfValue(myInstance->notifyNodes, 862 CFRangeMake(0, CFArrayGetCount(myInstance->notifyNodes)), 863 myData); 864 if (i != kCFNotFound) { 865 CFArrayRemoveValueAtIndex(myInstance->notifyNodes, i); 866 if (CFArrayGetCount(myInstance->notifyNodes) == 0) { 867 CFRelease(myInstance->notifyNodes); 868 myInstance->notifyNodes = NULL; 869 } 870 } 871 872 updateInterfaceList(myInstance); 873 return; 874} 875 876 877static void 878add_node_watcher(MyType *myInstance, io_registry_entry_t node, io_registry_entry_t interface) 879{ 880 kern_return_t kr; 881 CFMutableDataRef myData; 882 MyNode *myNode; 883 884 // wait for initialization to complete 885 myData = CFDataCreateMutable(NULL, sizeof(MyNode)); 886 CFDataSetLength(myData, sizeof(MyNode)); 887 888 /* ALIGN: CF aligns to at least >8 bytes */ 889 myNode = (MyNode *)(void *)CFDataGetBytePtr(myData); 890 891 bzero(myNode, sizeof(MyNode)); 892 myNode->interface = interface; 893 if (myNode->interface != MACH_PORT_NULL) { 894 IOObjectRetain(myNode->interface); 895 } 896 myNode->interface_node = (interface == MACH_PORT_NULL) ? node : interface; 897 if (myNode->interface != myNode->interface_node) { 898 IOObjectRetain(myNode->interface_node); 899 } 900 myNode->myInstance = myInstance; 901 myNode->notification = MACH_PORT_NULL; 902 903 kr = IOServiceAddInterestNotification(myInstance->notifyPort, // IONotificationPortRef 904 node, // io_service_t 905 kIOGeneralInterest, // interestType 906 update_node, // IOServiceInterestCallback 907 (void *)myData, // refCon 908 &myNode->notification); // notification 909 if (kr == KERN_SUCCESS) { 910 if (myInstance->notifyNodes == NULL) { 911 myInstance->notifyNodes = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 912 } 913 CFArrayAppendValue(myInstance->notifyNodes, myData); 914 } else { 915 SCLOG(NULL, myInstance->log_msg, ASL_LEVEL_ERR, 916 CFSTR("add_init_watcher IOServiceAddInterestNotification() failed, kr = 0x%x"), kr); 917 } 918 CFRelease(myData); 919} 920 921 922static void 923add_init_watcher(MyType *myInstance, io_registry_entry_t interface) 924{ 925 kern_return_t kr; 926 io_registry_entry_t node = interface; 927 CFTypeRef val = NULL; 928 929 while (node != MACH_PORT_NULL) { 930 io_registry_entry_t parent; 931 932 val = IORegistryEntryCreateCFProperty(node, kSCNetworkInterfaceHiddenPortKey, NULL, 0); 933 if (val != NULL) { 934 CFRelease(val); 935 val = NULL; 936 break; 937 } 938 939 val = IORegistryEntryCreateCFProperty(node, kSCNetworkInterfaceInitializingKey, NULL, 0); 940 if (val != NULL) { 941 break; 942 } 943 944 parent = MACH_PORT_NULL; 945 kr = IORegistryEntryGetParentEntry(node, kIOServicePlane, &parent); 946 switch (kr) { 947 case kIOReturnSuccess : // if we have a parent node 948 case kIOReturnNoDevice : // if we have hit the root node 949 break; 950 default : 951 SCLOG(NULL, myInstance->log_msg, ASL_LEVEL_ERR, CFSTR("add_init_watcher IORegistryEntryGetParentEntry() failed, kr = 0x%x"), kr); 952 break; 953 } 954 if (node != interface) { 955 IOObjectRelease(node); 956 } 957 node = parent; 958 } 959 960 if (val != NULL) { 961 if (isA_CFBoolean(val) && CFBooleanGetValue(val)) { 962 // watch the "Initializing" node 963 add_node_watcher(myInstance, node, interface); 964 } 965 966 CFRelease(val); 967 } 968 969 if ((node != MACH_PORT_NULL) && (node != interface)) { 970 IOObjectRelease(node); 971 } 972 973 return; 974} 975 976 977static void 978update_serial(void *refcon, io_iterator_t iter) 979{ 980 MyType *myInstance = (MyType *)refcon; 981 io_registry_entry_t obj; 982 983 while ((obj = IOIteratorNext(iter)) != MACH_PORT_NULL) { 984 SCNetworkInterfaceRef interface; 985 986 interface = _SCNetworkInterfaceCreateWithIONetworkInterfaceObject(obj); 987 if (interface != NULL) { 988 CFRelease(interface); 989 990 // watch interface (to see when/if it's removed) 991 add_node_watcher(myInstance, obj, MACH_PORT_NULL); 992 } else { 993 // check interface, watch if initializing 994 add_init_watcher(myInstance, obj); 995 } 996 997 IOObjectRelease(obj); 998 } 999 1000 return; 1001} 1002 1003 1004static void 1005update_serial_nodes(void *refcon, io_iterator_t iter) 1006{ 1007 MyType *myInstance = (MyType *)refcon; 1008 1009 update_serial(refcon, iter); 1010 updateInterfaceList(myInstance); 1011} 1012 1013 1014static void 1015watcher_add_serial(MyType *myInstance) 1016{ 1017 kern_return_t kr; 1018 1019 myInstance->notifyPort = IONotificationPortCreate(kIOMasterPortDefault); 1020 if (myInstance->notifyPort == NULL) { 1021 SCLOG(NULL, myInstance->log_msg, ASL_LEVEL_ERR, 1022 CFSTR("SCMonitor: IONotificationPortCreate failed")); 1023 return; 1024 } 1025 1026 // watch for the introduction of new network serial devices 1027 kr = IOServiceAddMatchingNotification(myInstance->notifyPort, 1028 kIOFirstMatchNotification, 1029 IOServiceMatching("IOSerialBSDClient"), 1030 &update_serial_nodes, 1031 (void *)myInstance, // refCon 1032 &myInstance->notifyIterator); // notification 1033 if (kr != KERN_SUCCESS) { 1034 SCLOG(NULL, myInstance->log_msg, ASL_LEVEL_ERR, 1035 CFSTR("SCMonitor : IOServiceAddMatchingNotification returned 0x%x"), 1036 kr); 1037 return; 1038 } 1039 1040 myInstance->notifyNodes = NULL; 1041 1042 // Get the current list of matches and arm the notification for 1043 // future interface arrivals. 1044 update_serial((void *)myInstance, myInstance->notifyIterator); 1045 1046 if (myInstance->notifyNodes != NULL) { 1047 // if we have any serial nodes, check if we already have the 1048 // "admin" (kSCPreferencesAuthorizationRight_write) right. If 1049 // so, we can automatically configure (without user intervention) 1050 // any "new" network interfaces that are present at login (e.g. a 1051 // USB modem that was plugged in before login). 1052 1053 if (!hasAuthorization(myInstance)) { 1054 CFIndex i; 1055 CFIndex n = CFArrayGetCount(myInstance->notifyNodes); 1056 1057 // ... and if we don't have the right then we populate the list of 1058 // known interfaces with those already named so that we avoid any 1059 // login prompts (that the user might have no choice but to dismiss) 1060 1061 for (i = 0; i < n; i++) { 1062 SCNetworkInterfaceRef interface; 1063 CFDataRef myData; 1064 MyNode *myNode; 1065 1066 myData = CFArrayGetValueAtIndex(myInstance->notifyNodes, i); 1067 1068 /* ALIGN: CF aligns to at least >8 bytes */ 1069 myNode = (MyNode *)(void *)CFDataGetBytePtr(myData); 1070 1071 interface = _SCNetworkInterfaceCreateWithIONetworkInterfaceObject(myNode->interface_node); 1072 if (interface != NULL) { 1073 CFSetAddValue(myInstance->interfaces_known, interface); 1074 CFRelease(interface); 1075 } 1076 } 1077 } 1078 } 1079 1080 // and keep watching 1081 CFRunLoopAddSource(CFRunLoopGetCurrent(), 1082 IONotificationPortGetRunLoopSource(myInstance->notifyPort), 1083 kCFRunLoopDefaultMode); 1084 return; 1085} 1086 1087 1088static void 1089watcher_remove_serial(MyType *myInstance) 1090{ 1091 if (myInstance->notifyNodes != NULL) { 1092 CFIndex i; 1093 CFIndex n = CFArrayGetCount(myInstance->notifyNodes); 1094 1095 for (i = 0; i < n; i++) { 1096 CFDataRef myData; 1097 MyNode *myNode; 1098 1099 myData = CFArrayGetValueAtIndex(myInstance->notifyNodes, i); 1100 1101 /* ALIGN: CF aligns to at least >8 bytes */ 1102 myNode = (MyNode *)(void *)CFDataGetBytePtr(myData); 1103 1104 if (myNode->interface != myNode->interface_node) { 1105 IOObjectRelease(myNode->interface_node); 1106 } 1107 if (myNode->interface != MACH_PORT_NULL) { 1108 IOObjectRelease(myNode->interface); 1109 } 1110 IOObjectRelease(myNode->notification); 1111 } 1112 1113 CFRelease(myInstance->notifyNodes); 1114 myInstance->notifyNodes = NULL; 1115 } 1116 1117 if (myInstance->notifyIterator != MACH_PORT_NULL) { 1118 IOObjectRelease(myInstance->notifyIterator); 1119 myInstance->notifyIterator = MACH_PORT_NULL; 1120 } 1121 1122 if (myInstance->notifyPort != MACH_PORT_NULL) { 1123 IONotificationPortDestroy(myInstance->notifyPort); 1124 myInstance->notifyPort = NULL; 1125 } 1126 1127 return; 1128} 1129 1130 1131#pragma mark - 1132 1133 1134static void 1135watcher_add(MyType *myInstance) 1136{ 1137 CFBundleRef bundle; 1138 1139 if (myInstance->log_msg == NULL) { 1140 myInstance->log_msg = asl_new(ASL_TYPE_MSG); 1141 asl_set(myInstance->log_msg, ASL_KEY_FACILITY, MY_BUNDLE_ID); 1142 } 1143 1144 bundle = CFBundleGetBundleWithIdentifier(CFSTR(MY_BUNDLE_ID)); 1145 if (bundle != NULL) { 1146 CFStringRef action; 1147 CFBooleanRef bVal; 1148 CFDictionaryRef info; 1149 1150 info = CFBundleGetInfoDictionary(bundle); 1151 1152 bVal = CFDictionaryGetValue(info, CFSTR("Debug")); 1153 bVal = isA_CFBoolean(bVal); 1154 if (bVal != NULL) { 1155 myInstance->debug = CFBooleanGetValue(bVal); 1156 } 1157 1158 action = CFDictionaryGetValue(info, kSCNetworkInterfaceConfigurationActionKey); 1159 action = isA_CFString(action); 1160 if (action != NULL) { 1161 myInstance->configuration_action = action; 1162 } else { 1163 CFBooleanRef user_intervention; 1164 1165 user_intervention = CFDictionaryGetValue(info, CFSTR("User Intervention")); 1166 if (isA_CFBoolean(user_intervention) && !CFBooleanGetValue(user_intervention)) { 1167 myInstance->configuration_action = kSCNetworkInterfaceConfigurationActionValueConfigure; 1168 } 1169 } 1170 } 1171 1172 // initialize the list of known interfaces 1173 myInstance->interfaces_known = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks); 1174 1175 // add LAN interfaces 1176 watcher_add_lan(myInstance); 1177 1178 // add SERIAL interfaces 1179 watcher_add_serial(myInstance); 1180 1181 // auto-configure (as needed) 1182 updateInterfaceList(myInstance); 1183 1184 return; 1185} 1186 1187 1188static void 1189watcher_remove(MyType *myInstance) 1190{ 1191 watcher_remove_lan(myInstance); 1192 watcher_remove_serial(myInstance); 1193 1194 if (myInstance->interfaces_known != NULL) { 1195 CFRelease(myInstance->interfaces_known); 1196 myInstance->interfaces_known = NULL; 1197 } 1198 1199 asl_release(myInstance->log_msg); 1200 myInstance->log_msg = NULL; 1201 return; 1202} 1203 1204 1205#pragma mark - 1206#pragma mark UserEventAgent stubs 1207 1208 1209static HRESULT 1210myQueryInterface(void *myInstance, REFIID iid, LPVOID *ppv) 1211{ 1212 CFUUIDRef interfaceID = CFUUIDCreateFromUUIDBytes(NULL, iid); 1213 1214 // Test the requested ID against the valid interfaces. 1215 if (CFEqual(interfaceID, kUserEventAgentInterfaceID)) { 1216 ((MyType *) myInstance)->_UserEventAgentInterface->AddRef(myInstance); 1217 *ppv = myInstance; 1218 CFRelease(interfaceID); 1219 return S_OK; 1220 } 1221 1222 if (CFEqual(interfaceID, IUnknownUUID)) { 1223 ((MyType *) myInstance)->_UserEventAgentInterface->AddRef(myInstance); 1224 *ppv = myInstance; 1225 CFRelease(interfaceID); 1226 return S_OK; 1227 } 1228 1229 // Requested interface unknown, bail with error. 1230 *ppv = NULL; 1231 CFRelease(interfaceID); 1232 return E_NOINTERFACE; 1233} 1234 1235 1236static ULONG 1237myAddRef(void *myInstance) 1238{ 1239 ((MyType *) myInstance)->_refCount++; 1240 return ((MyType *) myInstance)->_refCount; 1241} 1242 1243 1244static ULONG 1245myRelease(void *myInstance) 1246{ 1247 ((MyType *) myInstance)->_refCount--; 1248 if (((MyType *) myInstance)->_refCount == 0) { 1249 CFUUIDRef factoryID = ((MyType *) myInstance)->_factoryID; 1250 1251 if (factoryID != NULL) { 1252 CFPlugInRemoveInstanceForFactory(factoryID); 1253 CFRelease(factoryID); 1254 1255 watcher_remove((MyType *)myInstance); 1256 notify_remove((MyType *)myInstance, TRUE); 1257 freeAuthorization((MyType *)myInstance); 1258 } 1259 free(myInstance); 1260 return 0; 1261 } 1262 1263 return ((MyType *) myInstance)->_refCount; 1264} 1265 1266 1267static void 1268myInstall(void *myInstance) 1269{ 1270 watcher_add((MyType *)myInstance); 1271 return; 1272} 1273 1274 1275static UserEventAgentInterfaceStruct UserEventAgentInterfaceFtbl = { 1276 NULL, // Required padding for COM 1277 myQueryInterface, // These three are the required COM functions 1278 myAddRef, 1279 myRelease, 1280 myInstall // Interface implementation 1281}; 1282 1283 1284void * 1285UserEventAgentFactory(CFAllocatorRef allocator, CFUUIDRef typeID) 1286{ 1287 MyType *newOne = NULL; 1288 1289 if (CFEqual(typeID, kUserEventAgentTypeID)) { 1290 newOne = (MyType *)malloc(sizeof(MyType)); 1291 bzero(newOne, sizeof(*newOne)); 1292 newOne->_UserEventAgentInterface = &UserEventAgentInterfaceFtbl; 1293 newOne->_factoryID = (CFUUIDRef)CFRetain(kUserEventAgentFactoryID); 1294 CFPlugInAddInstanceForFactory(kUserEventAgentFactoryID); 1295 newOne->_refCount = 1; 1296 } 1297 1298 return newOne; 1299} 1300 1301 1302#ifdef MAIN 1303int 1304main(int argc, char **argv) 1305{ 1306 MyType *newOne = (MyType *)malloc(sizeof(MyType)); 1307 1308 _sc_log = FALSE; 1309 _sc_verbose = (argc > 1) ? TRUE : FALSE; 1310 1311 bzero(newOne, sizeof(*newOne)); 1312 myInstall(newOne); 1313 CFRunLoopRun(); 1314 exit(0); 1315 return (0); 1316} 1317#endif // MAIN 1318