ios_app_installer 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +22 -0
- data/README.md +17 -0
- data/Rakefile +2 -0
- data/bin/ios_app_installer +6 -0
- data/ios_app_installer-0.0.1.gem +0 -0
- data/ios_app_installer.gemspec +25 -0
- data/lib/ios_app_installer.rb +5 -0
- data/lib/ios_app_installer/version.rb +3 -0
- data/node_modules/.bin/ios-deploy +0 -0
- data/node_modules/ios_app_installer/.npmignore +5 -0
- data/node_modules/ios_app_installer/CONTRIBUTING.md +31 -0
- data/node_modules/ios_app_installer/Entitlements.plist +8 -0
- data/node_modules/ios_app_installer/Info.plist +24 -0
- data/node_modules/ios_app_installer/LICENSE +2 -0
- data/node_modules/ios_app_installer/Makefile +35 -0
- data/node_modules/ios_app_installer/MobileDevice.h +496 -0
- data/node_modules/ios_app_installer/ResourceRules.plist +25 -0
- data/node_modules/ios_app_installer/demo.c +9 -0
- data/node_modules/ios_app_installer/ios_app_installer +0 -0
- data/node_modules/ios_app_installer/ios_app_installer.c +904 -0
- data/node_modules/ios_app_installer/ios_app_installer.dSYM/Contents/Info.plist +20 -0
- data/node_modules/ios_app_installer/ios_app_installer.dSYM/Contents/Resources/DWARF/ios_app_installer +0 -0
- data/node_modules/ios_app_installer/package.json +58 -0
- data/node_modules/ios_app_installer/resources/buildbox/build.sh +3 -0
- metadata +99 -0
@@ -0,0 +1,25 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
3
|
+
<plist version="1.0">
|
4
|
+
<dict>
|
5
|
+
<key>rules</key>
|
6
|
+
<dict>
|
7
|
+
<key>.*</key>
|
8
|
+
<true/>
|
9
|
+
<key>Info.plist</key>
|
10
|
+
<dict>
|
11
|
+
<key>omit</key>
|
12
|
+
<true/>
|
13
|
+
<key>weight</key>
|
14
|
+
<real>10</real>
|
15
|
+
</dict>
|
16
|
+
<key>ResourceRules.plist</key>
|
17
|
+
<dict>
|
18
|
+
<key>omit</key>
|
19
|
+
<true/>
|
20
|
+
<key>weight</key>
|
21
|
+
<real>100</real>
|
22
|
+
</dict>
|
23
|
+
</dict>
|
24
|
+
</dict>
|
25
|
+
</plist>
|
Binary file
|
@@ -0,0 +1,904 @@
|
|
1
|
+
//TODO: don't copy/mount DeveloperDiskImage.dmg if it's already done - Xcode checks this somehow
|
2
|
+
|
3
|
+
#import <CoreFoundation/CoreFoundation.h>
|
4
|
+
#import <Foundation/Foundation.h>
|
5
|
+
#include <unistd.h>
|
6
|
+
#include <sys/socket.h>
|
7
|
+
#include <sys/types.h>
|
8
|
+
#include <sys/stat.h>
|
9
|
+
#include <sys/un.h>
|
10
|
+
#include <sys/sysctl.h>
|
11
|
+
#include <stdio.h>
|
12
|
+
#include <signal.h>
|
13
|
+
#include <getopt.h>
|
14
|
+
#include <pwd.h>
|
15
|
+
#include <netinet/in.h>
|
16
|
+
#include <netinet/tcp.h>
|
17
|
+
#include "MobileDevice.h"
|
18
|
+
|
19
|
+
#define APP_VERSION "1.5.0"
|
20
|
+
#define PREP_CMDS_PATH "/tmp/fruitstrap-lldb-prep-cmds-"
|
21
|
+
#define LLDB_SHELL "lldb -s " PREP_CMDS_PATH
|
22
|
+
/*
|
23
|
+
* Startup script passed to lldb.
|
24
|
+
* To see how xcode interacts with lldb, put this into .lldbinit:
|
25
|
+
* log enable -v -f /Users/vargaz/lldb.log lldb all
|
26
|
+
* log enable -v -f /Users/vargaz/gdb-remote.log gdb-remote all
|
27
|
+
*/
|
28
|
+
#define LLDB_PREP_CMDS CFSTR("\
|
29
|
+
platform select remote-ios --sysroot {symbols_path}\n\
|
30
|
+
target create \"{disk_app}\"\n\
|
31
|
+
script fruitstrap_device_app=\"{device_app}\"\n\
|
32
|
+
script fruitstrap_connect_url=\"connect://127.0.0.1:{device_port}\"\n\
|
33
|
+
command script import \"{python_file_path}\"\n\
|
34
|
+
command script add -f {python_command}.connect_command connect\n\
|
35
|
+
command script add -s asynchronous -f {python_command}.run_command run\n\
|
36
|
+
command script add -s asynchronous -f {python_command}.autoexit_command autoexit\n\
|
37
|
+
command script add -s asynchronous -f {python_command}.safequit_command safequit\n\
|
38
|
+
connect\n\
|
39
|
+
")
|
40
|
+
|
41
|
+
const char* lldb_prep_no_cmds = "";
|
42
|
+
|
43
|
+
const char* lldb_prep_interactive_cmds = "\
|
44
|
+
run\n\
|
45
|
+
";
|
46
|
+
|
47
|
+
const char* lldb_prep_noninteractive_justlaunch_cmds = "\
|
48
|
+
run\n\
|
49
|
+
safequit\n\
|
50
|
+
";
|
51
|
+
|
52
|
+
const char* lldb_prep_noninteractive_cmds = "\
|
53
|
+
run\n\
|
54
|
+
autoexit\n\
|
55
|
+
";
|
56
|
+
|
57
|
+
/*
|
58
|
+
* Some things do not seem to work when using the normal commands like process connect/launch, so we invoke them
|
59
|
+
* through the python interface. Also, Launch () doesn't seem to work when ran from init_module (), so we add
|
60
|
+
* a command which can be used by the user to run it.
|
61
|
+
*/
|
62
|
+
#define LLDB_FRUITSTRAP_MODULE CFSTR("\
|
63
|
+
import lldb\n\
|
64
|
+
import os\n\
|
65
|
+
import sys\n\
|
66
|
+
import shlex\n\
|
67
|
+
\n\
|
68
|
+
def connect_command(debugger, command, result, internal_dict):\n\
|
69
|
+
# These two are passed in by the script which loads us\n\
|
70
|
+
connect_url = internal_dict['fruitstrap_connect_url']\n\
|
71
|
+
error = lldb.SBError()\n\
|
72
|
+
\n\
|
73
|
+
process = lldb.target.ConnectRemote(lldb.target.GetDebugger().GetListener(), connect_url, None, error)\n\
|
74
|
+
\n\
|
75
|
+
# Wait for connection to succeed\n\
|
76
|
+
listener = lldb.target.GetDebugger().GetListener()\n\
|
77
|
+
listener.StartListeningForEvents(process.GetBroadcaster(), lldb.SBProcess.eBroadcastBitStateChanged)\n\
|
78
|
+
events = []\n\
|
79
|
+
state = (process.GetState() or lldb.eStateInvalid)\n\
|
80
|
+
while state != lldb.eStateConnected:\n\
|
81
|
+
event = lldb.SBEvent()\n\
|
82
|
+
if listener.WaitForEvent(1, event):\n\
|
83
|
+
state = process.GetStateFromEvent(event)\n\
|
84
|
+
events.append(event)\n\
|
85
|
+
else:\n\
|
86
|
+
state = lldb.eStateInvalid\n\
|
87
|
+
\n\
|
88
|
+
# Add events back to queue, otherwise lldb freezes\n\
|
89
|
+
for event in events:\n\
|
90
|
+
listener.AddEvent(event)\n\
|
91
|
+
\n\
|
92
|
+
def run_command(debugger, command, result, internal_dict):\n\
|
93
|
+
device_app = internal_dict['fruitstrap_device_app']\n\
|
94
|
+
args = command.split('--',1)\n\
|
95
|
+
error = lldb.SBError()\n\
|
96
|
+
lldb.target.modules[0].SetPlatformFileSpec(lldb.SBFileSpec(device_app))\n\
|
97
|
+
lldb.target.Launch(lldb.SBLaunchInfo(shlex.split(args[1] and args[1] or '{args}')), error)\n\
|
98
|
+
lockedstr = ': Locked'\n\
|
99
|
+
if lockedstr in str(error):\n\
|
100
|
+
print('\\nDevice Locked\\n')\n\
|
101
|
+
os._exit(254)\n\
|
102
|
+
else:\n\
|
103
|
+
print(str(error))\n\
|
104
|
+
\n\
|
105
|
+
def safequit_command(debugger, command, result, internal_dict):\n\
|
106
|
+
process = lldb.target.process\n\
|
107
|
+
listener = debugger.GetListener()\n\
|
108
|
+
listener.StartListeningForEvents(process.GetBroadcaster(), lldb.SBProcess.eBroadcastBitStateChanged | lldb.SBProcess.eBroadcastBitSTDOUT | lldb.SBProcess.eBroadcastBitSTDERR)\n\
|
109
|
+
event = lldb.SBEvent()\n\
|
110
|
+
while True:\n\
|
111
|
+
if listener.WaitForEvent(1, event) and lldb.SBProcess.EventIsProcessEvent(event):\n\
|
112
|
+
state = lldb.SBProcess.GetStateFromEvent(event)\n\
|
113
|
+
else:\n\
|
114
|
+
state = process.GetState()\n\
|
115
|
+
\n\
|
116
|
+
if state == lldb.eStateRunning:\n\
|
117
|
+
process.Detach()\n\
|
118
|
+
os._exit(0)\n\
|
119
|
+
elif state > lldb.eStateRunning:\n\
|
120
|
+
os._exit(state)\n\
|
121
|
+
\n\
|
122
|
+
def autoexit_command(debugger, command, result, internal_dict):\n\
|
123
|
+
process = lldb.target.process\n\
|
124
|
+
listener = debugger.GetListener()\n\
|
125
|
+
listener.StartListeningForEvents(process.GetBroadcaster(), lldb.SBProcess.eBroadcastBitStateChanged | lldb.SBProcess.eBroadcastBitSTDOUT | lldb.SBProcess.eBroadcastBitSTDERR)\n\
|
126
|
+
event = lldb.SBEvent()\n\
|
127
|
+
while True:\n\
|
128
|
+
if listener.WaitForEvent(1, event) and lldb.SBProcess.EventIsProcessEvent(event):\n\
|
129
|
+
state = lldb.SBProcess.GetStateFromEvent(event)\n\
|
130
|
+
else:\n\
|
131
|
+
state = process.GetState()\n\
|
132
|
+
\n\
|
133
|
+
if state == lldb.eStateExited:\n\
|
134
|
+
os._exit(process.GetExitStatus())\n\
|
135
|
+
elif state == lldb.eStateStopped:\n\
|
136
|
+
debugger.HandleCommand('bt')\n\
|
137
|
+
os._exit({exitcode_app_crash})\n\
|
138
|
+
\n\
|
139
|
+
stdout = process.GetSTDOUT(1024)\n\
|
140
|
+
while stdout:\n\
|
141
|
+
sys.stdout.write(stdout)\n\
|
142
|
+
stdout = process.GetSTDOUT(1024)\n\
|
143
|
+
\n\
|
144
|
+
stderr = process.GetSTDERR(1024)\n\
|
145
|
+
while stderr:\n\
|
146
|
+
sys.stdout.write(stderr)\n\
|
147
|
+
stderr = process.GetSTDERR(1024)\n\
|
148
|
+
")
|
149
|
+
|
150
|
+
typedef struct am_device * AMDeviceRef;
|
151
|
+
mach_error_t AMDeviceSecureStartService(struct am_device *device, CFStringRef service_name, unsigned int *unknown, service_conn_t *handle);
|
152
|
+
int AMDeviceSecureTransferPath(int zero, AMDeviceRef device, CFURLRef url, CFDictionaryRef options, void *callback, int cbarg);
|
153
|
+
int AMDeviceSecureInstallApplication(int zero, AMDeviceRef device, CFURLRef url, CFDictionaryRef options, void *callback, int cbarg);
|
154
|
+
int AMDeviceMountImage(AMDeviceRef device, CFStringRef image, CFDictionaryRef options, void *callback, int cbarg);
|
155
|
+
mach_error_t AMDeviceLookupApplications(AMDeviceRef device, CFDictionaryRef options, CFDictionaryRef *result);
|
156
|
+
int AMDeviceGetInterfaceType(struct am_device *device);
|
157
|
+
|
158
|
+
bool found_device = false, debug = false, verbose = false, unbuffered = false, nostart = false, detect_only = false, install = true, uninstall = false;
|
159
|
+
bool command_only = false;
|
160
|
+
char *command = NULL;
|
161
|
+
char *target_filename = NULL;
|
162
|
+
char *upload_pathname = NULL;
|
163
|
+
char *bundle_id = NULL;
|
164
|
+
bool interactive = true;
|
165
|
+
bool justlaunch = false;
|
166
|
+
char *app_path = NULL;
|
167
|
+
char *device_id = NULL;
|
168
|
+
char *args = NULL;
|
169
|
+
char *list_root = NULL;
|
170
|
+
int timeout = 0;
|
171
|
+
int port = 0; // 0 means "dynamically assigned"
|
172
|
+
CFStringRef last_path = NULL;
|
173
|
+
service_conn_t gdbfd;
|
174
|
+
pid_t parent = 0;
|
175
|
+
// PID of child process running lldb
|
176
|
+
pid_t child = 0;
|
177
|
+
// Signal sent from child to parent process when LLDB finishes.
|
178
|
+
const int SIGLLDB = SIGUSR1;
|
179
|
+
AMDeviceRef best_device_match = NULL;
|
180
|
+
|
181
|
+
// Error codes we report on different failures, so scripts can distinguish between user app exit
|
182
|
+
// codes and our exit codes. For non app errors we use codes in reserved 128-255 range.
|
183
|
+
const int exitcode_error = 253;
|
184
|
+
const int exitcode_app_crash = 254;
|
185
|
+
|
186
|
+
Boolean path_exists(CFTypeRef path) {
|
187
|
+
if (CFGetTypeID(path) == CFStringGetTypeID()) {
|
188
|
+
CFURLRef url = CFURLCreateWithFileSystemPath(NULL, path, kCFURLPOSIXPathStyle, true);
|
189
|
+
Boolean result = CFURLResourceIsReachable(url, NULL);
|
190
|
+
CFRelease(url);
|
191
|
+
return result;
|
192
|
+
} else if (CFGetTypeID(path) == CFURLGetTypeID()) {
|
193
|
+
return CFURLResourceIsReachable(path, NULL);
|
194
|
+
} else {
|
195
|
+
return false;
|
196
|
+
}
|
197
|
+
}
|
198
|
+
|
199
|
+
CFStringRef find_path(CFStringRef rootPath, CFStringRef namePattern, CFStringRef expression) {
|
200
|
+
FILE *fpipe = NULL;
|
201
|
+
CFStringRef quotedRootPath = rootPath;
|
202
|
+
CFStringRef cf_command;
|
203
|
+
CFRange slashLocation;
|
204
|
+
|
205
|
+
if (CFStringGetCharacterAtIndex(rootPath, 0) != '`') {
|
206
|
+
quotedRootPath = CFStringCreateWithFormat(NULL, NULL, CFSTR("'%@'"), rootPath);
|
207
|
+
}
|
208
|
+
|
209
|
+
slashLocation = CFStringFind(namePattern, CFSTR("/"), 0);
|
210
|
+
if (slashLocation.location == kCFNotFound) {
|
211
|
+
cf_command = CFStringCreateWithFormat(NULL, NULL, CFSTR("find %@ -name '%@' %@ 2>/dev/null | sort | tail -n 1"), quotedRootPath, namePattern, expression);
|
212
|
+
} else {
|
213
|
+
cf_command = CFStringCreateWithFormat(NULL, NULL, CFSTR("find %@ -path '%@' %@ 2>/dev/null | sort | tail -n 1"), quotedRootPath, namePattern, expression);
|
214
|
+
}
|
215
|
+
|
216
|
+
if (quotedRootPath != rootPath) {
|
217
|
+
CFRelease(quotedRootPath);
|
218
|
+
}
|
219
|
+
|
220
|
+
char command[1024] = { '\0' };
|
221
|
+
CFStringGetCString(cf_command, command, sizeof(command), kCFStringEncodingUTF8);
|
222
|
+
CFRelease(cf_command);
|
223
|
+
|
224
|
+
if (!(fpipe = (FILE *)popen(command, "r")))
|
225
|
+
{
|
226
|
+
perror("Error encountered while opening pipe");
|
227
|
+
exit(exitcode_error);
|
228
|
+
}
|
229
|
+
|
230
|
+
char buffer[256] = { '\0' };
|
231
|
+
|
232
|
+
fgets(buffer, sizeof(buffer), fpipe);
|
233
|
+
pclose(fpipe);
|
234
|
+
|
235
|
+
strtok(buffer, "\n");
|
236
|
+
return CFStringCreateWithCString(NULL, buffer, kCFStringEncodingUTF8);
|
237
|
+
}
|
238
|
+
|
239
|
+
CFStringRef copy_long_shot_disk_image_path() {
|
240
|
+
return find_path(CFSTR("`xcode-select --print-path`"), CFSTR("DeveloperDiskImage.dmg"), CFSTR(""));
|
241
|
+
}
|
242
|
+
|
243
|
+
CFStringRef copy_xcode_dev_path() {
|
244
|
+
static char xcode_dev_path[256] = { '\0' };
|
245
|
+
if (strlen(xcode_dev_path) == 0) {
|
246
|
+
FILE *fpipe = NULL;
|
247
|
+
char *command = "xcode-select -print-path";
|
248
|
+
|
249
|
+
if (!(fpipe = (FILE *)popen(command, "r")))
|
250
|
+
{
|
251
|
+
perror("Error encountered while opening pipe");
|
252
|
+
exit(exitcode_error);
|
253
|
+
}
|
254
|
+
|
255
|
+
char buffer[256] = { '\0' };
|
256
|
+
|
257
|
+
fgets(buffer, sizeof(buffer), fpipe);
|
258
|
+
pclose(fpipe);
|
259
|
+
|
260
|
+
strtok(buffer, "\n");
|
261
|
+
strcpy(xcode_dev_path, buffer);
|
262
|
+
}
|
263
|
+
return CFStringCreateWithCString(NULL, xcode_dev_path, kCFStringEncodingUTF8);
|
264
|
+
}
|
265
|
+
|
266
|
+
const char *get_home() {
|
267
|
+
const char* home = getenv("HOME");
|
268
|
+
if (!home) {
|
269
|
+
struct passwd *pwd = getpwuid(getuid());
|
270
|
+
home = pwd->pw_dir;
|
271
|
+
}
|
272
|
+
return home;
|
273
|
+
}
|
274
|
+
|
275
|
+
CFStringRef copy_xcode_path_for(CFStringRef subPath, CFStringRef search) {
|
276
|
+
CFStringRef xcodeDevPath = copy_xcode_dev_path();
|
277
|
+
CFStringRef path;
|
278
|
+
bool found = false;
|
279
|
+
const char* home = get_home();
|
280
|
+
CFRange slashLocation;
|
281
|
+
|
282
|
+
|
283
|
+
// Try using xcode-select --print-path
|
284
|
+
if (!found) {
|
285
|
+
path = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@/%@/%@"), xcodeDevPath, subPath, search);
|
286
|
+
found = path_exists(path);
|
287
|
+
}
|
288
|
+
// Try find `xcode-select --print-path` with search as a name pattern
|
289
|
+
if (!found) {
|
290
|
+
slashLocation = CFStringFind(search, CFSTR("/"), 0);
|
291
|
+
if (slashLocation.location == kCFNotFound) {
|
292
|
+
path = find_path(CFStringCreateWithFormat(NULL, NULL, CFSTR("%@/%@"), xcodeDevPath, subPath), search, CFSTR("-maxdepth 1"));
|
293
|
+
} else {
|
294
|
+
path = find_path(CFStringCreateWithFormat(NULL, NULL, CFSTR("%@/%@"), xcodeDevPath, subPath), search, CFSTR(""));
|
295
|
+
}
|
296
|
+
found = CFStringGetLength(path) > 0 && path_exists(path);
|
297
|
+
}
|
298
|
+
// If not look in the default xcode location (xcode-select is sometimes wrong)
|
299
|
+
if (!found) {
|
300
|
+
path = CFStringCreateWithFormat(NULL, NULL, CFSTR("/Applications/Xcode.app/Contents/Developer/%@&%@"), subPath, search);
|
301
|
+
found = path_exists(path);
|
302
|
+
}
|
303
|
+
// If not look in the users home directory, Xcode can store device support stuff there
|
304
|
+
if (!found) {
|
305
|
+
path = CFStringCreateWithFormat(NULL, NULL, CFSTR("%s/Library/Developer/Xcode/%@/%@"), home, subPath, search);
|
306
|
+
found = path_exists(path);
|
307
|
+
}
|
308
|
+
|
309
|
+
CFRelease(xcodeDevPath);
|
310
|
+
|
311
|
+
if (found) {
|
312
|
+
return path;
|
313
|
+
} else {
|
314
|
+
CFRelease(path);
|
315
|
+
return NULL;
|
316
|
+
}
|
317
|
+
}
|
318
|
+
|
319
|
+
#define GET_FRIENDLY_MODEL_NAME(VALUE, INTERNAL_NAME, FRIENDLY_NAME) if (kCFCompareEqualTo == CFStringCompare(VALUE, CFSTR(INTERNAL_NAME), kCFCompareNonliteral)) { return CFSTR( FRIENDLY_NAME); };
|
320
|
+
|
321
|
+
|
322
|
+
// Please ensure that device is connected or the name will be unknown
|
323
|
+
const CFStringRef get_device_hardware_name(const AMDeviceRef device) {
|
324
|
+
CFStringRef model = AMDeviceCopyValue(device, 0, CFSTR("HardwareModel"));
|
325
|
+
|
326
|
+
if (model == NULL) {
|
327
|
+
return CFSTR("Unknown Device");
|
328
|
+
}
|
329
|
+
|
330
|
+
// iPod Touch
|
331
|
+
|
332
|
+
GET_FRIENDLY_MODEL_NAME(model, "N45AP", "iPod Touch")
|
333
|
+
GET_FRIENDLY_MODEL_NAME(model, "N72AP", "iPod Touch 2G")
|
334
|
+
GET_FRIENDLY_MODEL_NAME(model, "N18AP", "iPod Touch 3G")
|
335
|
+
GET_FRIENDLY_MODEL_NAME(model, "N81AP", "iPod Touch 4G")
|
336
|
+
GET_FRIENDLY_MODEL_NAME(model, "N78AP", "iPod Touch 5G")
|
337
|
+
GET_FRIENDLY_MODEL_NAME(model, "N78AAP", "iPod Touch 5G")
|
338
|
+
|
339
|
+
// iPad
|
340
|
+
|
341
|
+
GET_FRIENDLY_MODEL_NAME(model, "K48AP", "iPad")
|
342
|
+
GET_FRIENDLY_MODEL_NAME(model, "K93AP", "iPad 2")
|
343
|
+
GET_FRIENDLY_MODEL_NAME(model, "K94AP", "iPad 2 (GSM)")
|
344
|
+
GET_FRIENDLY_MODEL_NAME(model, "K95AP", "iPad 2 (CDMA)")
|
345
|
+
GET_FRIENDLY_MODEL_NAME(model, "K93AAP", "iPad 2 (Wi-Fi, revision A)")
|
346
|
+
GET_FRIENDLY_MODEL_NAME(model, "J1AP", "iPad 3")
|
347
|
+
GET_FRIENDLY_MODEL_NAME(model, "J2AP", "iPad 3 (GSM)")
|
348
|
+
GET_FRIENDLY_MODEL_NAME(model, "J2AAP", "iPad 3 (CDMA)")
|
349
|
+
GET_FRIENDLY_MODEL_NAME(model, "P101AP", "iPad 4")
|
350
|
+
GET_FRIENDLY_MODEL_NAME(model, "P102AP", "iPad 4 (GSM)")
|
351
|
+
GET_FRIENDLY_MODEL_NAME(model, "P103AP", "iPad 4 (CDMA)")
|
352
|
+
|
353
|
+
// iPad Mini
|
354
|
+
|
355
|
+
GET_FRIENDLY_MODEL_NAME(model, "P105AP", "iPad mini")
|
356
|
+
GET_FRIENDLY_MODEL_NAME(model, "P106AP", "iPad mini (GSM)")
|
357
|
+
GET_FRIENDLY_MODEL_NAME(model, "P107AP", "iPad mini (CDMA)")
|
358
|
+
|
359
|
+
// Apple TV
|
360
|
+
|
361
|
+
GET_FRIENDLY_MODEL_NAME(model, "K66AP", "Apple TV 2G")
|
362
|
+
GET_FRIENDLY_MODEL_NAME(model, "J33AP", "Apple TV 3G")
|
363
|
+
GET_FRIENDLY_MODEL_NAME(model, "J33IAP", "Apple TV 3.1G")
|
364
|
+
|
365
|
+
// iPhone
|
366
|
+
|
367
|
+
GET_FRIENDLY_MODEL_NAME(model, "M68AP", "iPhone")
|
368
|
+
GET_FRIENDLY_MODEL_NAME(model, "N82AP", "iPhone 3G")
|
369
|
+
GET_FRIENDLY_MODEL_NAME(model, "N88AP", "iPhone 3GS")
|
370
|
+
GET_FRIENDLY_MODEL_NAME(model, "N90AP", "iPhone 4 (GSM)")
|
371
|
+
GET_FRIENDLY_MODEL_NAME(model, "N92AP", "iPhone 4 (CDMA)")
|
372
|
+
GET_FRIENDLY_MODEL_NAME(model, "N90BAP", "iPhone 4 (GSM, revision A)")
|
373
|
+
GET_FRIENDLY_MODEL_NAME(model, "N94AP", "iPhone 4S")
|
374
|
+
GET_FRIENDLY_MODEL_NAME(model, "N41AP", "iPhone 5 (GSM)")
|
375
|
+
GET_FRIENDLY_MODEL_NAME(model, "N42AP", "iPhone 5 (Global/CDMA)")
|
376
|
+
GET_FRIENDLY_MODEL_NAME(model, "N48AP", "iPhone 5c (GSM)")
|
377
|
+
GET_FRIENDLY_MODEL_NAME(model, "N49AP", "iPhone 5c (Global/CDMA)")
|
378
|
+
GET_FRIENDLY_MODEL_NAME(model, "N51AP", "iPhone 5s (GSM)")
|
379
|
+
GET_FRIENDLY_MODEL_NAME(model, "N53AP", "iPhone 5s (Global/CDMA)")
|
380
|
+
GET_FRIENDLY_MODEL_NAME(model, "N61AP", "iPhone 6 (GSM)")
|
381
|
+
GET_FRIENDLY_MODEL_NAME(model, "N56AP", "iPhone 6 Plus")
|
382
|
+
|
383
|
+
return model;
|
384
|
+
}
|
385
|
+
|
386
|
+
char * MYCFStringCopyUTF8String(CFStringRef aString) {
|
387
|
+
if (aString == NULL) {
|
388
|
+
return NULL;
|
389
|
+
}
|
390
|
+
|
391
|
+
CFIndex length = CFStringGetLength(aString);
|
392
|
+
CFIndex maxSize =
|
393
|
+
CFStringGetMaximumSizeForEncoding(length,
|
394
|
+
kCFStringEncodingUTF8);
|
395
|
+
char *buffer = (char *)malloc(maxSize);
|
396
|
+
if (CFStringGetCString(aString, buffer, maxSize,
|
397
|
+
kCFStringEncodingUTF8)) {
|
398
|
+
return buffer;
|
399
|
+
}
|
400
|
+
return NULL;
|
401
|
+
}
|
402
|
+
|
403
|
+
CFStringRef get_device_full_name(const AMDeviceRef device) {
|
404
|
+
CFStringRef full_name = NULL,
|
405
|
+
device_udid = AMDeviceCopyDeviceIdentifier(device),
|
406
|
+
device_name = NULL,
|
407
|
+
model_name = NULL;
|
408
|
+
|
409
|
+
AMDeviceConnect(device);
|
410
|
+
|
411
|
+
device_name = AMDeviceCopyValue(device, 0, CFSTR("DeviceName")),
|
412
|
+
model_name = get_device_hardware_name(device);
|
413
|
+
|
414
|
+
if (verbose)
|
415
|
+
{
|
416
|
+
char *devName = MYCFStringCopyUTF8String(device_name);
|
417
|
+
printf("Device Name:[%s]\n",devName);
|
418
|
+
CFShow(device_name);
|
419
|
+
printf("\n");
|
420
|
+
free(devName);
|
421
|
+
|
422
|
+
char *mdlName = MYCFStringCopyUTF8String(model_name);
|
423
|
+
printf("Model Name:[%s]\n",mdlName);
|
424
|
+
printf("MM: [%s]\n",CFStringGetCStringPtr(model_name, kCFStringEncodingUTF8));
|
425
|
+
CFShow(model_name);
|
426
|
+
printf("\n");
|
427
|
+
free(mdlName);
|
428
|
+
}
|
429
|
+
|
430
|
+
if(device_name != NULL && model_name != NULL)
|
431
|
+
{
|
432
|
+
full_name = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@ '%@' (%@)"), model_name, device_name, device_udid);
|
433
|
+
}
|
434
|
+
else
|
435
|
+
{
|
436
|
+
full_name = CFStringCreateWithFormat(NULL, NULL, CFSTR("(%@ss)"), device_udid);
|
437
|
+
}
|
438
|
+
|
439
|
+
AMDeviceDisconnect(device);
|
440
|
+
|
441
|
+
if(device_udid != NULL)
|
442
|
+
CFRelease(device_udid);
|
443
|
+
if(device_name != NULL)
|
444
|
+
CFRelease(device_name);
|
445
|
+
if(model_name != NULL)
|
446
|
+
CFRelease(model_name);
|
447
|
+
|
448
|
+
return full_name;
|
449
|
+
}
|
450
|
+
|
451
|
+
CFStringRef get_device_interface_name(const AMDeviceRef device) {
|
452
|
+
// AMDeviceGetInterfaceType(device) 0=Unknown, 1 = Direct/USB, 2 = Indirect/WIFI
|
453
|
+
switch(AMDeviceGetInterfaceType(device)) {
|
454
|
+
case 1:
|
455
|
+
return CFSTR("USB");
|
456
|
+
case 2:
|
457
|
+
return CFSTR("WIFI");
|
458
|
+
default:
|
459
|
+
return CFSTR("Unknown Connection");
|
460
|
+
}
|
461
|
+
}
|
462
|
+
|
463
|
+
mach_error_t transfer_callback(CFDictionaryRef dict, int arg) {
|
464
|
+
int percent;
|
465
|
+
CFStringRef status = CFDictionaryGetValue(dict, CFSTR("Status"));
|
466
|
+
CFNumberGetValue(CFDictionaryGetValue(dict, CFSTR("PercentComplete")), kCFNumberSInt32Type, &percent);
|
467
|
+
|
468
|
+
if (CFEqual(status, CFSTR("CopyingFile"))) {
|
469
|
+
CFStringRef path = CFDictionaryGetValue(dict, CFSTR("Path"));
|
470
|
+
|
471
|
+
if ((last_path == NULL || !CFEqual(path, last_path)) && !CFStringHasSuffix(path, CFSTR(".ipa"))) {
|
472
|
+
printf("[%3d%%] Copying %s to device\n", percent / 2, CFStringGetCStringPtr(path, kCFStringEncodingMacRoman));
|
473
|
+
}
|
474
|
+
|
475
|
+
if (last_path != NULL) {
|
476
|
+
CFRelease(last_path);
|
477
|
+
}
|
478
|
+
last_path = CFStringCreateCopy(NULL, path);
|
479
|
+
}
|
480
|
+
|
481
|
+
return 0;
|
482
|
+
}
|
483
|
+
|
484
|
+
mach_error_t install_callback(CFDictionaryRef dict, int arg) {
|
485
|
+
int percent;
|
486
|
+
CFStringRef status = CFDictionaryGetValue(dict, CFSTR("Status"));
|
487
|
+
CFNumberGetValue(CFDictionaryGetValue(dict, CFSTR("PercentComplete")), kCFNumberSInt32Type, &percent);
|
488
|
+
|
489
|
+
printf("[%3d%%] %s\n", (percent / 2) + 50, CFStringGetCStringPtr(status, kCFStringEncodingMacRoman));
|
490
|
+
return 0;
|
491
|
+
}
|
492
|
+
|
493
|
+
|
494
|
+
CFSocketRef server_socket;
|
495
|
+
CFSocketRef lldb_socket;
|
496
|
+
CFWriteStreamRef serverWriteStream = NULL;
|
497
|
+
CFWriteStreamRef lldbWriteStream = NULL;
|
498
|
+
|
499
|
+
int kill_ptree(pid_t root, int signum);
|
500
|
+
|
501
|
+
|
502
|
+
void kill_ptree_inner(pid_t root, int signum, struct kinfo_proc *kp, int kp_len) {
|
503
|
+
int i;
|
504
|
+
for (i = 0; i < kp_len; i++) {
|
505
|
+
if (kp[i].kp_eproc.e_ppid == root) {
|
506
|
+
kill_ptree_inner(kp[i].kp_proc.p_pid, signum, kp, kp_len);
|
507
|
+
}
|
508
|
+
}
|
509
|
+
if (root != getpid()) {
|
510
|
+
kill(root, signum);
|
511
|
+
}
|
512
|
+
}
|
513
|
+
|
514
|
+
int kill_ptree(pid_t root, int signum) {
|
515
|
+
int mib[3];
|
516
|
+
size_t len;
|
517
|
+
mib[0] = CTL_KERN;
|
518
|
+
mib[1] = KERN_PROC;
|
519
|
+
mib[2] = KERN_PROC_ALL;
|
520
|
+
if (sysctl(mib, 3, NULL, &len, NULL, 0) == -1) {
|
521
|
+
return -1;
|
522
|
+
}
|
523
|
+
|
524
|
+
struct kinfo_proc *kp = calloc(1, len);
|
525
|
+
if (!kp) {
|
526
|
+
return -1;
|
527
|
+
}
|
528
|
+
|
529
|
+
if (sysctl(mib, 3, kp, &len, NULL, 0) == -1) {
|
530
|
+
free(kp);
|
531
|
+
return -1;
|
532
|
+
}
|
533
|
+
|
534
|
+
kill_ptree_inner(root, signum, kp, len / sizeof(struct kinfo_proc));
|
535
|
+
|
536
|
+
free(kp);
|
537
|
+
return 0;
|
538
|
+
}
|
539
|
+
|
540
|
+
|
541
|
+
int app_exists(AMDeviceRef device)
|
542
|
+
{
|
543
|
+
if (bundle_id == NULL) {
|
544
|
+
printf("Bundle id is not specified\n");
|
545
|
+
return false;
|
546
|
+
}
|
547
|
+
|
548
|
+
AMDeviceConnect(device);
|
549
|
+
assert(AMDeviceIsPaired(device));
|
550
|
+
assert(AMDeviceValidatePairing(device) == 0);
|
551
|
+
assert(AMDeviceStartSession(device) == 0);
|
552
|
+
|
553
|
+
CFStringRef cf_bundle_id = CFStringCreateWithCString(NULL, bundle_id, kCFStringEncodingASCII);
|
554
|
+
|
555
|
+
NSArray *a = [NSArray arrayWithObjects:@"CFBundleIdentifier", nil];
|
556
|
+
NSDictionary *optionsDict = [NSDictionary dictionaryWithObject:a forKey:@"ReturnAttributes"];
|
557
|
+
CFDictionaryRef options = (CFDictionaryRef)optionsDict;
|
558
|
+
|
559
|
+
CFDictionaryRef result = nil;
|
560
|
+
afc_error_t resultStatus = AMDeviceLookupApplications(device, options, &result);
|
561
|
+
assert(resultStatus == 0);
|
562
|
+
|
563
|
+
CFDictionaryRef app_dict = CFDictionaryGetValue(result, cf_bundle_id);
|
564
|
+
|
565
|
+
int appExists = (app_dict == NULL) ? -1 : 0;
|
566
|
+
|
567
|
+
CFRelease(cf_bundle_id);
|
568
|
+
|
569
|
+
assert(AMDeviceStopSession(device) == 0);
|
570
|
+
assert(AMDeviceDisconnect(device) == 0);
|
571
|
+
if (appExists==0) {printf("the app exists\n");}
|
572
|
+
else {printf("couldn't find app\n");}
|
573
|
+
return appExists;
|
574
|
+
}
|
575
|
+
|
576
|
+
|
577
|
+
void uninstall_only(AMDeviceRef device) {
|
578
|
+
CFRetain(device); // don't know if this is necessary?
|
579
|
+
|
580
|
+
printf("------ Uninstall phase ------\n");
|
581
|
+
|
582
|
+
//Do we already have the bundle_id passed in via the command line? if so, use it.
|
583
|
+
CFStringRef cf_uninstall_bundle_id = NULL;
|
584
|
+
if (bundle_id != NULL)
|
585
|
+
{
|
586
|
+
cf_uninstall_bundle_id = CFStringCreateWithCString(NULL, bundle_id, kCFStringEncodingUTF8);
|
587
|
+
} else {
|
588
|
+
printf("Error: you need to pass in the bundle id, (i.e. --bundle_id com.my.app)\n");
|
589
|
+
}
|
590
|
+
|
591
|
+
if (cf_uninstall_bundle_id == NULL) {
|
592
|
+
printf("Error: Unable to get bundle id from user command or package.\nUninstall failed.\n");
|
593
|
+
} else {
|
594
|
+
AMDeviceConnect(device);
|
595
|
+
assert(AMDeviceIsPaired(device));
|
596
|
+
AMDeviceValidatePairing(device);
|
597
|
+
AMDeviceStartSession(device);
|
598
|
+
|
599
|
+
int code = AMDeviceSecureUninstallApplication(0, device, cf_uninstall_bundle_id, 0, NULL, 0);
|
600
|
+
if (code == 0) {
|
601
|
+
printf("[ OK ] Uninstalled package with bundle id\n");
|
602
|
+
} else {
|
603
|
+
printf("[ ERROR ] Could not uninstall package with bundle id\n");
|
604
|
+
}
|
605
|
+
AMDeviceStopSession(device);
|
606
|
+
AMDeviceDisconnect(device);
|
607
|
+
}
|
608
|
+
}
|
609
|
+
|
610
|
+
|
611
|
+
void handle_device(AMDeviceRef device) {
|
612
|
+
//if (found_device)
|
613
|
+
// return; // handle one device only
|
614
|
+
|
615
|
+
|
616
|
+
CFStringRef found_device_id = AMDeviceCopyDeviceIdentifier(device),
|
617
|
+
device_full_name = get_device_full_name(device),
|
618
|
+
device_interface_name = get_device_interface_name(device);
|
619
|
+
|
620
|
+
if (device_id != NULL) {
|
621
|
+
if(strcmp(device_id, CFStringGetCStringPtr(found_device_id, CFStringGetSystemEncoding())) == 0) {
|
622
|
+
found_device = true;
|
623
|
+
} else {
|
624
|
+
printf("Skipping %s.\n", CFStringGetCStringPtr(device_full_name, CFStringGetSystemEncoding()));
|
625
|
+
return;
|
626
|
+
}
|
627
|
+
} else {
|
628
|
+
device_id = MYCFStringCopyUTF8String(found_device_id);
|
629
|
+
found_device = true;
|
630
|
+
}
|
631
|
+
|
632
|
+
printf("[....] Using %s (%s).\n", CFStringGetCStringPtr(device_full_name, CFStringGetSystemEncoding()), CFStringGetCStringPtr(found_device_id, CFStringGetSystemEncoding()));
|
633
|
+
|
634
|
+
if (command_only) {
|
635
|
+
if (strcmp("exists", command) == 0) {
|
636
|
+
exit(app_exists(device));
|
637
|
+
}
|
638
|
+
else if (strcmp("uninstall", command) == 0) {
|
639
|
+
uninstall_only(device);
|
640
|
+
exit(0);
|
641
|
+
}
|
642
|
+
else if (strcmp("reinstall", command) == 0) {
|
643
|
+
uninstall_only(device);
|
644
|
+
}
|
645
|
+
else {
|
646
|
+
exit(0);
|
647
|
+
}
|
648
|
+
|
649
|
+
}
|
650
|
+
|
651
|
+
CFRetain(device); // don't know if this is necessary?
|
652
|
+
CFStringRef path = CFStringCreateWithCString(NULL, app_path, kCFStringEncodingASCII);
|
653
|
+
CFURLRef relative_url = CFURLCreateWithFileSystemPath(NULL, path, kCFURLPOSIXPathStyle, false);
|
654
|
+
CFURLRef url = CFURLCopyAbsoluteURL(relative_url);
|
655
|
+
CFRelease(relative_url);
|
656
|
+
|
657
|
+
//never used, but it has to be there because install fails otherwise....
|
658
|
+
if (uninstall) {
|
659
|
+
CFRetain(device); // don't know if this is necessary?
|
660
|
+
|
661
|
+
printf("------ Uninstall phase ------\n");
|
662
|
+
|
663
|
+
//Do we already have the bundle_id passed in via the command line? if so, use it.
|
664
|
+
CFStringRef cf_uninstall_bundle_id = NULL;
|
665
|
+
if (bundle_id != NULL)
|
666
|
+
{
|
667
|
+
cf_uninstall_bundle_id = CFStringCreateWithCString(NULL, bundle_id, kCFStringEncodingUTF8);
|
668
|
+
} else {
|
669
|
+
printf("Error: you need to pass in the bundle id, (i.e. --bundle_id com.my.app)\n");
|
670
|
+
}
|
671
|
+
|
672
|
+
if (cf_uninstall_bundle_id == NULL) {
|
673
|
+
printf("Error: Unable to get bundle id from user command or package.\nUninstall failed.\n");
|
674
|
+
} else {
|
675
|
+
AMDeviceConnect(device);
|
676
|
+
assert(AMDeviceIsPaired(device));
|
677
|
+
AMDeviceValidatePairing(device);
|
678
|
+
AMDeviceStartSession(device);
|
679
|
+
|
680
|
+
int code = AMDeviceSecureUninstallApplication(0, device, cf_uninstall_bundle_id, 0, NULL, 0);
|
681
|
+
if (code == 0) {
|
682
|
+
printf("[ OK ] Uninstalled package with bundle id\n");
|
683
|
+
} else {
|
684
|
+
printf("[ ERROR ] Could not uninstall package with bundle id\n");
|
685
|
+
}
|
686
|
+
AMDeviceStopSession(device);
|
687
|
+
AMDeviceDisconnect(device);
|
688
|
+
}
|
689
|
+
}
|
690
|
+
|
691
|
+
if(install) {
|
692
|
+
printf("------ Install phase ------\n");
|
693
|
+
printf("[ 0%%] Found %s connected through %s, beginning install\n", CFStringGetCStringPtr(device_full_name, CFStringGetSystemEncoding()), CFStringGetCStringPtr(device_interface_name, CFStringGetSystemEncoding()));
|
694
|
+
|
695
|
+
AMDeviceConnect(device);
|
696
|
+
assert(AMDeviceIsPaired(device));
|
697
|
+
assert(AMDeviceValidatePairing(device) == 0);
|
698
|
+
assert(AMDeviceStartSession(device) == 0);
|
699
|
+
|
700
|
+
|
701
|
+
// NOTE: the secure version doesn't seem to require us to start the AFC service
|
702
|
+
service_conn_t afcFd;
|
703
|
+
assert(AMDeviceSecureStartService(device, CFSTR("com.apple.afc"), NULL, &afcFd) == 0);
|
704
|
+
assert(AMDeviceStopSession(device) == 0);
|
705
|
+
assert(AMDeviceDisconnect(device) == 0);
|
706
|
+
|
707
|
+
CFStringRef keys[] = { CFSTR("PackageType") };
|
708
|
+
CFStringRef values[] = { CFSTR("Developer") };
|
709
|
+
CFDictionaryRef options = CFDictionaryCreate(NULL, (const void **)&keys, (const void **)&values, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
|
710
|
+
|
711
|
+
//assert(AMDeviceTransferApplication(afcFd, path, NULL, transfer_callback, NULL) == 0);
|
712
|
+
assert(AMDeviceSecureTransferPath(0, device, url, options, transfer_callback, 0)==0);
|
713
|
+
|
714
|
+
close(afcFd);
|
715
|
+
|
716
|
+
|
717
|
+
|
718
|
+
AMDeviceConnect(device);
|
719
|
+
assert(AMDeviceIsPaired(device));
|
720
|
+
assert(AMDeviceValidatePairing(device) == 0);
|
721
|
+
assert(AMDeviceStartSession(device) == 0);
|
722
|
+
|
723
|
+
// // NOTE: the secure version doesn't seem to require us to start the installation_proxy service
|
724
|
+
// // Although I can't find it right now, I in some code that the first param of AMDeviceSecureInstallApplication was a "dontStartInstallProxy"
|
725
|
+
// // implying this is done for us by iOS already
|
726
|
+
|
727
|
+
//service_conn_t installFd;
|
728
|
+
//assert(AMDeviceSecureStartService(device, CFSTR("com.apple.mobile.installation_proxy"), NULL, &installFd) == 0);
|
729
|
+
|
730
|
+
//mach_error_t result = AMDeviceInstallApplication(installFd, path, options, install_callback, NULL);
|
731
|
+
mach_error_t result = AMDeviceSecureInstallApplication(0, device, url, options, install_callback, 0);
|
732
|
+
if (result != 0)
|
733
|
+
{
|
734
|
+
char* error = "Unknown error.";
|
735
|
+
if (result == 0xe8008015)
|
736
|
+
error = "Your application failed code-signing checks. Check your certificates, provisioning profiles, and bundle ids.";
|
737
|
+
printf("AMDeviceInstallApplication failed: 0x%X: %s\n", result, error);
|
738
|
+
exit(exitcode_error);
|
739
|
+
}
|
740
|
+
|
741
|
+
// close(installFd);
|
742
|
+
|
743
|
+
assert(AMDeviceStopSession(device) == 0);
|
744
|
+
assert(AMDeviceDisconnect(device) == 0);
|
745
|
+
|
746
|
+
CFRelease(path);
|
747
|
+
CFRelease(options);
|
748
|
+
|
749
|
+
printf("[100%%] Installed package %s\n", app_path);
|
750
|
+
}
|
751
|
+
|
752
|
+
exit(0);
|
753
|
+
|
754
|
+
}
|
755
|
+
|
756
|
+
void device_callback(struct am_device_notification_callback_info *info, void *arg) {
|
757
|
+
switch (info->msg) {
|
758
|
+
case ADNCI_MSG_CONNECTED:
|
759
|
+
if(device_id != NULL || !debug || AMDeviceGetInterfaceType(info->dev) != 2) {
|
760
|
+
handle_device(info->dev);
|
761
|
+
} else if(best_device_match == NULL) {
|
762
|
+
best_device_match = info->dev;
|
763
|
+
CFRetain(best_device_match);
|
764
|
+
}
|
765
|
+
default:
|
766
|
+
break;
|
767
|
+
}
|
768
|
+
}
|
769
|
+
|
770
|
+
void timeout_callback(CFRunLoopTimerRef timer, void *info) {
|
771
|
+
if ((!found_device) && (!detect_only)) {
|
772
|
+
if(best_device_match != NULL) {
|
773
|
+
handle_device(best_device_match);
|
774
|
+
|
775
|
+
CFRelease(best_device_match);
|
776
|
+
best_device_match = NULL;
|
777
|
+
}
|
778
|
+
|
779
|
+
if(!found_device) {
|
780
|
+
printf("[....] Timed out waiting for device.\n");
|
781
|
+
exit(exitcode_error);
|
782
|
+
}
|
783
|
+
}
|
784
|
+
else
|
785
|
+
{
|
786
|
+
if (!debug) {
|
787
|
+
printf("[....] No more devices found.\n");
|
788
|
+
}
|
789
|
+
|
790
|
+
if (detect_only && !found_device) {
|
791
|
+
exit(exitcode_error);
|
792
|
+
return;
|
793
|
+
} else {
|
794
|
+
int mypid = getpid();
|
795
|
+
if ((parent != 0) && (parent == mypid) && (child != 0))
|
796
|
+
{
|
797
|
+
if (verbose)
|
798
|
+
{
|
799
|
+
printf("Timeout. Killing child (%d) tree\n", child);
|
800
|
+
}
|
801
|
+
kill_ptree(child, SIGHUP);
|
802
|
+
}
|
803
|
+
}
|
804
|
+
exit(0);
|
805
|
+
}
|
806
|
+
}
|
807
|
+
|
808
|
+
void usage(const char* app) {
|
809
|
+
printf(
|
810
|
+
"Usage: %s [OPTION]...\n"
|
811
|
+
" -u, --uninstall uninstall an app (requires -b)\n"
|
812
|
+
" -r, --reinstall uninstall an app and then install an app (requires -b for uninstall and -p for install) \n"
|
813
|
+
" -i, --isinstalled check if the app is installed or not (requires -b)\n",
|
814
|
+
" -d, --device <device_id> the id of the device to connect to (use all devices if not set?)\n"
|
815
|
+
" -p, --path <bundle.app> the path to the app bundle (.ipa) which should be installed\n"
|
816
|
+
" -b, --bundle <bundle id> specify bundle id \n"
|
817
|
+
" use only -p or -p and -d to install an app\n"
|
818
|
+
);
|
819
|
+
}
|
820
|
+
|
821
|
+
void show_version() {
|
822
|
+
printf("%s\n", APP_VERSION);
|
823
|
+
}
|
824
|
+
|
825
|
+
|
826
|
+
|
827
|
+
int main(int argc, char *argv[]) {
|
828
|
+
static struct option longopts[] = {
|
829
|
+
{ "device", required_argument, NULL, 'd' },
|
830
|
+
{ "path", required_argument, NULL, 'p' },
|
831
|
+
{ "reinstall", no_argument, NULL, 'r' },
|
832
|
+
{ "uninstall", no_argument, NULL, 'u' },
|
833
|
+
{ "bundle", required_argument, NULL, 'b'},
|
834
|
+
{ "isinstalled", no_argument, NULL, 'i'},
|
835
|
+
{ NULL, 0, NULL, 0 },
|
836
|
+
};
|
837
|
+
char ch;
|
838
|
+
|
839
|
+
while ((ch = getopt_long(argc, argv, "ruid:p:b:", longopts, NULL)) != -1)
|
840
|
+
{
|
841
|
+
switch (ch) {
|
842
|
+
case 'd':
|
843
|
+
device_id = optarg;
|
844
|
+
break;
|
845
|
+
case 'p':
|
846
|
+
app_path = optarg;
|
847
|
+
break;
|
848
|
+
case 'r':
|
849
|
+
command_only = true;
|
850
|
+
command = "reinstall";
|
851
|
+
break;
|
852
|
+
case 'u':
|
853
|
+
command_only = true;
|
854
|
+
command = "uninstall";
|
855
|
+
install = false;
|
856
|
+
break;
|
857
|
+
case 'b':
|
858
|
+
bundle_id = optarg;
|
859
|
+
break;
|
860
|
+
case 'i':
|
861
|
+
install = false;
|
862
|
+
command_only = true;
|
863
|
+
command = "exists";
|
864
|
+
break;
|
865
|
+
default:
|
866
|
+
usage(argv[0]);
|
867
|
+
return exitcode_error;
|
868
|
+
}
|
869
|
+
}
|
870
|
+
|
871
|
+
if (!app_path && !bundle_id && !command_only) {
|
872
|
+
usage(argv[0]);
|
873
|
+
exit(exitcode_error);
|
874
|
+
}
|
875
|
+
|
876
|
+
if (unbuffered) {
|
877
|
+
setbuf(stdout, NULL);
|
878
|
+
setbuf(stderr, NULL);
|
879
|
+
}
|
880
|
+
|
881
|
+
if (detect_only && timeout == 0) {
|
882
|
+
timeout = 5;
|
883
|
+
}
|
884
|
+
|
885
|
+
if (app_path) {
|
886
|
+
assert(access(app_path, F_OK) == 0);
|
887
|
+
}
|
888
|
+
|
889
|
+
AMDSetLogLevel(5); // otherwise syslog gets flooded with crap
|
890
|
+
if (timeout > 0)
|
891
|
+
{
|
892
|
+
CFRunLoopTimerRef timer = CFRunLoopTimerCreate(NULL, CFAbsoluteTimeGetCurrent() + timeout, 0, 0, 0, timeout_callback, NULL);
|
893
|
+
CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopCommonModes);
|
894
|
+
printf("[....] Waiting up to %d seconds for iOS device to be connected\n", timeout);
|
895
|
+
}
|
896
|
+
else
|
897
|
+
{
|
898
|
+
printf("[....] Waiting for iOS device to be connected\n");
|
899
|
+
}
|
900
|
+
|
901
|
+
struct am_device_notification *notify;
|
902
|
+
AMDeviceNotificationSubscribe(&device_callback, 0, 0, NULL, ¬ify);
|
903
|
+
CFRunLoopRun();
|
904
|
+
}
|