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