#include "powerwatch.h"
#include <IOKit/IOMessage.h>
namespace Security {
namespace MachPlusPlus {
PowerWatcher::~PowerWatcher()
{ }
void PowerWatcher::systemWillSleep()
{ }
void PowerWatcher::systemIsWaking()
{ }
void PowerWatcher::systemWillPowerDown()
{ }
void PowerWatcher::systemWillPowerOn()
{ }
void
IOPowerWatcher::iopmcallback(void * param,
IOPMConnection connection,
IOPMConnectionMessageToken token,
IOPMSystemPowerStateCapabilities capabilities)
{
IOPowerWatcher *me = (IOPowerWatcher *)param;
if (SECURITY_DEBUG_LOG_ENABLED()) {
secdebug("powerwatch", "powerstates");
if (capabilities & kIOPMSystemPowerStateCapabilityDisk)
secdebug("powerwatch", "disk");
if (capabilities & kIOPMSystemPowerStateCapabilityNetwork)
secdebug("powerwatch", "net");
if (capabilities & kIOPMSystemPowerStateCapabilityAudio)
secdebug("powerwatch", "audio");
if (capabilities & kIOPMSystemPowerStateCapabilityVideo)
secdebug("powerwatch", "video");
}
if ((capabilities & (kIOPMSystemPowerStateCapabilityCPU|kIOPMSystemPowerStateCapabilityVideo)) == kIOPMSystemPowerStateCapabilityCPU) {
secdebug("powerwatch", "enter DarkWake");
me->mInDarkWake = true;
} else if (me->mInDarkWake) {
secdebug("powerwatch", "exit DarkWake");
me->mInDarkWake = false;
}
(void)IOPMConnectionAcknowledgeEvent(connection, token);
return;
}
void
IOPowerWatcher::setupDarkWake()
{
IOReturn ret;
mInDarkWake = false;
ret = ::IOPMConnectionCreate(CFSTR("IOPowerWatcher"),
kIOPMSystemPowerStateCapabilityDisk
| kIOPMSystemPowerStateCapabilityNetwork
| kIOPMSystemPowerStateCapabilityAudio
| kIOPMSystemPowerStateCapabilityVideo,
&mIOPMconn);
if (ret == kIOReturnSuccess) {
ret = ::IOPMConnectionSetNotification(mIOPMconn, this,
(IOPMEventHandlerType)iopmcallback);
if (ret == kIOReturnSuccess) {
::IOPMConnectionSetDispatchQueue(mIOPMconn, mIOPMqueue);
}
}
mUserActiveHandle = IOPMScheduleUserActiveChangedNotification(mIOPMqueue, ^(bool active) {
if (active) {
mInDarkWake = false;
}
});
dispatch_group_leave(mDarkWakeGroup);
}
IOPowerWatcher::IOPowerWatcher() :
mKernelPort(0), mIOPMconn(NULL), mIOPMqueue(NULL), mDarkWakeGroup(NULL), mUserActiveHandle(NULL)
{
if (!(mKernelPort = ::IORegisterForSystemPower(this, &mPortRef, ioCallback, &mHandle)))
UnixError::throwMe(EINVAL);
mIOPMqueue = dispatch_queue_create("com.apple.security.IOPowerWatcher", NULL);
if (mIOPMqueue == NULL)
return;
mDarkWakeGroup = dispatch_group_create();
dispatch_group_enter(mDarkWakeGroup);
dispatch_async(mIOPMqueue, ^ { setupDarkWake(); });
}
IOPowerWatcher::~IOPowerWatcher()
{
if (mDarkWakeGroup) {
::dispatch_group_wait(mDarkWakeGroup, DISPATCH_TIME_FOREVER);
::dispatch_release(mDarkWakeGroup);
}
if (mKernelPort)
::IODeregisterForSystemPower(&mHandle);
if (mIOPMconn) {
::IOPMConnectionSetDispatchQueue(mIOPMconn, NULL);
::IOPMConnectionRelease(mIOPMconn);
}
if (mUserActiveHandle)
::IOPMUnregisterNotification(mUserActiveHandle);
if (mIOPMqueue)
::dispatch_release(mIOPMqueue);
}
void IOPowerWatcher::ioCallback(void *refCon, io_service_t service,
natural_t messageType, void *argument)
{
IOPowerWatcher *me = (IOPowerWatcher *)refCon;
enum { allow, refuse, ignore } reaction;
switch (messageType) {
case kIOMessageSystemWillSleep:
secdebug("powerwatch", "system will sleep");
me->systemWillSleep();
reaction = allow;
break;
case kIOMessageSystemHasPoweredOn:
secdebug("powerwatch", "system has powered on");
me->systemIsWaking();
reaction = ignore;
break;
case kIOMessageSystemWillPowerOff:
secdebug("powerwatch", "system will power off");
me->systemWillPowerDown();
reaction = allow;
break;
case kIOMessageSystemWillNotPowerOff:
secdebug("powerwatch", "system will not power off");
reaction = ignore;
break;
case kIOMessageCanSystemSleep:
secdebug("powerwatch", "can system sleep");
reaction = allow;
break;
case kIOMessageSystemWillNotSleep:
secdebug("powerwatch", "system will not sleep");
reaction = ignore;
break;
case kIOMessageCanSystemPowerOff:
secdebug("powerwatch", "can system power off");
reaction = allow;
break;
case kIOMessageSystemWillPowerOn:
secdebug("powerwatch", "system will power on");
me->systemWillPowerOn();
reaction = ignore;
break;
default:
secdebug("powerwatch",
"type 0x%x message received (ignored)", messageType);
reaction = ignore;
break;
}
switch (reaction) {
case allow:
secdebug("powerwatch", "calling IOAllowPowerChange");
IOAllowPowerChange(me->mKernelPort, long(argument));
break;
case refuse:
secdebug("powerwatch", "calling IOCancelPowerChange");
IOCancelPowerChange(me->mKernelPort, long(argument));
break;
case ignore:
secdebug("powerwatch", "sending no response");
break;
}
}
PortPowerWatcher::PortPowerWatcher()
{
port(IONotificationPortGetMachPort(mPortRef));
}
boolean_t PortPowerWatcher::handle(mach_msg_header_t *in)
{
IODispatchCalloutFromMessage(NULL, in, mPortRef);
return TRUE;
}
}
}