#include <SystemConfiguration/SystemConfiguration.h>
#include <SystemConfiguration/SCValidation.h>
#include "IOPMKeys.h"
#include "IOPMLib.h"
#include "IOPMLibPrivate.h"
enum {
kIOPMMaxScheduledEntries = 1000
};
static CFComparisonResult compare_dates(CFDictionaryRef a1, CFDictionaryRef a2, void *c)
{
CFDateRef d1, d2;
a1 = isA_CFDictionary(a1);
a2 = isA_CFDictionary(a2);
if(!a1) return kCFCompareGreaterThan;
else if(!a2) return kCFCompareLessThan;
d1 = isA_CFDate(CFDictionaryGetValue(a1, CFSTR(kIOPMPowerEventTimeKey)));
d2 = isA_CFDate(CFDictionaryGetValue(a2, CFSTR(kIOPMPowerEventTimeKey)));
if(!d1) return kCFCompareGreaterThan;
else if(!d2) return kCFCompareLessThan;
return CFDateCompare(d1, d2, 0);
}
static CFAbsoluteTime roundOffDate(CFAbsoluteTime time)
{
return (CFAbsoluteTime)nearbyint((time - fmod(time, (double)30.0)));
}
static CFDictionaryRef _IOPMCreatePowerOnDictionary(CFAbsoluteTime the_time, CFStringRef the_id, CFStringRef type)
{
CFMutableDictionaryRef d;
CFDateRef the_date;
the_id = isA_CFString(the_id);
the_time = roundOffDate(the_time);
the_date = CFDateCreate(0, the_time);
d = CFDictionaryCreateMutable(0, 3, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if(!d) return NULL;
CFDictionaryAddValue(d, CFSTR(kIOPMPowerEventTimeKey), the_date);
if(!the_id) the_id = CFSTR("");
CFDictionaryAddValue(d, CFSTR(kIOPMPowerEventAppNameKey), the_id);
CFDictionaryAddValue(d, CFSTR(kIOPMPowerEventTypeKey), type);
CFRelease(the_date);
return d;
}
static bool inputsValid(CFDateRef time_to_wake, CFStringRef my_id, CFStringRef type)
{
if(!isA_CFDate(time_to_wake)) return false;
if(!isA_CFString(type)) return false;
if(!(CFEqual(type, CFSTR(kIOPMAutoWake)) ||
CFEqual(type, CFSTR(kIOPMAutoPowerOn)) ||
CFEqual(type, CFSTR(kIOPMAutoWakeOrPowerOn)) ||
CFEqual(type, CFSTR(kIOPMAutoSleep)) ||
CFEqual(type, CFSTR(kIOPMAutoShutdown))))
{
return false;
}
return true;
}
static bool addEntryAndSetPrefs(SCPreferencesRef prefs, CFStringRef type, CFDictionaryRef package)
{
CFArrayRef arr = 0;
CFMutableArrayRef new_arr = 0;
bool ret = false;
arr = isA_CFArray(SCPreferencesGetValue(prefs, type));
if(arr)
{
new_arr = CFArrayCreateMutableCopy(0, 0, arr);
CFArrayAppendValue(new_arr, package);
CFArraySortValues(new_arr, CFRangeMake(0, CFArrayGetCount(new_arr)), (CFComparatorFunction)compare_dates, 0);
} else
{
new_arr = (CFMutableArrayRef)CFArrayCreate(0, (const void **)&package, 1, &kCFTypeArrayCallBacks);
}
if(!new_arr)
{
ret = false;
goto exit;
}
if(!SCPreferencesSetValue(prefs, type, new_arr))
{
ret = false;
goto exit;
}
ret = true;
exit:
if(new_arr) CFRelease(new_arr);
return ret;
}
static bool removeEntryAndSetPrefs(SCPreferencesRef prefs, CFStringRef type, CFDictionaryRef package)
{
CFArrayRef arr = 0;
CFMutableArrayRef mut_arr = 0;
CFIndex i;
CFDictionaryRef cancelee = 0;
bool ret = false;
arr = isA_CFArray(SCPreferencesGetValue(prefs, type));
if(!arr)
{
ret = true;
goto exit;
}
i = CFArrayBSearchValues(arr, CFRangeMake(0, CFArrayGetCount(arr)), package,
(CFComparatorFunction)compare_dates, 0);
if(0 <= i <= CFArrayGetCount(arr))
{
cancelee = CFArrayGetValueAtIndex(arr, i);
if(kCFCompareEqualTo == compare_dates(package, cancelee, 0))
{
if(kCFCompareEqualTo == CFStringCompare(CFDictionaryGetValue(package, CFSTR(kIOPMPowerEventAppNameKey)),
CFDictionaryGetValue(cancelee, CFSTR(kIOPMPowerEventAppNameKey)), 0))
{
mut_arr = CFArrayCreateMutableCopy(0, 0, arr);
CFArrayRemoveValueAtIndex(mut_arr, i);
if(!SCPreferencesSetValue(prefs, type, mut_arr))
{
ret = false;
goto exit;
}
ret = true;
}
}
}
exit:
if(mut_arr) CFRelease(mut_arr);
return ret;
}
extern IOReturn IOPMSchedulePowerEvent(CFDateRef time_to_wake, CFStringRef my_id, CFStringRef type)
{
CFDictionaryRef package = 0;
SCPreferencesRef prefs = 0;
IOReturn ret = false;
CFArrayRef tmp_wakeup_arr;
int total_count = 0;
CFAbsoluteTime abs_time_to_wake;
if(!inputsValid(time_to_wake, my_id, type))
{
ret = kIOReturnBadArgument;
goto exit;
}
abs_time_to_wake = CFDateGetAbsoluteTime(time_to_wake);
if(abs_time_to_wake < (CFAbsoluteTimeGetCurrent() + 30.0))
{
ret = kIOReturnNotReady;
goto exit;
}
package = _IOPMCreatePowerOnDictionary(abs_time_to_wake, my_id, type);
prefs = SCPreferencesCreate(0, CFSTR("IOKit-AutoWake"), CFSTR(kIOPMAutoWakePrefsPath));
if(!prefs || !SCPreferencesLock(prefs, true))
{
if(kSCStatusAccessError == SCError())
ret = kIOReturnNotPrivileged;
else ret = kIOReturnError;
goto exit;
}
total_count = 0;
tmp_wakeup_arr = isA_CFArray(SCPreferencesGetValue(prefs, CFSTR(kIOPMAutoPowerOn)));
if(tmp_wakeup_arr) total_count += CFArrayGetCount(tmp_wakeup_arr);
tmp_wakeup_arr = isA_CFArray(SCPreferencesGetValue(prefs, CFSTR(kIOPMAutoWake)));
if(tmp_wakeup_arr) total_count += CFArrayGetCount(tmp_wakeup_arr);
tmp_wakeup_arr = isA_CFArray(SCPreferencesGetValue(prefs, CFSTR(kIOPMAutoSleep)));
if(tmp_wakeup_arr) total_count += CFArrayGetCount(tmp_wakeup_arr);
tmp_wakeup_arr = isA_CFArray(SCPreferencesGetValue(prefs, CFSTR(kIOPMAutoShutdown)));
if(tmp_wakeup_arr) total_count += CFArrayGetCount(tmp_wakeup_arr);
if(total_count >= kIOPMMaxScheduledEntries)
{
ret = kIOReturnNoSpace;
goto exit;
}
if(CFEqual(type, CFSTR(kIOPMAutoWakeOrPowerOn)))
{
addEntryAndSetPrefs(prefs, CFSTR(kIOPMAutoWake), package);
addEntryAndSetPrefs(prefs, CFSTR(kIOPMAutoPowerOn), package);
} else {
addEntryAndSetPrefs(prefs, type, package);
}
SCPreferencesSetValue(prefs, CFSTR("WARNING"), CFSTR("Do not edit this file by hand - it must remain in sorted-by-date order."));
if(!SCPreferencesCommitChanges(prefs))
{
ret = kIOReturnError;
goto exit;
}
ret = kIOReturnSuccess;
exit:
if(package) CFRelease(package);
if(prefs) SCPreferencesUnlock(prefs);
if(prefs) CFRelease(prefs);
return ret;
}
extern IOReturn IOPMCancelScheduledPowerEvent(CFDateRef time_to_wake, CFStringRef my_id, CFStringRef wake_or_restart)
{
CFDictionaryRef package = 0;
SCPreferencesRef prefs = 0;
bool changed = false;
IOReturn ret = kIOReturnError;
if(!inputsValid(time_to_wake, my_id, wake_or_restart))
{
ret = kIOReturnBadArgument;
goto exit;
}
package = _IOPMCreatePowerOnDictionary(CFDateGetAbsoluteTime(time_to_wake), my_id, wake_or_restart);
if(!package) goto exit;
prefs = SCPreferencesCreate(0, CFSTR("IOKit-AutoWake"), CFSTR(kIOPMAutoWakePrefsPath));
if(!prefs || !SCPreferencesLock(prefs, true))
{
if(kSCStatusAccessError == SCError())
ret = kIOReturnNotPrivileged;
else ret = kIOReturnError;
goto exit;
}
if(CFEqual(wake_or_restart, CFSTR(kIOPMAutoWakeOrPowerOn)))
{
changed = removeEntryAndSetPrefs(prefs, CFSTR(kIOPMAutoWake), package);
changed |= removeEntryAndSetPrefs(prefs, CFSTR(kIOPMAutoPowerOn), package);
} else {
changed = removeEntryAndSetPrefs(prefs, wake_or_restart, package);
}
if(changed)
{
if(!SCPreferencesCommitChanges(prefs))
{
ret = kIOReturnError;
goto exit;
} else ret = kIOReturnSuccess;
} else ret = kIOReturnNotFound;
exit:
if(package) CFRelease(package);
if(prefs) SCPreferencesUnlock(prefs);
if(prefs) CFRelease(prefs);
return ret;
}
extern CFArrayRef IOPMCopyScheduledPowerEvents
(void)
{
SCPreferencesRef prefs;
CFArrayRef wake_arr, poweron_arr, sleep_arr, shutdown_arr;
CFMutableArrayRef new_arr;
prefs = SCPreferencesCreate(0, CFSTR("IOKit-AutoWake"), CFSTR(kIOPMAutoWakePrefsPath));
if(!prefs) return NULL;
wake_arr = isA_CFArray(SCPreferencesGetValue(prefs, CFSTR(kIOPMAutoWake)));
poweron_arr = isA_CFArray(SCPreferencesGetValue(prefs, CFSTR(kIOPMAutoPowerOn)));
sleep_arr = isA_CFArray(SCPreferencesGetValue(prefs, CFSTR(kIOPMAutoSleep)));
shutdown_arr = isA_CFArray(SCPreferencesGetValue(prefs, CFSTR(kIOPMAutoShutdown)));
new_arr = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
if(wake_arr) CFArrayAppendArray(new_arr, wake_arr, CFRangeMake(0, CFArrayGetCount(wake_arr)));
if(poweron_arr) CFArrayAppendArray(new_arr, poweron_arr, CFRangeMake(0, CFArrayGetCount(poweron_arr)));
if(sleep_arr) CFArrayAppendArray(new_arr, sleep_arr, CFRangeMake(0, CFArrayGetCount(sleep_arr)));
if(shutdown_arr) CFArrayAppendArray(new_arr, shutdown_arr, CFRangeMake(0, CFArrayGetCount(shutdown_arr)));
CFRelease(prefs);
if(!new_arr)
{
return NULL;
} else {
if(0 == CFArrayGetCount(new_arr))
{
CFRelease(new_arr);
return NULL;
}
}
return (CFArrayRef)new_arr;
}