#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <dispatch/dispatch.h>
#include <CoreFoundation/CFNumber.h>
#include <IOKit/pwr_mgt/IOPMLib.h>
#include <IOKit/pwr_mgt/IOPMLibPrivate.h>
typedef enum {
kDefaultAssertionFlag = 0,
kIdleAssertionFlag = (1 << 0),
kDisplayAssertionFlag = (1 << 1),
kSystemAssertionFlag = (1 << 2),
kUserActiveAssertionFlag = (1 << 3)
} AssertionFlag;
typedef struct {
AssertionFlag assertionFlag;
CFStringRef assertionType;
} AssertionMapEntry;
AssertionMapEntry assertionMap[] = {
{ kIdleAssertionFlag, kIOPMAssertionTypePreventUserIdleSystemSleep },
{ kDisplayAssertionFlag, kIOPMAssertionTypePreventUserIdleDisplaySleep },
{ kSystemAssertionFlag, kIOPMAssertionTypePreventSystemSleep},
{ kUserActiveAssertionFlag, kIOPMAssertionUserIsActive}};
static CFStringRef kHumanReadableReason = CFSTR("THE CAFFEINATE TOOL IS PREVENTING SLEEP.");
static CFStringRef kLocalizationBundlePath = CFSTR("/System/Library/CoreServices/powerd.bundle");
#define kAssertionNameString "caffeinate command-line tool"
int createAssertions(const char *progname, AssertionFlag flags, long timeout);
void forkChild(char *argv[], AssertionFlag flag);
void usage(void);
int
main(int argc, char *argv[])
{
AssertionFlag flags = kDefaultAssertionFlag;
char ch;
long timeout = 0;
errno = 0;
while ((ch = getopt(argc, argv, "dhisut:")) != -1) {
switch((char)ch) {
case 'd':
flags |= kDisplayAssertionFlag;
break;
case 'h':
usage();
exit(0);
case 'i':
flags |= kIdleAssertionFlag;
break;
case 's':
flags |= kSystemAssertionFlag;
break;
case 'u':
flags |= kUserActiveAssertionFlag;
break;
case 't':
timeout = strtol(optarg, NULL, 0);
if (errno != 0) {
usage();
exit(1);
}
break;
case '?':
default:
usage();
exit(1);
}
}
if (flags == kDefaultAssertionFlag) {
flags = kIdleAssertionFlag;
}
if (argc - optind) {
argv += optind;
(void) forkChild(argv, flags);
} else {
if (createAssertions(NULL, flags, timeout)) {
exit(1);
}
if (timeout) {
sleep(timeout+5);
exit(0);
}
}
dispatch_main();
}
int
createAssertions(const char *progname, AssertionFlag flags, long timeout)
{
IOReturn result = 1;
char assertionDetails[128];
CFStringRef assertionDetailsString = NULL;
IOPMAssertionID assertionID = 0;
u_int i = 0;
if (progname) {
(void)snprintf(assertionDetails, sizeof(assertionDetails),
"caffeinate asserting on behalf of %s", progname);
} else if (timeout) {
(void)snprintf(assertionDetails, sizeof(assertionDetails),
"caffeinate asserting for %ld secs", timeout);
} else {
(void)snprintf(assertionDetails, sizeof(assertionDetails),
"caffeinate asserting forever");
}
assertionDetailsString = CFStringCreateWithCString(kCFAllocatorDefault,
assertionDetails, kCFStringEncodingMacRoman);
if (!assertionDetailsString) {
fprintf(stderr, "Failed to create assertion name %s\n", progname);
goto finish;
}
for (i = 0; i < sizeof(assertionMap)/sizeof(AssertionMapEntry); ++i)
{
AssertionMapEntry *entry = assertionMap + i;
if (!(flags & entry->assertionFlag)) continue;
if ( (entry->assertionFlag == kUserActiveAssertionFlag) && (timeout == 0))
timeout = 5;
result = IOPMAssertionCreateWithDescription(entry->assertionType,
CFSTR(kAssertionNameString), assertionDetailsString,
kHumanReadableReason, kLocalizationBundlePath,
(CFTimeInterval)timeout, kIOPMAssertionTimeoutActionRelease,
&assertionID);
if (result != kIOReturnSuccess)
{
fprintf(stderr, "Failed to create %s assertion\n",
CFStringGetCStringPtr(entry->assertionType, kCFStringEncodingMacRoman));
goto finish;
}
}
result = kIOReturnSuccess;
finish:
if (assertionDetailsString) CFRelease(assertionDetailsString);
return result;
}
void
forkChild(char *argv[], AssertionFlag flags)
{
pid_t pid;
dispatch_source_t source;
switch(pid = fork()) {
case -1:
perror("");
exit(1);
case 0:
if (createAssertions(*argv, flags, 0)) {
_exit(1);
}
execvp(*argv, argv);
perror(*argv);
_exit((errno == ENOENT) ? 127 : 126);
}
(void)signal(SIGINT, SIG_IGN);
(void)signal(SIGQUIT, SIG_IGN);
source = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, pid,
DISPATCH_PROC_EXIT, dispatch_get_main_queue());
dispatch_source_set_event_handler(source, ^{
int status;
if (waitpid(pid, &status, 0) < 0) {
perror("");
exit(1);
}
exit(WIFEXITED(status) ? WEXITSTATUS(status) : EXIT_FAILURE);
});
dispatch_resume(source);
return;
}
void
usage(void)
{
fprintf(stderr, "usage: caffeinate [-disu] [-t timeout] [command] [arguments]\n");
return;
}