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