1/* 2 * Copyright (c) 2003 Apple Computer, 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#include <CoreFoundation/CoreFoundation.h> 26#include <IOKit/IOHibernatePrivate.h> 27#include <IOKit/hidsystem/IOHIDLib.h> 28#include <IOKit/graphics/IOGraphicsTypes.h> 29#include <IOKit/pwr_mgt/IOPMLibPrivate.h> 30#include <IOKit/IOHibernatePrivate.h> 31#if !TARGET_OS_EMBEDDED 32#include <IOKit/platform/IOPlatformSupportPrivate.h> 33#endif 34#include <Security/SecTask.h> 35#include <mach/mach.h> 36#include <mach/mach_time.h> 37#include <grp.h> 38#include <pwd.h> 39#include <syslog.h> 40#include <unistd.h> 41#include <asl.h> 42#include <membership.h> 43#include <sys/types.h> 44#include <sys/sysctl.h> 45#include <sys/stat.h> 46#include <sys/fcntl.h> 47#include <sys/mount.h> 48#include <unistd.h> 49#include <pthread.h> 50#include <dispatch/dispatch.h> 51#include <notify.h> 52 53#include "Platform.h" 54#include "PrivateLib.h" 55#include "BatteryTimeRemaining.h" 56#include "PMAssertions.h" 57#include "PMSettings.h" 58#include "PMAssertions.h" 59 60#define kIntegerStringLen 15 61 62#if !TARGET_OS_EMBEDDED 63#include <IOKit/smc/SMCUserClient.h> 64#include <systemstats/systemstats.h> 65#endif /* TARGET_OS_EMBEDDED */ 66 67#ifndef kIOHIDIdleTimeKy 68#define kIOHIDIdleTimeKey "HIDIdleTime" 69#endif 70 71#ifndef kIOPMMaintenanceScheduleImmediate 72#define kIOPMMaintenanceScheduleImmediate "MaintenanceImmediate" 73#endif 74 75// Duplicating the enum from IOServicePMPrivate.h 76enum { 77 kDriverCallInformPreChange, 78 kDriverCallInformPostChange, 79 kDriverCallSetPowerState, 80 kRootDomainInformPreChange 81}; 82enum 83{ 84 PowerManagerScheduledShutdown = 1, 85 PowerManagerScheduledSleep, 86 PowerManagerScheduledRestart 87}; 88 89/* If the battery doesn't specify an alternative time, we wait 16 seconds 90 of ignoring the battery's (or our own) time remaining estimate. 91*/ 92enum 93{ 94 kInvalidWakeSecsDefault = 16 95}; 96 97enum 98{ 99 // 2GB 100 kStandbyDesktopHibernateFileSize = 2ULL*1024*1024*1024, 101 // 1GB 102 kStandbyPortableHibernateFileSize = 1ULL*1024*1024*1024 103}; 104 105#define kPowerManagerActionNotificationName "com.apple.powermanager.action" 106#define kPowerManagerActionKey "action" 107#define kPowerManagerValueKey "value" 108 109// Track real batteries 110static CFMutableSetRef physicalBatteriesSet = NULL; 111static long physicalBatteriesCount = 0; 112static IOPMBattery **physicalBatteriesArray = NULL; 113 114#ifndef __I_AM_PMSET__ 115 116__private_extern__ bool isDisplayAsleep( ); 117#endif 118 119// Frequency with which to write out FDR records, in secs 120#define kFDRRegularInterval (10*60) 121// How long to wait after a power event to write out first FDR record, in secs 122// Power events include sleep, wake, AC change etc. 123#define kFDRIntervalAfterPE (1*60) 124 125#if !TARGET_OS_EMBEDDED 126#ifndef __I_AM_PMSET__ 127static uint64_t nextFDREventDue = 0; 128#endif 129#endif 130 131typedef struct { 132 CFStringRef sleepReason; 133 CFStringRef platformWakeReason; 134 CFStringRef platformWakeType; 135 CFArrayRef claimedWakeEventsArray; 136 CFStringRef claimedWake; 137 CFStringRef interpretedWake; 138} PowerEventReasons; 139 140static PowerEventReasons reasons = { 141 CFSTR(""), 142 CFSTR(""), 143 CFSTR(""), 144 NULL, 145 NULL, 146 NULL 147 }; 148 149/****** 150 * Do not remove DUMMY macros 151 * 152 * The following DUMMY macros aren't used in the source code, they're 153 * a placeholder for the 'genstrings' tool to read the strings we're using. 154 * 'genstrings' will encode these strings in a Localizable.strings file. 155 * Note: if you add to or modify these strings, you must re-run genstrings and 156 * replace this project's Localizable.strings file with the output. 157 ******/ 158 159#define DUMMY_UPS_HEADER(myBundle) CFCopyLocalizedStringWithDefaultValue( \ 160 CFSTR("WARNING!"), \ 161 CFSTR("Localizable"), \ 162 myBundle, \ 163 CFSTR("Warning!"), \ 164 NULL); 165 166#define DUMMY_UPS_BODY(myBundle) CFCopyLocalizedStringWithDefaultValue( \ 167 CFSTR("YOUR COMPUTER IS NOW RUNNING ON UPS BACKUP BATTERY. SAVE YOUR DOCUMENTS AND SHUTDOWN SOON."), \ 168 CFSTR("Localizable"), \ 169 myBundle, \ 170 CFSTR("Your computer is now running on UPS backup battery power. Save your documents and shut down soon."), \ 171 NULL); 172 173 174#define DUMMY_ASSERTION_STRING_TTY(myBundle) CFCopyLocalizedStringWithDefaultValue( \ 175 CFSTR("A remote user is connected. That prevents system sleep."), \ 176 CFSTR("Localizable"), \ 177 myBundle, \ 178 CFSTR("A remote user is connected. That prevents system sleep."), \ 179 NULL); 180 181#define DUMMY_CAFFEINATE_REASON_STRING(myBundle) CFCopyLocalizedStringWithDefaultValue( \ 182 CFSTR("THE CAFFEINATE TOOL IS PREVENTING SLEEP."), \ 183 CFSTR("Localizable"), \ 184 myBundle, \ 185 CFSTR("The caffeinate tool is preventing sleep."), \ 186 NULL); 187 188 189static int getAggressivenessFactorsFromProfile( 190 CFDictionaryRef p, 191 IOPMAggressivenessFactors *agg); 192static int ProcessHibernateSettings( 193 CFDictionaryRef dict, 194 bool standby, 195 bool desktop, 196 io_registry_entry_t rootDomain); 197 198 199#ifndef __I_AM_PMSET__ 200static void mt2PublishWakeReason(CFStringRef wakeTypeStr, CFStringRef claimedWakeStr); 201 202// dynamicStoreNotifyCallBack is defined in pmconfigd.c 203// is not defined in pmset! so we don't compile this code in pmset. 204 205extern SCDynamicStoreRef gSCDynamicStore; 206 207__private_extern__ SCDynamicStoreRef _getSharedPMDynamicStore(void) 208{ 209 return gSCDynamicStore; 210} 211#endif 212 213__private_extern__ CFRunLoopRef _getPMRunLoop(void) 214{ 215 static CFRunLoopRef pmRLS = NULL; 216 217 if (!pmRLS) { 218 pmRLS = CFRunLoopGetCurrent(); 219 } 220 221 return pmRLS; 222} 223 224__private_extern__ dispatch_queue_t _getPMDispatchQueue(void) 225{ 226 static dispatch_queue_t pmQ = NULL; 227 228 if (!pmQ) { 229 pmQ = dispatch_queue_create("Power Management configd queue", NULL); 230 } 231 232 return pmQ; 233} 234 235#ifndef __I_AM_PMSET__ 236__private_extern__ bool auditTokenHasEntitlement( 237 audit_token_t token, 238 CFStringRef entitlement) 239{ 240 SecTaskRef task = NULL; 241 CFTypeRef val = NULL; 242 bool caller_is_allowed = false; 243 CFErrorRef errorp = NULL; 244 245 task = SecTaskCreateWithAuditToken(kCFAllocatorDefault, token); 246 if (task) { 247 val = SecTaskCopyValueForEntitlement(task, entitlement, &errorp); 248 CFRelease(task); 249 250 if (kCFBooleanTrue == val) { 251 caller_is_allowed = true; 252 } 253 if (val) { 254 CFRelease(val); 255 } 256 } 257 return caller_is_allowed; 258 259} 260#endif 261 262__private_extern__ io_registry_entry_t getRootDomain(void) 263{ 264 static io_registry_entry_t gRoot = MACH_PORT_NULL; 265 266 if (MACH_PORT_NULL == gRoot) 267 gRoot = IORegistryEntryFromPath( kIOMasterPortDefault, 268 kIOPowerPlane ":/IOPowerConnection/IOPMrootDomain"); 269 270 return gRoot; 271} 272 273#define kIOPMSystemDefaultOverrideKey "SystemPowerProfileOverrideDict" 274 275__private_extern__ bool platformPluginLoaded(void) 276{ 277 static bool gPlatformPluginLoaded = false; 278 io_registry_entry_t rootDomain; 279 CFTypeRef prop; 280 281 if (gPlatformPluginLoaded) return (true); 282 283 rootDomain = getRootDomain(); 284 if (MACH_PORT_NULL == rootDomain) return (false); 285 prop = IORegistryEntryCreateCFProperty(rootDomain, CFSTR(kIOPMSystemDefaultOverrideKey), 286 kCFAllocatorDefault, kNilOptions); 287 if (prop) 288 { 289 gPlatformPluginLoaded = true; 290 CFRelease(prop); 291 } 292 return (gPlatformPluginLoaded); 293} 294 295__private_extern__ IOReturn 296_setRootDomainProperty( 297 CFStringRef key, 298 CFTypeRef val) 299{ 300 return IORegistryEntrySetCFProperty(getRootDomain(), key, val); 301} 302 303__private_extern__ CFTypeRef 304_copyRootDomainProperty( 305 CFStringRef key) 306{ 307 return IORegistryEntryCreateCFProperty(getRootDomain(), key, kCFAllocatorDefault, 0); 308} 309 310 311__private_extern__ bool 312_getUUIDString( 313 char *buf, 314 int buflen) 315{ 316 bool ret = false; 317 CFStringRef uuidString = NULL; 318 319 uuidString = IOPMSleepWakeCopyUUID(); 320 321 if (uuidString) { 322 if (!CFStringGetCString(uuidString, buf, buflen, 323 kCFStringEncodingUTF8)) 324 { 325 goto exit; 326 } 327 328 ret = true; 329 } 330exit: 331 if (uuidString) CFRelease(uuidString); 332 return ret; 333} 334 335__private_extern__ CFStringRef _updateSleepReason( ) 336{ 337 io_service_t iopm_rootdomain_ref = getRootDomain(); 338 339 if (reasons.sleepReason) CFRelease(reasons.sleepReason); 340 341 reasons.sleepReason = IORegistryEntryCreateCFProperty( 342 iopm_rootdomain_ref, 343 CFSTR("Last Sleep Reason"), 344 kCFAllocatorDefault, 0); 345 346 if (!isA_CFString(reasons.sleepReason)) 347 reasons.sleepReason = CFSTR(""); 348 349 return reasons.sleepReason; 350 351} 352 353__private_extern__ CFStringRef 354_getSleepReason() 355{ 356 return (reasons.sleepReason); 357} 358 359static bool 360_getSleepReasonLogStr( 361 char *buf, 362 int buflen) 363{ 364 bool ret = false; 365 io_service_t iopm_rootdomain_ref = getRootDomain(); 366 CFNumberRef sleepPID = NULL; 367 char reasonBuf[50]; 368 int spid = -1; 369 370 sleepPID = (CFNumberRef)IORegistryEntryCreateCFProperty( 371 iopm_rootdomain_ref, 372 CFSTR("SleepRequestedByPID"), 373 kCFAllocatorDefault, 0); 374 if (sleepPID && isA_CFNumber(sleepPID)) { 375 CFNumberGetValue(sleepPID, kCFNumberIntType, &spid); 376 } 377 if (reasons.sleepReason && isA_CFString(reasons.sleepReason)) 378 { 379 if (CFStringGetCString(reasons.sleepReason, reasonBuf, sizeof(reasonBuf), kCFStringEncodingUTF8)) 380 { 381 if (!strncmp(kIOPMSoftwareSleepKey, reasonBuf, strlen(kIOPMSoftwareSleepKey))) 382 { 383 snprintf(buf, buflen, "\'%s pid=%d\'", reasonBuf, spid); 384 } else { 385 snprintf(buf, buflen, "\'%s\'", reasonBuf); 386 } 387 ret = true; 388 } 389 } 390 if (sleepPID) CFRelease(sleepPID); 391 return ret; 392} 393 394__private_extern__ void _resetWakeReason( ) 395{ 396 if (reasons.platformWakeReason) CFRelease(reasons.platformWakeReason); 397 if (reasons.platformWakeType) CFRelease(reasons.platformWakeType); 398 if (reasons.claimedWake) CFRelease(reasons.claimedWake); 399 if (isA_CFArray(reasons.claimedWakeEventsArray)) CFRelease(reasons.claimedWakeEventsArray); 400 401 reasons.interpretedWake = NULL; 402 reasons.claimedWake = NULL; 403 reasons.claimedWakeEventsArray = NULL; 404 reasons.platformWakeReason = CFSTR(""); 405 reasons.platformWakeType = CFSTR(""); 406} 407 408#ifndef kIOPMDriverWakeEventsKey 409#define kIOPMDriverWakeEventsKey "IOPMDriverWakeEvents" 410#define kIOPMWakeEventTimeKey "Time" 411#define kIOPMWakeEventFlagsKey "Flags" 412#define kIOPMWakeEventReasonKey "Reason" 413#define kIOPMWakeEventDetailsKey "Details" 414#endif 415 416#define kWakeEventWiFiPrefix CFSTR("WiFi") 417#define kWakeEventEnetPrefix CFSTR("Enet") 418 419#ifndef __I_AM_PMSET__ 420static CFStringRef claimedReasonFromEventsArray(CFArrayRef wakeEvents) 421{ 422 CFDictionaryRef eachClaim = NULL; 423 CFStringRef eachReason = NULL; 424 int i = 0; 425 long count = 0; 426 427 if (!isA_CFArray(wakeEvents) || 428 !(count = CFArrayGetCount(wakeEvents))) { 429 return NULL; 430 } 431 432 for (i=0; i<count; i++) 433 { 434 eachClaim = CFArrayGetValueAtIndex(wakeEvents,i); 435 if (!isA_CFDictionary(eachClaim)) 436 continue; 437 eachReason = CFDictionaryGetValue(eachClaim, CFSTR(kIOPMWakeEventReasonKey)); 438 if (!isA_CFString(eachReason)) 439 continue; 440 if (CFStringHasPrefix(eachReason, kWakeEventWiFiPrefix) 441 || CFStringHasPrefix(eachReason, kWakeEventEnetPrefix)) 442 { 443 return CFRetain(eachReason); 444 } 445 } 446 return NULL; 447} 448 449 450__private_extern__ void _updateWakeReason 451 (CFStringRef *wakeReason, CFStringRef *wakeType) 452{ 453 _resetWakeReason(); 454 455 // This property may not exist on all platforms. 456 reasons.platformWakeReason = _copyRootDomainProperty(CFSTR(kIOPMRootDomainWakeReasonKey)); 457 if (!isA_CFString(reasons.platformWakeReason)) 458 reasons.platformWakeReason = CFSTR(""); 459 460 reasons.platformWakeType = _copyRootDomainProperty(CFSTR(kIOPMRootDomainWakeTypeKey)); 461 if (!isA_CFString(reasons.platformWakeType)) 462 reasons.platformWakeType = CFSTR(""); 463 464 reasons.claimedWakeEventsArray = _copyRootDomainProperty(CFSTR(kIOPMDriverWakeEventsKey)); 465 466 reasons.claimedWake = claimedReasonFromEventsArray(reasons.claimedWakeEventsArray); 467 if (reasons.claimedWake) { 468 reasons.interpretedWake = reasons.claimedWake; 469 mt2PublishWakeReason(reasons.platformWakeType, reasons.claimedWake); 470 } else { 471 reasons.interpretedWake = reasons.platformWakeType; 472 mt2PublishWakeReason(reasons.platformWakeType, reasons.platformWakeType); 473 } 474 475 getPlatformWakeReason(wakeReason, wakeType); 476 return ; 477} 478#endif 479 480__private_extern__ void getPlatformWakeReason 481 (CFStringRef *wakeReason, CFStringRef *wakeType) 482{ 483 484 if (!isA_CFString(reasons.platformWakeReason)) 485 reasons.platformWakeReason = CFSTR(""); 486 487 if (!isA_CFString(reasons.platformWakeType)) 488 reasons.platformWakeType = CFSTR(""); 489 490 if (wakeReason) *wakeReason = reasons.platformWakeReason; 491 if (wakeType) *wakeType = reasons.platformWakeType; 492 return ; 493 494} 495 496 497__private_extern__ bool 498_getHibernateState( 499 uint32_t *hibernateState) 500{ 501 bool ret = false; 502 io_service_t rootDomain = getRootDomain(); 503 CFDataRef hibStateData = NULL; 504 uint32_t *hibStatePtr; 505 506 // This property may not exist on all platforms. 507 hibStateData = IORegistryEntryCreateCFProperty( 508 rootDomain, 509 CFSTR(kIOHibernateStateKey), 510 kCFAllocatorDefault, 0); 511 512 if (isA_CFData(hibStateData) && 513 (CFDataGetLength(hibStateData) == sizeof(uint32_t)) && 514 (hibStatePtr = (uint32_t *)CFDataGetBytePtr(hibStateData))) 515 { 516 *hibernateState = *hibStatePtr; 517 ret = true; 518 } 519 520 if (hibStateData) CFRelease(hibStateData); 521 return ret; 522} 523 524__private_extern__ 525const char * getSleepTypeString(void) 526{ 527 const char *string = NULL; 528#if !TARGET_OS_EMBEDDED 529 io_service_t rootDomain = getRootDomain(); 530 bool isHibWake = false; 531 CFNumberRef sleepTypeNum; 532 uint32_t hibState; 533 534 sleepTypeNum = IORegistryEntryCreateCFProperty( 535 rootDomain, 536 CFSTR(kIOPMSystemSleepTypeKey), 537 kCFAllocatorDefault, 0); 538 539 if (_getHibernateState(&hibState) && 540 (hibState & kIOHibernateStateWakingFromHibernate)) 541 { 542 isHibWake = true; 543 } 544 545 if (isA_CFNumber(sleepTypeNum)) 546 { 547 int sleepType = kIOPMSleepTypeInvalid; 548 549 CFNumberGetValue(sleepTypeNum, kCFNumberIntType, &sleepType); 550 if (isHibWake) 551 { 552 // Hibernation types 553 switch (sleepType) 554 { 555 case kIOPMSleepTypeSafeSleep: 556 string = "Safe Sleep"; 557 break; 558 case kIOPMSleepTypeHibernate: 559 string = "Hibernate"; 560 break; 561 case kIOPMSleepTypeStandby: 562 string = "Standby"; 563 break; 564 case kIOPMSleepTypePowerOff: 565 string = "AutoPowerOff"; 566 break; 567 } 568 } 569 } 570 if (sleepTypeNum) 571 CFRelease(sleepTypeNum); 572 573#endif /* !TARGET_OS_EMBEDDED */ 574 return string; 575} 576 577__private_extern__ 578const char *stringForLWCode(uint8_t code) 579{ 580 const char *string; 581 switch (code) 582 { 583 default: 584 string = "OK"; 585 } 586 return string; 587} 588 589__private_extern__ 590const char *stringForPMCode(uint8_t code) 591{ 592 const char *string = ""; 593 594 switch (code) 595 { 596 case kIOPMTracePointSystemUp: 597 string = "On"; 598 break; 599 case kIOPMTracePointSleepStarted: 600 string = "SleepStarted"; 601 break; 602 case kIOPMTracePointSleepApplications: 603 string = "SleepApps"; 604 break; 605 case kIOPMTracePointSleepPriorityClients: 606 string = "SleepPriority"; 607 break; 608 case kIOPMTracePointSleepWillChangeInterests: 609 string = "SleepWillChangeInterests"; 610 break; 611 case kIOPMTracePointSleepPowerPlaneDrivers: 612 string = "SleepDrivers"; 613 break; 614 case kIOPMTracePointSleepDidChangeInterests: 615 string = "SleepDidChangeInterests"; 616 break; 617 case kIOPMTracePointSleepCapabilityClients: 618 string = "SleepCapabilityClients"; 619 break; 620 case kIOPMTracePointSleepPlatformActions: 621 string = "SleepPlatformActions"; 622 break; 623 case kIOPMTracePointSleepCPUs: 624 string = "SleepCPUs"; 625 break; 626 case kIOPMTracePointSleepPlatformDriver: 627 string = "SleepPlatformDriver"; 628 break; 629 case kIOPMTracePointSystemSleep: 630 string = "SleepPlatform"; 631 break; 632 case kIOPMTracePointHibernate: 633 string = "Hibernate"; 634 break; 635 case kIOPMTracePointWakePlatformDriver: 636 string = "WakePlatformDriver"; 637 break; 638 case kIOPMTracePointWakePlatformActions: 639 string = "WakePlatformActions"; 640 break; 641 case kIOPMTracePointWakeCPUs: 642 string = "WakeCPUs"; 643 break; 644 case kIOPMTracePointWakeWillPowerOnClients: 645 string = "WakeWillPowerOnClients"; 646 break; 647 case kIOPMTracePointWakeWillChangeInterests: 648 string = "WakeWillChangeInterests"; 649 break; 650 case kIOPMTracePointWakeDidChangeInterests: 651 string = "WakeDidChangeInterests"; 652 break; 653 case kIOPMTracePointWakePowerPlaneDrivers: 654 string = "WakeDrivers"; 655 break; 656 case kIOPMTracePointWakeCapabilityClients: 657 string = "WakeCapabilityClients"; 658 break; 659 case kIOPMTracePointWakeApplications: 660 string = "WakeApps"; 661 break; 662 case kIOPMTracePointSystemLoginwindowPhase: 663 string = "WakeLoginWindow"; 664 break; 665 case kIOPMTracePointDarkWakeEntry: 666 string = "DarkWakeEntry"; 667 break; 668 case kIOPMTracePointDarkWakeExit: 669 string = "DarkWakeExit"; 670 break; 671 } 672 return string; 673} 674 675 676static void sendNotification(int command) 677{ 678#if !TARGET_OS_EMBEDDED 679 CFMutableDictionaryRef dict = NULL; 680 int numberOfSeconds = 600; 681 682 CFNumberRef secondsValue = CFNumberCreate( NULL, kCFNumberIntType, &numberOfSeconds ); 683 CFNumberRef commandValue = CFNumberCreate( NULL, kCFNumberIntType, &command ); 684 685 dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 2, 686 &kCFTypeDictionaryKeyCallBacks, 687 &kCFTypeDictionaryValueCallBacks); 688 689 CFDictionarySetValue(dict, CFSTR(kPowerManagerActionKey), commandValue); 690 CFDictionarySetValue(dict, CFSTR(kPowerManagerValueKey), secondsValue); 691 692 CFNotificationCenterPostNotificationWithOptions ( 693 CFNotificationCenterGetDistributedCenter(), 694 CFSTR(kPowerManagerActionNotificationName), 695 NULL, dict, 696 (kCFNotificationPostToAllSessions | kCFNotificationDeliverImmediately)); 697 CFRelease(dict); 698 CFRelease(secondsValue); 699 CFRelease(commandValue); 700#endif 701} 702 703 704__private_extern__ void _askNicelyThenShutdownSystem(void) 705{ 706 sendNotification( PowerManagerScheduledShutdown ); 707} 708 709__private_extern__ void _askNicelyThenSleepSystem(void) 710{ 711 sendNotification( PowerManagerScheduledSleep ); 712} 713 714__private_extern__ void _askNicelyThenRestartSystem(void) 715{ 716 sendNotification( PowerManagerScheduledRestart ); 717} 718 719__private_extern__ CFAbsoluteTime _CFAbsoluteTimeFromPMEventTimeStamp(uint64_t kernelPackedTime) 720{ 721 uint32_t cal_sec = (uint32_t)(kernelPackedTime >> 32); 722 uint32_t cal_micro = (uint32_t)(kernelPackedTime & 0xFFFFFFFF); 723 CFAbsoluteTime timeKernelEpoch = (CFAbsoluteTime)(double)cal_sec + (double)cal_micro/1000.0; 724 725 // Adjust from kernel 1970 epoch to CF 2001 epoch 726 timeKernelEpoch -= kCFAbsoluteTimeIntervalSince1970; 727 728 return timeKernelEpoch; 729} 730 731#ifndef __I_AM_PMSET__ 732static bool _platformBackgroundTaskSupport = false; 733static bool _platformSleepServiceSupport = false; 734#endif 735 736 737static void sendEnergySettingsToKernel( 738 CFDictionaryRef useSettings, 739 bool removeUnsupportedSettings, 740 IOPMAggressivenessFactors *p) 741{ 742 io_registry_entry_t PMRootDomain = getRootDomain(); 743 io_connect_t PM_connection = MACH_PORT_NULL; 744 CFDictionaryRef _supportedCached = NULL; 745 CFStringRef providing_power = NULL; 746 CFNumberRef number1 = NULL; 747 CFNumberRef number0 = NULL; 748 CFNumberRef num = NULL; 749 uint32_t i; 750 751 i = 1; 752 number1 = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &i); 753 i = 0; 754 number0 = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &i); 755 756 if (!number0 || !number1) 757 goto exit; 758 759 PM_connection = IOPMFindPowerManagement(0); 760 761 if (!PM_connection) 762 goto exit; 763 764#ifdef __I_AM_PMSET__ 765 766 /* pmset calls IOPSCopyPowerSourcesInfo here; it doesn't link 767 * with powerd's alternative call getACtivePSType. 768 */ 769 CFTypeRef power_source_info = IOPSCopyPowerSourcesInfo(); 770 if(power_source_info) { 771 providing_power = IOPSGetProvidingPowerSourceType(power_source_info); 772 CFRelease(power_source_info); 773 } 774 775#else 776 /* powerd should call getActivePSType to determine active power source 777 * powred cannot call IOPSCopyPowerSourcesInfo() because that's 778 * a blocking IPC call into... powerd. 779 */ 780 781 // Determine type of power source 782 int powersource = getActivePSType(); 783 if (kIOPSProvidedByExternalBattery == powersource) { 784 providing_power = CFSTR(kIOPMUPSPowerKey); 785 } else if (kIOPSProvidedByBattery == powersource) { 786 providing_power = CFSTR(kIOPMBatteryPowerKey); 787 } else { 788 providing_power = CFSTR(kIOPMACPowerKey); 789 } 790#endif 791 792 // Grab a copy of RootDomain's supported energy saver settings 793 _supportedCached = IORegistryEntryCreateCFProperty(PMRootDomain, CFSTR("Supported Features"), kCFAllocatorDefault, kNilOptions); 794 795 IOPMSetAggressiveness(PM_connection, kPMMinutesToSleep, p->fMinutesToSleep); 796 IOPMSetAggressiveness(PM_connection, kPMMinutesToSpinDown, p->fMinutesToSpin); 797 IOPMSetAggressiveness(PM_connection, kPMMinutesToDim, p->fMinutesToDim); 798 799 800 // Wake on LAN 801 if(true == IOPMFeatureIsAvailableWithSupportedTable(CFSTR(kIOPMWakeOnLANKey), providing_power, _supportedCached)) 802 { 803 IOPMSetAggressiveness(PM_connection, kPMEthernetWakeOnLANSettings, p->fWakeOnLAN); 804 } else { 805 // Even if WakeOnLAN is reported as not supported, broadcast 0 as 806 // value. We may be on a supported machine, just on battery power. 807 // Wake on LAN is not supported on battery power on PPC hardware. 808 IOPMSetAggressiveness(PM_connection, kPMEthernetWakeOnLANSettings, 0); 809 } 810 811 // Display Sleep Uses Dim 812 if ( !removeUnsupportedSettings 813 || IOPMFeatureIsAvailableWithSupportedTable(CFSTR(kIOPMDisplaySleepUsesDimKey), providing_power, _supportedCached)) 814 { 815 IORegistryEntrySetCFProperty(PMRootDomain, 816 CFSTR(kIOPMSettingDisplaySleepUsesDimKey), 817 (p->fDisplaySleepUsesDimming?number1:number0)); 818 } 819 820 // Wake On Ring 821 if( !removeUnsupportedSettings 822 || IOPMFeatureIsAvailableWithSupportedTable(CFSTR(kIOPMWakeOnRingKey), providing_power, _supportedCached)) 823 { 824 IORegistryEntrySetCFProperty(PMRootDomain, 825 CFSTR(kIOPMSettingWakeOnRingKey), 826 (p->fWakeOnRing?number1:number0)); 827 } 828 829 // Automatic Restart On Power Loss, aka FileServer mode 830 if( !removeUnsupportedSettings 831 || IOPMFeatureIsAvailableWithSupportedTable(CFSTR(kIOPMRestartOnPowerLossKey), providing_power, _supportedCached)) 832 { 833 IORegistryEntrySetCFProperty(PMRootDomain, 834 CFSTR(kIOPMSettingRestartOnPowerLossKey), 835 (p->fAutomaticRestart?number1:number0)); 836 } 837 838 // Wake on change of AC state -- battery to AC or vice versa 839 if( !removeUnsupportedSettings 840 || IOPMFeatureIsAvailableWithSupportedTable(CFSTR(kIOPMWakeOnACChangeKey), providing_power, _supportedCached)) 841 { 842 IORegistryEntrySetCFProperty(PMRootDomain, 843 CFSTR(kIOPMSettingWakeOnACChangeKey), 844 (p->fWakeOnACChange?number1:number0)); 845 } 846 847 // Disable power button sleep on PowerMacs, Cubes, and iMacs 848 // Default is false == power button causes sleep 849 if( !removeUnsupportedSettings 850 || IOPMFeatureIsAvailableWithSupportedTable(CFSTR(kIOPMSleepOnPowerButtonKey), providing_power, _supportedCached)) 851 { 852 IORegistryEntrySetCFProperty(PMRootDomain, 853 CFSTR(kIOPMSettingSleepOnPowerButtonKey), 854 (p->fSleepOnPowerButton?kCFBooleanFalse:kCFBooleanTrue)); 855 } 856 857 // Wakeup on clamshell open 858 // Default is true == wakeup when the clamshell opens 859 if( !removeUnsupportedSettings 860 || IOPMFeatureIsAvailableWithSupportedTable(CFSTR(kIOPMWakeOnClamshellKey), providing_power, _supportedCached)) 861 { 862 IORegistryEntrySetCFProperty(PMRootDomain, 863 CFSTR(kIOPMSettingWakeOnClamshellKey), 864 (p->fWakeOnClamshell?number1:number0)); 865 } 866 867 // Mobile Motion Module 868 // Defaults to on 869 if( !removeUnsupportedSettings 870 || IOPMFeatureIsAvailableWithSupportedTable(CFSTR(kIOPMMobileMotionModuleKey), providing_power, _supportedCached)) 871 { 872 IORegistryEntrySetCFProperty(PMRootDomain, 873 CFSTR(kIOPMSettingMobileMotionModuleKey), 874 (p->fMobileMotionModule?number1:number0)); 875 } 876 877 /* 878 * GPU 879 */ 880 if( !removeUnsupportedSettings 881 || IOPMFeatureIsAvailableWithSupportedTable(CFSTR(kIOPMGPUSwitchKey), providing_power, _supportedCached)) 882 { 883 num = CFNumberCreate(0, kCFNumberIntType, &p->fGPU); 884 if (num) { 885 IORegistryEntrySetCFProperty(PMRootDomain, 886 CFSTR(kIOPMGPUSwitchKey), 887 num); 888 CFRelease(num); 889 } 890 } 891 892 // DeepSleepEnable 893 // Defaults to on 894 if( !removeUnsupportedSettings 895 || IOPMFeatureIsAvailableWithSupportedTable(CFSTR(kIOPMDeepSleepEnabledKey), providing_power, _supportedCached)) 896 { 897 IORegistryEntrySetCFProperty(PMRootDomain, 898 CFSTR(kIOPMDeepSleepEnabledKey), 899 (p->fDeepSleepEnable?kCFBooleanTrue:kCFBooleanFalse)); 900 } 901 902 // DeepSleepDelay 903 // In seconds 904 if( !removeUnsupportedSettings 905 || IOPMFeatureIsAvailableWithSupportedTable(CFSTR(kIOPMDeepSleepDelayKey), providing_power, _supportedCached)) 906 { 907 num = CFNumberCreate(0, kCFNumberIntType, &p->fDeepSleepDelay); 908 if (num) { 909 IORegistryEntrySetCFProperty(PMRootDomain, 910 CFSTR(kIOPMDeepSleepDelayKey), 911 num); 912 CFRelease(num); 913 } 914 } 915 916 // AutoPowerOffEnable 917 // Defaults to on 918 if( !removeUnsupportedSettings 919 || IOPMFeatureIsAvailableWithSupportedTable(CFSTR(kIOPMAutoPowerOffEnabledKey), providing_power, _supportedCached)) 920 { 921 IORegistryEntrySetCFProperty(PMRootDomain, 922 CFSTR(kIOPMAutoPowerOffEnabledKey), 923 (p->fAutoPowerOffEnable?kCFBooleanTrue:kCFBooleanFalse)); 924 } 925 926 // AutoPowerOffDelay 927 // In seconds 928 if( !removeUnsupportedSettings 929 || IOPMFeatureIsAvailableWithSupportedTable(CFSTR(kIOPMAutoPowerOffDelayKey), providing_power, _supportedCached)) 930 { 931 num = CFNumberCreate(0, kCFNumberIntType, &p->fAutoPowerOffDelay); 932 if (num) { 933 IORegistryEntrySetCFProperty(PMRootDomain, 934 CFSTR(kIOPMAutoPowerOffDelayKey), 935 num); 936 CFRelease(num); 937 } 938 } 939 940#ifndef __I_AM_PMSET__ 941 if ( !_platformSleepServiceSupport && !_platformBackgroundTaskSupport) 942 { 943 bool ssupdate, btupdate, pnupdate; 944 945 // On legacy systems, IOPPF publishes PowerNap support using 946 // the kIOPMDarkWakeBackgroundTaskKey and/or 947 // kIOPMSleepServicesKey 948 btupdate = IOPMFeatureIsAvailableWithSupportedTable( 949 CFSTR(kIOPMDarkWakeBackgroundTaskKey), 950 providing_power, _supportedCached); 951 ssupdate = IOPMFeatureIsAvailableWithSupportedTable( 952 CFSTR(kIOPMSleepServicesKey), 953 providing_power, _supportedCached); 954 955 // But going forward (late 2012 machines and beyond), IOPPF will publish 956 // PowerNap support as a PM feature using the kIOPMPowerNapSupportedKey 957 pnupdate = IOPMFeatureIsAvailableWithSupportedTable( 958 CFSTR(kIOPMPowerNapSupportedKey), 959 providing_power, _supportedCached); 960 961 // We have to check for one of either 'legacy' or 'modern' PowerNap 962 // support and configure BT assertion and other powerd-internal PowerNap 963 // settings accordingly 964 if (ssupdate || btupdate || pnupdate) { 965 _platformSleepServiceSupport = ssupdate; 966 _platformBackgroundTaskSupport = btupdate; 967 configAssertionType(kBackgroundTaskType, false); 968 mt2EvaluateSystemSupport(); 969 } 970 } 971#endif 972 973 if (useSettings) 974 { 975 bool isDesktop = (0 == _batteryCount()); 976 ProcessHibernateSettings(useSettings, p->fDeepSleepEnable, isDesktop, PMRootDomain); 977 } 978 979exit: 980 if (number0) { 981 CFRelease(number0); 982 } 983 if (number1) { 984 CFRelease(number1); 985 } 986 if (IO_OBJECT_NULL != PM_connection) { 987 IOServiceClose(PM_connection); 988 } 989 if (_supportedCached) { 990 CFRelease(_supportedCached); 991 } 992 return; 993} 994 995/* getAggressivenessValue 996 * 997 * returns true if the setting existed in the dictionary 998 */ 999__private_extern__ bool getAggressivenessValue( 1000 CFDictionaryRef dict, 1001 CFStringRef key, 1002 CFNumberType type, 1003 uint32_t *ret) 1004{ 1005 CFTypeRef obj = CFDictionaryGetValue(dict, key); 1006 1007 *ret = 0; 1008 if (isA_CFNumber(obj)) 1009 { 1010 CFNumberGetValue(obj, type, ret); 1011 return true; 1012 } 1013 else if (isA_CFBoolean(obj)) 1014 { 1015 *ret = CFBooleanGetValue(obj); 1016 return true; 1017 } 1018 return false; 1019} 1020 1021/* For internal use only */ 1022static int getAggressivenessFactorsFromProfile( 1023 CFDictionaryRef p, 1024 IOPMAggressivenessFactors *agg) 1025{ 1026 if( !agg || !p ) { 1027 return -1; 1028 } 1029 1030 getAggressivenessValue(p, CFSTR(kIOPMDisplaySleepKey), kCFNumberSInt32Type, &agg->fMinutesToDim); 1031 getAggressivenessValue(p, CFSTR(kIOPMDiskSleepKey), kCFNumberSInt32Type, &agg->fMinutesToSpin); 1032 getAggressivenessValue(p, CFSTR(kIOPMSystemSleepKey), kCFNumberSInt32Type, &agg->fMinutesToSleep); 1033 getAggressivenessValue(p, CFSTR(kIOPMWakeOnLANKey), kCFNumberSInt32Type, &agg->fWakeOnLAN); 1034 getAggressivenessValue(p, CFSTR(kIOPMWakeOnRingKey), kCFNumberSInt32Type, &agg->fWakeOnRing); 1035 getAggressivenessValue(p, CFSTR(kIOPMRestartOnPowerLossKey), kCFNumberSInt32Type, &agg->fAutomaticRestart); 1036 getAggressivenessValue(p, CFSTR(kIOPMSleepOnPowerButtonKey), kCFNumberSInt32Type, &agg->fSleepOnPowerButton); 1037 getAggressivenessValue(p, CFSTR(kIOPMWakeOnClamshellKey), kCFNumberSInt32Type, &agg->fWakeOnClamshell); 1038 getAggressivenessValue(p, CFSTR(kIOPMWakeOnACChangeKey), kCFNumberSInt32Type, &agg->fWakeOnACChange); 1039 getAggressivenessValue(p, CFSTR(kIOPMDisplaySleepUsesDimKey), kCFNumberSInt32Type, &agg->fDisplaySleepUsesDimming); 1040 getAggressivenessValue(p, CFSTR(kIOPMMobileMotionModuleKey), kCFNumberSInt32Type, &agg->fMobileMotionModule); 1041 getAggressivenessValue(p, CFSTR(kIOPMGPUSwitchKey), kCFNumberSInt32Type, &agg->fGPU); 1042 getAggressivenessValue(p, CFSTR(kIOPMDeepSleepEnabledKey), kCFNumberSInt32Type, &agg->fDeepSleepEnable); 1043 getAggressivenessValue(p, CFSTR(kIOPMDeepSleepDelayKey), kCFNumberSInt32Type, &agg->fDeepSleepDelay); 1044 getAggressivenessValue(p, CFSTR(kIOPMAutoPowerOffEnabledKey), kCFNumberSInt32Type, &agg->fAutoPowerOffEnable); 1045 getAggressivenessValue(p, CFSTR(kIOPMAutoPowerOffDelayKey), kCFNumberSInt32Type, &agg->fAutoPowerOffDelay); 1046 1047 return 0; 1048} 1049 1050__private_extern__ IOReturn ActivatePMSettings( 1051 CFDictionaryRef useSettings, 1052 bool removeUnsupportedSettings) 1053{ 1054 IOPMAggressivenessFactors theFactors; 1055 1056 if(!isA_CFDictionary(useSettings)) 1057 { 1058 return kIOReturnBadArgument; 1059 } 1060 1061 // Activate settings by sending them to the multiple owning drivers kernel 1062 getAggressivenessFactorsFromProfile(useSettings, &theFactors); 1063 1064 sendEnergySettingsToKernel(useSettings, removeUnsupportedSettings, &theFactors); 1065 1066#ifndef __I_AM_PMSET__ 1067 evalAllUserActivityAssertions(theFactors.fMinutesToDim); 1068 evalAllNetworkAccessAssertions(); 1069#endif 1070 1071 return kIOReturnSuccess; 1072} 1073 1074 1075 1076#define kIOPMAppName "Power Management configd plugin" 1077#define kIOPMPrefsPath "com.apple.PowerManagement.xml" 1078 1079IOReturn _getLowCapRatioTime(CFStringRef batterySerialNumber, 1080 boolean_t *hasLowCapRatio, 1081 time_t *since) 1082{ 1083 IOReturn ret = kIOReturnError; 1084 1085#if !TARGET_OS_EMBEDDED 1086 SCPreferencesRef energyPrefs = NULL; // must release 1087 CFPropertyListRef plist = NULL; // do not release 1088 CFNumberRef num = NULL; // do not release 1089 1090 if (!hasLowCapRatio || !since || !isA_CFString(batterySerialNumber)) { 1091 return ret; 1092 } 1093 1094 *hasLowCapRatio = false; 1095 *since = 0; 1096 1097 energyPrefs = SCPreferencesCreate(kCFAllocatorDefault, 1098 CFSTR(kIOPMAppName), 1099 CFSTR(kIOPMPrefsPath)); 1100 if (!energyPrefs) { 1101 goto exit; 1102 } 1103 1104 if (!SCPreferencesLock(energyPrefs, true)) { 1105 ret = kIOReturnInternalError; 1106 goto exit; 1107 } 1108 1109 plist = SCPreferencesGetValue(energyPrefs, CFSTR("BatteryWarn")); 1110 if (!plist || CFGetTypeID(plist) != CFDictionaryGetTypeID()) { 1111 SCPreferencesUnlock(energyPrefs); 1112 goto exit; 1113 } 1114 1115 SCPreferencesUnlock(energyPrefs); 1116 1117 num = CFDictionaryGetValue(plist, batterySerialNumber); 1118 if (num && CFNumberGetTypeID() == CFGetTypeID(num)) { 1119 if (!CFNumberGetValue(num, CFNumberGetType(num), since)) { 1120 goto exit; 1121 } 1122 *hasLowCapRatio = true; 1123 } 1124 1125 ret = kIOReturnSuccess; 1126 1127exit: 1128 if (energyPrefs) CFRelease(energyPrefs); 1129 1130#endif 1131 return ret; 1132} 1133 1134IOReturn _setLowCapRatioTime(CFStringRef batterySerialNumber, 1135 boolean_t hasLowCapRatio, 1136 time_t since) 1137{ 1138 IOReturn ret = kIOReturnError; 1139#if !TARGET_OS_EMBEDDED 1140 boolean_t contains = false; 1141 boolean_t locked = false; 1142 1143 SCPreferencesRef energyPrefs = NULL; // must release 1144 CFMutableDictionaryRef dict = NULL; // must release 1145 CFNumberRef num = NULL; // must release 1146 CFPropertyListRef plist = NULL; // do not release 1147 1148 if (!isA_CFString(batterySerialNumber)) 1149 goto exit; 1150 1151 energyPrefs = SCPreferencesCreate(kCFAllocatorDefault, 1152 CFSTR(kIOPMAppName), 1153 CFSTR(kIOPMPrefsPath)); 1154 if (!energyPrefs) { 1155 goto exit; 1156 } 1157 1158 if (!SCPreferencesLock(energyPrefs, true)) { 1159 ret = kIOReturnInternalError; 1160 goto exit; 1161 } 1162 1163 locked = true; 1164 1165 plist = SCPreferencesGetValue(energyPrefs, CFSTR("BatteryWarn")); 1166 1167 if (!plist) { 1168 contains = false; 1169 } 1170 else { 1171 if (CFGetTypeID(plist) != CFDictionaryGetTypeID()) { 1172 goto exit; 1173 } 1174 contains = CFDictionaryContainsKey(plist, batterySerialNumber); 1175 } 1176 1177 if (!(hasLowCapRatio ^ contains)) { 1178 // no change needed 1179 ret = kIOReturnSuccess; 1180 goto exit; 1181 } 1182 1183 // need to make changes to the SCPreferencesRef 1184 1185 if (!plist) { 1186 dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 1187 0, 1188 &kCFTypeDictionaryKeyCallBacks, 1189 &kCFTypeDictionaryValueCallBacks); 1190 if (!dict) { 1191 goto exit; 1192 } 1193 } 1194 else { 1195 dict = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 1196 0, 1197 plist); 1198 if (!dict) { 1199 goto exit; 1200 } 1201 } 1202 1203 if (hasLowCapRatio && !contains) { 1204 // need to add entry 1205 num = CFNumberCreate(kCFAllocatorDefault, 1206 kCFNumberSInt64Type, 1207 &since); 1208 CFDictionarySetValue(dict, batterySerialNumber, num); 1209 CFRelease(num); 1210 } 1211 1212 if (!hasLowCapRatio && contains) { 1213 // need to remove entry 1214 CFDictionaryRemoveValue(dict, batterySerialNumber); 1215 } 1216 1217 if (CFDictionaryGetCount(dict) == 0) { 1218 // if dictionary is empty, remove it from the SCPreferences. 1219 if (!SCPreferencesRemoveValue(energyPrefs, CFSTR("BatteryWarn"))) { 1220 goto exit; 1221 } 1222 } 1223 else { 1224 if (!SCPreferencesSetValue(energyPrefs, 1225 CFSTR("BatteryWarn"), 1226 dict)) { 1227 goto exit; 1228 } 1229 } 1230 1231 if (!SCPreferencesCommitChanges(energyPrefs)) 1232 { 1233 // handle error 1234 if (kSCStatusAccessError == SCError()) ret = kIOReturnNotPrivileged; 1235 else ret = kIOReturnError; 1236 goto exit; 1237 } 1238 1239 if (!SCPreferencesApplyChanges(energyPrefs)) 1240 { 1241 // handle error 1242 if (kSCStatusAccessError == SCError()) ret = kIOReturnNotPrivileged; 1243 else ret = kIOReturnError; 1244 goto exit; 1245 } 1246 1247 ret = kIOReturnSuccess; 1248 1249exit: 1250 if (locked) SCPreferencesUnlock(energyPrefs); 1251 1252 if (energyPrefs) CFRelease(energyPrefs); 1253 if (dict) CFRelease(dict); 1254 1255#endif 1256 return ret; 1257} 1258 1259static void _unpackBatteryState(IOPMBattery *b, CFDictionaryRef prop) 1260{ 1261 CFBooleanRef boo; 1262 CFNumberRef n; 1263 1264 if(!isA_CFDictionary(prop)) return; 1265 1266 boo = CFDictionaryGetValue(prop, CFSTR(kIOPMPSExternalConnectedKey)); 1267 b->externalConnected = (kCFBooleanTrue == boo); 1268 1269 boo = CFDictionaryGetValue(prop, CFSTR(kIOPMPSExternalChargeCapableKey)); 1270 b->externalChargeCapable = (kCFBooleanTrue == boo); 1271 1272 boo = CFDictionaryGetValue(prop, CFSTR(kIOPMPSBatteryInstalledKey)); 1273 b->isPresent = (kCFBooleanTrue == boo); 1274 1275 boo = CFDictionaryGetValue(prop, CFSTR(kIOPMPSIsChargingKey)); 1276 b->isCharging = (kCFBooleanTrue == boo); 1277 1278#if TARGET_OS_EMBEDDED 1279 boo = CFDictionaryGetValue(prop, CFSTR(kIOPMPSAtCriticalLevelKey)); 1280 b->isCritical = (kCFBooleanTrue == boo); 1281 1282 boo = CFDictionaryGetValue(prop, CFSTR(kIOPMPSRestrictedModeKey)); 1283 b->isRestricted = (kCFBooleanTrue == boo); 1284#endif 1285 1286 b->failureDetected = (CFStringRef)CFDictionaryGetValue(prop, CFSTR(kIOPMPSErrorConditionKey)); 1287 1288 b->batterySerialNumber = (CFStringRef)CFDictionaryGetValue(prop, CFSTR("BatterySerialNumber")); 1289 1290 b->chargeStatus = (CFStringRef)CFDictionaryGetValue(prop, CFSTR(kIOPMPSBatteryChargeStatusKey)); 1291 1292 _getLowCapRatioTime(b->batterySerialNumber, 1293 &(b->hasLowCapRatio), 1294 &(b->lowCapRatioSinceTime)); 1295 1296 n = CFDictionaryGetValue(prop, CFSTR(kIOPMPSVoltageKey)); 1297 if(n) { 1298 CFNumberGetValue(n, kCFNumberIntType, &b->voltage); 1299 } 1300 n = CFDictionaryGetValue(prop, CFSTR(kIOPMPSCurrentCapacityKey)); 1301 if(n) { 1302 CFNumberGetValue(n, kCFNumberIntType, &b->currentCap); 1303 } 1304 n = CFDictionaryGetValue(prop, CFSTR(kIOPMPSMaxCapacityKey)); 1305 if(n) { 1306 CFNumberGetValue(n, kCFNumberIntType, &b->maxCap); 1307 } 1308 n = CFDictionaryGetValue(prop, CFSTR(kIOPMPSDesignCapacityKey)); 1309 if(n) { 1310 CFNumberGetValue(n, kCFNumberIntType, &b->designCap); 1311 } 1312 n = CFDictionaryGetValue(prop, CFSTR(kIOPMPSTimeRemainingKey)); 1313 if(n) { 1314 CFNumberGetValue(n, kCFNumberIntType, &b->hwAverageTR); 1315 } 1316 1317 1318 n = CFDictionaryGetValue(prop, CFSTR("InstantAmperage")); 1319 if(n) { 1320 CFNumberGetValue(n, kCFNumberIntType, &b->instantAmperage); 1321 } 1322 n = CFDictionaryGetValue(prop, CFSTR(kIOPMPSAmperageKey)); 1323 if(n) { 1324 CFNumberGetValue(n, kCFNumberIntType, &b->avgAmperage); 1325 } 1326 n = CFDictionaryGetValue(prop, CFSTR(kIOPMPSMaxErrKey)); 1327 if(n) { 1328 CFNumberGetValue(n, kCFNumberIntType, &b->maxerr); 1329 } 1330 n = CFDictionaryGetValue(prop, CFSTR(kIOPMPSCycleCountKey)); 1331 if(n) { 1332 CFNumberGetValue(n, kCFNumberIntType, &b->cycleCount); 1333 } 1334 n = CFDictionaryGetValue(prop, CFSTR(kIOPMPSLocationKey)); 1335 if(n) { 1336 CFNumberGetValue(n, kCFNumberIntType, &b->location); 1337 } 1338 n = CFDictionaryGetValue(prop, CFSTR(kIOPMPSInvalidWakeSecondsKey)); 1339 if(n) { 1340 CFNumberGetValue(n, kCFNumberIntType, &b->invalidWakeSecs); 1341 } else { 1342 b->invalidWakeSecs = kInvalidWakeSecsDefault; 1343 } 1344 n = CFDictionaryGetValue(prop, CFSTR("PermanentFailureStatus")); 1345 if (n) { 1346 CFNumberGetValue(n, kCFNumberIntType, &b->pfStatus); 1347 } else { 1348 b->pfStatus = 0; 1349 } 1350 1351 return; 1352} 1353 1354/* 1355 * _batteries 1356 */ 1357__private_extern__ IOPMBattery **_batteries(void) 1358{ 1359 return physicalBatteriesArray; 1360} 1361 1362/* 1363 * _batteryCount 1364 */ 1365__private_extern__ int _batteryCount(void) 1366{ 1367 return ((int)physicalBatteriesCount); 1368} 1369 1370__private_extern__ IOPMBattery *_newBatteryFound(io_registry_entry_t where) 1371{ 1372 IOPMBattery *new_battery = NULL; 1373 static int new_battery_index = 0; 1374 // Populate new battery in array 1375 new_battery = calloc(1, sizeof(IOPMBattery)); 1376 new_battery->me = where; 1377 new_battery->name = CFStringCreateWithFormat( 1378 kCFAllocatorDefault, 1379 NULL, 1380 CFSTR("InternalBattery-%d"), 1381 new_battery_index); 1382 1383 new_battery_index++; 1384 _batteryChanged(new_battery); 1385 1386 /* Real, physical battery found */ 1387 if (!physicalBatteriesSet) { 1388 physicalBatteriesSet = CFSetCreateMutable(0, 1, NULL); 1389 } 1390 CFSetAddValue(physicalBatteriesSet, new_battery); 1391 physicalBatteriesCount = CFSetGetCount(physicalBatteriesSet); 1392 if (physicalBatteriesArray) { 1393 free(physicalBatteriesArray); 1394 physicalBatteriesArray = NULL; 1395 } 1396 physicalBatteriesArray = (IOPMBattery **)calloc(physicalBatteriesCount, sizeof(IOPMBattery *)); 1397 CFSetGetValues(physicalBatteriesSet, (const void **)physicalBatteriesArray); 1398 1399 // TODO: We should really be posting this kIOPSNotifyAttach from 1400 // BatteryTimeRemaining.c, not right here. 1401 notify_post(kIOPSNotifyAttach); 1402 1403 return new_battery; 1404} 1405 1406 1407__private_extern__ void _batteryChanged(IOPMBattery *changed_battery) 1408{ 1409 kern_return_t kr; 1410 1411 if(!changed_battery) { 1412 // This is unexpected; we're not tracking this battery 1413 return; 1414 } 1415 1416 // Free the last set of properties 1417 if(changed_battery->properties) { 1418 CFRelease(changed_battery->properties); 1419 changed_battery->properties = NULL; 1420 } 1421 1422 kr = IORegistryEntryCreateCFProperties( 1423 changed_battery->me, 1424 &(changed_battery->properties), 1425 kCFAllocatorDefault, 0); 1426 if(KERN_SUCCESS != kr) { 1427 changed_battery->properties = NULL; 1428 goto exit; 1429 } 1430 1431 _unpackBatteryState(changed_battery, changed_battery->properties); 1432exit: 1433 return; 1434} 1435 1436__private_extern__ bool _batteryHas(IOPMBattery *b, CFStringRef property) 1437{ 1438 if(!property || !b->properties) return false; 1439 1440 // If the battery's descriptior dictionary has an entry at all for the 1441 // given 'property' it is supported, i.e. the battery 'has' it. 1442 return CFDictionaryGetValue(b->properties, property) ? true : false; 1443} 1444 1445 1446#if HAVE_CF_USER_NOTIFICATION 1447 1448__private_extern__ CFUserNotificationRef _copyUPSWarning(void) 1449{ 1450 CFMutableDictionaryRef alert_dict; 1451 SInt32 error; 1452 CFUserNotificationRef note_ref; 1453 CFBundleRef myBundle; 1454 CFStringRef header_unlocalized; 1455 CFStringRef message_unlocalized; 1456 CFURLRef bundle_url; 1457 1458 myBundle = CFBundleGetBundleWithIdentifier(kPowerdBundleIdentifier); 1459 if (!myBundle) 1460 return NULL; 1461 1462 alert_dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, 1463 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 1464 if(!alert_dict) 1465 return NULL; 1466 1467 bundle_url = CFBundleCopyBundleURL(myBundle); 1468 CFDictionarySetValue(alert_dict, kCFUserNotificationLocalizationURLKey, bundle_url); 1469 CFRelease(bundle_url); 1470 1471 header_unlocalized = CFSTR("WARNING!"); 1472 message_unlocalized = CFSTR("YOUR COMPUTER IS NOW RUNNING ON UPS BACKUP BATTERY. SAVE YOUR DOCUMENTS AND SHUTDOWN SOON."); 1473 1474 CFDictionaryAddValue(alert_dict, kCFUserNotificationAlertHeaderKey, header_unlocalized); 1475 CFDictionaryAddValue(alert_dict, kCFUserNotificationAlertMessageKey, message_unlocalized); 1476 1477 note_ref = CFUserNotificationCreate(kCFAllocatorDefault, 0, 0, &error, alert_dict); 1478 CFRelease(alert_dict); 1479 1480 asl_log(0, 0, ASL_LEVEL_ERR, "PowerManagement: UPS low power warning\n"); 1481 1482 return note_ref; 1483} 1484 1485#endif 1486 1487/***************************************************************************/ 1488/***************************************************************************/ 1489/***************************************************************************/ 1490/* AAAAAAAAAAAAAA SSSSSSSSSSSSSS LLLLLLLLLLLLLLLL */ 1491/***************************************************************************/ 1492/***************************************************************************/ 1493/***************************************************************************/ 1494 1495static bool powerString(char *powerBuf, int bufSize) 1496{ 1497 IOPMBattery **batteries; 1498 int batteryCount = 0; 1499 uint32_t capPercent = 0; 1500 int i; 1501 batteryCount = _batteryCount(); 1502 if (0 < batteryCount) { 1503 batteries = _batteries(); 1504 for (i=0; i< batteryCount; i++) { 1505 if (batteries[i]->isPresent 1506 && (0 != batteries[i]->maxCap)) { 1507 capPercent += (batteries[i]->currentCap * 100) / batteries[i]->maxCap; 1508 } 1509 } 1510 snprintf(powerBuf, bufSize, "%s \(Charge:%d%%)", 1511 batteries[0]->externalConnected ? "Using AC":"Using BATT", capPercent); 1512 return true; 1513 } else { 1514 snprintf(powerBuf, bufSize, "Using AC"); 1515 return false; 1516 } 1517} 1518 1519static void printCapabilitiesToBuf(char *buf, int buf_size, IOPMCapabilityBits in_caps) 1520{ 1521 uint64_t caps = (uint64_t)in_caps; 1522 1523// snprintf(buf, buf_size, "%s:%s%s%s%s%s%s%s", 1524// on_sleep_dark, 1525 snprintf(buf, buf_size, " [%s%s%s%s%s%s%s]", 1526 (caps & kIOPMCapabilityCPU) ? "C":"<off> ", 1527 (caps & kIOPMCapabilityDisk) ? "D":"", 1528 (caps & kIOPMCapabilityNetwork) ? "N":"", 1529 (caps & kIOPMCapabilityVideo) ? "V":"", 1530 (caps & kIOPMCapabilityAudio) ? "A":"", 1531 (caps & kIOPMCapabilityPushServiceTask) ? "P":"", 1532 (caps & kIOPMCapabilityBackgroundTask) ? "B":""); 1533} 1534 1535 1536__private_extern__ aslmsg new_msg_pmset_log(void) 1537{ 1538 aslmsg m = asl_new(ASL_TYPE_MSG); 1539 1540 asl_set(m, ASL_KEY_LEVEL, ASL_STRING_NOTICE); 1541 asl_set(m, ASL_KEY_FACILITY, kPMFacility); 1542 1543 return m; 1544} 1545 1546 1547__private_extern__ void logASLMessagePMStart(void) 1548{ 1549 aslmsg m; 1550 char uuidString[150]; 1551 1552 m = new_msg_pmset_log(); 1553 1554 if (_getUUIDString(uuidString, sizeof(uuidString))) { 1555 asl_set(m, kPMASLUUIDKey, uuidString); 1556 } 1557 asl_set(m, kPMASLDomainKey, kPMASLDomainPMStart); 1558 asl_set(m, ASL_KEY_MSG, "powerd process is started\n"); 1559 asl_send(NULL, m); 1560 asl_release(m); 1561} 1562 1563#if TCPKEEPALIVE 1564#ifndef __I_AM_PMSET__ 1565 1566static void attachTCPKeepAliveKeys( 1567 aslmsg m, 1568 char *tcpString, 1569 unsigned int tcpStringLen) 1570 1571{ 1572 CFTypeRef platformSupport = NULL; 1573 char keepAliveString[100]; 1574 1575 IOPlatformCopyFeatureDefault(kIOPlatformTCPKeepAliveDuringSleep, &platformSupport); 1576 if (kCFBooleanTrue == platformSupport) 1577 { 1578 asl_set(m, kPMASLTCPKeepAlive, "supported"); 1579 1580 getTCPKeepAliveState(keepAliveString, sizeof(keepAliveString)); 1581 1582 asl_set(m, kPMASLTCPKeepAliveExpired, keepAliveString); 1583 snprintf(tcpString, tcpStringLen, "TCPKeepAlive=%s", keepAliveString); 1584 } 1585 1586 if (platformSupport){ 1587 CFRelease(platformSupport); 1588 } 1589} 1590 1591#else 1592 1593static void attachTCPKeepAliveKeys( 1594 aslmsg m __unused, 1595 char *tcpString __unused, 1596 unsigned int tcpStringLen __unused) 1597{ 1598 return; 1599} 1600 1601#endif 1602#endif 1603 1604__private_extern__ void logASLMessageSleep( 1605 const char *sig, 1606 const char *uuidStr, 1607 const char *failureStr, 1608 int sleepType 1609) 1610{ 1611 static int sleepCyclesCount = 0; 1612 aslmsg m; 1613 char uuidString[150]; 1614 char powerLevelBuf[50]; 1615 char numbuf[15]; 1616 bool success = true; 1617 char messageString[200]; 1618 char reasonString[100]; 1619 char tcpKeepAliveString[50]; 1620 1621 m = new_msg_pmset_log(); 1622 1623 reasonString[0] = messageString[0] = tcpKeepAliveString[0] = powerLevelBuf[0] = 0; 1624 1625 _getSleepReasonLogStr(reasonString, sizeof(reasonString)); 1626 1627 if (!strncmp(sig, kPMASLSigSuccess, sizeof(kPMASLSigSuccess))) 1628 { 1629 success = true; 1630 if (sleepType == kIsS0Sleep) { 1631 snprintf(messageString, sizeof(messageString), "Entering Sleep state"); 1632#if !TARGET_OS_EMBEDDED && TCPKEEPALIVE 1633 attachTCPKeepAliveKeys(m, tcpKeepAliveString, sizeof(tcpKeepAliveString)); 1634#endif 1635 } else { 1636 snprintf(messageString, sizeof(messageString), "Entering DarkWake state"); 1637 } 1638 1639 if (reasonString[0] != 0) { 1640 snprintf(messageString, sizeof(messageString), "%s due to %s", messageString, reasonString); 1641 } 1642 1643 } else { 1644 success = false; 1645 snprintf(messageString, sizeof(messageString), "Sleep Failure [code:%s]", 1646 failureStr); 1647 } 1648 1649 if (success) { 1650 // Value == Sleep Cycles Count 1651 // Note: unknown on the failure case, so we won't publish the sleep count 1652 // unless sig == success 1653 snprintf(numbuf, 10, "%d", ++sleepCyclesCount); 1654 asl_set(m, kPMASLValueKey, numbuf); 1655 asl_set(m, kPMASLDomainKey, kPMASLDomainPMSleep); 1656 powerString(powerLevelBuf, sizeof(powerLevelBuf)); 1657 } 1658 else { 1659 asl_set(m, kPMASLDomainKey, kPMASLDomainSWFailure); 1660 } 1661 1662 // UUID 1663 if (uuidStr) { 1664 asl_set(m, kPMASLUUIDKey, uuidStr); // Caller Provided 1665 } else if (_getUUIDString(uuidString, sizeof(uuidString))) { 1666 asl_set(m, kPMASLUUIDKey, uuidString); 1667 } 1668 1669 1670 snprintf(messageString, sizeof(messageString), "%s: %s %s", 1671 messageString, powerLevelBuf, tcpKeepAliveString); 1672 1673 asl_set(m, kPMASLSignatureKey, sig); 1674 asl_set(m, ASL_KEY_MSG, messageString); 1675 asl_send(NULL, m); 1676 asl_release(m); 1677} 1678 1679/*****************************************************************************/ 1680#pragma mark ASL 1681 1682__private_extern__ void logASLMessageWake( 1683 const char *sig, 1684 const char *uuidStr, 1685 const char *failureStr, 1686 IOPMCapabilityBits in_capabilities, 1687 WakeTypeEnum dark_wake 1688) 1689{ 1690 aslmsg m; 1691 int i = 0; 1692 CFStringRef tmpStr = NULL; 1693 char buf[200]; 1694 char powerLevelBuf[50]; 1695 char wakeReasonBuf[50]; 1696 char cBuf[50]; 1697 const char * detailString = NULL; 1698 static int darkWakeCnt = 0; 1699 char numbuf[15]; 1700 static char prev_uuid[50]; 1701 CFStringRef wakeType = NULL; 1702 const char * sleepTypeString; 1703 bool success = true; 1704 1705 m = new_msg_pmset_log(); 1706 1707 asl_set(m, kPMASLSignatureKey, sig); 1708 if (_getUUIDString(buf, sizeof(buf))) { 1709 asl_set(m, kPMASLUUIDKey, buf); 1710 if (strncmp(buf, prev_uuid, sizeof(prev_uuid))) { 1711 // New sleep/wake cycle. 1712 snprintf(prev_uuid, sizeof(prev_uuid), "%s", buf); 1713 darkWakeCnt = 0; 1714 } 1715 } 1716 1717 buf[0] = powerLevelBuf[0] = 0; 1718 if (!strncmp(sig, kPMASLSigSuccess, sizeof(kPMASLSigSuccess))) 1719 { 1720 char wakeTypeBuf[50]; 1721 1722 wakeReasonBuf[0] = wakeTypeBuf[0] = 0; 1723 if (isA_CFString(reasons.platformWakeReason)) { 1724 CFStringGetCString(reasons.platformWakeReason, wakeReasonBuf, sizeof(wakeReasonBuf), kCFStringEncodingUTF8); 1725 } 1726 if (isA_CFString(reasons.platformWakeType)) { 1727 CFStringGetCString(reasons.platformWakeType, wakeTypeBuf, sizeof(wakeTypeBuf), kCFStringEncodingUTF8); 1728 } 1729 snprintf(wakeReasonBuf, sizeof(wakeReasonBuf), "%s/%s", wakeReasonBuf, wakeTypeBuf); 1730 detailString = wakeReasonBuf; 1731 powerString(powerLevelBuf, sizeof(powerLevelBuf)); 1732 } else { 1733 detailString = failureStr; 1734 snprintf(buf, sizeof(buf), "%s during wake", 1735 (sig) ? sig : ""); 1736 success = false; 1737 } 1738 1739 /* populate driver wake reasons */ 1740 if (success && isA_CFArray(reasons.claimedWakeEventsArray)) 1741 { 1742 int keyIndex = 0; 1743 long claimedCount = CFArrayGetCount(reasons.claimedWakeEventsArray); 1744 1745 for (i=0; i<claimedCount; i++) { 1746 /* Legal requirement: 16513925 & 16544525 1747 * Limit the length of the reported string to 60 characters. 1748 */ 1749 const int kMaxClaimReportLen = 60; 1750 1751 CFDictionaryRef claimedEvent = NULL; 1752 char claimedReasonStr[kMaxClaimReportLen]; 1753 char claimedDetailsStr[kMaxClaimReportLen]; 1754 char claimed[255]; 1755 char key[255]; 1756 1757 1758 claimedReasonStr[0] = 0; 1759 claimedDetailsStr[0] = 0; 1760 1761 claimedEvent = CFArrayGetValueAtIndex(reasons.claimedWakeEventsArray, i); 1762 if (!isA_CFDictionary(claimedEvent)) continue; 1763 1764 tmpStr = CFDictionaryGetValue(claimedEvent, 1765 CFSTR(kIOPMWakeEventReasonKey)); 1766 if (isA_CFString(tmpStr)) { 1767 CFStringGetCString(tmpStr, claimedReasonStr, sizeof(claimedReasonStr), kCFStringEncodingUTF8); 1768 } 1769 if (!claimedReasonStr[0]) continue; 1770 1771 tmpStr = CFDictionaryGetValue(claimedEvent, 1772 CFSTR(kIOPMWakeEventDetailsKey)); 1773 if (isA_CFString(tmpStr)) { 1774 CFStringGetCString(tmpStr, claimedDetailsStr, sizeof(claimedDetailsStr), kCFStringEncodingUTF8); 1775 } 1776 1777 snprintf(claimed, sizeof(claimed), "DriverReason:%s - DriverDetails:%s", 1778 claimedReasonStr, claimedDetailsStr); 1779 1780 snprintf(key, sizeof(key), "%s-%d", kPMASLClaimedEventKey, keyIndex); 1781 1782 asl_set(m, key, claimed); 1783 keyIndex++; 1784 } 1785 1786 } 1787 1788 1789 if (!success) 1790 { 1791 asl_set(m, kPMASLDomainKey, kPMASLDomainSWFailure); 1792 } 1793 else if (dark_wake == kIsDarkWake) 1794 { 1795 asl_set(m, kPMASLDomainKey, kPMASLDomainPMDarkWake); 1796 snprintf(buf, sizeof(buf), "%s", "DarkWake"); 1797 darkWakeCnt++; 1798 snprintf(numbuf, sizeof(numbuf), "%d", darkWakeCnt); 1799 asl_set(m, kPMASLValueKey, numbuf); 1800 } 1801 else if (dark_wake == kIsDarkToFullWake) 1802 { 1803 wakeType = _copyRootDomainProperty(CFSTR(kIOPMRootDomainWakeTypeKey)); 1804 if (isA_CFString(wakeType)) { 1805 CFStringGetCString(wakeType, wakeReasonBuf, sizeof(wakeReasonBuf), kCFStringEncodingUTF8); 1806 } 1807 if (wakeType) { 1808 CFRelease(wakeType); 1809 } 1810 asl_set(m, kPMASLDomainKey, kPMASLDomainPMWake); 1811 snprintf(buf, sizeof(buf), "%s", "DarkWake to FullWake"); 1812 } 1813 else 1814 { 1815 asl_set(m, kPMASLDomainKey, kPMASLDomainPMWake); 1816 snprintf(buf, sizeof(buf), "%s", "Wake"); 1817 } 1818 1819 if (success) 1820 { 1821 if ((sleepTypeString = getSleepTypeString())) 1822 { 1823 snprintf(buf, sizeof(buf), "%s from %s", buf, sleepTypeString); 1824 } 1825 1826 printCapabilitiesToBuf(cBuf, sizeof(cBuf), in_capabilities); 1827 strncat(buf, cBuf, sizeof(buf)-strlen(buf)-1); 1828 } 1829 1830 snprintf(buf, sizeof(buf), "%s %s %s: %s\n", buf, 1831 detailString ? "due to" : "", 1832 detailString ? detailString : "", 1833 powerLevelBuf); 1834 1835 asl_set(m, ASL_KEY_MSG, buf); 1836 asl_send(NULL, m); 1837 asl_release(m); 1838 1839 logASLMessageHibernateStatistics( ); 1840} 1841/*****************************************************************************/ 1842 1843__private_extern__ void logASLAppWakeReason( 1844 const char * ident, 1845 const char * reason) 1846{ 1847#define kPMASLDomainAppWakeReason "AppWakeReason" 1848 1849 aslmsg m = new_msg_pmset_log(); 1850 1851 asl_set(m, kPMASLDomainKey, kPMASLDomainAppWakeReason); 1852 if (ident) { 1853 asl_set(m, kPMASLSignatureKey, ident); 1854 } 1855 1856 char msg[255]; 1857 snprintf(msg, sizeof(msg), "AppWoke:%s Reason:%s", ident?ident:"--none--", reason?reason:"--none--"); 1858 asl_set(m, ASL_KEY_MSG, msg); 1859 1860 asl_send(NULL, m); 1861 asl_release(m); 1862} 1863 1864 1865/*****************************************************************************/ 1866 1867__private_extern__ void logASLMessageHibernateStatistics(void) 1868{ 1869 aslmsg m; 1870 CFDataRef statsData = NULL; 1871 PMStatsStruct *stats = NULL; 1872 uint64_t readHIBImageMS = 0; 1873 uint64_t writeHIBImageMS = 0; 1874 CFNumberRef hibernateModeNum = NULL; 1875 CFNumberRef hibernateDelayNum = NULL; 1876 int hibernateMode = 0; 1877 char valuestring[25]; 1878 int hibernateDelay = 0; 1879 char buf[100]; 1880 char uuidString[150]; 1881 1882 hibernateModeNum = (CFNumberRef)_copyRootDomainProperty(CFSTR(kIOHibernateModeKey)); 1883 if (!hibernateModeNum) 1884 goto exit; 1885 CFNumberGetValue(hibernateModeNum, kCFNumberIntType, &hibernateMode); 1886 CFRelease(hibernateModeNum); 1887 1888 hibernateDelayNum= (CFNumberRef)_copyRootDomainProperty(CFSTR(kIOPMDeepSleepDelayKey)); 1889 if (!hibernateDelayNum) 1890 goto exit; 1891 CFNumberGetValue(hibernateDelayNum, kCFNumberIntType, &hibernateDelay); 1892 CFRelease(hibernateDelayNum); 1893 1894 statsData = (CFDataRef)_copyRootDomainProperty(CFSTR(kIOPMSleepStatisticsKey)); 1895 if (!statsData || !(stats = (PMStatsStruct *)CFDataGetBytePtr(statsData))) 1896 { 1897 goto exit; 1898 } else { 1899 writeHIBImageMS = (stats->hibWrite.stop - stats->hibWrite.start)/1000000UL; 1900 1901 readHIBImageMS =(stats->hibRead.stop - stats->hibRead.start)/1000000UL; 1902 1903 /* Hibernate image is not generated on every sleep for some h/w */ 1904 if ( !writeHIBImageMS && !readHIBImageMS) 1905 goto exit; 1906 } 1907 1908 m = new_msg_pmset_log(); 1909 1910 asl_set(m, kPMASLDomainKey, kPMASLDomainHibernateStatistics); 1911 1912 asl_set(m, ASL_KEY_LEVEL, ASL_STRING_NOTICE); 1913 1914 if (_getUUIDString(uuidString, sizeof(uuidString))) { 1915 asl_set(m, kPMASLUUIDKey, uuidString); 1916 } 1917 1918 snprintf(valuestring, sizeof(valuestring), "hibernatemode=%d", hibernateMode); 1919 asl_set(m, kPMASLSignatureKey, valuestring); 1920 // If readHibImageMS == zero, that means we woke from the contents of memory 1921 // and did not read the hibernate image. 1922 if (writeHIBImageMS) 1923 snprintf(buf, sizeof(buf), "wr=%qd ms ", writeHIBImageMS); 1924 1925 if (readHIBImageMS) 1926 snprintf(buf, sizeof(buf), "rd=%qd ms", readHIBImageMS); 1927 asl_set(m, kPMASLDelayKey, buf); 1928 1929 snprintf(buf, sizeof(buf), "hibmode=%d standbydelay=%d", hibernateMode, hibernateDelay); 1930 1931 asl_set(m, ASL_KEY_MSG, buf); 1932 asl_send(NULL, m); 1933 asl_release(m); 1934exit: 1935 if(statsData) 1936 CFRelease(statsData); 1937 return; 1938} 1939 1940/*****************************************************************************/ 1941 1942__private_extern__ void logASLPMConnectionNotify( 1943 CFStringRef appNameString, 1944 int notificationBits 1945 ) 1946{ 1947 1948 aslmsg m; 1949 char buf[128]; 1950 char appName[100]; 1951 1952 1953 m = new_msg_pmset_log(); 1954 asl_set(m, kPMASLDomainKey, kPMASLDomainAppNotify); 1955 1956 1957 if (!CFStringGetCString(appNameString, appName, sizeof(appName), kCFStringEncodingUTF8)) 1958 snprintf(appName, sizeof(appName), "Unknown app"); 1959 1960 1961 asl_set(m, kPMASLSignatureKey, appName); 1962 1963 // UUID 1964 if (_getUUIDString(buf, sizeof(buf))) { 1965 asl_set(m, kPMASLUUIDKey, buf); 1966 } 1967 1968 snprintf(buf, sizeof(buf), "Notification sent to %s (powercaps:0x%x)", 1969 appName,notificationBits ); 1970 1971 asl_set(m, ASL_KEY_MSG, buf); 1972 asl_send(NULL, m); 1973 asl_release(m); 1974} 1975 1976#ifndef __I_AM_PMSET__ 1977__private_extern__ void logASLDisplayStateChange() 1978{ 1979 1980#if !TARGET_OS_EMBEDDED 1981 aslmsg m; 1982 char buf[128]; 1983 bool displayState = isDisplayAsleep(); 1984 1985 1986 m = new_msg_pmset_log(); 1987 asl_set(m, kPMASLDomainKey, kPMASLDomainAppNotify); 1988 1989 // UUID 1990 if (_getUUIDString(buf, sizeof(buf))) { 1991 asl_set(m, kPMASLUUIDKey, buf); 1992 } 1993 1994 snprintf(buf, sizeof(buf), "Display is turned %s", 1995 displayState ? "off" : "on"); 1996 1997 asl_set(m, ASL_KEY_MSG, buf); 1998 asl_send(NULL, m); 1999 asl_release(m); 2000 2001 if (displayState) { 2002 /* Log all assertions when display goes off */ 2003 CFRunLoopPerformBlock(_getPMRunLoop(), kCFRunLoopDefaultMode, ^{ logASLAllAssertions(); }); 2004 CFRunLoopWakeUp(_getPMRunLoop()); 2005 } 2006#endif 2007} 2008#endif 2009 2010__private_extern__ void logASLMessagePMConnectionResponse( 2011 CFStringRef logSourceString, 2012 CFStringRef appNameString, 2013 CFStringRef responseTypeString, 2014 CFNumberRef responseTime, 2015 int notificationBits 2016) 2017{ 2018 aslmsg m; 2019 char appName[128]; 2020 char *appNamePtr = NULL; 2021 int time = 0; 2022 char buf[128]; 2023 char qualifier[30]; 2024 bool timeout = false; 2025 2026 // String identifying the source of the log is required. 2027 if (!logSourceString) 2028 return; 2029 2030 m = new_msg_pmset_log(); 2031 2032 if (responseTypeString && CFEqual(responseTypeString, CFSTR(kIOPMStatsResponseTimedOut))) 2033 { 2034 asl_set(m, kPMASLDomainKey, kPMASLDomainAppResponseTimedOut); 2035 snprintf(qualifier, sizeof(qualifier), "timed out"); 2036 timeout = true; 2037 } else 2038 if (responseTypeString && CFEqual(responseTypeString, CFSTR(kIOPMStatsResponseCancel))) 2039 { 2040 asl_set(m, kPMASLDomainKey, kPMASLDomainAppResponseCancel); 2041 snprintf(qualifier, sizeof(qualifier), "is to cancel state change"); 2042 } else 2043 if (responseTypeString && CFEqual(responseTypeString, CFSTR(kIOPMStatsResponseSlow))) 2044 { 2045 asl_set(m, kPMASLDomainKey, kPMASLDomainAppResponseSlow); 2046 snprintf(qualifier, sizeof(qualifier), "is slow"); 2047 } else 2048 if (responseTypeString && CFEqual(responseTypeString, CFSTR(kPMASLDomainSleepServiceCapApp))) 2049 { 2050 asl_set(m, kPMASLDomainKey, kPMASLDomainSleepServiceCapApp); 2051 snprintf(qualifier, sizeof(qualifier), "exceeded SleepService cap"); 2052 } else 2053 if (responseTypeString && CFEqual(responseTypeString, CFSTR(kPMASLDomainAppResponse))) 2054 { 2055 asl_set(m, kPMASLDomainKey, kPMASLDomainAppResponseReceived); 2056 snprintf(qualifier, sizeof(qualifier), "received"); 2057 } else { 2058 asl_release(m); 2059 return; 2060 } 2061 2062 // Message = Failing process name 2063 if (appNameString) 2064 { 2065 if (CFStringGetCString(appNameString, appName, sizeof(appName), kCFStringEncodingUTF8)) 2066 { 2067 appNamePtr = &appName[0]; 2068 } 2069 } 2070 if (!appNamePtr) { 2071 appNamePtr = "AppNameUnknown"; 2072 } 2073 2074 asl_set(m, kPMASLSignatureKey, appNamePtr); 2075 2076 // UUID 2077 if (_getUUIDString(buf, sizeof(buf))) { 2078 asl_set(m, kPMASLUUIDKey, buf); 2079 } 2080 2081 // Value == Time 2082 if (responseTime) { 2083 if (CFNumberGetValue(responseTime, kCFNumberIntType, &time)) { 2084 snprintf(buf, sizeof(buf), "%d", time); 2085 asl_set(m, kPMASLValueKey, buf); 2086 } 2087 } 2088 2089 if (CFStringGetCString(logSourceString, buf, sizeof(buf), kCFStringEncodingUTF8)) { 2090 snprintf(buf, sizeof(buf), "%s: Response from %s %s", 2091 buf, appNamePtr, qualifier); 2092 } else { 2093 snprintf(buf, sizeof(buf), "Response from %s %s", 2094 appNamePtr, qualifier); 2095 } 2096 2097 if (notificationBits != -1) 2098 snprintf(buf, sizeof(buf), "%s (powercaps:0x%x)", buf, notificationBits); 2099 2100 asl_set(m, ASL_KEY_MSG, buf); 2101 2102 if (time != 0) { 2103 snprintf(buf, sizeof(buf), "%d ms", time); 2104 asl_set(m, kPMASLDelayKey, buf); 2105 } 2106 2107 asl_send(NULL, m); 2108 asl_release(m); 2109 2110#ifndef __I_AM_PMSET__ 2111 if (timeout) { 2112 mt2RecordAppTimeouts(reasons.sleepReason, appNameString); 2113 } 2114#endif 2115} 2116 2117/*****************************************************************************/ 2118 2119/* logASLMessageAppStats 2120 * 2121 * Logs ASL message for delays and timeouts in acknowledging power notifications 2122 * 2123 */ 2124__private_extern__ void logASLMessageAppStats(CFArrayRef appFailuresArray, char *domain) 2125{ 2126 CFDictionaryRef appFailures = NULL; 2127 CFStringRef appNameString = NULL; 2128 CFStringRef transString = NULL; 2129 CFNumberRef numRef = NULL; 2130 CFStringRef responseTypeString = NULL; 2131 long numElems = 0; 2132 int appCnt = 0; 2133 int i = 0; 2134 aslmsg m; 2135 char appName[128]; 2136 char responseType[32]; 2137 int num = 0; 2138 char key[128]; 2139 char numStr[10]; 2140 2141 2142 if (!isA_CFArray(appFailuresArray)) 2143 return; 2144 2145 numElems = CFArrayGetCount(appFailuresArray); 2146 if (numElems == 0) 2147 return; 2148 2149 m = new_msg_pmset_log(); 2150 asl_set(m, kPMASLDomainKey, domain); 2151 2152 for (i = 0; i < numElems; i++) 2153 { 2154 appFailures = CFArrayGetValueAtIndex(appFailuresArray, i); 2155 if ( !isA_CFDictionary(appFailures)) { 2156 break; 2157 } 2158 2159 2160 appNameString = CFDictionaryGetValue(appFailures, CFSTR(kIOPMStatsNameKey)); 2161 if (!isA_CFString(appNameString) || 2162 (!CFStringGetCString(appNameString, appName, sizeof(appName), kCFStringEncodingUTF8))) { 2163 continue; 2164 } 2165 2166 numRef = CFDictionaryGetValue(appFailures, CFSTR(kIOPMStatsTimeMSKey)); 2167 if (!isA_CFNumber(numRef) || (!CFNumberGetValue(numRef, kCFNumberIntType, &num))) { 2168 continue; 2169 } 2170 numStr[0] = 0; 2171 snprintf(numStr, sizeof(numStr), "%d", num); 2172 2173 responseTypeString = CFDictionaryGetValue(appFailures, CFSTR(kIOPMStatsApplicationResponseTypeKey)); 2174 if (!isA_CFString(responseTypeString) || 2175 (!CFStringGetCString(responseTypeString, responseType, sizeof(responseType), kCFStringEncodingUTF8))) { 2176 continue; 2177 } 2178 2179 if (!_getUUIDString(key, sizeof(key))) 2180 continue; 2181 asl_set(m, kPMASLUUIDKey, key); 2182 2183 if (transString == NULL) { 2184 // Transition should be same for all apps listed in appFailuresArray. 2185 // So, set kPMASLResponseSystemTransition only once 2186 transString = CFDictionaryGetValue(appFailures, CFSTR(kIOPMStatsSystemTransitionKey)); 2187 if (isA_CFString(transString) && 2188 (CFStringGetCString(transString, key, sizeof(key), kCFStringEncodingUTF8))) { 2189 asl_set(m, kPMASLResponseSystemTransition, key); 2190 } 2191 } 2192 2193 snprintf(key, sizeof(key), "%s%d",kPMASLResponseAppNamePrefix, appCnt); 2194 asl_set(m, key, appName); 2195 2196 snprintf(key, sizeof(key), "%s%d", kPMASLResponseRespTypePrefix, appCnt); 2197 asl_set(m, key, responseType); 2198 2199 snprintf(key, sizeof(key), "%s%d", kPMASLResponseDelayPrefix, appCnt); 2200 asl_set(m, key, numStr); 2201 2202 numRef = CFDictionaryGetValue(appFailures, CFSTR(kIOPMStatsMessageTypeKey)); 2203 if (isA_CFNumber(numRef) && (CFNumberGetValue(numRef, kCFNumberIntType, &num))) { 2204 2205 snprintf(key, sizeof(key), "%s%d", kPMASLResponseMessagePrefix, appCnt); 2206 if (num == kDriverCallSetPowerState) 2207 asl_set(m, key, "SetState"); 2208 else if (num == kDriverCallInformPreChange) 2209 asl_set(m, key, "WillChangeState"); 2210 else 2211 asl_set(m, key, "DidChangeState"); 2212 } 2213 2214 numRef = CFDictionaryGetValue(appFailures, CFSTR(kIOPMStatsPowerCapabilityKey)); 2215 if (isA_CFNumber(numRef) && (CFNumberGetValue(numRef, kCFNumberIntType, &num))) { 2216 numStr[0] = 0; 2217 snprintf(numStr, sizeof(numStr), "%d", num); 2218 2219 snprintf(key, sizeof(key), "%s%d", kPMASLResponsePSCapsPrefix, appCnt); 2220 asl_set(m, key, numStr); 2221 } 2222 2223 appCnt++; 2224 2225#ifndef __I_AM_PMSET__ 2226 if (CFEqual(responseTypeString, CFSTR(kIOPMStatsResponseTimedOut))) { 2227 mt2RecordAppTimeouts(reasons.sleepReason, appNameString); 2228 } 2229#endif 2230 } 2231 asl_send(NULL, m); 2232 asl_release(m); 2233 2234} 2235 2236 2237__private_extern__ void logASLMessagePMConnectionScheduledWakeEvents(CFStringRef requestedMaintenancesString) 2238{ 2239 aslmsg m; 2240 char buf[100]; 2241 char requestors[500]; 2242 CFMutableStringRef messageString = NULL; 2243 2244 messageString = CFStringCreateMutable(0, 0); 2245 if (!messageString) 2246 return; 2247 2248 m = new_msg_pmset_log(); 2249 2250 if (_getUUIDString(buf, sizeof(buf))) { 2251 asl_set(m, kPMASLUUIDKey, buf); 2252 } 2253 2254 CFStringAppendCString(messageString, "Clients requested wake events: ", kCFStringEncodingUTF8); 2255 if (requestedMaintenancesString && (0 < CFStringGetLength(requestedMaintenancesString))) { 2256 CFStringAppend(messageString, requestedMaintenancesString); 2257 } else { 2258 CFStringAppend(messageString, CFSTR("None")); 2259 } 2260 2261 CFStringGetCString(messageString, requestors, sizeof(requestors), kCFStringEncodingUTF8); 2262 2263 asl_set(m, kPMASLDomainKey, kPMASLDomainPMWakeRequests); 2264 asl_set(m, ASL_KEY_MSG, requestors); 2265 asl_send(NULL, m); 2266 asl_release(m); 2267 CFRelease(messageString); 2268 2269} 2270 2271__private_extern__ void logASLMessageExecutedWakeupEvent(CFStringRef requestedMaintenancesString) 2272{ 2273 aslmsg m; 2274 char buf[100]; 2275 char requestors[500]; 2276 CFMutableStringRef messageString = CFStringCreateMutable(0, 0); 2277 2278 if (!messageString) 2279 return; 2280 2281 m = new_msg_pmset_log(); 2282 2283 if (_getUUIDString(buf, sizeof(buf))) { 2284 asl_set(m, kPMASLUUIDKey, buf); 2285 } 2286 2287 CFStringAppendCString(messageString, "PM scheduled RTC wake event: ", kCFStringEncodingUTF8); 2288 CFStringAppend(messageString, requestedMaintenancesString); 2289 2290 CFStringGetCString(messageString, requestors, sizeof(requestors), kCFStringEncodingUTF8); 2291 2292 asl_set(m, kPMASLDomainKey, kPMASLDomainPMWakeRequests); 2293 asl_set(m, ASL_KEY_MSG, requestors); 2294 asl_send(NULL, m); 2295 asl_release(m); 2296 CFRelease(messageString); 2297} 2298 2299#if !TARGET_OS_EMBEDDED 2300__private_extern__ void logASLMessageIgnoredDWTEmergency(void) 2301{ 2302 aslmsg m; 2303 char strbuf[125]; 2304 char tcpKeepAliveString[50]; 2305 2306 bzero(strbuf, sizeof(strbuf)); 2307 bzero(tcpKeepAliveString, sizeof(tcpKeepAliveString)); 2308 2309 m = new_msg_pmset_log(); 2310#if TCPKEEPALIVE 2311 attachTCPKeepAliveKeys(m, tcpKeepAliveString, sizeof(tcpKeepAliveString)); 2312#endif 2313 2314 asl_set(m, kPMASLDomainKey, kPMASLDomainDWTEmergency); 2315 2316 snprintf( 2317 strbuf, 2318 sizeof(strbuf), 2319 "Ignored DarkWake thermal emergency signal %s", tcpKeepAliveString); 2320 asl_set(m, ASL_KEY_MSG, strbuf); 2321 2322 asl_send(NULL, m); 2323 asl_release(m); 2324} 2325#endif 2326 2327__private_extern__ void logASLMessageSleepCanceledAtLastCall(void) 2328{ 2329 aslmsg m; 2330 char strbuf[125]; 2331 char tcpKeepAliveString[50]; 2332 2333 bzero(strbuf, sizeof(strbuf)); 2334 bzero(tcpKeepAliveString, sizeof(tcpKeepAliveString)); 2335 2336 m = new_msg_pmset_log(); 2337 2338#if TCPKEEPALIVE 2339 attachTCPKeepAliveKeys(m, tcpKeepAliveString, sizeof(tcpKeepAliveString)); 2340#endif 2341 2342 asl_set(m, kPMASLDomainKey, kPMASLDomainSleepRevert); 2343 2344 snprintf( 2345 strbuf, 2346 sizeof(strbuf), 2347 "Sleep in process aborted due to power assertion %s", tcpKeepAliveString); 2348 asl_set(m, ASL_KEY_MSG, strbuf); 2349 2350 asl_send(NULL, m); 2351 asl_release(m); 2352} 2353 2354__private_extern__ void logASLBatteryHealthChanged(const char *health, 2355 const char *oldhealth, 2356 const char *reason) 2357{ 2358 aslmsg m; 2359 char strbuf[125]; 2360 2361 bzero(strbuf, sizeof(strbuf)); 2362 2363 m = new_msg_pmset_log(); 2364 2365 asl_set(m, kPMASLDomainKey, kPMASLDomainBattery); 2366 2367 if (!strncmp(oldhealth, "", 5)) { 2368 snprintf( 2369 strbuf, 2370 sizeof(strbuf), 2371 "Battery health: %s", health); 2372 } else if (!strncmp(reason, "", 5)){ 2373 snprintf( 2374 strbuf, 2375 sizeof(strbuf), 2376 "Battery health: %s; was: %s", health, oldhealth); 2377 } else { 2378 snprintf( 2379 strbuf, 2380 sizeof(strbuf), 2381 "Battery health: %s; was: %s; reason %s", health, oldhealth, reason); 2382 } 2383 asl_set(m, ASL_KEY_MSG, strbuf); 2384 2385 asl_send(NULL, m); 2386 asl_release(m); 2387} 2388 2389__private_extern__ void logASLLowBatteryWarning(IOPSLowBatteryWarningLevel level, 2390 int time, int ccap) 2391{ 2392#if !TARGET_OS_EMBEDDED 2393 aslmsg m; 2394 char strbuf[125]; 2395 2396 bzero(strbuf, sizeof(strbuf)); 2397 2398 m = new_msg_pmset_log(); 2399 2400 asl_set(m, kPMASLDomainKey, kPMASLDomainBattery); 2401 2402 snprintf(strbuf, sizeof(strbuf), "Warning level: %d time: %d cap: %d\n", 2403 level, time, ccap); 2404 asl_set(m, ASL_KEY_MSG, strbuf); 2405 2406 asl_send(NULL, m); 2407 asl_release(m); 2408#endif 2409} 2410/*****************************************************************************/ 2411/*****************************************************************************/ 2412 2413__private_extern__ CFCalendarRef _gregorian(void) 2414{ 2415 static CFCalendarRef g = NULL; 2416 if (!g) { 2417 g = CFCalendarCreateWithIdentifier(NULL, kCFGregorianCalendar); 2418 } 2419 return g; 2420} 2421 2422/***************************************************************************/ 2423/***************************************************************************/ 2424/***************************************************************************/ 2425/***************************************************************************/ 2426#pragma mark MT2 DarkWake 2427#ifndef __I_AM_PMSET__ 2428 2429#if TARGET_OS_EMBEDDED 2430/* These are stubs of MT2 functions, so we can build for embedded, without this functionality. */ 2431void initializeMT2Aggregator(void) {}; 2432void mt2DarkWakeEnded(void) {}; 2433void mt2EvaluateSystemSupport(void) {}; 2434void mt2RecordWakeEvent(uint32_t description) {}; 2435void mt2RecordThermalEvent(uint32_t description) {}; 2436void mt2RecordAssertionEvent(assertionOps action, assertion_t *theAssertion) {}; 2437void mt2PublishReports(void) {}; 2438void mt2PublishSleepFailure(const char *failType, const char *pci_string) {}; 2439void mt2PublishWakeFailure(const char *failType, const char *pci_string) {}; 2440void mt2RecordAppTimeouts(CFStringRef sleepReason, CFStringRef procName) {}; 2441static void mt2PublishWakeReason(CFStringRef wakeTypeStr, CFStringRef claimedWakeStr) {}; 2442#else 2443 2444/* 2445 * MessageTracer2 DarkWake Keys 2446 */ 2447 2448typedef struct { 2449 CFAbsoluteTime startedPeriod; 2450 dispatch_source_t nextFireSource; 2451 2452 /* for domain com.apple.darkwake.capable */ 2453 int SMCSupport:1; 2454 int PlatformSupport:1; 2455 int checkedforAC:1; 2456 int checkedforBatt:1; 2457 /* for domain com.apple.darkwake.wakes */ 2458 uint16_t wakeEvents[kWakeStateCount]; 2459 /* for domain com.apple.darkwake.thermal */ 2460 uint16_t thermalEvents[kThermalStateCount]; 2461 /* for domain com.apple.darkwake.backgroundtasks */ 2462 CFMutableSetRef alreadyRecordedBackground; 2463 CFMutableDictionaryRef tookBackground; 2464 /* for domain com.apple.darkwake.pushservicetasks */ 2465 CFMutableSetRef alreadyRecordedPush; 2466 CFMutableDictionaryRef tookPush; 2467 /* for domain com.apple.darkwake.pushservicetasktimeout */ 2468 CFMutableSetRef alreadyRecordedPushTimeouts; 2469 CFMutableDictionaryRef timeoutPush; 2470 CFMutableDictionaryRef idleSleepAppTimeouts; 2471 CFMutableDictionaryRef demandSleepAppTimeouts; 2472 CFMutableDictionaryRef darkwakeSleepAppTimeouts; 2473} MT2Aggregator; 2474 2475static const uint64_t kMT2CheckIntervalTimer = 4ULL*60ULL*60ULL*NSEC_PER_SEC; /* Check every 4 hours */ 2476static CFAbsoluteTime kMT2SendReportsAtInterval = 7.0*24.0*60.0*60.0; /* Publish reports every 7 days */ 2477static const uint64_t kBigLeeway = (30Ull * NSEC_PER_SEC); 2478 2479static MT2Aggregator *mt2 = NULL; 2480 2481void initializeMT2Aggregator(void) 2482{ 2483 if (mt2) 2484 { 2485 /* Zero out & recycle MT2Aggregator structure */ 2486 if (mt2->nextFireSource) { 2487 dispatch_release(mt2->nextFireSource); 2488 } 2489 CFRelease(mt2->alreadyRecordedBackground); 2490 CFRelease(mt2->tookBackground); 2491 CFRelease(mt2->alreadyRecordedPush); 2492 CFRelease(mt2->tookPush); 2493 CFRelease(mt2->alreadyRecordedPushTimeouts); 2494 CFRelease(mt2->timeoutPush); 2495 CFRelease(mt2->idleSleepAppTimeouts); 2496 CFRelease(mt2->demandSleepAppTimeouts); 2497 CFRelease(mt2->darkwakeSleepAppTimeouts); 2498 2499 bzero(mt2, sizeof(MT2Aggregator)); 2500 } else { 2501 /* New datastructure */ 2502 mt2 = calloc(1, sizeof(MT2Aggregator)); 2503 } 2504 mt2->startedPeriod = CFAbsoluteTimeGetCurrent(); 2505 mt2->alreadyRecordedBackground = CFSetCreateMutable(0, 0, &kCFTypeSetCallBacks); 2506 mt2->alreadyRecordedPush = CFSetCreateMutable(0, 0, &kCFTypeSetCallBacks); 2507 mt2->alreadyRecordedPushTimeouts = CFSetCreateMutable(0, 0, &kCFTypeSetCallBacks); 2508 mt2->tookBackground = CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, NULL); 2509 mt2->tookPush = CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, NULL); 2510 mt2->timeoutPush = CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, NULL); 2511 mt2->idleSleepAppTimeouts = CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, NULL); 2512 mt2->demandSleepAppTimeouts = CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, NULL); 2513 mt2->darkwakeSleepAppTimeouts = CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, NULL); 2514 2515 mt2->nextFireSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue()); 2516 if (mt2->nextFireSource) { 2517 dispatch_source_set_event_handler(mt2->nextFireSource, ^(){ mt2PublishReports(); }); 2518 dispatch_source_set_timer(mt2->nextFireSource, dispatch_time(DISPATCH_TIME_NOW, kMT2CheckIntervalTimer), 2519 kMT2CheckIntervalTimer, kBigLeeway); 2520 dispatch_resume(mt2->nextFireSource); 2521 } 2522 mt2EvaluateSystemSupport(); 2523 return; 2524} 2525 2526 2527 2528static int mt2PublishDomainCapable(void) 2529{ 2530#define kMT2DomainDarkWakeCapable "com.apple.darkwake.capable" 2531#define kMT2KeySupport "com.apple.message.hardware_support" 2532#define kMT2ValNoSupport "none" 2533#define kMT2ValPlatformSupport "platform_without_smc" 2534#define kMT2ValSMCSupport "smc_without_platform" 2535#define kMT2ValFullDWSupport "dark_wake_supported" 2536#define kMT2KeySettings "com.apple.message.settings" 2537#define kMT2ValSettingsNone "none" 2538#define kMT2ValSettingsAC "ac" 2539#define kMT2ValSettingsBatt "battery" 2540#define kMT2ValSettingsACPlusBatt "ac_and_battery" 2541 if (!mt2) { 2542 return 0; 2543 } 2544 2545 aslmsg m = asl_new(ASL_TYPE_MSG); 2546 asl_set(m, "com.apple.message.domain", kMT2DomainDarkWakeCapable ); 2547 if (mt2->SMCSupport && mt2->PlatformSupport) { 2548 asl_set(m, kMT2KeySupport, kMT2ValFullDWSupport); 2549 } else if (mt2->PlatformSupport) { 2550 asl_set(m, kMT2KeySupport, kMT2ValPlatformSupport); 2551 } else if (mt2->SMCSupport) { 2552 asl_set(m, kMT2KeySupport, kMT2ValSMCSupport); 2553 } else { 2554 asl_set(m, kMT2KeySupport, kMT2ValNoSupport); 2555 } 2556 2557 if (mt2->checkedforAC && mt2->checkedforBatt) { 2558 asl_set(m, kMT2KeySettings, kMT2ValSettingsACPlusBatt); 2559 } else if (mt2->checkedforAC) { 2560 asl_set(m, kMT2KeySettings, kMT2ValSettingsAC); 2561 } else if (mt2->checkedforBatt) { 2562 asl_set(m, kMT2KeySettings, kMT2ValSettingsBatt); 2563 } else { 2564 asl_set(m, kMT2KeySettings, kMT2ValSettingsNone); 2565 } 2566 2567 asl_log(NULL, m, ASL_LEVEL_ERR, ""); 2568 asl_release(m); 2569 2570 return 1; 2571} 2572 2573 2574static int mt2PublishDomainWakes(void) 2575{ 2576#define kMT2DomainWakes "com.apple.darkwake.wakes" 2577#define kMT2KeyWakeType "com.apple.message.waketype" 2578#define kMT2ValWakeDark "dark" 2579#define kMT2ValWakeFull "full" 2580#define kMT2KeyPowerSource "com.apple.message.powersource" 2581#define kMT2ValPowerAC "ac" 2582#define kMT2ValPowerBatt "battery" 2583#define kMT2KeyLid "com.apple.message.lid" 2584#define kMT2ValLidOpen "open" 2585#define kMT2ValLidClosed "closed" 2586 2587 int sentCount = 0; 2588 int i = 0; 2589 char buf[kIntegerStringLen]; 2590 2591 if (!mt2) { 2592 return 0; 2593 } 2594 for (i=0; i<kWakeStateCount; i++) 2595 { 2596 if (0 == mt2->wakeEvents[i]) { 2597 continue; 2598 } 2599 aslmsg m = asl_new(ASL_TYPE_MSG); 2600 asl_set(m, "com.apple.message.domain", kMT2DomainWakes); 2601 if (i & kWakeStateDark) { 2602 asl_set(m, kMT2KeyWakeType, kMT2ValWakeDark); 2603 } else { 2604 asl_set(m, kMT2KeyWakeType, kMT2ValWakeFull); 2605 } 2606 if (i & kWakeStateBattery) { 2607 asl_set(m, kMT2KeyPowerSource, kMT2ValPowerBatt); 2608 } else { 2609 asl_set(m, kMT2KeyPowerSource, kMT2ValPowerAC); 2610 } 2611 if (i & kWakeStateLidClosed) { 2612 asl_set(m, kMT2KeyLid, kMT2ValLidClosed); 2613 } else { 2614 asl_set(m, kMT2KeyLid, kMT2ValLidOpen); 2615 } 2616 2617 snprintf(buf, sizeof(buf), "%d", mt2->wakeEvents[i]); 2618 asl_set(m, "com.apple.message.count", buf); 2619 asl_log(NULL, m, ASL_LEVEL_ERR, ""); 2620 asl_release(m); 2621 sentCount++; 2622 } 2623 return sentCount; 2624} 2625 2626static int mt2PublishDomainThermals(void) 2627{ 2628#define kMT2DomainThermal "com.apple.darkwake.thermalevent" 2629#define kMT2KeySleepRequest "com.apple.message.sleeprequest" 2630#define kMT2KeyFansSpin "com.apple.message.fansspin" 2631#define kMT2ValTrue "true" 2632#define kMT2ValFalse "false" 2633 2634 int sentCount = 0; 2635 int i = 0; 2636 char buf[kIntegerStringLen]; 2637 2638 if (!mt2) { 2639 return 0; 2640 } 2641 2642 for (i=0; i<kThermalStateCount; i++) 2643 { 2644 if (0 == mt2->thermalEvents[i]) { 2645 continue; 2646 } 2647 aslmsg m = asl_new(ASL_TYPE_MSG); 2648 asl_set(m, "com.apple.message.domain", kMT2DomainThermal ); 2649 if (i & kThermalStateSleepRequest) { 2650 asl_set(m, kMT2KeySleepRequest, kMT2ValTrue); 2651 } else { 2652 asl_set(m, kMT2KeySleepRequest, kMT2ValFalse); 2653 } 2654 if (i & kThermalStateFansOn) { 2655 asl_set(m, kMT2KeyFansSpin, kMT2ValTrue); 2656 } else { 2657 asl_set(m, kMT2KeyFansSpin, kMT2ValFalse); 2658 } 2659 2660 snprintf(buf, sizeof(buf), "%d", mt2->thermalEvents[i]); 2661 asl_set(m, "com.apple.message.count", buf); 2662 asl_log(NULL, m, ASL_LEVEL_ERR, ""); 2663 asl_release(m); 2664 sentCount++; 2665 } 2666 return sentCount; 2667} 2668 2669static int mt2PublishDomainProcess(const char *appdomain, CFDictionaryRef apps) 2670{ 2671#define kMT2KeyApp "com.apple.message.process" 2672 2673 CFStringRef *keys; 2674 uintptr_t *counts; 2675 char buf[2*kProcNameBufLen]; 2676 int sendCount = 0; 2677 int appcount = 0; 2678 int i = 0; 2679 2680 if (!mt2 || !apps || (0 == (appcount = CFDictionaryGetCount(apps)))) 2681 { 2682 return 0; 2683 } 2684 2685 keys = (CFStringRef *)calloc(sizeof(CFStringRef), appcount); 2686 counts = (uintptr_t *)calloc(sizeof(uintptr_t), appcount); 2687 2688 CFDictionaryGetKeysAndValues(apps, (const void **)keys, (const void **)counts); 2689 2690 for (i=0; i<appcount; i++) 2691 { 2692 if (0 == counts[i]) { 2693 continue; 2694 } 2695 aslmsg m = asl_new(ASL_TYPE_MSG); 2696 asl_set(m, "com.apple.message.domain", appdomain); 2697 2698 if (!CFStringGetCString(keys[i], buf, sizeof(buf), kCFStringEncodingUTF8)) { 2699 snprintf(buf, sizeof(buf), "com.apple.message.%s", "Unknown"); 2700 } 2701 asl_set(m, kMT2KeyApp, buf); 2702 2703 snprintf(buf, sizeof(buf), "%d", (int)counts[i]); 2704 asl_set(m, "com.apple.message.count", buf); 2705 2706 asl_log(NULL, m, ASL_LEVEL_ERR,""); 2707 asl_release(m); 2708 sendCount++; 2709 2710 } 2711 2712 free(keys); 2713 free(counts); 2714 2715 return sendCount; 2716} 2717 2718void mt2PublishReports(void) 2719{ 2720#define kMT2DomainPushTasks "com.apple.darkwake.pushservicetasks" 2721#define kMT2DomainPushTimeouts "com.apple.darkwake.pushservicetimeouts" 2722#define kMT2DomainBackgroundTasks "com.apple.darkwake.backgroundtasks" 2723#define kMT2DomainIdleSlpAckTo "com.apple.ackto.idlesleep" /* Idle sleep ack timeouts */ 2724#define kMT2DomainDemandSlpAckTo "com.apple.ackto.demandsleep" /* Demand sleep ack timeouts */ 2725#define kMT2DomainDarkWkSlpAckTo "com.apple.ackto.demandsleep" /* Dark wake sleep ack timeouts */ 2726#define kMT2DomainclaimedWakeEvents "com.apple.wake.claimedevents" 2727 2728 if (!mt2) { 2729 return; 2730 } 2731 2732 if ((mt2->startedPeriod + kMT2SendReportsAtInterval) < CFAbsoluteTimeGetCurrent()) 2733 { 2734 /* mt2PublishReports should only publish a new batch of ASL no more 2735 * frequently than once every kMT2SendReportsAtInterval seconds. 2736 * If it's too soon to publish ASL keys, just return. 2737 */ 2738 return; 2739 } 2740 2741 mt2PublishDomainCapable(); 2742 2743 if (mt2->PlatformSupport && mt2->SMCSupport) 2744 { 2745 mt2PublishDomainWakes(); 2746 mt2PublishDomainThermals(); 2747 mt2PublishDomainProcess(kMT2DomainPushTasks, mt2->tookPush); 2748 mt2PublishDomainProcess(kMT2DomainPushTimeouts, mt2->timeoutPush); 2749 mt2PublishDomainProcess(kMT2DomainBackgroundTasks, mt2->tookBackground); 2750 mt2PublishDomainProcess(kMT2DomainIdleSlpAckTo, mt2->idleSleepAppTimeouts); 2751 mt2PublishDomainProcess(kMT2DomainDemandSlpAckTo, mt2->demandSleepAppTimeouts); 2752 mt2PublishDomainProcess(kMT2DomainDarkWkSlpAckTo, mt2->darkwakeSleepAppTimeouts); 2753 2754 // Recyle the data structure for the next reporting. 2755 initializeMT2Aggregator(); 2756 } 2757 2758 // If the system lacks (PlatformSupport && SMC Support), this is where we stop scheduling MT2 reports. 2759 // If the system has PowerNap support, then we'll set a periodic timer in initializeMT2Aggregator() and 2760 // we'll keep publish messages on a schedule. 2761 2762 return; 2763} 2764 2765void mt2DarkWakeEnded(void) 2766{ 2767 if (!mt2) { 2768 return; 2769 } 2770 CFSetRemoveAllValues(mt2->alreadyRecordedBackground); 2771 CFSetRemoveAllValues(mt2->alreadyRecordedPush); 2772 CFSetRemoveAllValues(mt2->alreadyRecordedPushTimeouts); 2773} 2774 2775void mt2EvaluateSystemSupport(void) 2776{ 2777 CFDictionaryRef energySettings = NULL; 2778 CFDictionaryRef per = NULL; 2779 CFNumberRef num = NULL; 2780 int value = 0; 2781 2782 if (!mt2) { 2783 return; 2784 } 2785 2786 mt2->SMCSupport = smcSilentRunningSupport() ? 1:0; 2787 mt2->PlatformSupport = (_platformBackgroundTaskSupport || _platformSleepServiceSupport) ? 1:0; 2788 2789 mt2->checkedforAC = 0; 2790 mt2->checkedforBatt = 0; 2791 if ((energySettings = IOPMCopyActivePMPreferences())) { 2792 per = CFDictionaryGetValue(energySettings, CFSTR(kIOPMACPowerKey)); 2793 if (per) { 2794 num = CFDictionaryGetValue(per, CFSTR(kIOPMDarkWakeBackgroundTaskKey)); 2795 if (num) { 2796 CFNumberGetValue(num, kCFNumberIntType, &value); 2797 mt2->checkedforAC = value ? 1:0; 2798 } 2799 } 2800 per = CFDictionaryGetValue(energySettings, CFSTR(kIOPMBatteryPowerKey)); 2801 if (per) { 2802 num = CFDictionaryGetValue(per, CFSTR(kIOPMDarkWakeBackgroundTaskKey)); 2803 if (num) { 2804 CFNumberGetValue(num, kCFNumberIntType, &value); 2805 mt2->checkedforBatt = value ? 1:0; 2806 } 2807 } 2808 2809 CFRelease(energySettings); 2810 } 2811 return; 2812} 2813 2814void mt2RecordWakeEvent(uint32_t description) 2815{ 2816 CFStringRef lidString = NULL; 2817 CFBooleanRef lidIsClosed = NULL; 2818 2819 if (!mt2) { 2820 return; 2821 } 2822 2823 if (kWakeStateFull & description) { 2824 /* The system just woke into FullWake. 2825 * To make sure that we publish mt2 reports in a timely manner, 2826 * we'll try now. It'll only actually happen if the PublishInterval has 2827 * elapsed since the last time we published. 2828 */ 2829 mt2PublishReports(); 2830 } 2831 2832 lidString = CFStringCreateWithCString(0, kAppleClamshellStateKey, kCFStringEncodingUTF8); 2833 if (lidString) { 2834 lidIsClosed = _copyRootDomainProperty(lidString); 2835 CFRelease(lidString); 2836 } 2837 2838 description |= ((_getPowerSource() == kBatteryPowered) ? kWakeStateBattery : kWakeStateAC) 2839 | ((kCFBooleanTrue == lidIsClosed) ? kWakeStateLidClosed : kWakeStateLidOpen); 2840 2841 if (lidIsClosed) { 2842 CFRelease(lidIsClosed); 2843 } 2844 2845 mt2->wakeEvents[description]++; 2846 return; 2847} 2848 2849void mt2RecordThermalEvent(uint32_t description) 2850{ 2851 if (!mt2) { 2852 return; 2853 } 2854 description &= (kThermalStateFansOn | kThermalStateSleepRequest); 2855 mt2->thermalEvents[description]++; 2856 return; 2857} 2858 2859/* PMConnection.c */ 2860bool isA_DarkWakeState(); 2861 2862void mt2RecordAssertionEvent(assertionOps action, assertion_t *theAssertion) 2863{ 2864 CFStringRef processName; 2865 CFStringRef assertionType; 2866 2867 if (!mt2) { 2868 return; 2869 } 2870 2871 if (!theAssertion || !theAssertion->props || !isA_DarkWakeState()) { 2872 return; 2873 } 2874 2875 if (!(processName = processInfoGetName(theAssertion->pinfo->pid))) { 2876 processName = CFSTR("Unknown"); 2877 } 2878 2879 if (!(assertionType = CFDictionaryGetValue(theAssertion->props, kIOPMAssertionTypeKey)) 2880 || (!CFEqual(assertionType, kIOPMAssertionTypeBackgroundTask) 2881 && !CFEqual(assertionType, kIOPMAssertionTypeApplePushServiceTask))) 2882 { 2883 return; 2884 } 2885 2886 if (CFEqual(assertionType, kIOPMAssertionTypeBackgroundTask)) 2887 { 2888 if (kAssertionOpRaise == action) { 2889 if (!CFSetContainsValue(mt2->alreadyRecordedBackground, processName)) { 2890 int x = (int)CFDictionaryGetValue(mt2->tookBackground, processName); 2891 x++; 2892 CFDictionarySetValue(mt2->tookBackground, processName, (uintptr_t)x); 2893 CFSetAddValue(mt2->alreadyRecordedBackground, processName); 2894 } 2895 } 2896 } 2897 else if (CFEqual(assertionType, kIOPMAssertionTypeApplePushServiceTask)) 2898 { 2899 if (kAssertionOpRaise == action) { 2900 if (!CFSetContainsValue(mt2->alreadyRecordedPush, processName)) { 2901 int x = (int)CFDictionaryGetValue(mt2->tookPush, processName); 2902 x++; 2903 CFDictionarySetValue(mt2->tookPush, processName, (uintptr_t)x); 2904 CFSetAddValue(mt2->alreadyRecordedPush, processName); 2905 } 2906 } 2907 else if (kAssertionOpGlobalTimeout == action) { 2908 if (!CFSetContainsValue(mt2->alreadyRecordedPushTimeouts, processName)) { 2909 int x = (int)CFDictionaryGetValue(mt2->timeoutPush, processName); 2910 x++; 2911 CFDictionarySetValue(mt2->timeoutPush, (const void *)processName, (uintptr_t)x); 2912 CFSetAddValue(mt2->alreadyRecordedPushTimeouts, processName); 2913 } 2914 } 2915 } 2916 2917 return; 2918} 2919 2920void mt2RecordAppTimeouts(CFStringRef sleepReason, CFStringRef procName) 2921{ 2922 CFMutableDictionaryRef dict; 2923 2924 if ( !mt2 || !isA_CFString(procName)) return; 2925 2926 if (CFStringCompare(sleepReason, CFSTR(kIOPMIdleSleepKey), 0) == kCFCompareEqualTo) { 2927 dict = mt2->idleSleepAppTimeouts; 2928 } 2929 else if ((CFStringCompare(sleepReason, CFSTR(kIOPMClamshellSleepKey), 0) == kCFCompareEqualTo) || 2930 (CFStringCompare(sleepReason, CFSTR(kIOPMPowerButtonSleepKey), 0) == kCFCompareEqualTo) || 2931 (CFStringCompare(sleepReason, CFSTR(kIOPMSoftwareSleepKey), 0) == kCFCompareEqualTo)) { 2932 dict = mt2->demandSleepAppTimeouts; 2933 } 2934 else { 2935 dict = mt2->darkwakeSleepAppTimeouts; 2936 } 2937 2938 int x = (int)CFDictionaryGetValue(dict, procName); 2939 x++; 2940 CFDictionarySetValue(dict, (const void *)procName, (uintptr_t)x); 2941 2942} 2943 2944 2945#define kMT2DomainWakeReasons "com.apple.iokit.wakereasons" 2946#define kMT2DomainSleepFailure "com.apple.sleep.failure" 2947#define kMT2DomainWakeFailure "com.apple.wake.failure" 2948#define kMT2KeyFailType "com.apple.message.signature" 2949#define kMT2KeyPCI "com.apple.message.signature2" 2950 2951 2952static void mt2PublishWakeReason(CFStringRef wakeTypeStr, CFStringRef claimedWakeStr) 2953{ 2954 char wakeType[64]; 2955 char claimedWake[64]; 2956 tcpKeepAliveStates_et state = getTCPKeepAliveState(NULL, 0); 2957 2958 2959 if (!isA_CFString(wakeTypeStr) 2960 || !CFStringGetCString(wakeTypeStr, wakeType, sizeof(wakeType), kCFStringEncodingUTF8)) 2961 { 2962 return; 2963 } 2964 2965 if (!isA_CFString(claimedWakeStr) 2966 || !CFStringGetCString(claimedWakeStr, claimedWake, sizeof(claimedWake), kCFStringEncodingUTF8)) 2967 { 2968 return; 2969 } 2970 2971 aslmsg m = asl_new(ASL_TYPE_MSG); 2972 asl_set(m, "com.apple.message.domain", kMT2DomainWakeReasons); 2973 2974 asl_set(m, "com.apple.message.signature", wakeType); 2975 if (state == kNotSupported) { 2976 asl_set(m, "com.apple.message.signature2", "unsupported"); 2977 } 2978 else if (state == kActive) { 2979 asl_set(m, "com.apple.message.signature2", "active"); 2980 } 2981 else { 2982 asl_set(m, "com.apple.message.signature2", "inactive"); 2983 } 2984 asl_set(m, "com.apple.message.signature3", claimedWake); 2985 2986 asl_set(m, "com.apple.message.summarize", "YES"); 2987 asl_log(NULL, m, ASL_LEVEL_NOTICE, ""); 2988 asl_release(m); 2989 2990} 2991 2992void mt2PublishSleepFailure(const char *failType, const char *pci_string) 2993{ 2994 aslmsg m = asl_new(ASL_TYPE_MSG); 2995 asl_set(m, "com.apple.message.domain", kMT2DomainSleepFailure); 2996 asl_set(m, kMT2KeyFailType, failType); 2997 asl_set(m, kMT2KeyPCI, pci_string); 2998 asl_set(m, "com.apple.message.summarize", "YES"); 2999 asl_log(NULL, m, ASL_LEVEL_NOTICE, ""); 3000 asl_release(m); 3001} 3002 3003void mt2PublishWakeFailure(const char *failType, const char *pci_string) 3004{ 3005 aslmsg m = asl_new(ASL_TYPE_MSG); 3006 asl_set(m, "com.apple.message.domain", kMT2DomainWakeFailure); 3007 asl_set(m, kMT2KeyFailType, failType); 3008 asl_set(m, kMT2KeyPCI, pci_string); 3009 asl_set(m, "com.apple.message.summarize", "YES"); 3010 asl_log(NULL, m, ASL_LEVEL_NOTICE, ""); 3011 asl_release(m); 3012} 3013 3014#endif /* #endif for iOS */ 3015#endif /* #endif pmset */ 3016 3017#pragma mark FDR 3018 3019/************************* 3020 FDR functionality 3021 *************************/ 3022void recordFDREvent(int eventType, bool checkStandbyStatus, IOPMBattery **batteries) 3023{ 3024#if !TARGET_OS_EMBEDDED 3025#ifndef __I_AM_PMSET__ 3026 3027 struct systemstats_sleep_s s; 3028 struct systemstats_wake_s w; 3029 struct systemstats_power_state_change_s psc; 3030 IOPMBattery *b; 3031 char wt[50]; 3032 wt[0] = 0; 3033 3034 switch(eventType) { 3035 case kFDRInit: 3036 systemstats_init(SYSTEMSTATS_WRITER_powerd, NULL); 3037 break; 3038 3039 case kFDRACChanged: 3040 if(!batteries) 3041 return; 3042 3043 b = batteries[0]; 3044 3045 bzero(&psc, sizeof(struct systemstats_power_state_change_s)); 3046 psc.now_on_battery = !(b->externalConnected); 3047 psc.is_fully_charged = isFullyCharged(b); 3048 systemstats_write_power_state_change(&psc); 3049 nextFDREventDue = (uint64_t)kFDRIntervalAfterPE + getMonotonicTime(); 3050 break; 3051 3052 case kFDRSleepEvent: 3053 bzero(&s, sizeof(struct systemstats_sleep_s)); 3054 s.reason = 0; 3055 systemstats_write_sleep(&s); 3056 nextFDREventDue = (uint64_t)kFDRRegularInterval + getMonotonicTime(); 3057 break; 3058 3059 case kFDRUserWakeEvent: 3060 case kFDRDarkWakeEvent: 3061 3062 bzero(&w, sizeof(struct systemstats_wake_s)); 3063 if (kFDRUserWakeEvent == eventType) { 3064 w.reason = 1; 3065 } else if (kFDRDarkWakeEvent == eventType) { 3066 w.reason = 2; 3067 } 3068 3069 if (checkStandbyStatus) { 3070 const char *sleepTypeString = getSleepTypeString(); 3071 if(sleepTypeString && 3072 (!strncmp(sleepTypeString, "Standby", 15) || 3073 !strncmp(sleepTypeString, "AutoPowerOff", 15))) { 3074 w.wake_from_standby = true; 3075 } 3076 } 3077 3078 if (isA_CFString(reasons.interpretedWake)) { 3079 if (CFStringGetCString(reasons.interpretedWake, wt, sizeof(wt), 3080 kCFStringEncodingUTF8) && wt[0]) { 3081 w.wake_type = wt; 3082 } 3083 } 3084 3085 systemstats_write_wake(&w); 3086 nextFDREventDue = (uint64_t)kFDRIntervalAfterPE + getMonotonicTime(); 3087 break; 3088 3089 case kFDRBattEventPeriodic: 3090 // If last FDR event was < X mins ago, do nothing 3091 if(nextFDREventDue && getMonotonicTime() <= nextFDREventDue) { 3092 break; 3093 } 3094 3095 // Fall thru 3096 case kFDRBattEventAsync: 3097 if(!batteries) 3098 return; 3099 3100 IOPMBattery *b = batteries[0]; 3101 struct systemstats_battery_charge_level_s binfo; 3102 bzero(&binfo, sizeof(struct systemstats_battery_charge_level_s)); 3103 3104 binfo.charge = b->currentCap; 3105 binfo.max_charge = b->maxCap; 3106 binfo.cycle_count = b->cycleCount; 3107 binfo.instant_amperage = b->instantAmperage; 3108 binfo.instant_voltage = b->voltage; 3109 binfo.last_minute_wattage = ((abs(b->voltage))*(abs(b->avgAmperage)))/1000; 3110 binfo.estimated_time_remaining = b->hwAverageTR; 3111 binfo.smoothed_estimated_time_remaining = b->swCalculatedTR; 3112 binfo.is_fully_charged = isFullyCharged(b); 3113 3114 systemstats_write_battery_charge_level(&binfo); 3115 nextFDREventDue = (uint64_t)kFDRRegularInterval + getMonotonicTime(); 3116 break; 3117 3118 default: 3119 return; 3120 } 3121 3122#endif 3123#endif 3124} 3125 3126#pragma mark SMC and Hardware 3127/************************* One off hack for AppleSMC 3128 ************************* 3129 ************************* Send AppleSMC a kCFPropertyTrue 3130 ************************* on time discontinuities. 3131 *************************/ 3132#if !TARGET_OS_EMBEDDED 3133 3134static void setSMCProperty(void) 3135{ 3136 static io_registry_entry_t _smc = MACH_PORT_NULL; 3137 3138 if(MACH_PORT_NULL == _smc) { 3139 _smc = IOServiceGetMatchingService( MACH_PORT_NULL, 3140 IOServiceMatching("AppleSMCFamily")); 3141 } 3142 3143 if(!_smc) { 3144 return; 3145 } 3146 3147 // And simply AppleSMC with kCFBooleanTrue to let them know time is changed. 3148 // We don't pass any information down. 3149 IORegistryEntrySetCFProperty( _smc, 3150 CFSTR("TheTimesAreAChangin"), 3151 kCFBooleanTrue); 3152} 3153 3154static void handleMachCalendarMessage(CFMachPortRef port, void *msg, 3155 CFIndex size, void *info) 3156{ 3157 kern_return_t result; 3158 mach_port_t mport = CFMachPortGetPort(port); 3159 mach_port_t host_port; 3160 3161 // Re-register for notification 3162 host_port = mach_host_self(); 3163 result = host_request_notification(host_port, HOST_NOTIFY_CALENDAR_CHANGE, mport); 3164 if (host_port) { 3165 mach_port_deallocate(mach_task_self(), host_port); 3166 } 3167 if (result != KERN_SUCCESS) { 3168 return; 3169 } 3170 3171 setSMCProperty(); 3172} 3173 3174static void registerForCalendarChangedNotification(void) 3175{ 3176 mach_port_t tport; 3177 mach_port_t host_port; 3178 kern_return_t result; 3179 CFRunLoopSourceRef rls; 3180 static CFMachPortRef calChangeReceivePort = NULL; 3181 3182 // allocate the mach port we'll be listening to 3183 result = mach_port_allocate(mach_task_self(),MACH_PORT_RIGHT_RECEIVE, &tport); 3184 if (result != KERN_SUCCESS) { 3185 return; 3186 } 3187 3188 calChangeReceivePort = CFMachPortCreateWithPort( 3189 kCFAllocatorDefault, 3190 tport, 3191 (CFMachPortCallBack)handleMachCalendarMessage, 3192 NULL, /* context */ 3193 false); /* shouldFreeInfo */ 3194 if (calChangeReceivePort) { 3195 rls = CFMachPortCreateRunLoopSource( 3196 kCFAllocatorDefault, 3197 calChangeReceivePort, 3198 0); /* index Order */ 3199 if (rls) { 3200 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode); 3201 CFRelease(rls); 3202 } 3203 CFRelease(calChangeReceivePort); 3204 } 3205 3206 // register for notification 3207 host_port = mach_host_self(); 3208 host_request_notification(host_port,HOST_NOTIFY_CALENDAR_CHANGE, tport); 3209 if (host_port) { 3210 mach_port_deallocate(mach_task_self(), host_port); 3211 } 3212} 3213#endif 3214 3215/* Returns monotonic time in secs */ 3216__private_extern__ uint64_t getMonotonicTime( ) 3217{ 3218 static mach_timebase_info_data_t timebaseInfo; 3219 3220 if (timebaseInfo.denom == 0) 3221 mach_timebase_info(&timebaseInfo); 3222 3223 return ( (mach_absolute_time( ) * timebaseInfo.numer) / (timebaseInfo.denom * NSEC_PER_SEC)); 3224} 3225 3226__private_extern__ int callerIsRoot(int uid) 3227{ 3228 return (0 == uid); 3229} 3230 3231__private_extern__ int 3232callerIsAdmin( 3233 int uid, 3234 int gid 3235) 3236{ 3237 int ngroups = NGROUPS_MAX+1; 3238 int group_list[NGROUPS_MAX+1]; 3239 int i; 3240 struct group *adminGroup; 3241 struct passwd *pw; 3242 3243 3244 pw = getpwuid(uid); 3245 if (!pw) 3246 return false; 3247 3248 getgrouplist(pw->pw_name, pw->pw_gid, group_list, &ngroups); 3249 3250 adminGroup = getgrnam("admin"); 3251 if (adminGroup != NULL) { 3252 gid_t adminGid = adminGroup->gr_gid; 3253 for(i=0; i<ngroups; i++) 3254 { 3255 if (group_list[i] == adminGid) { 3256 return TRUE; // if a member of group "admin" 3257 } 3258 } 3259 } 3260 return false; 3261 3262} 3263 3264__private_extern__ int 3265callerIsConsole( 3266 int uid, 3267 int gid) 3268{ 3269#if TARGET_OS_EMBEDDED 3270 return false; 3271#else 3272 CFStringRef user_name = NULL; 3273 uid_t console_uid; 3274 gid_t console_gid; 3275 3276 user_name = (CFStringRef)SCDynamicStoreCopyConsoleUser(NULL, 3277 &console_uid, &console_gid); 3278 3279 if(user_name) { 3280 CFRelease(user_name); 3281 return ((uid == console_uid) && (gid == console_gid)); 3282 } else { 3283 // no data returned re: console user's uid or gid; return "false" 3284 return false; 3285 } 3286#endif /* !TARGET_OS_EMBEDDED */ 3287} 3288 3289 3290void _oneOffHacksSetup(void) 3291{ 3292#if !TARGET_OS_EMBEDDED 3293 registerForCalendarChangedNotification(); 3294#endif 3295} 3296 3297static const CFTimeInterval kTimeNSPerSec = 1000000000.0; 3298 3299CFTimeInterval _getHIDIdleTime(void) 3300{ 3301 static io_registry_entry_t hidsys = IO_OBJECT_NULL; 3302 CFNumberRef hidsys_idlenum = NULL; 3303 CFTimeInterval ret_time = 0.0; 3304 uint64_t idle_nanos = 0; 3305 3306 if (IO_OBJECT_NULL == hidsys) { 3307 hidsys = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOHIDSystem")); 3308 } 3309 if (!hidsys) 3310 goto exit; 3311 3312 hidsys_idlenum = IORegistryEntryCreateCFProperty(hidsys, CFSTR(kIOHIDIdleTimeKey), 0, 0); 3313 3314 if (!isA_CFNumber(hidsys_idlenum)) 3315 goto exit; 3316 3317 if (CFNumberGetValue(hidsys_idlenum, kCFNumberSInt64Type, &idle_nanos)) 3318 { 3319 ret_time = ((CFTimeInterval)idle_nanos)/kTimeNSPerSec; 3320 } 3321 3322exit: 3323 if (hidsys_idlenum) 3324 CFRelease(hidsys_idlenum); 3325 return ret_time; 3326} 3327 3328/************************************************************************/ 3329/************************************************************************/ 3330/************************************************************************/ 3331/************************************************************************/ 3332/************************************************************************/ 3333 3334// Code to read AppleSMC 3335 3336/************************************************************************/ 3337/************************************************************************/ 3338/************************************************************************/ 3339/************************************************************************/ 3340/************************************************************************/ 3341// Forwards 3342#if !TARGET_OS_EMBEDDED 3343static IOReturn _smcWriteKey( 3344 uint32_t key, 3345 uint8_t *outBuf, 3346 uint8_t outBufMax); 3347static IOReturn _smcReadKey( 3348 uint32_t key, 3349 uint8_t *outBuf, 3350 uint8_t *outBufMax); 3351 3352#endif 3353 3354/************************************************************************/ 3355__private_extern__ IOReturn _getACAdapterInfo( 3356 uint64_t *val) 3357{ 3358#if !TARGET_OS_EMBEDDED 3359 uint8_t readKeyLen = 8; 3360 return _smcReadKey('ACID', (void *)val, &readKeyLen); 3361#else 3362 return kIOReturnNotReadable; 3363#endif 3364} 3365/************************************************************************/ 3366__private_extern__ PowerSources _getPowerSource(void) 3367{ 3368#if !TARGET_OS_EMBEDDED 3369 IOPMBattery **batteries; 3370 3371 if (_batteryCount() && (batteries = _batteries()) 3372 && (!batteries[0]->externalConnected) ) 3373 return kBatteryPowered; 3374 else 3375 return kACPowered; 3376#else 3377 return kBatteryPowered; 3378#endif 3379} 3380 3381 3382#if !TARGET_OS_EMBEDDED 3383/************************************************************************/ 3384__private_extern__ IOReturn _smcWakeTimerPrimer(void) 3385{ 3386 uint8_t buf[2]; 3387 3388 buf[0] = 0; 3389 buf[1] = 1; 3390 return _smcWriteKey('CLWK', buf, 2); 3391} 3392 3393/************************************************************************/ 3394__private_extern__ IOReturn _smcWakeTimerGetResults(uint16_t *mSec) 3395{ 3396 uint8_t size = 2; 3397 uint8_t buf[2]; 3398 IOReturn ret; 3399 ret = _smcReadKey('CLWK', buf, &size); 3400 3401 if (kIOReturnSuccess == ret) { 3402 *mSec = buf[0] | (buf[1] << 8); 3403 } 3404 3405 return ret; 3406} 3407 3408bool smcSilentRunningSupport(void) 3409{ 3410 uint8_t size = 1; 3411 uint8_t buf[1]; 3412 static IOReturn ret = kIOReturnInvalid; 3413 3414 if (ret != kIOReturnSuccess) { 3415 ret = _smcReadKey('WKTP', buf, &size); 3416 } 3417 3418 if (kIOReturnSuccess == ret) { 3419 return true; 3420 } 3421 return false; 3422} 3423 3424 3425 3426/************************************************************************/ 3427/************************************************************************/ 3428 3429static IOReturn callSMCFunction( 3430 int which, 3431 SMCParamStruct *inputValues, 3432 SMCParamStruct *outputValues); 3433 3434/************************************************************************/ 3435// Methods 3436static IOReturn _smcWriteKey( 3437 uint32_t key, 3438 uint8_t *outBuf, 3439 uint8_t outBufMax) 3440{ 3441 SMCParamStruct stuffMeIn; 3442 SMCParamStruct stuffMeOut; 3443 IOReturn ret; 3444 int i; 3445 3446 if (key == 0) 3447 return kIOReturnCannotWire; 3448 3449 bzero(&stuffMeIn, sizeof(SMCParamStruct)); 3450 bzero(&stuffMeOut, sizeof(SMCParamStruct)); 3451 3452 // Determine key's data size 3453 stuffMeIn.data8 = kSMCGetKeyInfo; 3454 stuffMeIn.key = key; 3455 3456 ret = callSMCFunction(kSMCHandleYPCEvent, &stuffMeIn, &stuffMeOut); 3457 if (kIOReturnSuccess != ret) { 3458 goto exit; 3459 } 3460 3461 if (stuffMeOut.result == kSMCKeyNotFound) { 3462 ret = kIOReturnNotFound; 3463 goto exit; 3464 } else if (stuffMeOut.result != kSMCSuccess) { 3465 ret = kIOReturnInternalError; 3466 goto exit; 3467 } 3468 3469 // Write Key 3470 stuffMeIn.data8 = kSMCWriteKey; 3471 stuffMeIn.key = key; 3472 stuffMeIn.keyInfo.dataSize = stuffMeOut.keyInfo.dataSize; 3473 if (outBuf) { 3474 if (outBufMax > 32) outBufMax = 32; 3475 for (i=0; i<outBufMax; i++) { 3476 stuffMeIn.bytes[i] = outBuf[i]; 3477 } 3478 } 3479 bzero(&stuffMeOut, sizeof(SMCParamStruct)); 3480 ret = callSMCFunction(kSMCHandleYPCEvent, &stuffMeIn, &stuffMeOut); 3481 3482 if (stuffMeOut.result != kSMCSuccess) { 3483 ret = kIOReturnInternalError; 3484 goto exit; 3485 } 3486 3487exit: 3488 return ret; 3489} 3490 3491static IOReturn _smcReadKey( 3492 uint32_t key, 3493 uint8_t *outBuf, 3494 uint8_t *outBufMax) 3495{ 3496 SMCParamStruct stuffMeIn; 3497 SMCParamStruct stuffMeOut; 3498 IOReturn ret; 3499 int i; 3500 3501 if (key == 0 || outBuf == NULL) 3502 return kIOReturnCannotWire; 3503 3504 // Determine key's data size 3505 bzero(outBuf, *outBufMax); 3506 bzero(&stuffMeIn, sizeof(SMCParamStruct)); 3507 bzero(&stuffMeOut, sizeof(SMCParamStruct)); 3508 stuffMeIn.data8 = kSMCGetKeyInfo; 3509 stuffMeIn.key = key; 3510 3511 ret = callSMCFunction(kSMCHandleYPCEvent, &stuffMeIn, &stuffMeOut); 3512 if (kIOReturnSuccess != ret) { 3513 goto exit; 3514 } 3515 3516 if (stuffMeOut.result == kSMCKeyNotFound) { 3517 ret = kIOReturnNotFound; 3518 goto exit; 3519 } else if (stuffMeOut.result != kSMCSuccess) { 3520 ret = kIOReturnInternalError; 3521 goto exit; 3522 } 3523 3524 // Get Key Value 3525 stuffMeIn.data8 = kSMCReadKey; 3526 stuffMeIn.key = key; 3527 stuffMeIn.keyInfo.dataSize = stuffMeOut.keyInfo.dataSize; 3528 bzero(&stuffMeOut, sizeof(SMCParamStruct)); 3529 ret = callSMCFunction(kSMCHandleYPCEvent, &stuffMeIn, &stuffMeOut); 3530 if (stuffMeOut.result == kSMCKeyNotFound) { 3531 ret = kIOReturnNotFound; 3532 goto exit; 3533 } else if (stuffMeOut.result != kSMCSuccess) { 3534 ret = kIOReturnInternalError; 3535 goto exit; 3536 } 3537 3538 if (*outBufMax > stuffMeIn.keyInfo.dataSize) 3539 *outBufMax = stuffMeIn.keyInfo.dataSize; 3540 3541 // Byte-swap data returning from the SMC. 3542 // The data at key 'ACID' are not provided by the SMC and do 3543 // NOT need to be byte-swapped. 3544 for (i=0; i<*outBufMax; i++) 3545 { 3546 if ('ACID' == key) 3547 { 3548 // Do not byte swap 3549 outBuf[i] = stuffMeOut.bytes[i]; 3550 } else { 3551 // Byte swap 3552 outBuf[i] = stuffMeOut.bytes[*outBufMax - (i + 1)]; 3553 } 3554 } 3555exit: 3556 return ret; 3557} 3558 3559static IOReturn callSMCFunction( 3560 int which, 3561 SMCParamStruct *inputValues, 3562 SMCParamStruct *outputValues) 3563{ 3564 IOReturn result = kIOReturnError; 3565 3566 size_t inStructSize = sizeof(SMCParamStruct); 3567 size_t outStructSize = sizeof(SMCParamStruct); 3568 3569 io_connect_t _SMCConnect = IO_OBJECT_NULL; 3570 io_service_t smc = IO_OBJECT_NULL; 3571 3572 smc = IOServiceGetMatchingService( 3573 kIOMasterPortDefault, 3574 IOServiceMatching("AppleSMC")); 3575 if (IO_OBJECT_NULL == smc) { 3576 return kIOReturnNotFound; 3577 } 3578 3579 result = IOServiceOpen(smc, mach_task_self(), 1, &_SMCConnect); 3580 if (result != kIOReturnSuccess || 3581 IO_OBJECT_NULL == _SMCConnect) { 3582 _SMCConnect = IO_OBJECT_NULL; 3583 goto exit; 3584 } 3585 3586 result = IOConnectCallMethod(_SMCConnect, kSMCUserClientOpen, 3587 NULL, 0, NULL, 0, NULL, NULL, NULL, NULL); 3588 if (result != kIOReturnSuccess) { 3589 goto exit; 3590 } 3591 3592 result = IOConnectCallStructMethod(_SMCConnect, which, 3593 inputValues, inStructSize, 3594 outputValues, &outStructSize); 3595 3596exit: 3597 if (IO_OBJECT_NULL != _SMCConnect) { 3598 IOConnectCallMethod(_SMCConnect, kSMCUserClientClose, 3599 NULL, 0, NULL, 0, NULL, NULL, NULL, NULL); 3600 IOServiceClose(_SMCConnect); 3601 } 3602 3603 return result; 3604} 3605 3606#else 3607bool smcSilentRunningSupport(void) 3608{ 3609 return false; 3610} 3611#endif /* TARGET_OS_EMBEDDED */ 3612 3613/*****************************************************************************/ 3614/*****************************************************************************/ 3615 3616__private_extern__ IOReturn getNvramArgInt(char *key, int *value) 3617{ 3618 io_registry_entry_t optionsRef; 3619 IOReturn ret = kIOReturnError; 3620 CFDataRef dataRef = NULL; 3621 int *dataPtr = NULL; 3622 kern_return_t kr; 3623 CFMutableDictionaryRef dict = NULL; 3624 CFStringRef keyRef = NULL; 3625 3626 3627 optionsRef = IORegistryEntryFromPath(kIOMasterPortDefault, "IODeviceTree:/options"); 3628 if (optionsRef == 0) 3629 return kIOReturnError; 3630 3631 kr = IORegistryEntryCreateCFProperties(optionsRef, &dict, 0, 0); 3632 if (kr != KERN_SUCCESS) 3633 goto exit; 3634 3635 keyRef = CFStringCreateWithCStringNoCopy(0, key, kCFStringEncodingUTF8, kCFAllocatorNull); 3636 if (!isA_CFString(keyRef)) 3637 goto exit; 3638 dataRef = CFDictionaryGetValue(dict, keyRef); 3639 3640 if (!dataRef) 3641 goto exit; 3642 3643 dataPtr = (int*)CFDataGetBytePtr(dataRef); 3644 *value = *dataPtr; 3645 3646 ret = kIOReturnSuccess; 3647 3648exit: 3649 if (keyRef) CFRelease(keyRef); 3650 if (dict) CFRelease(dict); 3651 IOObjectRelease(optionsRef); 3652 return ret; 3653} 3654 3655/* extern symbol defined in IOKit.framework 3656 * IOCFURLAccess.c 3657 */ 3658extern Boolean _IOReadBytesFromFile(CFAllocatorRef alloc, const char *path, void **bytes, CFIndex *length, CFIndex maxLength); 3659 3660static int ProcessHibernateSettings(CFDictionaryRef dict, bool standby, bool isDesktop, io_registry_entry_t rootDomain) 3661{ 3662 IOReturn ret; 3663 CFTypeRef obj; 3664 CFNumberRef modeNum; 3665 CFNumberRef num; 3666 SInt32 modeValue = 0; 3667 CFURLRef url = NULL; 3668 Boolean createFile = false; 3669 Boolean haveFile = false; 3670 struct stat statBuf; 3671 char path[MAXPATHLEN]; 3672 int fd; 3673 long long size; 3674 size_t len; 3675 fstore_t prealloc; 3676 off_t filesize; 3677 off_t minFileSize = 0; 3678 off_t maxFileSize = 0; 3679 bool apo_available = false; 3680 SInt32 apo_enabled = 0; 3681 CFNumberRef apo_enabled_cf = NULL; 3682 3683 if (!platformPluginLoaded()) return (0); 3684 3685 if ( !IOPMFeatureIsAvailable( CFSTR(kIOHibernateFeatureKey), NULL ) ) 3686 { 3687 // Hibernation is not supported; return before we touch anything. 3688 return 0; 3689 } 3690 3691 3692 if ((modeNum = CFDictionaryGetValue(dict, CFSTR(kIOHibernateModeKey))) 3693 && isA_CFNumber(modeNum)) 3694 CFNumberGetValue(modeNum, kCFNumberSInt32Type, &modeValue); 3695 else 3696 modeNum = NULL; 3697 3698 apo_available = IOPMFeatureIsAvailable(CFSTR(kIOPMAutoPowerOffEnabledKey), NULL); 3699 if (apo_available && 3700 (apo_enabled_cf = CFDictionaryGetValue(dict, CFSTR(kIOPMAutoPowerOffEnabledKey ))) && 3701 isA_CFNumber(apo_enabled_cf)) 3702 { 3703 CFNumberGetValue(apo_enabled_cf, kCFNumberSInt32Type, &apo_enabled); 3704 } 3705 3706 if ((modeValue || (apo_available && apo_enabled)) 3707 && (obj = CFDictionaryGetValue(dict, CFSTR(kIOHibernateFileKey))) 3708 && isA_CFString(obj)) 3709 do 3710 { 3711 url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, obj, kCFURLPOSIXPathStyle, true); 3712 3713 if (!url || !CFURLGetFileSystemRepresentation(url, TRUE, (UInt8 *) path, MAXPATHLEN)) 3714 break; 3715 3716 len = sizeof(size); 3717 if (sysctlbyname("hw.memsize", &size, &len, NULL, 0)) 3718 break; 3719 3720 filesize = (size >> 1); 3721 if (isDesktop) 3722 { 3723 if (standby && (filesize > kStandbyDesktopHibernateFileSize)) filesize = kStandbyDesktopHibernateFileSize; 3724 } 3725 else 3726 { 3727 if (standby && (filesize > kStandbyPortableHibernateFileSize)) filesize = kStandbyPortableHibernateFileSize; 3728 } 3729 minFileSize = filesize; 3730 maxFileSize = 0; 3731 3732 if (0 != stat(path, &statBuf)) createFile = true; 3733 else 3734 { 3735 if ((S_IFBLK == (S_IFMT & statBuf.st_mode)) 3736 || (S_IFCHR == (S_IFMT & statBuf.st_mode))) 3737 { 3738 haveFile = true; 3739 } 3740 else if (S_IFREG == (S_IFMT & statBuf.st_mode)) 3741 { 3742 if ((statBuf.st_size == filesize) || (kIOHibernateModeFileResize & modeValue)) 3743 haveFile = true; 3744 else 3745 createFile = true; 3746 } 3747 else 3748 break; 3749 } 3750 3751 if (createFile) 3752 { 3753 do 3754 { 3755 char * patchpath, save = 0; 3756 struct statfs sfs; 3757 u_int64_t fsfree; 3758 3759 fd = -1; 3760 3761 /* 3762 * get rid of the filename at the end of the file specification 3763 * we only want the portion of the pathname that should already exist 3764 */ 3765 if ((patchpath = strrchr(path, '/'))) 3766 { 3767 save = *patchpath; 3768 *patchpath = 0; 3769 } 3770 3771 if (-1 == statfs(path, &sfs)) 3772 break; 3773 3774 fsfree = ((u_int64_t)sfs.f_bfree * (u_int64_t)sfs.f_bsize); 3775 if ((fsfree - filesize) < kIOHibernateMinFreeSpace) 3776 break; 3777 3778 if (patchpath) 3779 *patchpath = save; 3780 fd = open(path, O_CREAT | O_TRUNC | O_RDWR, 01600); 3781 if (-1 == fd) 3782 break; 3783 if (-1 == fchmod(fd, 01600)) 3784 break; 3785 3786 prealloc.fst_flags = F_ALLOCATEALL; // F_ALLOCATECONTIG 3787 prealloc.fst_posmode = F_PEOFPOSMODE; 3788 prealloc.fst_offset = 0; 3789 prealloc.fst_length = filesize; 3790 if (((-1 == fcntl(fd, F_PREALLOCATE, &prealloc)) 3791 || (-1 == fcntl(fd, F_SETSIZE, &prealloc.fst_length))) 3792 && (-1 == ftruncate(fd, prealloc.fst_length))) 3793 break; 3794 3795 haveFile = true; 3796 } 3797 while (false); 3798 if (-1 != fd) 3799 { 3800 close(fd); 3801 if (!haveFile) 3802 unlink(path); 3803 } 3804 } 3805 3806 if (!haveFile) 3807 break; 3808 3809#if defined (__i386__) || defined(__x86_64__) 3810#define kBootXPath "/System/Library/CoreServices/boot.efi" 3811#define kBootXSignaturePath "/System/Library/Caches/com.apple.bootefisignature" 3812#else 3813#define kBootXPath "/System/Library/CoreServices/BootX" 3814#define kBootXSignaturePath "/System/Library/Caches/com.apple.bootxsignature" 3815#endif 3816#define kCachesPath "/System/Library/Caches" 3817#define kGenSignatureCommand "/bin/cat " kBootXPath " | /usr/bin/openssl dgst -sha1 -hex -out " kBootXSignaturePath 3818 3819 3820 struct stat bootx_stat_buf; 3821 struct stat bootsignature_stat_buf; 3822 3823 if (0 != stat(kBootXPath, &bootx_stat_buf)) 3824 break; 3825 3826 if ((0 != stat(kBootXSignaturePath, &bootsignature_stat_buf)) 3827 || (bootsignature_stat_buf.st_mtime != bootx_stat_buf.st_mtime)) 3828 { 3829 if (-1 == stat(kCachesPath, &bootsignature_stat_buf)) 3830 { 3831 mkdir(kCachesPath, 0777); 3832 chmod(kCachesPath, 0777); 3833 } 3834 3835 // generate signature file 3836 if (0 != system(kGenSignatureCommand)) 3837 break; 3838 3839 // set mod time to that of source 3840 struct timeval fileTimes[2]; 3841 TIMESPEC_TO_TIMEVAL(&fileTimes[0], &bootx_stat_buf.st_atimespec); 3842 TIMESPEC_TO_TIMEVAL(&fileTimes[1], &bootx_stat_buf.st_mtimespec); 3843 if ((0 != utimes(kBootXSignaturePath, fileTimes))) 3844 break; 3845 } 3846 3847 3848 // send signature to kernel 3849 CFAllocatorRef alloc; 3850 void * sigBytes; 3851 CFIndex sigLen; 3852 3853 alloc = CFRetain(CFAllocatorGetDefault()); 3854 if (_IOReadBytesFromFile(alloc, kBootXSignaturePath, &sigBytes, &sigLen, 0)) 3855 ret = sysctlbyname("kern.bootsignature", NULL, NULL, sigBytes, sigLen); 3856 else 3857 ret = -1; 3858 if (sigBytes) 3859 CFAllocatorDeallocate(alloc, sigBytes); 3860 CFRelease(alloc); 3861 if (0 != ret) 3862 break; 3863 3864 IORegistryEntrySetCFProperty(rootDomain, CFSTR(kIOHibernateFileKey), obj); 3865 } 3866 while (false); 3867 3868 if (modeNum) 3869 IORegistryEntrySetCFProperty(rootDomain, CFSTR(kIOHibernateModeKey), modeNum); 3870 3871 if ((obj = CFDictionaryGetValue(dict, CFSTR(kIOHibernateFreeRatioKey))) 3872 && isA_CFNumber(obj)) 3873 { 3874 IORegistryEntrySetCFProperty(rootDomain, CFSTR(kIOHibernateFreeRatioKey), obj); 3875 } 3876 if ((obj = CFDictionaryGetValue(dict, CFSTR(kIOHibernateFreeTimeKey))) 3877 && isA_CFNumber(obj)) 3878 { 3879 IORegistryEntrySetCFProperty(rootDomain, CFSTR(kIOHibernateFreeTimeKey), obj); 3880 } 3881 if (minFileSize && (num = CFNumberCreate(NULL, kCFNumberLongLongType, &minFileSize))) 3882 { 3883 IORegistryEntrySetCFProperty(rootDomain, CFSTR(kIOHibernateFileMinSizeKey), num); 3884 CFRelease(num); 3885 } 3886 if (maxFileSize && (num = CFNumberCreate(NULL, kCFNumberLongLongType, &maxFileSize))) 3887 { 3888 IORegistryEntrySetCFProperty(rootDomain, CFSTR(kIOHibernateFileMaxSizeKey), num); 3889 CFRelease(num); 3890 } 3891 3892 if (url) 3893 CFRelease(url); 3894 3895 return (0); 3896} 3897 3898