X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/80e2389990082500d76eb566d4946be3e786c3ef..d8f41ccd20de16f8ebe2ccc84d47bf1cb2b26bbb:/Security/libsecurity_utilities/lib/powerwatch.cpp?ds=sidebyside diff --git a/Security/libsecurity_utilities/lib/powerwatch.cpp b/Security/libsecurity_utilities/lib/powerwatch.cpp new file mode 100644 index 00000000..f4e9e42f --- /dev/null +++ b/Security/libsecurity_utilities/lib/powerwatch.cpp @@ -0,0 +1,246 @@ +/* + * Copyright (c) 2000-2004,2011-2014 Apple Inc. All Rights Reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +// +// powerwatch - hook into system notifications of power events +// +#include "powerwatch.h" +#include + + +namespace Security { +namespace MachPlusPlus { + + +// +// The obligatory empty virtual destructor +// +PowerWatcher::~PowerWatcher() +{ } + + +// +// The default NULL implementations of the callback virtuals. +// We define these (rather than leaving them abstract) since +// many users want only one of these events. +// +void PowerWatcher::systemWillSleep() +{ } + +void PowerWatcher::systemIsWaking() +{ } + +void PowerWatcher::systemWillPowerDown() +{ } + +void PowerWatcher::systemWillPowerOn() +{ } + +// +// IOPowerWatchers +// + +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 cpu and no display -> in DarkWake */ + 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); + } + } + + dispatch_group_leave(mDarkWakeGroup); +} + +IOPowerWatcher::IOPowerWatcher() +{ + if (!(mKernelPort = ::IORegisterForSystemPower(this, &mPortRef, ioCallback, &mHandle))) + UnixError::throwMe(EINVAL); // no clue + + mIOPMqueue = dispatch_queue_create("com.apple.security.IOPowerWatcher", NULL); + if (mIOPMqueue == NULL) + return; + + // Running in background since this will wait for the power + // management in configd and we are not willing to block on + // that, power events will come in when they do. + mDarkWakeGroup = dispatch_group_create(); + dispatch_group_enter(mDarkWakeGroup); + dispatch_async(mIOPMqueue, ^ { setupDarkWake(); }); +} + +IOPowerWatcher::~IOPowerWatcher() +{ + // Make sure to wait until the asynchronous method + // finishes, to avoid + if (mDarkWakeGroup) { + ::dispatch_group_wait(mDarkWakeGroup, DISPATCH_TIME_FOREVER); + ::dispatch_release(mDarkWakeGroup); + } + if (mKernelPort) + ::IODeregisterForSystemPower(&mHandle); + + if (mIOPMconn) { + ::IOPMConnectionSetDispatchQueue(mIOPMconn, NULL); + ::IOPMConnectionRelease(mIOPMconn); + } + if (mIOPMqueue) + ::dispatch_release(mIOPMqueue); +} + + +// +// The callback dispatcher +// +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; + } + + // handle acknowledgments + 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; + } +} + + +// +// The MachServer hookup +// +PortPowerWatcher::PortPowerWatcher() +{ + port(IONotificationPortGetMachPort(mPortRef)); +} + +boolean_t PortPowerWatcher::handle(mach_msg_header_t *in) +{ + IODispatchCalloutFromMessage(NULL, in, mPortRef); + return TRUE; +} + + +} // end namespace MachPlusPlus + +} // end namespace Security