/* * Copyright (c) 2012 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@ */ /* cc -o darktool darktool.c -framework IOKit -framework CoreFoundation */ #include #include #include #include #include #include #include #include #include #include #define PMTestLog(x...) do {print_pretty_date(false); printf(x);} while(0); #define PMTestPass printf #define PMTestFail printf /*************************************************************************/ static void usage(void); static struct option *long_opts_from_darktool_opts(void); static bool parse_it_all(int argc, char *argv[]); static void print_the_plan(void); static void print_pretty_date(bool newline); static void print_everything_dark(void); static void bringTheHeat(void); static void executeTimedActions(const char *why); static void createAssertion(CFStringRef type, long timeout); static void makeTheCall(const char *callname); static void createPMConnectionListener(void); static void myPMConnectionHandler( void *param, IOPMConnection, IOPMConnectionMessageToken, IOPMSystemPowerStateCapabilities); static CFDictionaryRef HandleBackgroundTaskCapabilitiesChanged( IOPMSystemPowerStateCapabilities cap); static CFDictionaryRef HandleSleepServiceCapabilitiesChanged( IOPMSystemPowerStateCapabilities cap); static CFDictionaryRef HandleMaintenanceCapabilitiesChanged( IOPMSystemPowerStateCapabilities cap); static void _CFDictionarySetLong( CFMutableDictionaryRef d, CFStringRef k, long val); static void _CFDictionarySetDate( CFMutableDictionaryRef d, CFStringRef k, CFAbsoluteTime atime); /*************************************************************************/ /*************************************************************************/ /* * IOKit Assertions - command-line arguments for creating IOKit power assertions. * * callers should pass these strings as an argument to "darktool --createassertion " */ #define kAssertPushService "applepushservice" #define kInteractivePushService "interactivepush" #define kAssertBackground "backgroundtask" #define kAssertUserIsActive "userisactive" #define kAssertRemoteUserIsActive "remoteuserisactive" #define kAssertDisplayWake "displaywake" #define kAssertInternalPreventSleep "internalpreventsleep" #define kAssertMaintenanceWake "maintenancewake" #define kAssertSystemIsActive "systemisactive" struct DTAssertionOption { CFStringRef assertionType; const char *arg; const char *desc; }; typedef struct DTAssertionOption DTAssertionOption; static DTAssertionOption *assertionTypes = NULL; static int assertionsArgCount = 0; static DTAssertionOption *createAssertionOptions(int *); /*************************************************************************/ /* * IOKit SPI/API calls * * callers should pass these strings as arguments to "darktool --call " */ #define kCallDeclareUserIsActive kAssertUserIsActive #define kCallDeclareSystemIsActive kAssertSystemIsActive #define kCallDeclareNotificationEvent "declarenotificationevent" struct DTCallOption { const char *arg; const char *desc; }; typedef struct DTCallOption DTCallOption; DTCallOption calls[] = { {kCallDeclareUserIsActive, "IOPMAssertionDeclareUserActivity"}, {kCallDeclareSystemIsActive, "IOPMAssertionDeclareSystemActivity"}, {kCallDeclareNotificationEvent, "IOPMAssertionDeclareNotificationEvent"}, { NULL, NULL} }; /*************************************************************************/ typedef enum { kExitIfNextWakeIsNotPushIndex = 0, kPrintDarkWakeResidencyTimeIndex, kCreateAssertionIndex, kCallIndex, kBringTheHeatIndex, kTCPKeepaliveOverride, kActionsCount } DarkToolActions; typedef enum { kIOPMConnectRequestBackgroundIndex, kIOPMConnectRequestSleepServiceIndex, kIOPMConnectRequestMaintenanceIndex, kRequestWakeCount } DarkToolRequests; typedef enum { kDoItNow = (1<<0), kDoItUponDarkWake = (1<<1), kDoItUponUserWake = (1<<2) } DoItWhenOptions; struct args_struct { /* "Sec" suffix indicates value is seconds */ long dwIntervalSec; long sleepIntervalSec; long assertionTimeoutSec; long sleepServiceCapTimeoutSec; int doItWhen; int sleepNow; /* If takeAssertionNamed != NULL; that implies our action is to take an assertion */ CFStringRef takeAssertionNamed; /* If callIOKit != NULL; that implies our action is to mame an SPI/API call */ char callIOKit[50]; /* "do" prefix indicates 0/1 are the only acceptable values */ int doAction[kActionsCount]; int doRequestWake[kRequestWakeCount]; }; typedef struct args_struct args_struct; static args_struct args; static const long _default_dwintervalSec = 1800; static const long _default_sleepIntervalSec = 1800; static const long _default_assertionTimeoutSec = 0; /* * Options - caller may specify 0 or more */ #define kOptionDWInterval "darkwakeinterval" #define kOptionSleepInterval "sleepinterval" #define kOptionAssertionTimeout "assertiontimeout" #define kOptionDoItNow "now" #define kOptionUponDarkWake "enterdarkwake" #define kOptionUponUserWake "enteruserwake" #define kOptionSleepServiceCapTimeout "sleepservicecap" #define kOptionSleepNow "sleepnow" /* * Actions - caller may specify only one */ #define kActionGet "get" #define kActionCreateAssertion "createassertion" #define kActionCall "call" //#define kActionTakePushAssertionUponDarkWake "takepushupondarkwake" //#define kActionDeclareUserActivityUponDarkWake "declareuseractivityupondarkwake" #define kActionPrintDarkWakeResidency "printdarkwakeresidencytime" #define kActionExitIfNotPushWake "exitifnextwakeisnotpush" #define kActionBringTheHeat "cpuheater" #define kActionRequestBackgroundWake "backgroundwake" #define kActionRequestSleepServiceWake "sleepservicewake" #define kActionRequestMaintenanceWake "maintenancewake" #define kActionSetTCPKeepAliveExpirationTimeout "tcpkeepaliveexpiration" #define kActionSetTCPWakeQuotaInterval "tcpwakequotainterval" #define kActionSetTCPWakeQuota "tcpwakequota" typedef enum {kNilType, kActionType, kOptionType} DTOptionType; struct DTOption { struct option getopt_long; DTOptionType type; const char *desc; const char *required_options[10]; const char *optional_options[10]; }; typedef struct DTOption DTOption; static DTOption darktool_options[] = { /* Actions */ { {kActionExitIfNotPushWake, no_argument, &args.doAction[kExitIfNextWakeIsNotPushIndex], 1}, kActionType, "Waits, and exits if the next wake returns false for IOPMAllowsPushService()", { NULL }, { NULL }}, { {kActionPrintDarkWakeResidency, no_argument, &args.doAction[kPrintDarkWakeResidencyTimeIndex], 1}, kActionType, "Waits, and prints the time spent in DarkWake upon exiting DarkWake. Does not request wakeups or take assertions.", { NULL }, { NULL }}, { {kActionCreateAssertion, required_argument, &args.doAction[kCreateAssertionIndex], 1}, kActionType, "Creates an IOKit assertion of the specified type.", { kOptionDoItNow, kOptionUponDarkWake, kOptionUponUserWake, NULL }, { kOptionAssertionTimeout, NULL } }, { {kActionCall, required_argument,&args.doAction[kCallIndex], 1}, kActionType, "Calls the specified IOKit API/SPI.", { kOptionDoItNow, kOptionUponDarkWake, kOptionUponUserWake, NULL }, { kOptionAssertionTimeout, NULL } }, { {kActionBringTheHeat, no_argument, &args.doAction[kBringTheHeatIndex], 1}, kActionType, "Immediately launches several threads to saturate the CPU's and generate heat.", { NULL }, { NULL }}, { {kActionSetTCPKeepAliveExpirationTimeout, required_argument, NULL, 0}, kActionType, "Override the system default TCPKeepAliveExpiration override (in seconds). Please specify 1 second for fastest timeout (not 0). This setting does not persist across reboots.", { NULL }, { NULL }}, { {kActionSetTCPWakeQuotaInterval, required_argument, NULL, 0}, kActionType, "Override the system default TCPKeepAlive wake quota interval (in seconds). Passing 0 will disable wake quota. This setting does not persist across reboots.", { NULL }, { NULL }}, { {kActionSetTCPWakeQuota, required_argument, NULL, 0}, kActionType, "Override the system default TCPKeepAlive wake quota count (in number of non-user wakes to allow per WakeQuotaInterval). Passing 0 will disable wake quota. This setting does not persist across reboots.", { NULL }, { NULL }}, { {kActionGet, no_argument, NULL, 'g'}, kActionType, "Print all darktool system settings.", { NULL }, { NULL }}, /* Options */ /* { {kActionRequestBackgroundWake, required_argument, &args.doRequestWake[kIOPMConnectRequestBackgroundIndex], 1}, kOptionType, "Use IOPMConnection API to emulate UserEventAgent", { NULL }, { kOptionSleepInterval}}, */ { {kActionRequestSleepServiceWake, required_argument, &args.doRequestWake[kIOPMConnectRequestSleepServiceIndex], 1}, kOptionType, "Use IOPMConnection API to emulate sleepservicesd", { NULL }, { kOptionSleepInterval, kOptionSleepServiceCapTimeout}}, { {kActionRequestMaintenanceWake, required_argument, &args.doRequestWake[kIOPMConnectRequestMaintenanceIndex], 1}, kOptionType, "User IOPMConnection API to emulate mDNSResponder and SU Do It Later", { NULL }, { kOptionSleepInterval}}, /* { {kOptionDWInterval, required_argument, NULL, 0}, kOptionType, "Specifies how long to hold assertions in DarkWake. Takes an integer seconds argument.", { NULL }, { NULL } }, */ { {kOptionSleepInterval, required_argument, NULL, 0}, kOptionType, "Specifies how long to stay asleep before entering a scheduled DarkWake. Takes an integer seconds argument.", { NULL }, { NULL } }, { {kOptionAssertionTimeout, required_argument, NULL, 0}, kOptionType, "Specifies the how long hold an IOKit powerassertion. Takes an integer seconds argument.", { NULL }, { NULL } }, { {kOptionDoItNow, no_argument, &args.doItWhen, kDoItNow}, kOptionType, "Specifies to Act immediately.", { NULL }, { NULL } }, { {kOptionUponDarkWake, no_argument, &args.doItWhen, kDoItUponDarkWake}, kOptionType, "Specifies to Act when the system transitions into a DarkWake.", { NULL }, { NULL } }, { {kOptionUponUserWake, no_argument, &args.doItWhen, kDoItUponUserWake}, kOptionType, "Specifies to Act when the system transitions into a Full.", { NULL }, { NULL } }, { {kOptionSleepServiceCapTimeout, required_argument, NULL, 0}, kOptionType, "Specifies a SleepService Cap Timeout (for use with --requestiopmconnectionsleepservicewake).", { NULL }, { NULL } }, { {kOptionSleepNow, no_argument, &args.sleepNow, 1}, kOptionType, "Puts the system to sleep immediately, after setting up all other actions & options.", { NULL }, { NULL } }, { {NULL, 0, NULL, 0}, kNilType, NULL, { NULL }, { NULL } } }; /*************************************************************************/ int main(int argc, char *argv[]) { if (!parse_it_all(argc, argv)) { usage(); } print_the_plan(); if (args.doRequestWake[kIOPMConnectRequestBackgroundIndex] || args.doRequestWake[kIOPMConnectRequestSleepServiceIndex] || args.doRequestWake[kIOPMConnectRequestMaintenanceIndex] || args.doAction[kExitIfNextWakeIsNotPushIndex] || args.doAction[kPrintDarkWakeResidencyTimeIndex]) { createPMConnectionListener(); } if (args.doAction[kBringTheHeatIndex]) { bringTheHeat(); } if (args.doAction[kCreateAssertionIndex]) { if (!args.doItWhen) { printf("You must specify a time option for your assertion. See usage(); pass \"--now\" or \"--upondarkwake\"\n"); exit(1); } } if (args.doAction[kPrintDarkWakeResidencyTimeIndex]) { printf("Error - print dark wake residency is not implemetned.\n"); return 1; } if (args.doItWhen & kDoItNow) { executeTimedActions("Now"); } if (args.sleepNow) { double delayInSeconds = 5.0; printf("Will force sleep the system with IOPMSleepSystem in %d seconds.\n", (int)delayInSeconds); dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ io_connect_t connect = IOPMFindPowerManagement(kIOMasterPortDefault); IOReturn ret = IOPMSleepSystem(connect); if (kIOReturnSuccess != ret) { printf("Error: Couldn't put the system to sleep. IOPMSleepSystem() returns error 0x%08x\n", ret); } }); } CFRunLoopRun(); return 0; } /*************************************************************************/ static void usage(void) { printf("Usage: darktool [action] [options]\ndarktool is an OS X Core OS test tool to exercise DarkWake, Power Nap, and related API & SPI.\n"); printf("[v0.1 BETA] This version of darktool was built on %s %s\n\n", __DATE__, __TIME__); printf("Options: Options affect the behavior of actions, below. You may specify zero or more options. \n"); int optcount = sizeof(darktool_options) / sizeof(DTOption); for(int i=0; i