#include <CoreFoundation/CoreFoundation.h>
#include <SystemConfiguration/SystemConfiguration.h>
#include <SystemConfiguration/SCValidation.h>
#include <SystemConfiguration/SCPreferencesPrivate.h>
#include <SystemConfiguration/SCDynamicStorePrivate.h>
#include <SystemConfiguration/SCDynamicStoreCopySpecificPrivate.h>
#include <SystemConfiguration/SCDPlugin.h>
#include <IOKit/pwr_mgt/IOPM.h>
#include <IOKit/pwr_mgt/IOPMLib.h>
#include <IOKit/pwr_mgt/IOPMLibPrivate.h>
#include <IOKit/IOKitKeysPrivate.h>
#include <IOKit/IOCFSerialize.h>
#include <IOKit/IOCFUnserialize.h>
#include <IOKit/IOMessage.h>
#include <IOKit/ps/IOPSKeys.h>
#include <IOKit/ps/IOPowerSources.h>
#include <syslog.h>
#include <unistd.h>
#include <grp.h>
#include <pwd.h>
#include <mach/mach.h>
#include <mach/mach_host.h>
#include <mach/mach_error.h>
#include <servers/bootstrap.h>
#include "PMSettings.h"
#include "SetActive.h"
#include "powermanagementServer.h"
#define kIOPMAppName "Power Management configd plugin"
#define kIOPMPrefsPath "com.apple.PowerManagement.xml"
#define kAssertionsArraySize 5
#define kIOPMTaskPortKey CFSTR("task")
#define kIOPMAssertionsKey CFSTR("assertions")
#define kIOPMNumAssertionTypes 2
#define kHighPerfIndex 0
#define kPreventIdleIndex 1
static int callerIsRoot(int uid, int gid);
static int callerIsAdmin(int uid, int gid);
static int callerIsConsole(int uid, int gid);
extern CFMachPortRef serverMachPort;
__private_extern__ void mig_server_callback(CFMachPortRef port, void *msg, CFIndex size, void *info);
__private_extern__ void cleanupAssertions(mach_port_t dead_port);
static void evaluateAssertions(void);
static void calculateAggregates(void);
static CFMutableDictionaryRef assertionsDict = NULL;
static int aggregate_assertions[kIOPMNumAssertionTypes];
static CFStringRef assertion_types_arr[kIOPMNumAssertionTypes];
static void
calculateAggregates(void)
{
CFDictionaryRef *process_assertions = NULL;
int process_count = 0;
int i, j;
for(i=0; i<kIOPMNumAssertionTypes; i++)
{
aggregate_assertions[i] = 0;
}
process_count = CFDictionaryGetCount(assertionsDict);
process_assertions = malloc(sizeof(CFDictionaryRef) * process_count);
CFDictionaryGetKeysAndValues(assertionsDict, NULL, (const void **)process_assertions);
for(i=0; i<process_count; i++)
{
CFArrayRef asst_arr = NULL;
int asst_arr_count = 0;
if(!isA_CFDictionary(process_assertions[i])) continue;
asst_arr = isA_CFArray(
CFDictionaryGetValue(process_assertions[i], kIOPMAssertionsKey));
if(!asst_arr) continue;
asst_arr_count = CFArrayGetCount(asst_arr);
for(j=0; j<asst_arr_count; j++)
{
CFDictionaryRef this_assertion = NULL;
CFStringRef asst_type = NULL;
CFNumberRef asst_val = NULL;
int val = -1;
this_assertion = CFArrayGetValueAtIndex(asst_arr, j);
if(isA_CFDictionary(this_assertion))
{
asst_type = isA_CFString(
CFDictionaryGetValue(this_assertion, kIOPMAssertionTypeKey));
asst_val = isA_CFNumber(
CFDictionaryGetValue(this_assertion, kIOPMAssertionValueKey));
if(asst_type && asst_val) {
CFNumberGetValue(asst_val, kCFNumberIntType, &val);
if(kIOPMAssertionEnable == val)
{
if(kCFCompareEqualTo ==
CFStringCompare(asst_type, kIOPMCPUBoundAssertion, 0))
{
aggregate_assertions[kHighPerfIndex] = 1;
} else if(kCFCompareEqualTo ==
CFStringCompare(asst_type, kIOPMPreventIdleSleepAssertion, 0))
{
aggregate_assertions[kPreventIdleIndex] = 1;
}
}
}
}
}
}
free(process_assertions);
}
static void
evaluateAssertions(void)
{
calculateAggregates();
if(aggregate_assertions[kHighPerfIndex]) {
overrideSetting(kPMForceHighSpeed, 1);
} else {
overrideSetting(kPMForceHighSpeed, 0);
}
activateSettingOverrides();
}
__private_extern__ IOReturn
_IOPMSetActivePowerProfilesRequiresRoot
(
CFDictionaryRef which_profile,
int uid,
int gid
)
{
IOReturn ret = kIOReturnError;
SCPreferencesRef energyPrefs = NULL;
if( !callerIsRoot(uid, gid) &&
!callerIsAdmin(uid, gid) &&
!callerIsConsole(uid, gid) ||
( (-1 == uid) || (-1 == gid) ))
{
ret = kIOReturnNotPrivileged;
goto exit;
}
if(!which_profile) {
ret = kIOReturnBadArgument;
goto exit;
}
energyPrefs = SCPreferencesCreate( kCFAllocatorDefault, CFSTR(kIOPMAppName), CFSTR(kIOPMPrefsPath) );
if(!energyPrefs) {
goto exit;
}
if(!SCPreferencesLock(energyPrefs, true))
{
ret = kIOReturnInternalError;
goto exit;
}
if(!SCPreferencesSetValue(energyPrefs, CFSTR("ActivePowerProfiles"), which_profile)) {
goto exit;
}
if(!SCPreferencesCommitChanges(energyPrefs))
{
if(kSCStatusAccessError == SCError()) ret = kIOReturnNotPrivileged;
else ret = kIOReturnError;
goto exit;
}
if(!SCPreferencesApplyChanges(energyPrefs))
{
if(kSCStatusAccessError == SCError()) ret = kIOReturnNotPrivileged;
else ret = kIOReturnError;
goto exit;
}
ret = kIOReturnSuccess;
exit:
if(energyPrefs) {
SCPreferencesUnlock(energyPrefs);
CFRelease(energyPrefs);
}
return ret;
}
static int
callerIsRoot(
int uid,
int gid
)
{
return (0 == uid);
}
static int
callerIsAdmin(
int uid,
int gid
)
{
int ngroups = NGROUPS_MAX+1;
int group_list[NGROUPS_MAX+1];
int i;
struct group *adminGroup;
struct passwd *pw;
pw = getpwuid(uid);
if(!pw) return false;
getgrouplist(pw->pw_name, pw->pw_gid, group_list, &ngroups);
adminGroup = getgrnam("admin");
if (adminGroup != NULL) {
gid_t adminGid = adminGroup->gr_gid;
for(i=0; i<ngroups; i++)
{
if (group_list[i] == adminGid) {
return TRUE; }
}
}
return false;
}
static int
callerIsConsole(
int uid,
int gid)
{
CFStringRef user_name = 0;
uid_t console_uid;
gid_t console_gid;
user_name = SCDynamicStoreCopyConsoleUser(
NULL, &console_uid, &console_gid);
if(user_name) CFRelease(user_name);
else return false;
return ((uid == console_uid) && (gid == console_gid));
}
__private_extern__ void
PMAssertions_prime(void)
{
assertion_types_arr[kHighPerfIndex] = kIOPMCPUBoundAssertion;
assertion_types_arr[kPreventIdleIndex] = kIOPMPreventIdleSleepAssertion;
}
kern_return_t _io_pm_assertion_create
(
mach_port_t server,
mach_port_t task,
string_t profile,
mach_msg_type_number_t profileCnt,
int level,
int *assertion_id,
int *result
)
{
CFMachPortRef cf_port_for_task = NULL;
mach_port_name_t rcv_right = MACH_PORT_NULL;
kern_return_t kern_result = KERN_SUCCESS;
CFDictionaryRef tmp_task = NULL;
CFMutableDictionaryRef this_task = NULL;
CFArrayRef tmp_assertions = NULL;
CFMutableArrayRef assertions = NULL;
mach_port_t oldNotify = MACH_PORT_NULL;
kern_return_t err = KERN_SUCCESS;
int i;
*assertion_id = -1;
cf_port_for_task = CFMachPortCreateWithPort(0, task, mig_server_callback, 0, 0);
if(!cf_port_for_task) {
*result = kIOReturnNoMemory;
goto exit;
}
if(assertionsDict &&
(tmp_task = CFDictionaryGetValue(assertionsDict, cf_port_for_task)) )
{
this_task = CFDictionaryCreateMutableCopy(0, 0, tmp_task);
CFDictionarySetValue(assertionsDict, cf_port_for_task, this_task);
} else {
this_task = CFDictionaryCreateMutable(0, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
if(!this_task){
*result = kIOReturnNoMemory;
goto exit;
}
CFDictionarySetValue(this_task, CFSTR("task"), cf_port_for_task);
err = mach_port_request_notification(
mach_task_self(), task, MACH_NOTIFY_DEAD_NAME, 1, CFMachPortGetPort(serverMachPort), MACH_MSG_TYPE_MAKE_SEND_ONCE, &oldNotify); if(KERN_SUCCESS != err)
{
syslog(LOG_ERR, "mach port request notification error %s(%08x)\n",
mach_error_string(err), err);
*result = err;
goto exit;
}
if(!assertionsDict) {
assertionsDict = CFDictionaryCreateMutable(0, 0,
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
}
CFDictionarySetValue(assertionsDict, cf_port_for_task, this_task);
}
tmp_assertions = CFDictionaryGetValue(this_task, kIOPMAssertionsKey);
if(!tmp_assertions) {
assertions = CFArrayCreateMutable(0, kAssertionsArraySize, &kCFTypeArrayCallBacks);
for(i=0; i<kAssertionsArraySize; i++) {
CFArraySetValueAtIndex(assertions, i, kCFBooleanFalse);
}
} else {
assertions = CFArrayCreateMutableCopy(0, 0, tmp_assertions);
}
CFDictionarySetValue(this_task, kIOPMAssertionsKey, assertions);
int index = -1;
int asst_count;
asst_count = CFArrayGetCount(assertions);
if(0 == asst_count) {
index = 0;
} else {
for(i=0; i<asst_count; i++) {
if(kCFBooleanFalse == CFArrayGetValueAtIndex(assertions, i)) break;
}
if(kAssertionsArraySize == i) {
*result = kIOReturnNoMemory;
goto exit;
} else index = i;
}
CFMutableDictionaryRef new_assertion_dict = NULL;
CFStringRef cf_assertion_str = NULL;
CFNumberRef cf_assertion_val = NULL;
*assertion_id = index;
new_assertion_dict = CFDictionaryCreateMutable(0, 2,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
cf_assertion_str = CFStringCreateWithCString(0, profile, kCFStringEncodingMacRoman);
cf_assertion_val = CFNumberCreate(0, kCFNumberIntType, &level);
CFDictionarySetValue(new_assertion_dict, kIOPMAssertionTypeKey, cf_assertion_str);
CFDictionarySetValue(new_assertion_dict, kIOPMAssertionValueKey, cf_assertion_val);
CFRelease(cf_assertion_str);
CFRelease(cf_assertion_val);
CFArraySetValueAtIndex(assertions, index, new_assertion_dict);
CFRelease(new_assertion_dict);
evaluateAssertions();
*result = kIOReturnSuccess;
exit:
if(this_task) CFRelease(this_task);
if(assertions) CFRelease(assertions);
if(cf_port_for_task) CFRelease(cf_port_for_task);
return KERN_SUCCESS;
}
kern_return_t _io_pm_assertion_release
(
mach_port_t server,
mach_port_t task,
int assertion_id,
int *return_code
)
{
CFMachPortRef cf_port_for_task = NULL;
CFDictionaryRef calling_task = NULL;
CFMutableArrayRef assertions = NULL;
CFTypeRef assertion_to_release = NULL;
if((assertion_id < 0) || (assertion_id >= kAssertionsArraySize)) {
*return_code = kIOReturnBadArgument;
goto exit;
}
cf_port_for_task = CFMachPortCreateWithPort(0, task, mig_server_callback, 0, 0);
if(!cf_port_for_task) {
*return_code = kIOReturnNoMemory;
goto exit;
}
calling_task = CFDictionaryGetValue(assertionsDict, cf_port_for_task);
if(!calling_task) {
*return_code = kIOReturnNotFound;
goto exit;
}
assertions = (CFMutableArrayRef)CFDictionaryGetValue(calling_task, kIOPMAssertionsKey);
if(!assertions) {
*return_code = kIOReturnInternalError;
goto exit;
}
assertion_to_release = CFArrayGetValueAtIndex(assertions, assertion_id);
if(!assertion_to_release || !isA_CFDictionary(assertion_to_release)) {
*return_code = kIOReturnNotFound;
goto exit;
}
CFArraySetValueAtIndex(assertions, assertion_id, kCFBooleanFalse);
evaluateAssertions();
*return_code = kIOReturnSuccess;
exit:
if(cf_port_for_task) CFRelease(cf_port_for_task);
return KERN_SUCCESS;
}
kern_return_t _io_pm_copy_active_assertions
(
mach_port_t server,
vm_offset_t *profiles,
mach_msg_type_number_t *profilesCnt
)
{
CFDataRef serialized_object = NULL;
CFMachPortRef *task_id_arr = NULL;
CFDictionaryRef *task_assertion_dict_arr = NULL;
CFMutableDictionaryRef ret_dict = 0;
int dict_count;
int i;
*profiles = 0;
*profilesCnt = 0;
if(!assertionsDict) goto exit;
dict_count = CFDictionaryGetCount(assertionsDict);
if(0 == dict_count) goto exit;
ret_dict = CFDictionaryCreateMutable(0, 0,
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
task_id_arr = (CFMachPortRef *)malloc(sizeof(CFMachPortRef)*dict_count);
task_assertion_dict_arr = (CFDictionaryRef *)malloc(sizeof(CFDictionaryRef)*dict_count);
if(!task_id_arr || !task_assertion_dict_arr) goto exit;
CFDictionaryGetKeysAndValues(assertionsDict,
(const void **)task_id_arr, (const void **)task_assertion_dict_arr);
for(i=0; i<dict_count; i++)
{
int ppid;
CFStringRef cf_ppid;
CFArrayRef cf_process_assertions_arr;
char pid_str[10];
if(!task_id_arr[i] || !task_assertion_dict_arr[i]) continue;
pid_for_task(CFMachPortGetPort(task_id_arr[i]), &ppid);
snprintf(pid_str, 9, "%d", ppid);
cf_ppid = CFStringCreateWithCString(0, pid_str, kCFStringEncodingMacRoman);
cf_process_assertions_arr = CFDictionaryGetValue(
task_assertion_dict_arr[i], kIOPMAssertionsKey);
CFDictionaryAddValue(ret_dict, cf_ppid, cf_process_assertions_arr);
CFRelease(cf_ppid);
}
free(task_id_arr);
free(task_assertion_dict_arr);
serialized_object = (CFDataRef)IOCFSerialize(ret_dict, 0);
if(serialized_object)
{
kern_return_t status = KERN_SUCCESS;
void *tmp_buf = NULL;
int len = CFDataGetLength(serialized_object);
status = vm_allocate(mach_task_self(), (void *)&tmp_buf, len, TRUE);
if(KERN_SUCCESS != status) goto exit;
bcopy((void *)CFDataGetBytePtr(serialized_object), tmp_buf, len);
CFRelease(serialized_object);
*profiles = (vm_offset_t)tmp_buf;
*profilesCnt = len;
}
CFRelease(ret_dict);
exit:
return KERN_SUCCESS;
}
kern_return_t _io_pm_copy_assertions_status
(
mach_port_t server,
vm_offset_t *profiles,
mach_msg_type_number_t *profilesCnt
)
{
CFDictionaryRef assertions_info = NULL;
CFDataRef serialized_object = NULL;
CFNumberRef cf_agg_vals[kIOPMNumAssertionTypes];
int i;
for(i=0; i<kIOPMNumAssertionTypes; i++)
{
cf_agg_vals[i] = CFNumberCreate(
0,
kCFNumberIntType,
&aggregate_assertions[i]);
}
assertions_info = CFDictionaryCreate(
0,
(const void **)assertion_types_arr, (const void **)cf_agg_vals, kIOPMNumAssertionTypes,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
for(i=0; i<kIOPMNumAssertionTypes; i++)
{
CFRelease(cf_agg_vals[i]);
}
serialized_object = (CFDataRef)IOCFSerialize((CFTypeRef)assertions_info, 0);
CFRelease(assertions_info);
if(serialized_object)
{
kern_return_t status = KERN_SUCCESS;
void *tmp_buf = NULL;
int len = CFDataGetLength(serialized_object);
status = vm_allocate(mach_task_self(), (void *)&tmp_buf, len, TRUE);
if(KERN_SUCCESS != status) goto exit;
bcopy((void *)CFDataGetBytePtr(serialized_object), tmp_buf, len);
CFRelease(serialized_object);
*profiles = (vm_offset_t)tmp_buf;
*profilesCnt = len;
} else {
*profiles = 0;
*profilesCnt = 0;
}
exit:
return KERN_SUCCESS;
}
__private_extern__ void
cleanupAssertions(
mach_port_t dead_port
)
{
CFMachPortRef cf_task_port = NULL;
int dead_pid = -1;
if(!assertionsDict) {
return;
}
cf_task_port = CFMachPortCreateWithPort(0, dead_port, mig_server_callback, 0, 0);
if(!cf_task_port) return;
pid_for_task(dead_port, &dead_pid);
dead_pid, dead_port);
CFDictionaryRemoveValue(assertionsDict, cf_task_port);
evaluateAssertions();
exit:
return;
}