1/* 2 * Copyright (c) 2000-2004 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// 26// powerwatch - hook into system notifications of power events 27// 28#include "powerwatch.h" 29#include <IOKit/IOMessage.h> 30 31 32namespace Security { 33namespace MachPlusPlus { 34 35 36// 37// The obligatory empty virtual destructor 38// 39PowerWatcher::~PowerWatcher() 40{ } 41 42 43// 44// The default NULL implementations of the callback virtuals. 45// We define these (rather than leaving them abstract) since 46// many users want only one of these events. 47// 48void PowerWatcher::systemWillSleep() 49{ } 50 51void PowerWatcher::systemIsWaking() 52{ } 53 54void PowerWatcher::systemWillPowerDown() 55{ } 56 57void PowerWatcher::systemWillPowerOn() 58{ } 59 60// 61// IOPowerWatchers 62// 63 64void 65IOPowerWatcher::iopmcallback(void * param, 66 IOPMConnection connection, 67 IOPMConnectionMessageToken token, 68 IOPMSystemPowerStateCapabilities capabilities) 69{ 70 IOPowerWatcher *me = (IOPowerWatcher *)param; 71 72 if (SECURITY_DEBUG_LOG_ENABLED()) { 73 secdebug("powerwatch", "powerstates"); 74 if (capabilities & kIOPMSystemPowerStateCapabilityDisk) 75 secdebug("powerwatch", "disk"); 76 if (capabilities & kIOPMSystemPowerStateCapabilityNetwork) 77 secdebug("powerwatch", "net"); 78 if (capabilities & kIOPMSystemPowerStateCapabilityAudio) 79 secdebug("powerwatch", "audio"); 80 if (capabilities & kIOPMSystemPowerStateCapabilityVideo) 81 secdebug("powerwatch", "video"); 82 } 83 84 /* if cpu and no display -> in DarkWake */ 85 if ((capabilities & (kIOPMSystemPowerStateCapabilityCPU|kIOPMSystemPowerStateCapabilityVideo)) == kIOPMSystemPowerStateCapabilityCPU) { 86 secdebug("powerwatch", "enter DarkWake"); 87 me->mInDarkWake = true; 88 } else if (me->mInDarkWake) { 89 secdebug("powerwatch", "exit DarkWake"); 90 me->mInDarkWake = false; 91 } 92 93 (void)IOPMConnectionAcknowledgeEvent(connection, token); 94 95 return; 96} 97 98 99void 100IOPowerWatcher::setupDarkWake() 101{ 102 IOReturn ret; 103 104 mInDarkWake = false; 105 106 ret = ::IOPMConnectionCreate(CFSTR("IOPowerWatcher"), 107 kIOPMSystemPowerStateCapabilityDisk 108 | kIOPMSystemPowerStateCapabilityNetwork 109 | kIOPMSystemPowerStateCapabilityAudio 110 | kIOPMSystemPowerStateCapabilityVideo, 111 &mIOPMconn); 112 if (ret == kIOReturnSuccess) { 113 ret = ::IOPMConnectionSetNotification(mIOPMconn, this, 114 (IOPMEventHandlerType)iopmcallback); 115 if (ret == kIOReturnSuccess) { 116 ::IOPMConnectionSetDispatchQueue(mIOPMconn, mIOPMqueue); 117 } 118 } 119 120 dispatch_group_leave(mDarkWakeGroup); 121} 122 123IOPowerWatcher::IOPowerWatcher() 124{ 125 if (!(mKernelPort = ::IORegisterForSystemPower(this, &mPortRef, ioCallback, &mHandle))) 126 UnixError::throwMe(EINVAL); // no clue 127 128 mIOPMqueue = dispatch_queue_create("com.apple.security.IOPowerWatcher", NULL); 129 if (mIOPMqueue == NULL) 130 return; 131 132 // Running in background since this will wait for the power 133 // management in configd and we are not willing to block on 134 // that, power events will come in when they do. 135 mDarkWakeGroup = dispatch_group_create(); 136 dispatch_group_enter(mDarkWakeGroup); 137 dispatch_async(mIOPMqueue, ^ { setupDarkWake(); }); 138} 139 140IOPowerWatcher::~IOPowerWatcher() 141{ 142 // Make sure to wait until the asynchronous method 143 // finishes, to avoid <rdar://problem/14355434> 144 if (mDarkWakeGroup) { 145 ::dispatch_group_wait(mDarkWakeGroup, DISPATCH_TIME_FOREVER); 146 ::dispatch_release(mDarkWakeGroup); 147 } 148 if (mKernelPort) 149 ::IODeregisterForSystemPower(&mHandle); 150 151 if (mIOPMconn) { 152 ::IOPMConnectionSetDispatchQueue(mIOPMconn, NULL); 153 ::IOPMConnectionRelease(mIOPMconn); 154 } 155 if (mIOPMqueue) 156 ::dispatch_release(mIOPMqueue); 157} 158 159 160// 161// The callback dispatcher 162// 163void IOPowerWatcher::ioCallback(void *refCon, io_service_t service, 164 natural_t messageType, void *argument) 165{ 166 IOPowerWatcher *me = (IOPowerWatcher *)refCon; 167 enum { allow, refuse, ignore } reaction; 168 switch (messageType) { 169 case kIOMessageSystemWillSleep: 170 secdebug("powerwatch", "system will sleep"); 171 me->systemWillSleep(); 172 reaction = allow; 173 break; 174 case kIOMessageSystemHasPoweredOn: 175 secdebug("powerwatch", "system has powered on"); 176 me->systemIsWaking(); 177 reaction = ignore; 178 break; 179 case kIOMessageSystemWillPowerOff: 180 secdebug("powerwatch", "system will power off"); 181 me->systemWillPowerDown(); 182 reaction = allow; 183 break; 184 case kIOMessageSystemWillNotPowerOff: 185 secdebug("powerwatch", "system will not power off"); 186 reaction = ignore; 187 break; 188 case kIOMessageCanSystemSleep: 189 secdebug("powerwatch", "can system sleep"); 190 reaction = allow; 191 break; 192 case kIOMessageSystemWillNotSleep: 193 secdebug("powerwatch", "system will not sleep"); 194 reaction = ignore; 195 break; 196 case kIOMessageCanSystemPowerOff: 197 secdebug("powerwatch", "can system power off"); 198 reaction = allow; 199 break; 200 case kIOMessageSystemWillPowerOn: 201 secdebug("powerwatch", "system will power on"); 202 me->systemWillPowerOn(); 203 reaction = ignore; 204 break; 205 default: 206 secdebug("powerwatch", 207 "type 0x%x message received (ignored)", messageType); 208 reaction = ignore; 209 break; 210 } 211 212 // handle acknowledgments 213 switch (reaction) { 214 case allow: 215 secdebug("powerwatch", "calling IOAllowPowerChange"); 216 IOAllowPowerChange(me->mKernelPort, long(argument)); 217 break; 218 case refuse: 219 secdebug("powerwatch", "calling IOCancelPowerChange"); 220 IOCancelPowerChange(me->mKernelPort, long(argument)); 221 break; 222 case ignore: 223 secdebug("powerwatch", "sending no response"); 224 break; 225 } 226} 227 228 229// 230// The MachServer hookup 231// 232PortPowerWatcher::PortPowerWatcher() 233{ 234 port(IONotificationPortGetMachPort(mPortRef)); 235} 236 237boolean_t PortPowerWatcher::handle(mach_msg_header_t *in) 238{ 239 IODispatchCalloutFromMessage(NULL, in, mPortRef); 240 return TRUE; 241} 242 243 244} // end namespace MachPlusPlus 245 246} // end namespace Security 247