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