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