-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathclamshell.c
More file actions
115 lines (97 loc) · 3.33 KB
/
Copy pathclamshell.c
File metadata and controls
115 lines (97 loc) · 3.33 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
// vim: et sw=4 ts=4
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOKitLib.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
#define LID_OPENED 0
#define LID_CLOSED 1
void usage(void);
uint8_t clamshell_state(void);
void debug(const char * restrict format, ...);
static int debug_flag = 0;
int main(int argc, char *argv[]) {
if (argc == 1) {
usage();
exit(0);
}
// Enable debug if DEBUG environment variable is set
if (getenv("DEBUG") != NULL) {
debug_flag = 1;
}
// Main loop check clamshell lid every 10 seconds
pid_t child_pid = 0;
uint8_t lid_state0 = LID_CLOSED;
uint8_t lid_state1 = LID_CLOSED;
while (true) {
debug("Querying lid status\n");
lid_state1 = lid_state0;
lid_state0 = clamshell_state();
debug("lid_state0 %s lid_state1 %s\n",
lid_state0 == LID_OPENED ? "LID_OPENED" : "LID_CLOSED",
lid_state1 == LID_OPENED ? "LID_OPENED" : "LID_CLOSED");
if (lid_state0 == LID_OPENED && lid_state1 == LID_CLOSED) {
debug("Lid transitioned to open\n");
child_pid = fork();
if (child_pid == -1) {
fprintf(stderr, "Error: failed to fork().\n");
exit(1);
}
else if (child_pid == 0) {
debug("Executing command\n");
execvp(argv[1], &argv[1]);
}
}
else if (lid_state0 == LID_CLOSED && lid_state1 == LID_OPENED) {
debug("Lid transitioned to closed\n");
if (child_pid != 0) {
debug("Killing command %i\n", child_pid);
kill(child_pid, SIGTERM);
}
}
// Reap child processes that exit before we have a chance to kill them
int child_status;
if (child_pid != 0 && waitpid(child_pid, &child_status, WNOHANG) != 0) {
debug("Child exited %i\n", WEXITSTATUS(child_status));
child_pid = 0;
}
// Wait before repeating the loop
debug("Sleeping 10 seconds\n");
sleep(10);
}
return 0;
}
uint8_t clamshell_state(void) {
uint8_t clamshell_state = 0;
io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOPMrootDomain"));
CFTypeRef clamshell = IORegistryEntryCreateCFProperty(service, CFSTR("AppleClamshellState"), kCFAllocatorDefault, 0);
if (clamshell != nil && CFBooleanGetTypeID() == CFGetTypeID(clamshell)) {
clamshell_state = CFBooleanGetValue(clamshell);
}
CFRelease(clamshell);
IOObjectRelease(service);
return clamshell_state;
}
void usage(void) {
printf("Usage:\n");
printf(" clamshell <command>\n");
printf("\n");
printf("Executes the command when the clamshell lid is opened. When the lid is closed\n");
printf("the process is killed. Checking for changes between opened and closed is done\n");
printf("every 10 seconds. Set the DEBUG variable to any value to enable debug messages.\n");
printf("\n");
}
void debug(const char * restrict format, ...) {
va_list ap;
if (debug_flag) {
va_start(ap, format);
printf("%i ", getpid());
vprintf(format, ap);
va_end(ap);
}
}