1/* 2 * Copyright (c) 2012 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 cc -o darktool darktool.c -framework IOKit -framework CoreFoundation 27 28 */ 29 30#include <CoreFoundation/CoreFoundation.h> 31#include <IOKit/pwr_mgt/IOPMLib.h> 32#include <IOKit/pwr_mgt/IOPMLibPrivate.h> 33#include <IOKit/platform/IOPlatformSupportPrivate.h> 34#include <IOKit/IOReturn.h> 35#include <dispatch/dispatch.h> 36 37#include <unistd.h> 38#include <stdio.h> 39#include <stdlib.h> 40#include <getopt.h> 41 42#define PMTestLog(x...) do {print_pretty_date(false); printf(x);} while(0); 43#define PMTestPass printf 44#define PMTestFail printf 45 46/*************************************************************************/ 47static void usage(void); 48static struct option *long_opts_from_darktool_opts(void); 49static bool parse_it_all(int argc, char *argv[]); 50static void print_the_plan(void); 51static void print_pretty_date(bool newline); 52static void print_everything_dark(void); 53 54static void bringTheHeat(void); 55static void executeTimedActions(const char *why); 56static void createAssertion(CFStringRef type, long timeout); 57static void makeTheCall(const char *callname); 58 59static void createPMConnectionListener(void); 60static void myPMConnectionHandler( 61 void *param, IOPMConnection, 62 IOPMConnectionMessageToken, IOPMSystemPowerStateCapabilities); 63 64static CFDictionaryRef HandleBackgroundTaskCapabilitiesChanged( 65 IOPMSystemPowerStateCapabilities cap); 66static CFDictionaryRef HandleSleepServiceCapabilitiesChanged( 67 IOPMSystemPowerStateCapabilities cap); 68static CFDictionaryRef HandleMaintenanceCapabilitiesChanged( 69 IOPMSystemPowerStateCapabilities cap); 70 71static void _CFDictionarySetLong( 72 CFMutableDictionaryRef d, 73 CFStringRef k, 74 long val); 75static void _CFDictionarySetDate( 76 CFMutableDictionaryRef d, 77 CFStringRef k, 78 CFAbsoluteTime atime); 79 80/*************************************************************************/ 81 82 83 84/*************************************************************************/ 85 86/* 87 * IOKit Assertions - command-line arguments for creating IOKit power assertions. 88 * 89 * callers should pass these strings as an argument to "darktool --createassertion <assertionname>" 90 */ 91#define kAssertPushService "applepushservice" 92#define kInteractivePushService "interactivepush" 93#define kAssertBackground "backgroundtask" 94#define kAssertUserIsActive "userisactive" 95#define kAssertRemoteUserIsActive "remoteuserisactive" 96#define kAssertDisplayWake "displaywake" 97#define kAssertInternalPreventSleep "internalpreventsleep" 98#define kAssertMaintenanceWake "maintenancewake" 99#define kAssertSystemIsActive "systemisactive" 100 101struct DTAssertionOption { 102 CFStringRef assertionType; 103 const char *arg; 104 const char *desc; 105}; 106typedef struct DTAssertionOption DTAssertionOption; 107 108static DTAssertionOption *assertionTypes = NULL; 109static int assertionsArgCount = 0; 110 111static DTAssertionOption *createAssertionOptions(int *); 112 113/*************************************************************************/ 114/* 115 * IOKit SPI/API calls 116 * 117 * callers should pass these strings as arguments to "darktool --call <functionname>" 118 */ 119 120#define kCallDeclareUserIsActive kAssertUserIsActive 121#define kCallDeclareSystemIsActive kAssertSystemIsActive 122#define kCallDeclareNotificationEvent "declarenotificationevent" 123 124struct DTCallOption { 125 const char *arg; 126 const char *desc; 127}; 128typedef struct DTCallOption DTCallOption; 129 130DTCallOption calls[] = { 131 {kCallDeclareUserIsActive, "IOPMAssertionDeclareUserActivity"}, 132 {kCallDeclareSystemIsActive, "IOPMAssertionDeclareSystemActivity"}, 133 {kCallDeclareNotificationEvent, "IOPMAssertionDeclareNotificationEvent"}, 134 { NULL, NULL} 135}; 136 137/*************************************************************************/ 138 139typedef enum { 140 kExitIfNextWakeIsNotPushIndex = 0, 141 kPrintDarkWakeResidencyTimeIndex, 142 kCreateAssertionIndex, 143 kCallIndex, 144 kBringTheHeatIndex, 145 kTCPKeepaliveOverride, 146 kActionsCount 147} DarkToolActions; 148 149typedef enum { 150 kIOPMConnectRequestBackgroundIndex, 151 kIOPMConnectRequestSleepServiceIndex, 152 kIOPMConnectRequestMaintenanceIndex, 153 kRequestWakeCount 154} DarkToolRequests; 155 156typedef enum { 157 kDoItNow = (1<<0), 158 kDoItUponDarkWake = (1<<1), 159 kDoItUponUserWake = (1<<2) 160} DoItWhenOptions; 161 162struct args_struct { 163 /* "Sec" suffix indicates value is seconds */ 164 long dwIntervalSec; 165 long sleepIntervalSec; 166 long assertionTimeoutSec; 167 long sleepServiceCapTimeoutSec; 168 int doItWhen; 169 int sleepNow; 170 171 /* If takeAssertionNamed != NULL; that implies our action is to take an assertion */ 172 CFStringRef takeAssertionNamed; 173 174 /* If callIOKit != NULL; that implies our action is to mame an SPI/API call */ 175 char callIOKit[50]; 176 177 /* "do" prefix indicates 0/1 are the only acceptable values */ 178 int doAction[kActionsCount]; 179 int doRequestWake[kRequestWakeCount]; 180}; 181typedef struct args_struct args_struct; 182static args_struct args; 183 184static const long _default_dwintervalSec = 1800; 185static const long _default_sleepIntervalSec = 1800; 186static const long _default_assertionTimeoutSec = 0; 187 188/* 189 * Options - caller may specify 0 or more 190 */ 191#define kOptionDWInterval "darkwakeinterval" 192#define kOptionSleepInterval "sleepinterval" 193#define kOptionAssertionTimeout "assertiontimeout" 194#define kOptionDoItNow "now" 195#define kOptionUponDarkWake "enterdarkwake" 196#define kOptionUponUserWake "enteruserwake" 197#define kOptionSleepServiceCapTimeout "sleepservicecap" 198#define kOptionSleepNow "sleepnow" 199 200/* 201 * Actions - caller may specify only one 202 */ 203#define kActionGet "get" 204#define kActionCreateAssertion "createassertion" 205#define kActionCall "call" 206//#define kActionTakePushAssertionUponDarkWake "takepushupondarkwake" 207//#define kActionDeclareUserActivityUponDarkWake "declareuseractivityupondarkwake" 208#define kActionPrintDarkWakeResidency "printdarkwakeresidencytime" 209#define kActionExitIfNotPushWake "exitifnextwakeisnotpush" 210#define kActionBringTheHeat "cpuheater" 211#define kActionRequestBackgroundWake "backgroundwake" 212#define kActionRequestSleepServiceWake "sleepservicewake" 213#define kActionRequestMaintenanceWake "maintenancewake" 214#define kActionSetTCPKeepAliveExpirationTimeout "tcpkeepaliveexpiration" 215#define kActionSetTCPWakeQuotaInterval "tcpwakequotainterval" 216#define kActionSetTCPWakeQuota "tcpwakequota" 217 218typedef enum {kNilType, kActionType, kOptionType} DTOptionType; 219 220struct DTOption { 221 struct option getopt_long; 222 DTOptionType type; 223 const char *desc; 224 const char *required_options[10]; 225 const char *optional_options[10]; 226 227}; 228typedef struct DTOption DTOption; 229static DTOption darktool_options[] = 230{ 231/* Actions 232 */ 233 { {kActionExitIfNotPushWake, 234 no_argument, &args.doAction[kExitIfNextWakeIsNotPushIndex], 1}, kActionType, 235 "Waits, and exits if the next wake returns false for IOPMAllowsPushService()", 236 { NULL }, { NULL }}, 237 238 { {kActionPrintDarkWakeResidency, 239 no_argument, &args.doAction[kPrintDarkWakeResidencyTimeIndex], 1}, kActionType, 240 "Waits, and prints the time spent in DarkWake upon exiting DarkWake. Does not request wakeups or take assertions.", 241 { NULL }, { NULL }}, 242 243 { {kActionCreateAssertion, 244 required_argument, &args.doAction[kCreateAssertionIndex], 1}, kActionType, 245 "Creates an IOKit assertion of the specified type.", 246 { kOptionDoItNow, kOptionUponDarkWake, kOptionUponUserWake, NULL }, 247 { kOptionAssertionTimeout, NULL } }, 248 249 { {kActionCall, 250 required_argument,&args.doAction[kCallIndex], 1}, kActionType, 251 "Calls the specified IOKit API/SPI.", 252 { kOptionDoItNow, kOptionUponDarkWake, kOptionUponUserWake, NULL }, 253 { kOptionAssertionTimeout, NULL } }, 254 255 { {kActionBringTheHeat, 256 no_argument, &args.doAction[kBringTheHeatIndex], 1}, kActionType, 257 "Immediately launches several threads to saturate the CPU's and generate heat.", 258 { NULL }, { NULL }}, 259 260 { {kActionSetTCPKeepAliveExpirationTimeout, 261 required_argument, NULL, 0}, kActionType, 262 "Override the system default TCPKeepAliveExpiration override (in seconds). Please specify 1 second for fastest timeout (not 0). This setting does not persist across reboots.", 263 { NULL }, { NULL }}, 264 265 { {kActionSetTCPWakeQuotaInterval, 266 required_argument, NULL, 0}, kActionType, 267 "Override the system default TCPKeepAlive wake quota interval (in seconds). Passing 0 will disable wake quota. This setting does not persist across reboots.", 268 { NULL }, { NULL }}, 269 270 { {kActionSetTCPWakeQuota, 271 required_argument, NULL, 0}, kActionType, 272 "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.", 273 { NULL }, { NULL }}, 274 275 { {kActionGet, 276 no_argument, NULL, 'g'}, kActionType, 277 "Print all darktool system settings.", 278 { NULL }, { NULL }}, 279 280/* Options 281 */ 282/* 283 { {kActionRequestBackgroundWake, 284 required_argument, &args.doRequestWake[kIOPMConnectRequestBackgroundIndex], 1}, kOptionType, 285 "Use IOPMConnection API to emulate UserEventAgent", 286 { NULL }, { kOptionSleepInterval}}, 287*/ 288 { {kActionRequestSleepServiceWake, 289 required_argument, &args.doRequestWake[kIOPMConnectRequestSleepServiceIndex], 1}, kOptionType, 290 "Use IOPMConnection API to emulate sleepservicesd", 291 { NULL }, { kOptionSleepInterval, kOptionSleepServiceCapTimeout}}, 292 293 { {kActionRequestMaintenanceWake, 294 required_argument, &args.doRequestWake[kIOPMConnectRequestMaintenanceIndex], 1}, kOptionType, 295 "User IOPMConnection API to emulate mDNSResponder and SU Do It Later", 296 { NULL }, { kOptionSleepInterval}}, 297 298 299/* { {kOptionDWInterval, 300 required_argument, NULL, 0}, kOptionType, 301 "Specifies how long to hold assertions in DarkWake. Takes an integer seconds argument.", 302 { NULL }, { NULL } }, 303 */ 304 { {kOptionSleepInterval, 305 required_argument, NULL, 0}, kOptionType, 306 "Specifies how long to stay asleep before entering a scheduled DarkWake. Takes an integer seconds argument.", 307 { NULL }, { NULL } }, 308 309 { {kOptionAssertionTimeout, 310 required_argument, NULL, 0}, kOptionType, 311 "Specifies the how long hold an IOKit powerassertion. Takes an integer seconds argument.", 312 { NULL }, { NULL } }, 313 314 { {kOptionDoItNow, 315 no_argument, &args.doItWhen, kDoItNow}, kOptionType, 316 "Specifies to Act immediately.", 317 { NULL }, { NULL } }, 318 319 { {kOptionUponDarkWake, 320 no_argument, &args.doItWhen, kDoItUponDarkWake}, kOptionType, 321 "Specifies to Act when the system transitions into a DarkWake.", 322 { NULL }, { NULL } }, 323 324 { {kOptionUponUserWake, 325 no_argument, &args.doItWhen, kDoItUponUserWake}, kOptionType, 326 "Specifies to Act when the system transitions into a Full.", 327 { NULL }, { NULL } }, 328 329 { {kOptionSleepServiceCapTimeout, 330 required_argument, NULL, 0}, kOptionType, 331 "Specifies a SleepService Cap Timeout (for use with --requestiopmconnectionsleepservicewake).", 332 { NULL }, { NULL } }, 333 334 { {kOptionSleepNow, 335 no_argument, &args.sleepNow, 1}, kOptionType, 336 "Puts the system to sleep immediately, after setting up all other actions & options.", 337 { NULL }, { NULL } }, 338 339 { {NULL, 0, NULL, 0}, kNilType, NULL, { NULL }, { NULL } } 340}; 341 342/*************************************************************************/ 343 344int main(int argc, char *argv[]) 345{ 346 if (!parse_it_all(argc, argv)) { 347 usage(); 348 } 349 350 print_the_plan(); 351 352 353 if (args.doRequestWake[kIOPMConnectRequestBackgroundIndex] 354 || args.doRequestWake[kIOPMConnectRequestSleepServiceIndex] 355 || args.doRequestWake[kIOPMConnectRequestMaintenanceIndex] 356 || args.doAction[kExitIfNextWakeIsNotPushIndex] 357 || args.doAction[kPrintDarkWakeResidencyTimeIndex]) 358 { 359 createPMConnectionListener(); 360 } 361 362 if (args.doAction[kBringTheHeatIndex]) { 363 bringTheHeat(); 364 } 365 366 if (args.doAction[kCreateAssertionIndex]) { 367 if (!args.doItWhen) { 368 printf("You must specify a time option for your assertion. See usage(); pass \"--now\" or \"--upondarkwake\"\n"); 369 exit(1); 370 } 371 } 372 373 if (args.doAction[kPrintDarkWakeResidencyTimeIndex]) { 374 printf("Error - print dark wake residency is not implemetned.\n"); 375 return 1; 376 } 377 378 if (args.doItWhen & kDoItNow) { 379 executeTimedActions("Now"); 380 } 381 382 if (args.sleepNow) { 383 double delayInSeconds = 5.0; 384 printf("Will force sleep the system with IOPMSleepSystem in %d seconds.\n", (int)delayInSeconds); 385 dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); 386 dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ 387 io_connect_t connect = IOPMFindPowerManagement(kIOMasterPortDefault); 388 IOReturn ret = IOPMSleepSystem(connect); 389 if (kIOReturnSuccess != ret) { 390 printf("Error: Couldn't put the system to sleep. IOPMSleepSystem() returns error 0x%08x\n", ret); 391 } 392 }); 393 } 394 395 CFRunLoopRun(); 396 return 0; 397} 398 399/*************************************************************************/ 400 401static void usage(void) 402{ 403 printf("Usage: darktool [action] [options]\ndarktool is an OS X Core OS test tool to exercise DarkWake, Power Nap, and related API & SPI.\n"); 404 printf("[v0.1 BETA] This version of darktool was built on %s %s\n\n", __DATE__, __TIME__); 405 printf("Options: Options affect the behavior of actions, below. You may specify zero or more options. \n"); 406 int optcount = sizeof(darktool_options) / sizeof(DTOption); 407 for(int i=0; i<optcount; i++) 408 { 409 if ((NULL != darktool_options[i].getopt_long.name) 410 && (kOptionType == darktool_options[i].type)) 411 { 412 printf(" --%s\n %s\n", 413 darktool_options[i].getopt_long.name, 414 darktool_options[i].desc); 415 416 if (i != optcount) { 417 printf("\n"); 418 } 419 } 420 } 421 422 printf("\nActions: Actions tell darktool what to do. You must specify exactly one action. \n"); 423 for(int i=0; i<optcount; i++) 424 { 425 if ((NULL != darktool_options[i].getopt_long.name) 426 && (kActionType == darktool_options[i].type)) 427 { 428 printf(" --%s\n %s\n", 429 darktool_options[i].getopt_long.name, 430 darktool_options[i].desc); 431 432 /* Print required options */ 433 if (darktool_options[i].required_options[0]) 434 { 435 int oind = 0; 436 const char *printopt = NULL; 437 printf(" You must specify one of: "); 438 while ((printopt = darktool_options[i].required_options[oind])) { 439 printf("--%s ", printopt); 440 oind++; 441 } 442 printf("\n"); 443 } 444 445 /* Print required options */ 446 if (darktool_options[i].optional_options[0]) 447 { 448 int oind = 0; 449 const char *printopt = NULL; 450 printf(" You may specify: "); 451 while ((printopt = darktool_options[i].optional_options[oind])) { 452 printf("--%s ", printopt); 453 oind++; 454 } 455 printf("\n"); 456 } 457 if (i != optcount) { 458 printf("\n"); 459 } 460 461 if (!strcmp(darktool_options[i].getopt_long.name, kActionCreateAssertion)) { 462 for (int a_index = 0; a_index<assertionsArgCount; a_index++) { 463 printf("%25s for type %s\n", assertionTypes[a_index].arg, assertionTypes[a_index].desc); 464 } 465 printf("\n"); 466 } 467 if (!strcmp(darktool_options[i].getopt_long.name, kActionCall)) { 468 int c_index = 0; 469 while (calls[c_index].arg) { 470 printf("%25s for call %s\n", calls[c_index].arg, calls[c_index].desc); 471 c_index++; 472 } 473 printf("\n"); 474 } 475 } 476 } 477} 478 479static bool parse_it_all(int argc, char *argv[]) { 480 int optind; 481 char ch = 0; 482 struct option *long_opts = long_opts_from_darktool_opts(); 483 long temp_arg = 0; 484 485 bzero(&args, sizeof(args)); 486 487 assertionTypes = createAssertionOptions(&assertionsArgCount); 488 489 args.dwIntervalSec = _default_dwintervalSec; 490 args.sleepIntervalSec = _default_sleepIntervalSec; 491 args.assertionTimeoutSec = _default_assertionTimeoutSec; 492 493 do { 494 ch = getopt_long(argc, argv, "", long_opts, &optind); 495 496 if (-1 == ch) 497 break; 498 499 if ('?' == ch) 500 continue; 501 502 if (!strcmp(long_opts[optind].name, kActionGet)) { 503 print_everything_dark(); 504 exit(0); 505 } 506 507 if (!strcmp(long_opts[optind].name, kOptionDWInterval)) { 508 args.dwIntervalSec = strtol(optarg, NULL, 10); 509 } 510 if (!strcmp(long_opts[optind].name, kActionRequestMaintenanceWake) 511 || !strcmp(long_opts[optind].name, kActionRequestBackgroundWake) 512 || !strcmp(long_opts[optind].name, kActionRequestSleepServiceWake)) { 513 args.sleepIntervalSec = strtol(optarg, NULL, 10); 514 } 515 516 if (!strcmp(long_opts[optind].name, kOptionAssertionTimeout)) { 517 args.assertionTimeoutSec = strtol(optarg, NULL, 10); 518 } 519 520 if (!strcmp(long_opts[optind].name, kActionSetTCPKeepAliveExpirationTimeout)) { 521 temp_arg = strtol(optarg, NULL, 10); 522 IOPMSetValueInt(kIOPMTCPKeepAliveExpirationOverride, (int)temp_arg); 523 printf("Updated \"TCPKeepAliveExpiration\" to %lds\n", temp_arg); 524 exit(0); 525 } 526 527 if (!strcmp(long_opts[optind].name, kActionSetTCPWakeQuotaInterval)) { 528 temp_arg = strtol(optarg, NULL, 10); 529 IOPMSetValueInt(kIOPMTCPWakeQuotaInterval, (int)temp_arg); 530 printf("Updated \"TCPWakeQuotaInterval\" to %lds\n", temp_arg); 531 exit(0); 532 } 533 534 if (!strcmp(long_opts[optind].name, kActionSetTCPWakeQuota)) { 535 temp_arg = strtol(optarg, NULL, 10); 536 IOPMSetValueInt(kIOPMTCPWakeQuota, (int)temp_arg); 537 printf("Updated \"TCPWakeQuota\" to %ld\n", temp_arg); 538 exit(0); 539 } 540 541 if (!strcmp(long_opts[optind].name, kOptionSleepServiceCapTimeout)) { 542 args.sleepServiceCapTimeoutSec = strtol(optarg, NULL, 10); 543 } 544 545 if (!strcmp(long_opts[optind].name, kActionCreateAssertion)) 546 { 547 for (int j=0; j<assertionsArgCount; j++) 548 { 549 if (!strcmp(assertionTypes[j].arg, optarg)) { 550 args.takeAssertionNamed = assertionTypes[j].assertionType; 551 break; 552 } 553 } 554 if (!args.takeAssertionNamed) { 555 printf("Unrecognized assertion type %s.\n", optarg); 556 usage(); 557 exit(1); 558 } 559 } 560 561 if (!strcmp(long_opts[optind].name, kActionCall)) { 562 int j=0; 563 while (calls[j].arg) 564 { 565 if (!strcmp(calls[j].arg, optarg)) 566 { 567 strncpy(args.callIOKit, calls[j].arg, sizeof(args.callIOKit)); 568 } 569 j++; 570 } 571 572 if (!args.callIOKit[0]) { 573 printf("Error: Unrecognized call %s.\n", optarg); 574 usage(); 575 exit(1); 576 } 577 } 578 579 } while (1); 580 581 return true; 582} 583 584static void print_the_plan(void) 585{ 586 int actions_count = 0; 587 588 print_pretty_date(true); 589 590 for (int i=0; i<kActionsCount; i++) { 591 if (args.doAction[i]) { 592 actions_count++; 593 printf("Action: %s", darktool_options[i].getopt_long.name); 594 if (kCallIndex == i) { 595 printf(" %s", args.callIOKit); 596 } 597 printf("\n"); 598 } 599 } 600 601 if (actions_count != 1 ) { 602 printf("Error: Please specify one (and only one) action.\n"); 603 usage(); 604 exit(1); 605 } 606 607 608 if (args.doItWhen) { 609 printf("When: "); 610 if (args.doItWhen & kDoItNow) { 611 printf("Now\n"); 612 } 613 else if (args.doItWhen & kDoItUponDarkWake) { 614 printf("Upon entering the next DarkWake\n"); 615 } 616 else if (args.doItWhen & kDoItUponUserWake) { 617 printf("Upon entering the next UserWake\n"); 618 } 619 } 620 621 if (args.doRequestWake[kIOPMConnectRequestBackgroundIndex]) 622 { 623 printf("Request: BackgroundWake in %ld\n", args.sleepIntervalSec); 624 } 625 else if (args.doRequestWake[kIOPMConnectRequestMaintenanceIndex]) 626 { 627 printf("Request: MaintenanceWake in %ld\n", args.sleepIntervalSec); 628 } 629 else if (args.doRequestWake[kIOPMConnectRequestSleepServiceIndex]) 630 { 631 printf("Request: SleepServicesWake in %lds with cap %lds\n", args.sleepIntervalSec, args.assertionTimeoutSec); 632 } 633 printf("\n"); 634 635} 636 637static struct option *long_opts_from_darktool_opts(void) 638{ 639 int i; 640 int count = sizeof(darktool_options)/sizeof(DTOption); 641 struct option *retopt = NULL; 642 643 retopt = calloc(count, sizeof(struct option)); 644 645 for (i=0; i<count; i++) { 646 bcopy(&darktool_options[i].getopt_long, &retopt[i], sizeof(struct option)); 647 } 648 649 return retopt; 650} 651 652static DTAssertionOption *createAssertionOptions(int *count) 653{ 654 DTAssertionOption *assertions_heap = NULL; 655 656 DTAssertionOption assertions_local[] = 657 { 658 {kIOPMAssertionTypeApplePushServiceTask, 659 kAssertPushService, 660 "kIOPMAssertionTypeApplePushServiceTask"}, 661 {kIOPMAssertInteractivePushServiceTask, 662 kInteractivePushService, 663 "kIOPMAssertInteractivePushServiceTask"}, 664 {kIOPMAssertionTypeBackgroundTask, 665 kAssertBackground, 666 "kIOPMAssertionTypeBackgroundTask"}, 667 {kIOPMAssertionUserIsActive, 668 kAssertUserIsActive, 669 "kIOPMAssertionUserIsActive"}, 670 {kIOPMAssertDisplayWake, 671 kAssertDisplayWake, 672 "kIOPMAssertDisplayWake"}, 673 {kIOPMAssertInternalPreventSleep, 674 kAssertInternalPreventSleep, 675 "kIOPMAssertInternalPreventSleep"}, 676 {kIOPMAssertMaintenanceActivity, 677 kAssertMaintenanceWake, 678 "kIOPMAssertMaintenanceActivity"}, 679 {kIOPMAssertionTypeSystemIsActive, 680 kAssertSystemIsActive, 681 "kIOPMAssertionTypeSystemIsActive"} 682 }; 683 684 assertions_heap = calloc(1, sizeof(assertions_local)); 685 bcopy(assertions_local, assertions_heap, sizeof(assertions_local)); 686 687 688 *count = sizeof(assertions_local) / sizeof(DTAssertionOption); 689 690 return assertions_heap; 691} 692 693static void executeTimedActions(const char *why) 694{ 695 print_pretty_date(false); 696 printf("%s ", why); 697 698 if (args.takeAssertionNamed) 699 { 700 printf("Create assertion %s\n", CFStringGetCStringPtr(args.takeAssertionNamed, kCFStringEncodingUTF8)); 701 createAssertion(args.takeAssertionNamed, args.assertionTimeoutSec); 702 } 703 else if (args.callIOKit[0]) { 704 printf("Call IOKit function %s\n", args.callIOKit); 705 makeTheCall(args.callIOKit); 706 } 707} 708 709/*************************************************************************/ 710 711static void createAssertion(CFStringRef type, long timeout) 712{ 713 IOReturn ret; 714 IOPMAssertionID id = kIOPMNullAssertionID; 715 CFDictionaryRef d = NULL; 716 CFNumberRef obj = NULL; 717 int level; 718 719 ret = IOPMAssertionCreateWithDescription( 720 type, CFSTR("com.apple.darkmaintenance"), 721 NULL, NULL, NULL, 722 (CFTimeInterval)timeout, kIOPMAssertionTimeoutActionRelease, 723 &id); 724 725 if (kIOReturnSuccess != ret) { 726 PMTestLog("Create \'%s\' assertion returns error=0x%08x\n", 727 CFStringGetCStringPtr(type, kCFStringEncodingMacRoman), ret); 728 exit(1); 729 } 730 PMTestLog("Created \'%s\' assertion with timeout of %d secs. ID:0x%x\n", 731 CFStringGetCStringPtr(type, kCFStringEncodingMacRoman), (int)timeout, id); 732 733 IOPMCopyAssertionsStatus(&d); 734 735 if (!d || !(obj = CFDictionaryGetValue(d, type))) 736 { 737 PMTestFail("Failed to get information about status of assertion type \'%s\'\n", 738 CFStringGetCStringPtr(type, kCFStringEncodingMacRoman)); 739 } 740 if (obj) { 741 CFNumberGetValue(obj, kCFNumberIntType, &level); 742 if (0 != level) { 743 PMTestPass("Assertion level is ON for type \'%s\'\n", CFStringGetCStringPtr(type, kCFStringEncodingMacRoman)); 744 } else { 745 PMTestFail("Assertion level is OFF for type \'%s\'\n", CFStringGetCStringPtr(type, kCFStringEncodingMacRoman)); 746 } 747 } 748 if (d) CFRelease(d); 749 750} 751/*************************************************************************/ 752 753static void makeTheCall(const char *callname) 754{ 755 IOPMAssertionID dontcare; 756 IOPMSystemState systemstate; 757 IOReturn ret; 758 759 if (!callname) { 760 printf("Error: trying to call a (null) IOKit function call\n"); 761 return; 762 } 763 764 if (!strcmp(kCallDeclareNotificationEvent, callname)) 765 { 766 ret = IOPMAssertionDeclareNotificationEvent(CFSTR("darktool"), args.assertionTimeoutSec, &dontcare); 767 } else if (!strcmp(kCallDeclareSystemIsActive, callname)) 768 { 769 ret = IOPMAssertionDeclareSystemActivity(CFSTR("darktool-system"), &dontcare, &systemstate); 770 if (kIOReturnSuccess == ret) { 771 if (kIOPMSystemSleepReverted == systemstate) { 772 printf("IOPMAssertionDeclareSystemActivity reverted system sleep.\n"); 773 } else { 774 printf("IOPMAssertionDeclareSystemActivity did not revert system sleep.\n"); 775 } 776 } 777 } else if (!strcmp(kCallDeclareUserIsActive, callname)) { 778 ret = IOPMAssertionDeclareUserActivity(CFSTR("darktool-user"), kIOPMUserActiveLocal, &dontcare); 779 } else { 780 printf("Error: No recognized IOKit calls named \"%s\"\n", callname); 781 return; 782 } 783 784 if (kIOReturnSuccess != ret) { 785 printf("Fail: IOKit call %s returne 0x%08x\n", callname, ret); 786 } 787 788 return; 789} 790 791 792/*************************************************************************/ 793 794static void print_pretty_date(bool newline) 795{ 796 CFDateFormatterRef date_format = NULL; 797 CFTimeZoneRef tz = NULL; 798 CFStringRef time_date = NULL; 799 CFLocaleRef loc = NULL; 800 char _date[60]; 801 802 loc = CFLocaleCopyCurrent(); 803 if (loc) { 804 date_format = CFDateFormatterCreate(0, loc, kCFDateFormatterShortStyle, kCFDateFormatterLongStyle); 805 CFRelease(loc); 806 } 807 if (date_format) { 808 tz = CFTimeZoneCopySystem(); 809 if (tz) { 810 CFDateFormatterSetProperty(date_format, kCFDateFormatterTimeZone, tz); 811 CFRelease(tz); 812 } 813 time_date = CFDateFormatterCreateStringWithAbsoluteTime(0, date_format, CFAbsoluteTimeGetCurrent()); 814 CFRelease(date_format); 815 } 816 if(time_date) { 817 CFStringGetCString(time_date, _date, 60, kCFStringEncodingUTF8); 818 printf("%s ", _date); fflush(stdout); 819 if(newline) printf("\n"); 820 CFRelease(time_date); 821 } 822} 823 824static void createPMConnectionListener(void) 825{ 826 IOReturn ret; 827 IOPMConnection myConnection; 828 829 /* 830 * Test PM Connection SleepService arguments 831 */ 832 ret = IOPMConnectionCreate( 833 CFSTR("SleepWakeLogTool"), 834 kIOPMCapabilityDisk | kIOPMCapabilityNetwork 835 | kIOPMCapabilityAudio| kIOPMCapabilityVideo, 836 &myConnection); 837 838 if (kIOReturnSuccess != ret) { 839 PMTestFail("Error 0x%08x from IOPMConnectionCreate.\n", ret); 840 exit(1); 841 } 842 843 ret = IOPMConnectionSetNotification(myConnection, NULL, 844 (IOPMEventHandlerType)myPMConnectionHandler); 845 846 if (kIOReturnSuccess != ret) { 847 PMTestFail("Error 0x%08x from IOPMConnectionSetNotification.\n", ret); 848 exit(1); 849 } 850 851 ret = IOPMConnectionScheduleWithRunLoop( 852 myConnection, CFRunLoopGetCurrent(), 853 kCFRunLoopDefaultMode); 854 855 if (kIOReturnSuccess != ret) { 856 PMTestFail("Error 0x%08x from IOPMConnectionScheduleWithRunloop.\n", ret); 857 exit(1); 858 } 859 860 print_pretty_date(false); 861 printf("Created an IOPMConnection client.\n"); 862} 863 864 865 866/*************************************************************************/ 867 868 869static void myPMConnectionHandler( 870 void *param, 871 IOPMConnection connection, 872 IOPMConnectionMessageToken token, 873 IOPMSystemPowerStateCapabilities capabilities) 874{ 875 IOReturn ret = kIOReturnSuccess; 876 CFDictionaryRef ackDictionary = NULL; 877 static IOPMSystemPowerStateCapabilities previousCapabilities = kIOPMCapabilityCPU; 878 879 char buf[100]; 880 int buf_size = sizeof(buf); 881 IOPMGetCapabilitiesDescription(buf, buf_size, previousCapabilities); 882 printf("Transition from \'%s\'\n", buf); 883 IOPMGetCapabilitiesDescription(buf, buf_size, capabilities); 884 printf(", to \'%s\'\n", buf); 885 886 887 if(IOPMIsADarkWake(capabilities) 888 && !IOPMIsADarkWake(previousCapabilities) 889 && args.doItWhen & kDoItUponDarkWake) 890 { 891 executeTimedActions("DarkWake"); 892 893 894 if (args.doAction[kExitIfNextWakeIsNotPushIndex]) 895 { 896 if (!IOPMAllowsPushServiceTask(capabilities)) { 897 printf("Woke to DarkWake; but Push was not allowed. Exiting with error 1.\n"); 898 exit(1); 899 } else { 900 printf("Push bit is set; push is allowed. Exiting with status 0."); 901 exit(0); 902 } 903 } 904 905 } 906 else if(IOPMIsAUserWake(capabilities) 907 && !IOPMIsAUserWake(previousCapabilities) 908 && args.doItWhen & kDoItUponUserWake) 909 { 910 executeTimedActions("FullWake"); 911 } 912 913 previousCapabilities = capabilities; 914 915 916 /* Acknowledge the IOPMConnection event; possibly with a wakeup */ 917 if (args.doRequestWake[kIOPMConnectRequestSleepServiceIndex]) { 918 ackDictionary = HandleSleepServiceCapabilitiesChanged(capabilities); 919 920 } else if (args.doRequestWake[kIOPMConnectRequestMaintenanceIndex]) { 921 ackDictionary = HandleMaintenanceCapabilitiesChanged(capabilities); 922 } 923 else if (args.doRequestWake[kIOPMConnectRequestBackgroundIndex]) { 924 ackDictionary = HandleBackgroundTaskCapabilitiesChanged(capabilities); 925 } 926 927 if (ackDictionary) { 928 ret = IOPMConnectionAcknowledgeEventWithOptions(connection, token, ackDictionary); 929 CFRelease(ackDictionary); 930 } else { 931 ret = IOPMConnectionAcknowledgeEvent(connection, token); 932 } 933 934 if (kIOReturnSuccess != ret) { 935 PMTestFail("IOPMConnectionAcknowledgement%s failed with 0x%08x", ackDictionary?"WithOptions ":" ", ret); 936 } 937 938 return; 939} 940 941 942static CFDictionaryRef HandleBackgroundTaskCapabilitiesChanged(IOPMSystemPowerStateCapabilities cap) 943{ 944 CFMutableDictionaryRef ackDictionary = NULL; 945 946 ackDictionary = CFDictionaryCreateMutable(0, 1, 947 &kCFTypeDictionaryKeyCallBacks, 948 &kCFTypeDictionaryValueCallBacks); 949 if (ackDictionary) { 950 return NULL; 951 } 952 953 if (IOPMIsASleep(cap)) 954 { 955 PMTestLog("To Sleep: Ack'ing with request for BackgroundTask Wake.\n"); 956 /* Sleep */ 957 _CFDictionarySetDate(ackDictionary, 958 kIOPMAckTimerPluginWakeDate, 959 CFAbsoluteTimeGetCurrent() + (CFTimeInterval)args.sleepIntervalSec); 960 } 961 return ackDictionary; 962} 963 964static CFDictionaryRef HandleSleepServiceCapabilitiesChanged(IOPMSystemPowerStateCapabilities cap) 965{ 966 CFMutableDictionaryRef ackDictionary = NULL; 967 968 ackDictionary = CFDictionaryCreateMutable(0, 1, 969 &kCFTypeDictionaryKeyCallBacks, 970 &kCFTypeDictionaryValueCallBacks); 971 972 if (!ackDictionary) 973 return NULL; 974 975 if (IOPMAllowsPushServiceTask(cap)) 976 { 977 PMTestLog("DarkWake with Push: Ack'ing with sleepService cap time %ld sec.\n", args.sleepServiceCapTimeoutSec); 978 979 _CFDictionarySetLong(ackDictionary, 980 kIOPMAckSleepServiceCapTimeout, 981 args.sleepServiceCapTimeoutSec); 982 983 } 984 else if (IOPMIsASleep(cap)) 985 { 986 PMTestLog("To Sleep: Ack'ing with request for SleepService in %ld sec.\n", args.sleepIntervalSec); 987 988 _CFDictionarySetDate(ackDictionary, 989 kIOPMAckSleepServiceDate, 990 CFAbsoluteTimeGetCurrent() + (CFTimeInterval)args.sleepIntervalSec); 991 } 992 993 return ackDictionary; 994} 995 996static CFDictionaryRef HandleMaintenanceCapabilitiesChanged(IOPMSystemPowerStateCapabilities cap) 997{ 998 CFMutableDictionaryRef ackDictionary = NULL; 999 1000 ackDictionary = CFDictionaryCreateMutable(0, 1, 1001 &kCFTypeDictionaryKeyCallBacks, 1002 &kCFTypeDictionaryValueCallBacks); 1003 if (!ackDictionary) 1004 return NULL; 1005 1006 if (IOPMIsASleep(cap)) 1007 { 1008 PMTestLog("To Sleep: Ack'ing with request for maintenance in %ld sec.\n", args.sleepIntervalSec); 1009 1010 _CFDictionarySetDate(ackDictionary, 1011 kIOPMAckWakeDate, 1012 CFAbsoluteTimeGetCurrent() + (CFTimeInterval)args.sleepIntervalSec); 1013 1014 _CFDictionarySetLong(ackDictionary, 1015 kIOPMAckSystemCapabilityRequirements, 1016 (kIOPMCapabilityDisk | kIOPMCapabilityNetwork)); 1017 } 1018 1019 return ackDictionary; 1020} 1021 1022static void print_everything_dark(void) 1023{ 1024 1025 CFTypeRef b; 1026 1027 IOPlatformCopyFeatureDefault(kIOPlatformTCPKeepAliveDuringSleep, &b); 1028 printf(" Platform: TCPKeepAliveDuringSleep = %s\n", (kCFBooleanTrue == b) ? "yes":"no"); 1029 IOPlatformCopyFeatureDefault(CFSTR("NotificationWake"), &b); 1030 printf(" Platform: NotificationWake = %s\n", (kCFBooleanTrue == b) ? "yes":"no"); 1031 IOPlatformCopyFeatureDefault(CFSTR("DNDWhileDisplaySleeps"), &b); 1032 printf(" Platform: DNDWhileDisplaySleeps = %s\n", (kCFBooleanTrue == b) ? "yes":"no"); 1033 1034 1035 int tcpKeepAliveActive = IOPMGetValueInt(kIOPMTCPKeepAliveIsActive); 1036 int tcpKeepAliveExpires = IOPMGetValueInt(kIOPMTCPKeepAliveExpirationOverride); 1037 int tcpWakeQuotaInterval = IOPMGetValueInt(kIOPMTCPWakeQuotaInterval); 1038 int tcpWakeQuotaCount = IOPMGetValueInt(kIOPMTCPWakeQuota); 1039 1040 printf(" TCPKeepAlive Active = %d\n", tcpKeepAliveActive); 1041 printf(" TCPKeepAlive Expiration = %ds\n", tcpKeepAliveExpires); 1042 printf(" TCPKeepAlive WakeQuotaInterval = %ds\n", tcpWakeQuotaInterval); 1043 printf(" TCPKeepAlive WakeQuotaCount = %d\n", tcpWakeQuotaCount); 1044} 1045 1046 1047 1048#define kHeatersCount 32 1049static void bringTheHeat(void) 1050{ 1051 int i; 1052 1053 printf("Generating some CPU heat! %d parallel queues are doing busy work.\n", kHeatersCount); 1054 1055 for (i=0; i<kHeatersCount; i++) 1056 { 1057 dispatch_queue_t q; 1058 q = dispatch_queue_create("heater queue", DISPATCH_QUEUE_SERIAL); 1059 dispatch_async(q, ^{ while(1); }); 1060 } 1061 dispatch_main(); 1062} 1063 1064void _CFDictionarySetLong(CFMutableDictionaryRef d, CFStringRef k, long val) 1065{ 1066 CFNumberRef num = NULL; 1067 1068 1069 num = CFNumberCreate(0, kCFNumberSInt32Type, (const void *)&val); 1070 if (num) 1071 { 1072 CFDictionarySetValue(d, k, num); 1073 CFRelease(num); 1074 } 1075} 1076 1077 1078void _CFDictionarySetDate(CFMutableDictionaryRef d, CFStringRef k, CFAbsoluteTime atime) 1079{ 1080 CFDateRef thedate = NULL; 1081 1082 thedate = CFDateCreate(0, atime); 1083 if (thedate) { 1084 CFDictionarySetValue(d, k, thedate); 1085 CFRelease(thedate); 1086 } 1087} 1088 1089 1090 1091 1092 1093