ios-deploy 1.3.2

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.
@@ -0,0 +1,75 @@
1
+ ios-deploy
2
+ ==========
3
+ Install and debug iOS apps without using Xcode. Designed to work on un-jailbroken devices.
4
+
5
+ ## Requirements
6
+
7
+ * Mac OS X. Tested on 10.10 Yosemite and iOS 8.1
8
+ * You need to have a valid iOS development certificate installed.
9
+ * Xcode 6.1 should be installed
10
+
11
+ ## Usage
12
+
13
+ Usage: ios-deploy [OPTION]...
14
+ -d, --debug launch the app in GDB after installation
15
+ -i, --id <device_id> the id of the device to connect to
16
+ -c, --detect only detect if the device is connected
17
+ -b, --bundle <bundle.app> the path to the app bundle to be installed
18
+ -a, --args <args> command line arguments to pass to the app when launching it
19
+ -t, --timeout <timeout> number of seconds to wait for a device to be connected
20
+ -u, --unbuffered don't buffer stdout
21
+ -n, --nostart do not start the app when debugging
22
+ -I, --noninteractive start in non interactive mode (quit when app crashes or exits)
23
+ -L, --justlaunch just launch the app and exit lldb
24
+ -v, --verbose enable verbose output
25
+ -m, --noinstall directly start debugging without app install (-d not required)
26
+ -p, --port <number> port used for device, default: 12345
27
+ -r, --uninstall uninstall the app before install (do not use with -m; app cache and data are cleared)
28
+ -1, --bundle_id <bundle id> specify bundle id for list and upload
29
+ -l, --list list files
30
+ -o, --upload <file> upload file
31
+ -w, --download download app tree
32
+ -2, --to <target pathname> use together with up/download file/tree. specify target
33
+ -V, --version print the executable version
34
+
35
+ ## Examples
36
+
37
+ The commands below assume that you have an app called `my.app` with bundle id `bundle.id`. Substitute where necessary.
38
+
39
+ // deploy and debug your app to a connected device
40
+ ios-deploy --debug --bundle my.app
41
+
42
+ // deploy and launch your app to a connected device, but quit the debugger after
43
+ ios-deploy --justlaunch --debug --bundle my.app
44
+
45
+ // deploy and launch your app to a connected device, quit when app crashes or exits
46
+ ios-deploy --noninteractive --debug --bundle my.app
47
+
48
+ // Upload a file to your app's Documents folder
49
+ ios-deploy --bundle_id 'bundle.id' --upload test.txt --to Documents/test.txt
50
+
51
+ // Download your app's Documents, Library and tmp folders
52
+ ios-deploy --bundle_id 'bundle.id' --download --to MyDestinationFolder
53
+
54
+ // List the contents of your app's Documents, Library and tmp folders
55
+ ios-deploy --bundle_id 'bundle.id' --list
56
+
57
+ // deploy and debug your app to a connected device, uninstall the app first
58
+ ios-deploy --uninstall --debug --bundle my.app
59
+
60
+ ## Demo
61
+
62
+ * The included demo.app represents the minimum required to get code running on iOS.
63
+ * `make install` will install demo.app to the device.
64
+ * `make debug` will install demo.app and launch a GDB session.
65
+
66
+ ## Notes
67
+
68
+ * With some modifications, it may be possible to use this without Xcode installed; however, you would need a copy of the relevant DeveloperDiskImage.dmg (included with Xcode). lldb would also run slower as symbols would be downloaded from the device on-the-fly.
69
+
70
+
71
+ ## Listing Device Ids
72
+
73
+ Device Ids are the UDIDs of the iOS devices. From the command line, you can list device ids [this way](http://javierhz.blogspot.com/2012/06/how-to-get-udid-of-iphone-using-shell.html):
74
+
75
+ system_profiler SPUSBDataType | sed -n -e '/iPod/,/Serial/p' | sed -n -e '/iPad/,/Serial/p' -e '/iPhone/,/Serial/p' | grep "Serial Number:" | awk -F ": " '{print $2}'
@@ -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>
@@ -0,0 +1,9 @@
1
+ #include <stdio.h>
2
+
3
+ int main(int argc, const char* argv[]) {
4
+ int i;
5
+ for (i = 0; i < argc; i++) {
6
+ printf("argv[%d] = %s\n", i, argv[i]);
7
+ }
8
+ return 0;
9
+ }
@@ -0,0 +1,1801 @@
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.3.2"
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 sys\n\
65
+ import shlex\n\
66
+ \n\
67
+ def connect_command(debugger, command, result, internal_dict):\n\
68
+ # These two are passed in by the script which loads us\n\
69
+ connect_url = internal_dict['fruitstrap_connect_url']\n\
70
+ error = lldb.SBError()\n\
71
+ \n\
72
+ process = lldb.target.ConnectRemote(lldb.target.GetDebugger().GetListener(), connect_url, None, error)\n\
73
+ \n\
74
+ # Wait for connection to succeed\n\
75
+ listener = lldb.target.GetDebugger().GetListener()\n\
76
+ listener.StartListeningForEvents(process.GetBroadcaster(), lldb.SBProcess.eBroadcastBitStateChanged)\n\
77
+ events = []\n\
78
+ state = (process.GetState() or lldb.eStateInvalid)\n\
79
+ while state != lldb.eStateConnected:\n\
80
+ event = lldb.SBEvent()\n\
81
+ if listener.WaitForEvent(1, event):\n\
82
+ state = process.GetStateFromEvent(event)\n\
83
+ events.append(event)\n\
84
+ else:\n\
85
+ state = lldb.eStateInvalid\n\
86
+ \n\
87
+ # Add events back to queue, otherwise lldb freezes\n\
88
+ for event in events:\n\
89
+ listener.AddEvent(event)\n\
90
+ \n\
91
+ def run_command(debugger, command, result, internal_dict):\n\
92
+ device_app = internal_dict['fruitstrap_device_app']\n\
93
+ error = lldb.SBError()\n\
94
+ lldb.target.modules[0].SetPlatformFileSpec(lldb.SBFileSpec(device_app))\n\
95
+ lldb.target.Launch(lldb.SBLaunchInfo(shlex.split('{args}')), error)\n\
96
+ lockedstr = ': Locked'\n\
97
+ if lockedstr in str(error):\n\
98
+ print('\\nDevice Locked\\n')\n\
99
+ sys.exit(254)\n\
100
+ else:\n\
101
+ print(str(error))\n\
102
+ \n\
103
+ def safequit_command(debugger, command, result, internal_dict):\n\
104
+ process = lldb.target.process\n\
105
+ listener = debugger.GetListener()\n\
106
+ listener.StartListeningForEvents(process.GetBroadcaster(), lldb.SBProcess.eBroadcastBitStateChanged | lldb.SBProcess.eBroadcastBitSTDOUT | lldb.SBProcess.eBroadcastBitSTDERR)\n\
107
+ event = lldb.SBEvent()\n\
108
+ while True:\n\
109
+ if listener.WaitForEvent(1, event):\n\
110
+ state = process.GetStateFromEvent(event)\n\
111
+ else:\n\
112
+ state = lldb.eStateInvalid\n\
113
+ process.Detach()\n\
114
+ sys.exit(0)\n\
115
+ \n\
116
+ def autoexit_command(debugger, command, result, internal_dict):\n\
117
+ process = lldb.target.process\n\
118
+ listener = debugger.GetListener()\n\
119
+ listener.StartListeningForEvents(process.GetBroadcaster(), lldb.SBProcess.eBroadcastBitStateChanged | lldb.SBProcess.eBroadcastBitSTDOUT | lldb.SBProcess.eBroadcastBitSTDERR)\n\
120
+ event = lldb.SBEvent()\n\
121
+ while True:\n\
122
+ if listener.WaitForEvent(1, event):\n\
123
+ state = process.GetStateFromEvent(event)\n\
124
+ else:\n\
125
+ state = lldb.eStateInvalid\n\
126
+ \n\
127
+ stdout = process.GetSTDOUT(1024)\n\
128
+ while stdout:\n\
129
+ sys.stdout.write(stdout)\n\
130
+ stdout = process.GetSTDOUT(1024)\n\
131
+ \n\
132
+ stderr = process.GetSTDERR(1024)\n\
133
+ while stderr:\n\
134
+ sys.stdout.write(stderr)\n\
135
+ stderr = process.GetSTDERR(1024)\n\
136
+ \n\
137
+ if lldb.SBProcess.EventIsProcessEvent(event):\n\
138
+ if state == lldb.eStateExited:\n\
139
+ sys.exit(process.GetExitStatus())\n\
140
+ if state == lldb.eStateStopped:\n\
141
+ debugger.HandleCommand('frame select')\n\
142
+ debugger.HandleCommand('bt')\n\
143
+ sys.exit({exitcode_app_crash})\n\
144
+ ")
145
+
146
+ typedef struct am_device * AMDeviceRef;
147
+ mach_error_t AMDeviceSecureStartService(struct am_device *device, CFStringRef service_name, unsigned int *unknown, service_conn_t *handle);
148
+ int AMDeviceSecureTransferPath(int zero, AMDeviceRef device, CFURLRef url, CFDictionaryRef options, void *callback, int cbarg);
149
+ int AMDeviceSecureInstallApplication(int zero, AMDeviceRef device, CFURLRef url, CFDictionaryRef options, void *callback, int cbarg);
150
+ int AMDeviceMountImage(AMDeviceRef device, CFStringRef image, CFDictionaryRef options, void *callback, int cbarg);
151
+ mach_error_t AMDeviceLookupApplications(AMDeviceRef device, CFDictionaryRef options, CFDictionaryRef *result);
152
+ int AMDeviceGetInterfaceType(struct am_device *device);
153
+
154
+ bool found_device = false, debug = false, verbose = false, unbuffered = false, nostart = false, detect_only = false, install = true, uninstall = false;
155
+ bool command_only = false;
156
+ char *command = NULL;
157
+ char *target_filename = NULL;
158
+ char *upload_pathname = NULL;
159
+ char *bundle_id = NULL;
160
+ bool interactive = true;
161
+ bool justlaunch = false;
162
+ char *app_path = NULL;
163
+ char *device_id = NULL;
164
+ char *args = NULL;
165
+ char *list_root = NULL;
166
+ int timeout = 0;
167
+ int port = 12345;
168
+ CFStringRef last_path = NULL;
169
+ service_conn_t gdbfd;
170
+ pid_t parent = 0;
171
+ // PID of child process running lldb
172
+ pid_t child = 0;
173
+ // Signal sent from child to parent process when LLDB finishes.
174
+ const int SIGLLDB = SIGUSR1;
175
+ AMDeviceRef best_device_match = NULL;
176
+
177
+ // Error codes we report on different failures, so scripts can distinguish between user app exit
178
+ // codes and our exit codes. For non app errors we use codes in reserved 128-255 range.
179
+ const int exitcode_error = 253;
180
+ const int exitcode_app_crash = 254;
181
+
182
+ Boolean path_exists(CFTypeRef path) {
183
+ if (CFGetTypeID(path) == CFStringGetTypeID()) {
184
+ CFURLRef url = CFURLCreateWithFileSystemPath(NULL, path, kCFURLPOSIXPathStyle, true);
185
+ Boolean result = CFURLResourceIsReachable(url, NULL);
186
+ CFRelease(url);
187
+ return result;
188
+ } else if (CFGetTypeID(path) == CFURLGetTypeID()) {
189
+ return CFURLResourceIsReachable(path, NULL);
190
+ } else {
191
+ return false;
192
+ }
193
+ }
194
+
195
+ CFStringRef find_path(CFStringRef rootPath, CFStringRef namePattern, CFStringRef expression) {
196
+ FILE *fpipe = NULL;
197
+ CFStringRef quotedRootPath = rootPath;
198
+ CFStringRef cf_command;
199
+ CFRange slashLocation;
200
+
201
+ if (CFStringGetCharacterAtIndex(rootPath, 0) != '`') {
202
+ quotedRootPath = CFStringCreateWithFormat(NULL, NULL, CFSTR("'%@'"), rootPath);
203
+ }
204
+
205
+ slashLocation = CFStringFind(namePattern, CFSTR("/"), 0);
206
+ if (slashLocation.location == kCFNotFound) {
207
+ cf_command = CFStringCreateWithFormat(NULL, NULL, CFSTR("find %@ -name '%@' %@ 2>/dev/null | sort | tail -n 1"), quotedRootPath, namePattern, expression);
208
+ } else {
209
+ cf_command = CFStringCreateWithFormat(NULL, NULL, CFSTR("find %@ -path '%@' %@ 2>/dev/null | sort | tail -n 1"), quotedRootPath, namePattern, expression);
210
+ }
211
+
212
+ if (quotedRootPath != rootPath) {
213
+ CFRelease(quotedRootPath);
214
+ }
215
+
216
+ char command[1024] = { '\0' };
217
+ CFStringGetCString(cf_command, command, sizeof(command), kCFStringEncodingUTF8);
218
+ CFRelease(cf_command);
219
+
220
+ if (!(fpipe = (FILE *)popen(command, "r")))
221
+ {
222
+ perror("Error encountered while opening pipe");
223
+ exit(exitcode_error);
224
+ }
225
+
226
+ char buffer[256] = { '\0' };
227
+
228
+ fgets(buffer, sizeof(buffer), fpipe);
229
+ pclose(fpipe);
230
+
231
+ strtok(buffer, "\n");
232
+ return CFStringCreateWithCString(NULL, buffer, kCFStringEncodingUTF8);
233
+ }
234
+
235
+ CFStringRef copy_long_shot_disk_image_path() {
236
+ return find_path(CFSTR("`xcode-select --print-path`"), CFSTR("DeveloperDiskImage.dmg"), CFSTR(""));
237
+ }
238
+
239
+ CFStringRef copy_xcode_dev_path() {
240
+ static char xcode_dev_path[256] = { '\0' };
241
+ if (strlen(xcode_dev_path) == 0) {
242
+ FILE *fpipe = NULL;
243
+ char *command = "xcode-select -print-path";
244
+
245
+ if (!(fpipe = (FILE *)popen(command, "r")))
246
+ {
247
+ perror("Error encountered while opening pipe");
248
+ exit(exitcode_error);
249
+ }
250
+
251
+ char buffer[256] = { '\0' };
252
+
253
+ fgets(buffer, sizeof(buffer), fpipe);
254
+ pclose(fpipe);
255
+
256
+ strtok(buffer, "\n");
257
+ strcpy(xcode_dev_path, buffer);
258
+ }
259
+ return CFStringCreateWithCString(NULL, xcode_dev_path, kCFStringEncodingUTF8);
260
+ }
261
+
262
+ const char *get_home() {
263
+ const char* home = getenv("HOME");
264
+ if (!home) {
265
+ struct passwd *pwd = getpwuid(getuid());
266
+ home = pwd->pw_dir;
267
+ }
268
+ return home;
269
+ }
270
+
271
+ CFStringRef copy_xcode_path_for(CFStringRef subPath, CFStringRef search) {
272
+ CFStringRef xcodeDevPath = copy_xcode_dev_path();
273
+ CFStringRef path;
274
+ bool found = false;
275
+ const char* home = get_home();
276
+ CFRange slashLocation;
277
+
278
+
279
+ // Try using xcode-select --print-path
280
+ if (!found) {
281
+ path = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@/%@/%@"), xcodeDevPath, subPath, search);
282
+ found = path_exists(path);
283
+ }
284
+ // Try find `xcode-select --print-path` with search as a name pattern
285
+ if (!found) {
286
+ slashLocation = CFStringFind(search, CFSTR("/"), 0);
287
+ if (slashLocation.location == kCFNotFound) {
288
+ path = find_path(CFStringCreateWithFormat(NULL, NULL, CFSTR("%@/%@"), xcodeDevPath, subPath), search, CFSTR("-maxdepth 1"));
289
+ } else {
290
+ path = find_path(CFStringCreateWithFormat(NULL, NULL, CFSTR("%@/%@"), xcodeDevPath, subPath), search, CFSTR(""));
291
+ }
292
+ found = CFStringGetLength(path) > 0 && path_exists(path);
293
+ }
294
+ // If not look in the default xcode location (xcode-select is sometimes wrong)
295
+ if (!found) {
296
+ path = CFStringCreateWithFormat(NULL, NULL, CFSTR("/Applications/Xcode.app/Contents/Developer/%@&%@"), subPath, search);
297
+ found = path_exists(path);
298
+ }
299
+ // If not look in the users home directory, Xcode can store device support stuff there
300
+ if (!found) {
301
+ path = CFStringCreateWithFormat(NULL, NULL, CFSTR("%s/Library/Developer/Xcode/%@/%@"), home, subPath, search);
302
+ found = path_exists(path);
303
+ }
304
+
305
+ CFRelease(xcodeDevPath);
306
+
307
+ if (found) {
308
+ return path;
309
+ } else {
310
+ CFRelease(path);
311
+ return NULL;
312
+ }
313
+ }
314
+
315
+ // Please ensure that device is connected or the name will be unknown
316
+ const CFStringRef get_device_hardware_name(const AMDeviceRef device) {
317
+ CFStringRef model = AMDeviceCopyValue(device, 0, CFSTR("HardwareModel"));
318
+ if (kCFCompareEqualTo == CFStringCompare(model,CFSTR("M68AP"), kCFCompareNonliteral))
319
+ return CFSTR("iPhone");
320
+ if (kCFCompareEqualTo == CFStringCompare(model,CFSTR("N45AP"), kCFCompareNonliteral))
321
+ return CFSTR("iPod touch");
322
+ if (kCFCompareEqualTo == CFStringCompare(model,CFSTR("N82AP"), kCFCompareNonliteral))
323
+ return CFSTR("iPhone 3G");
324
+ if (kCFCompareEqualTo == CFStringCompare(model,CFSTR("N72AP"), kCFCompareNonliteral))
325
+ return CFSTR("iPod touch 2G");
326
+ if (kCFCompareEqualTo == CFStringCompare(model,CFSTR("N88AP"), kCFCompareNonliteral))
327
+ return CFSTR("iPhone 3GS");
328
+ if (kCFCompareEqualTo == CFStringCompare(model,CFSTR("N18AP"), kCFCompareNonliteral))
329
+ return CFSTR("iPod touch 3G");
330
+ if (kCFCompareEqualTo == CFStringCompare(model,CFSTR("K48AP"), kCFCompareNonliteral))
331
+ return CFSTR("iPad");
332
+ if (kCFCompareEqualTo == CFStringCompare(model,CFSTR("N90AP"), kCFCompareNonliteral))
333
+ return CFSTR("iPhone 4 (GSM)");
334
+ if (kCFCompareEqualTo == CFStringCompare(model,CFSTR("N81AP"), kCFCompareNonliteral))
335
+ return CFSTR("iPod touch 4G");
336
+ if (kCFCompareEqualTo == CFStringCompare(model,CFSTR("K66AP"), kCFCompareNonliteral))
337
+ return CFSTR("Apple TV 2G");
338
+ if (kCFCompareEqualTo == CFStringCompare(model,CFSTR("N92AP"), kCFCompareNonliteral))
339
+ return CFSTR("iPhone 4 (CDMA)");
340
+ if (kCFCompareEqualTo == CFStringCompare(model,CFSTR("N90BAP"), kCFCompareNonliteral))
341
+ return CFSTR("iPhone 4 (GSM, revision A)");
342
+ if (kCFCompareEqualTo == CFStringCompare(model,CFSTR("K93AP"), kCFCompareNonliteral))
343
+ return CFSTR("iPad 2");
344
+ if (kCFCompareEqualTo == CFStringCompare(model,CFSTR("K94AP"), kCFCompareNonliteral))
345
+ return CFSTR("iPad 2 (GSM)");
346
+ if (kCFCompareEqualTo == CFStringCompare(model,CFSTR("K95AP"), kCFCompareNonliteral))
347
+ return CFSTR("iPad 2 (CDMA)");
348
+ if (kCFCompareEqualTo == CFStringCompare(model,CFSTR("K93AAP"), kCFCompareNonliteral))
349
+ return CFSTR("iPad 2 (Wi-Fi, revision A)");
350
+ if (kCFCompareEqualTo == CFStringCompare(model,CFSTR("P105AP"), kCFCompareNonliteral))
351
+ return CFSTR("iPad mini");
352
+ if (kCFCompareEqualTo == CFStringCompare(model,CFSTR("P106AP"), kCFCompareNonliteral))
353
+ return CFSTR("iPad mini (GSM)");
354
+ if (kCFCompareEqualTo == CFStringCompare(model,CFSTR("P107AP"), kCFCompareNonliteral))
355
+ return CFSTR("iPad mini (CDMA)");
356
+ if (kCFCompareEqualTo == CFStringCompare(model,CFSTR("N94AP"), kCFCompareNonliteral))
357
+ return CFSTR("iPhone 4S");
358
+ if (kCFCompareEqualTo == CFStringCompare(model,CFSTR("N41AP"), kCFCompareNonliteral))
359
+ return CFSTR("iPhone 5 (GSM)");
360
+ if (kCFCompareEqualTo == CFStringCompare(model,CFSTR("N42AP"), kCFCompareNonliteral))
361
+ return CFSTR("iPhone 5 (Global/CDMA)");
362
+ if (kCFCompareEqualTo == CFStringCompare(model,CFSTR("N48AP"), kCFCompareNonliteral))
363
+ return CFSTR("iPhone 5c (GSM)");
364
+ if (kCFCompareEqualTo == CFStringCompare(model,CFSTR("N49AP"), kCFCompareNonliteral))
365
+ return CFSTR("iPhone 5c (Global/CDMA)");
366
+ if (kCFCompareEqualTo == CFStringCompare(model,CFSTR("N51AP"), kCFCompareNonliteral))
367
+ return CFSTR("iPhone 5s (GSM)");
368
+ if (kCFCompareEqualTo == CFStringCompare(model,CFSTR("N53AP"), kCFCompareNonliteral))
369
+ return CFSTR("iPhone 5s (Global/CDMA)");
370
+ if (kCFCompareEqualTo == CFStringCompare(model,CFSTR("N61AP"), kCFCompareNonliteral))
371
+ return CFSTR("iPhone 6 (GSM)");
372
+ if (kCFCompareEqualTo == CFStringCompare(model,CFSTR("J1AP"), kCFCompareNonliteral))
373
+ return CFSTR("iPad 3");
374
+ if (kCFCompareEqualTo == CFStringCompare(model,CFSTR("J2AP"), kCFCompareNonliteral))
375
+ return CFSTR("iPad 3 (GSM)");
376
+ if (kCFCompareEqualTo == CFStringCompare(model,CFSTR("J2AAP"), kCFCompareNonliteral))
377
+ return CFSTR("iPad 3 (CDMA)");
378
+ if (kCFCompareEqualTo == CFStringCompare(model,CFSTR("P101AP"), kCFCompareNonliteral))
379
+ return CFSTR("iPad 4");
380
+ if (kCFCompareEqualTo == CFStringCompare(model,CFSTR("P102AP"), kCFCompareNonliteral))
381
+ return CFSTR("iPad 4 (GSM)");
382
+ if (kCFCompareEqualTo == CFStringCompare(model,CFSTR("P103AP"), kCFCompareNonliteral))
383
+ return CFSTR("iPad 4 (CDMA)");
384
+ if (kCFCompareEqualTo == CFStringCompare(model,CFSTR("N78AP"), kCFCompareNonliteral))
385
+ return CFSTR("iPod touch 5G");
386
+ if (kCFCompareEqualTo == CFStringCompare(model,CFSTR("A1509"), kCFCompareNonliteral))
387
+ return CFSTR("iPod touch 5G");
388
+ if (kCFCompareEqualTo == CFStringCompare(model,CFSTR("J33AP"), kCFCompareNonliteral))
389
+ return CFSTR("Apple TV 3G");
390
+ if (kCFCompareEqualTo == CFStringCompare(model,CFSTR("J33IAP"), kCFCompareNonliteral))
391
+ return CFSTR("Apple TV 3.1G");
392
+
393
+ return model;
394
+ //return CFStringCreateWithFormat(NULL, NULL, CFSTR("%s"), hwmodel);
395
+ //return CFSTR("Unknown Device");
396
+ }
397
+
398
+ char * MYCFStringCopyUTF8String(CFStringRef aString) {
399
+ if (aString == NULL) {
400
+ return NULL;
401
+ }
402
+
403
+ CFIndex length = CFStringGetLength(aString);
404
+ CFIndex maxSize =
405
+ CFStringGetMaximumSizeForEncoding(length,
406
+ kCFStringEncodingUTF8);
407
+ char *buffer = (char *)malloc(maxSize);
408
+ if (CFStringGetCString(aString, buffer, maxSize,
409
+ kCFStringEncodingUTF8)) {
410
+ return buffer;
411
+ }
412
+ return NULL;
413
+ }
414
+
415
+ CFStringRef get_device_full_name(const AMDeviceRef device) {
416
+ CFStringRef full_name = NULL,
417
+ device_udid = AMDeviceCopyDeviceIdentifier(device),
418
+ device_name = NULL,
419
+ model_name = NULL;
420
+
421
+ AMDeviceConnect(device);
422
+
423
+ device_name = AMDeviceCopyValue(device, 0, CFSTR("DeviceName")),
424
+ model_name = get_device_hardware_name(device);
425
+
426
+ if (verbose)
427
+ {
428
+ char *devName = MYCFStringCopyUTF8String(device_name);
429
+ printf("Device Name:[%s]\n",devName);
430
+ CFShow(device_name);
431
+ printf("\n");
432
+ free(devName);
433
+
434
+ char *mdlName = MYCFStringCopyUTF8String(model_name);
435
+ printf("Model Name:[%s]\n",mdlName);
436
+ printf("MM: [%s]\n",CFStringGetCStringPtr(model_name, kCFStringEncodingUTF8));
437
+ CFShow(model_name);
438
+ printf("\n");
439
+ free(mdlName);
440
+ }
441
+
442
+ if(device_name != NULL && model_name != NULL)
443
+ {
444
+ full_name = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@ '%@' (%@)"), model_name, device_name, device_udid);
445
+ }
446
+ else
447
+ {
448
+ full_name = CFStringCreateWithFormat(NULL, NULL, CFSTR("(%@ss)"), device_udid);
449
+ }
450
+
451
+ AMDeviceDisconnect(device);
452
+
453
+ if(device_udid != NULL)
454
+ CFRelease(device_udid);
455
+ if(device_name != NULL)
456
+ CFRelease(device_name);
457
+ if(model_name != NULL)
458
+ CFRelease(model_name);
459
+
460
+ return full_name;
461
+ }
462
+
463
+ CFStringRef get_device_interface_name(const AMDeviceRef device) {
464
+ // AMDeviceGetInterfaceType(device) 0=Unknown, 1 = Direct/USB, 2 = Indirect/WIFI
465
+ switch(AMDeviceGetInterfaceType(device)) {
466
+ case 1:
467
+ return CFSTR("USB");
468
+ case 2:
469
+ return CFSTR("WIFI");
470
+ default:
471
+ return CFSTR("Unknown Connection");
472
+ }
473
+ }
474
+
475
+ CFMutableArrayRef get_device_product_version_parts(AMDeviceRef device) {
476
+ CFStringRef version = AMDeviceCopyValue(device, 0, CFSTR("ProductVersion"));
477
+ CFArrayRef parts = CFStringCreateArrayBySeparatingStrings(NULL, version, CFSTR("."));
478
+ CFMutableArrayRef result = CFArrayCreateMutableCopy(NULL, CFArrayGetCount(parts), parts);
479
+ CFRelease(version);
480
+ CFRelease(parts);
481
+ return result;
482
+ }
483
+
484
+ CFStringRef copy_device_support_path(AMDeviceRef device) {
485
+ CFStringRef version = NULL;
486
+ CFStringRef build = AMDeviceCopyValue(device, 0, CFSTR("BuildVersion"));
487
+ CFStringRef path = NULL;
488
+ CFMutableArrayRef version_parts = get_device_product_version_parts(device);
489
+
490
+ while (CFArrayGetCount(version_parts) > 0) {
491
+ version = CFStringCreateByCombiningStrings(NULL, version_parts, CFSTR("."));
492
+ if (path == NULL) {
493
+ path = copy_xcode_path_for(CFSTR("iOS DeviceSupport"), CFStringCreateWithFormat(NULL, NULL, CFSTR("%@ (%@)"), version, build));
494
+ }
495
+ if (path == NULL) {
496
+ path = copy_xcode_path_for(CFSTR("Platforms/iPhoneOS.platform/DeviceSupport"), CFStringCreateWithFormat(NULL, NULL, CFSTR("%@ (%@)"), version, build));
497
+ }
498
+ if (path == NULL) {
499
+ path = copy_xcode_path_for(CFSTR("Platforms/iPhoneOS.platform/DeviceSupport"), CFStringCreateWithFormat(NULL, NULL, CFSTR("%@ (*)"), version));
500
+ }
501
+ if (path == NULL) {
502
+ path = copy_xcode_path_for(CFSTR("Platforms/iPhoneOS.platform/DeviceSupport"), version);
503
+ }
504
+ if (path == NULL) {
505
+ path = copy_xcode_path_for(CFSTR("Platforms/iPhoneOS.platform/DeviceSupport/Latest"), CFSTR(""));
506
+ }
507
+ CFRelease(version);
508
+ if (path != NULL) {
509
+ break;
510
+ }
511
+ CFArrayRemoveValueAtIndex(version_parts, CFArrayGetCount(version_parts) - 1);
512
+ }
513
+
514
+ CFRelease(version_parts);
515
+ CFRelease(build);
516
+
517
+ if (path == NULL)
518
+ {
519
+ printf("[ !! ] Unable to locate DeviceSupport directory.\n[ !! ] This probably means you don't have Xcode installed, you will need to launch the app manually and logging output will not be shown!\n");
520
+ exit(exitcode_error);
521
+ }
522
+
523
+ return path;
524
+ }
525
+
526
+ CFStringRef copy_developer_disk_image_path(AMDeviceRef device) {
527
+ CFStringRef version = NULL;
528
+ CFStringRef build = AMDeviceCopyValue(device, 0, CFSTR("BuildVersion"));
529
+ CFStringRef path = NULL;
530
+ CFMutableArrayRef version_parts = get_device_product_version_parts(device);
531
+
532
+ while (CFArrayGetCount(version_parts) > 0) {
533
+ version = CFStringCreateByCombiningStrings(NULL, version_parts, CFSTR("."));
534
+ if (path == NULL) {
535
+ path = copy_xcode_path_for(CFSTR("iOS DeviceSupport"), CFStringCreateWithFormat(NULL, NULL, CFSTR("%@ (%@)/DeveloperDiskImage.dmg"), version, build));
536
+ }
537
+ if (path == NULL) {
538
+ path = copy_xcode_path_for(CFSTR("Platforms/iPhoneOS.platform/DeviceSupport"), CFStringCreateWithFormat(NULL, NULL, CFSTR("%@ (%@)/DeveloperDiskImage.dmg"), version, build));
539
+ }
540
+ if (path == NULL) {
541
+ path = copy_xcode_path_for(CFSTR("Platforms/iPhoneOS.platform/DeviceSupport"), CFStringCreateWithFormat(NULL, NULL, CFSTR("*/%@ (*)/DeveloperDiskImage.dmg"), version));
542
+ }
543
+ if (path == NULL) {
544
+ path = copy_xcode_path_for(CFSTR("Platforms/iPhoneOS.platform/DeviceSupport"), CFStringCreateWithFormat(NULL, NULL, CFSTR("%@/DeveloperDiskImage.dmg"), version));
545
+ }
546
+ if (path == NULL) {
547
+ path = copy_xcode_path_for(CFSTR("Platforms/iPhoneOS.platform/DeviceSupport/Latest"), CFSTR("DeveloperDiskImage.dmg"));
548
+ }
549
+ CFRelease(version);
550
+ if (path != NULL) {
551
+ break;
552
+ }
553
+ CFArrayRemoveValueAtIndex(version_parts, CFArrayGetCount(version_parts) - 1);
554
+ }
555
+
556
+ CFRelease(version_parts);
557
+ CFRelease(build);
558
+ if (path == NULL)
559
+ {
560
+ printf("[ !! ] Unable to locate DeveloperDiskImage.dmg.\n[ !! ] This probably means you don't have Xcode installed, you will need to launch the app manually and logging output will not be shown!\n");
561
+ exit(exitcode_error);
562
+ }
563
+
564
+ return path;
565
+ }
566
+
567
+ void mount_callback(CFDictionaryRef dict, int arg) {
568
+ CFStringRef status = CFDictionaryGetValue(dict, CFSTR("Status"));
569
+
570
+ if (CFEqual(status, CFSTR("LookingUpImage"))) {
571
+ printf("[ 0%%] Looking up developer disk image\n");
572
+ } else if (CFEqual(status, CFSTR("CopyingImage"))) {
573
+ printf("[ 30%%] Copying DeveloperDiskImage.dmg to device\n");
574
+ } else if (CFEqual(status, CFSTR("MountingImage"))) {
575
+ printf("[ 90%%] Mounting developer disk image\n");
576
+ }
577
+ }
578
+
579
+ void mount_developer_image(AMDeviceRef device) {
580
+ CFStringRef ds_path = copy_device_support_path(device);
581
+ CFStringRef image_path = copy_developer_disk_image_path(device);
582
+ CFStringRef sig_path = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@.signature"), image_path);
583
+
584
+ if (verbose) {
585
+ printf("Device support path: %s\n", CFStringGetCStringPtr(ds_path, CFStringGetSystemEncoding()));
586
+ printf("Developer disk image: %s\n", CFStringGetCStringPtr(image_path, CFStringGetSystemEncoding()));
587
+ }
588
+ CFRelease(ds_path);
589
+
590
+ FILE* sig = fopen(CFStringGetCStringPtr(sig_path, kCFStringEncodingMacRoman), "rb");
591
+ void *sig_buf = malloc(128);
592
+ assert(fread(sig_buf, 1, 128, sig) == 128);
593
+ fclose(sig);
594
+ CFDataRef sig_data = CFDataCreateWithBytesNoCopy(NULL, sig_buf, 128, NULL);
595
+ CFRelease(sig_path);
596
+
597
+ CFTypeRef keys[] = { CFSTR("ImageSignature"), CFSTR("ImageType") };
598
+ CFTypeRef values[] = { sig_data, CFSTR("Developer") };
599
+ CFDictionaryRef options = CFDictionaryCreate(NULL, (const void **)&keys, (const void **)&values, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
600
+ CFRelease(sig_data);
601
+
602
+ int result = AMDeviceMountImage(device, image_path, options, &mount_callback, 0);
603
+ if (result == 0) {
604
+ printf("[ 95%%] Developer disk image mounted successfully\n");
605
+ } else if (result == 0xe8000076 /* already mounted */) {
606
+ printf("[ 95%%] Developer disk image already mounted\n");
607
+ } else {
608
+ printf("[ !! ] Unable to mount developer disk image. (%x)\n", result);
609
+ exit(exitcode_error);
610
+ }
611
+
612
+ CFRelease(image_path);
613
+ CFRelease(options);
614
+ }
615
+
616
+ mach_error_t transfer_callback(CFDictionaryRef dict, int arg) {
617
+ int percent;
618
+ CFStringRef status = CFDictionaryGetValue(dict, CFSTR("Status"));
619
+ CFNumberGetValue(CFDictionaryGetValue(dict, CFSTR("PercentComplete")), kCFNumberSInt32Type, &percent);
620
+
621
+ if (CFEqual(status, CFSTR("CopyingFile"))) {
622
+ CFStringRef path = CFDictionaryGetValue(dict, CFSTR("Path"));
623
+
624
+ if ((last_path == NULL || !CFEqual(path, last_path)) && !CFStringHasSuffix(path, CFSTR(".ipa"))) {
625
+ printf("[%3d%%] Copying %s to device\n", percent / 2, CFStringGetCStringPtr(path, kCFStringEncodingMacRoman));
626
+ }
627
+
628
+ if (last_path != NULL) {
629
+ CFRelease(last_path);
630
+ }
631
+ last_path = CFStringCreateCopy(NULL, path);
632
+ }
633
+
634
+ return 0;
635
+ }
636
+
637
+ mach_error_t install_callback(CFDictionaryRef dict, int arg) {
638
+ int percent;
639
+ CFStringRef status = CFDictionaryGetValue(dict, CFSTR("Status"));
640
+ CFNumberGetValue(CFDictionaryGetValue(dict, CFSTR("PercentComplete")), kCFNumberSInt32Type, &percent);
641
+
642
+ printf("[%3d%%] %s\n", (percent / 2) + 50, CFStringGetCStringPtr(status, kCFStringEncodingMacRoman));
643
+ return 0;
644
+ }
645
+
646
+ CFURLRef copy_device_app_url(AMDeviceRef device, CFStringRef identifier) {
647
+ CFDictionaryRef result = nil;
648
+
649
+ NSArray *a = [NSArray arrayWithObjects:
650
+ @"CFBundleIdentifier", // absolute must
651
+ @"ApplicationDSID",
652
+ @"ApplicationType",
653
+ @"CFBundleExecutable",
654
+ @"CFBundleDisplayName",
655
+ @"CFBundleIconFile",
656
+ @"CFBundleName",
657
+ @"CFBundleShortVersionString",
658
+ @"CFBundleSupportedPlatforms",
659
+ @"CFBundleURLTypes",
660
+ @"CodeInfoIdentifier",
661
+ @"Container",
662
+ @"Entitlements",
663
+ @"HasSettingsBundle",
664
+ @"IsUpgradeable",
665
+ @"MinimumOSVersion",
666
+ @"Path",
667
+ @"SignerIdentity",
668
+ @"UIDeviceFamily",
669
+ @"UIFileSharingEnabled",
670
+ @"UIStatusBarHidden",
671
+ @"UISupportedInterfaceOrientations",
672
+ nil];
673
+
674
+ NSDictionary *optionsDict = [NSDictionary dictionaryWithObject:a forKey:@"ReturnAttributes"];
675
+ CFDictionaryRef options = (CFDictionaryRef)optionsDict;
676
+
677
+ afc_error_t resultStatus = AMDeviceLookupApplications(device, options, &result);
678
+ assert(resultStatus == 0);
679
+
680
+ CFDictionaryRef app_dict = CFDictionaryGetValue(result, identifier);
681
+ assert(app_dict != NULL);
682
+
683
+ CFStringRef app_path = CFDictionaryGetValue(app_dict, CFSTR("Path"));
684
+ assert(app_path != NULL);
685
+
686
+ CFURLRef url = CFURLCreateWithFileSystemPath(NULL, app_path, kCFURLPOSIXPathStyle, true);
687
+ CFRelease(result);
688
+ return url;
689
+ }
690
+
691
+ CFStringRef copy_disk_app_identifier(CFURLRef disk_app_url) {
692
+ CFURLRef plist_url = CFURLCreateCopyAppendingPathComponent(NULL, disk_app_url, CFSTR("Info.plist"), false);
693
+ CFReadStreamRef plist_stream = CFReadStreamCreateWithFile(NULL, plist_url);
694
+ CFReadStreamOpen(plist_stream);
695
+ CFPropertyListRef plist = CFPropertyListCreateWithStream(NULL, plist_stream, 0, kCFPropertyListImmutable, NULL, NULL);
696
+ CFStringRef bundle_identifier = CFRetain(CFDictionaryGetValue(plist, CFSTR("CFBundleIdentifier")));
697
+ CFReadStreamClose(plist_stream);
698
+
699
+ CFRelease(plist_url);
700
+ CFRelease(plist_stream);
701
+ CFRelease(plist);
702
+
703
+ return bundle_identifier;
704
+ }
705
+
706
+ void write_lldb_prep_cmds(AMDeviceRef device, CFURLRef disk_app_url) {
707
+ CFStringRef ds_path = copy_device_support_path(device);
708
+ CFStringRef symbols_path = CFStringCreateWithFormat(NULL, NULL, CFSTR("'%@/Symbols'"), ds_path);
709
+
710
+ CFMutableStringRef cmds = CFStringCreateMutableCopy(NULL, 0, LLDB_PREP_CMDS);
711
+ CFRange range = { 0, CFStringGetLength(cmds) };
712
+
713
+ CFStringFindAndReplace(cmds, CFSTR("{symbols_path}"), symbols_path, range, 0);
714
+ range.length = CFStringGetLength(cmds);
715
+
716
+ CFStringFindAndReplace(cmds, CFSTR("{ds_path}"), ds_path, range, 0);
717
+ range.length = CFStringGetLength(cmds);
718
+
719
+ CFMutableStringRef pmodule = CFStringCreateMutableCopy(NULL, 0, LLDB_FRUITSTRAP_MODULE);
720
+
721
+ CFRange rangeLLDB = { 0, CFStringGetLength(pmodule) };
722
+ CFStringRef exitcode_app_crash_str = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d"), exitcode_app_crash);
723
+ CFStringFindAndReplace(pmodule, CFSTR("{exitcode_app_crash}"), exitcode_app_crash_str, rangeLLDB, 0);
724
+ rangeLLDB.length = CFStringGetLength(pmodule);
725
+
726
+ if (args) {
727
+ CFStringRef cf_args = CFStringCreateWithCString(NULL, args, kCFStringEncodingASCII);
728
+ CFStringFindAndReplace(cmds, CFSTR("{args}"), cf_args, range, 0);
729
+ rangeLLDB.length = CFStringGetLength(pmodule);
730
+ CFStringFindAndReplace(pmodule, CFSTR("{args}"), cf_args, rangeLLDB, 0);
731
+
732
+ //printf("write_lldb_prep_cmds:args: [%s][%s]\n", CFStringGetCStringPtr (cmds,kCFStringEncodingMacRoman),
733
+ // CFStringGetCStringPtr(pmodule, kCFStringEncodingMacRoman));
734
+ CFRelease(cf_args);
735
+ } else {
736
+ CFStringFindAndReplace(cmds, CFSTR("{args}"), CFSTR(""), range, 0);
737
+ CFStringFindAndReplace(pmodule, CFSTR("{args}"), CFSTR(""), rangeLLDB, 0);
738
+ //printf("write_lldb_prep_cmds: [%s][%s]\n", CFStringGetCStringPtr (cmds,kCFStringEncodingMacRoman),
739
+ // CFStringGetCStringPtr(pmodule, kCFStringEncodingMacRoman));
740
+ }
741
+ range.length = CFStringGetLength(cmds);
742
+
743
+ CFStringRef bundle_identifier = copy_disk_app_identifier(disk_app_url);
744
+ CFURLRef device_app_url = copy_device_app_url(device, bundle_identifier);
745
+ CFStringRef device_app_path = CFURLCopyFileSystemPath(device_app_url, kCFURLPOSIXPathStyle);
746
+ CFStringFindAndReplace(cmds, CFSTR("{device_app}"), device_app_path, range, 0);
747
+ range.length = CFStringGetLength(cmds);
748
+
749
+ CFStringRef disk_app_path = CFURLCopyFileSystemPath(disk_app_url, kCFURLPOSIXPathStyle);
750
+ CFStringFindAndReplace(cmds, CFSTR("{disk_app}"), disk_app_path, range, 0);
751
+ range.length = CFStringGetLength(cmds);
752
+
753
+ CFStringRef device_port = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d"), port);
754
+ CFStringFindAndReplace(cmds, CFSTR("{device_port}"), device_port, range, 0);
755
+ range.length = CFStringGetLength(cmds);
756
+
757
+ CFURLRef device_container_url = CFURLCreateCopyDeletingLastPathComponent(NULL, device_app_url);
758
+ CFStringRef device_container_path = CFURLCopyFileSystemPath(device_container_url, kCFURLPOSIXPathStyle);
759
+ CFMutableStringRef dcp_noprivate = CFStringCreateMutableCopy(NULL, 0, device_container_path);
760
+ range.length = CFStringGetLength(dcp_noprivate);
761
+ CFStringFindAndReplace(dcp_noprivate, CFSTR("/private/var/"), CFSTR("/var/"), range, 0);
762
+ range.length = CFStringGetLength(cmds);
763
+ CFStringFindAndReplace(cmds, CFSTR("{device_container}"), dcp_noprivate, range, 0);
764
+ range.length = CFStringGetLength(cmds);
765
+
766
+ CFURLRef disk_container_url = CFURLCreateCopyDeletingLastPathComponent(NULL, disk_app_url);
767
+ CFStringRef disk_container_path = CFURLCopyFileSystemPath(disk_container_url, kCFURLPOSIXPathStyle);
768
+ CFStringFindAndReplace(cmds, CFSTR("{disk_container}"), disk_container_path, range, 0);
769
+
770
+ char python_file_path[300] = "/tmp/fruitstrap_";
771
+ char python_command[300] = "fruitstrap_";
772
+ if(device_id != NULL) {
773
+ strcat(python_file_path, device_id);
774
+ strcat(python_command, device_id);
775
+ }
776
+ strcat(python_file_path, ".py");
777
+
778
+ CFStringRef cf_python_command = CFStringCreateWithCString(NULL, python_command, kCFStringEncodingASCII);
779
+ CFStringFindAndReplace(cmds, CFSTR("{python_command}"), cf_python_command, range, 0);
780
+ range.length = CFStringGetLength(cmds);
781
+ CFStringRef cf_python_file_path = CFStringCreateWithCString(NULL, python_file_path, kCFStringEncodingASCII);
782
+ CFStringFindAndReplace(cmds, CFSTR("{python_file_path}"), cf_python_file_path, range, 0);
783
+ range.length = CFStringGetLength(cmds);
784
+
785
+ CFDataRef cmds_data = CFStringCreateExternalRepresentation(NULL, cmds, kCFStringEncodingASCII, 0);
786
+ char prep_cmds_path[300] = PREP_CMDS_PATH;
787
+ if(device_id != NULL)
788
+ strcat(prep_cmds_path, device_id);
789
+ FILE *out = fopen(prep_cmds_path, "w");
790
+ fwrite(CFDataGetBytePtr(cmds_data), CFDataGetLength(cmds_data), 1, out);
791
+ // Write additional commands based on mode we're running in
792
+ const char* extra_cmds;
793
+ if (!interactive)
794
+ {
795
+ if (justlaunch)
796
+ extra_cmds = lldb_prep_noninteractive_justlaunch_cmds;
797
+ else
798
+ extra_cmds = lldb_prep_noninteractive_cmds;
799
+ }
800
+ else if (nostart)
801
+ extra_cmds = lldb_prep_no_cmds;
802
+ else
803
+ extra_cmds = lldb_prep_interactive_cmds;
804
+ fwrite(extra_cmds, strlen(extra_cmds), 1, out);
805
+ fclose(out);
806
+
807
+ CFDataRef pmodule_data = CFStringCreateExternalRepresentation(NULL, pmodule, kCFStringEncodingASCII, 0);
808
+
809
+ out = fopen(python_file_path, "w");
810
+ fwrite(CFDataGetBytePtr(pmodule_data), CFDataGetLength(pmodule_data), 1, out);
811
+ fclose(out);
812
+
813
+ CFRelease(cmds);
814
+ if (ds_path != NULL) CFRelease(ds_path);
815
+ CFRelease(bundle_identifier);
816
+ CFRelease(device_app_url);
817
+ CFRelease(device_app_path);
818
+ CFRelease(disk_app_path);
819
+ CFRelease(device_container_url);
820
+ CFRelease(device_container_path);
821
+ CFRelease(dcp_noprivate);
822
+ CFRelease(disk_container_url);
823
+ CFRelease(disk_container_path);
824
+ CFRelease(cmds_data);
825
+ CFRelease(cf_python_command);
826
+ CFRelease(cf_python_file_path);
827
+ }
828
+
829
+ CFSocketRef server_socket;
830
+ CFSocketRef lldb_socket;
831
+ CFWriteStreamRef serverWriteStream = NULL;
832
+ CFWriteStreamRef lldbWriteStream = NULL;
833
+
834
+ int kill_ptree(pid_t root, int signum);
835
+ void
836
+ server_callback (CFSocketRef s, CFSocketCallBackType callbackType, CFDataRef address, const void *data, void *info)
837
+ {
838
+ int res;
839
+
840
+ if (CFDataGetLength (data) == 0) {
841
+ // FIXME: Close the socket
842
+ //shutdown (CFSocketGetNative (lldb_socket), SHUT_RDWR);
843
+ //close (CFSocketGetNative (lldb_socket));
844
+ CFSocketInvalidate(lldb_socket);
845
+ CFSocketInvalidate(server_socket);
846
+ int mypid = getpid();
847
+ assert((child != 0) && (child != mypid)); //child should not be here
848
+ if ((parent != 0) && (parent == mypid) && (child != 0))
849
+ {
850
+ if (verbose)
851
+ {
852
+ printf("Got an empty packet hence killing child (%d) tree\n", child);
853
+ }
854
+ kill_ptree(child, SIGHUP);
855
+ }
856
+ exit(exitcode_error);
857
+ return;
858
+ }
859
+ res = write (CFSocketGetNative (lldb_socket), CFDataGetBytePtr (data), CFDataGetLength (data));
860
+ }
861
+
862
+ void lldb_callback(CFSocketRef s, CFSocketCallBackType callbackType, CFDataRef address, const void *data, void *info)
863
+ {
864
+ //printf ("lldb: %s\n", CFDataGetBytePtr (data));
865
+
866
+ if (CFDataGetLength (data) == 0)
867
+ return;
868
+ write (gdbfd, CFDataGetBytePtr (data), CFDataGetLength (data));
869
+ }
870
+
871
+ void fdvendor_callback(CFSocketRef s, CFSocketCallBackType callbackType, CFDataRef address, const void *data, void *info) {
872
+ CFSocketNativeHandle socket = (CFSocketNativeHandle)(*((CFSocketNativeHandle *)data));
873
+
874
+ assert (callbackType == kCFSocketAcceptCallBack);
875
+ //PRINT ("callback!\n");
876
+
877
+ lldb_socket = CFSocketCreateWithNative(NULL, socket, kCFSocketDataCallBack, &lldb_callback, NULL);
878
+ CFRunLoopAddSource(CFRunLoopGetMain(), CFSocketCreateRunLoopSource(NULL, lldb_socket, 0), kCFRunLoopCommonModes);
879
+ }
880
+
881
+ void start_remote_debug_server(AMDeviceRef device) {
882
+ char buf [256];
883
+ int res, err, i;
884
+ char msg [256];
885
+ int chsum, len;
886
+ struct stat s;
887
+ socklen_t buflen;
888
+ struct sockaddr name;
889
+ int namelen;
890
+
891
+ assert(AMDeviceStartService(device, CFSTR("com.apple.debugserver"), &gdbfd, NULL) == 0);
892
+ assert (gdbfd);
893
+
894
+ /*
895
+ * The debugserver connection is through a fd handle, while lldb requires a host/port to connect, so create an intermediate
896
+ * socket to transfer data.
897
+ */
898
+ server_socket = CFSocketCreateWithNative (NULL, gdbfd, kCFSocketDataCallBack, &server_callback, NULL);
899
+ CFRunLoopAddSource(CFRunLoopGetMain(), CFSocketCreateRunLoopSource(NULL, server_socket, 0), kCFRunLoopCommonModes);
900
+
901
+ struct sockaddr_in addr4;
902
+ memset(&addr4, 0, sizeof(addr4));
903
+ addr4.sin_len = sizeof(addr4);
904
+ addr4.sin_family = AF_INET;
905
+ addr4.sin_port = htons(port);
906
+ addr4.sin_addr.s_addr = htonl(INADDR_ANY);
907
+
908
+ CFSocketRef fdvendor = CFSocketCreate(NULL, PF_INET, 0, 0, kCFSocketAcceptCallBack, &fdvendor_callback, NULL);
909
+
910
+ int yes = 1;
911
+ setsockopt(CFSocketGetNative(fdvendor), SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
912
+ int flag = 1;
913
+ res = setsockopt(CFSocketGetNative(fdvendor), IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int));
914
+ assert (res == 0);
915
+
916
+ CFDataRef address_data = CFDataCreate(NULL, (const UInt8 *)&addr4, sizeof(addr4));
917
+
918
+ CFSocketSetAddress(fdvendor, address_data);
919
+ CFRelease(address_data);
920
+ CFRunLoopAddSource(CFRunLoopGetMain(), CFSocketCreateRunLoopSource(NULL, fdvendor, 0), kCFRunLoopCommonModes);
921
+ }
922
+
923
+ void kill_ptree_inner(pid_t root, int signum, struct kinfo_proc *kp, int kp_len) {
924
+ int i;
925
+ for (i = 0; i < kp_len; i++) {
926
+ if (kp[i].kp_eproc.e_ppid == root) {
927
+ kill_ptree_inner(kp[i].kp_proc.p_pid, signum, kp, kp_len);
928
+ }
929
+ }
930
+ if (root != getpid()) {
931
+ kill(root, signum);
932
+ }
933
+ }
934
+
935
+ int kill_ptree(pid_t root, int signum) {
936
+ int mib[3];
937
+ size_t len;
938
+ mib[0] = CTL_KERN;
939
+ mib[1] = KERN_PROC;
940
+ mib[2] = KERN_PROC_ALL;
941
+ if (sysctl(mib, 3, NULL, &len, NULL, 0) == -1) {
942
+ return -1;
943
+ }
944
+
945
+ struct kinfo_proc *kp = calloc(1, len);
946
+ if (!kp) {
947
+ return -1;
948
+ }
949
+
950
+ if (sysctl(mib, 3, kp, &len, NULL, 0) == -1) {
951
+ free(kp);
952
+ return -1;
953
+ }
954
+
955
+ kill_ptree_inner(root, signum, kp, len / sizeof(struct kinfo_proc));
956
+
957
+ free(kp);
958
+ return 0;
959
+ }
960
+
961
+ void killed(int signum) {
962
+ // SIGKILL needed to kill lldb, probably a better way to do this.
963
+ kill(0, SIGKILL);
964
+ _exit(0);
965
+ }
966
+
967
+ void lldb_finished_handler(int signum)
968
+ {
969
+ int status = 0;
970
+ if (waitpid(child, &status, 0) == -1)
971
+ perror("waitpid failed");
972
+ _exit(WEXITSTATUS(status));
973
+ }
974
+
975
+ void bring_process_to_foreground() {
976
+ if (setpgid(0, 0) == -1)
977
+ perror("setpgid failed");
978
+
979
+ signal(SIGTTOU, SIG_IGN);
980
+ if (tcsetpgrp(STDIN_FILENO, getpid()) == -1)
981
+ perror("tcsetpgrp failed");
982
+ signal(SIGTTOU, SIG_DFL);
983
+ }
984
+
985
+ void setup_dummy_pipe_on_stdin(int pfd[2]) {
986
+ if (pipe(pfd) == -1)
987
+ perror("pipe failed");
988
+ if (dup2(pfd[0], STDIN_FILENO) == -1)
989
+ perror("dup2 failed");
990
+ }
991
+
992
+ void setup_lldb(AMDeviceRef device, CFURLRef url) {
993
+ CFStringRef device_full_name = get_device_full_name(device),
994
+ device_interface_name = get_device_interface_name(device);
995
+
996
+ AMDeviceConnect(device);
997
+ assert(AMDeviceIsPaired(device));
998
+ assert(AMDeviceValidatePairing(device) == 0);
999
+ assert(AMDeviceStartSession(device) == 0);
1000
+
1001
+ printf("------ Debug phase ------\n");
1002
+
1003
+ if(AMDeviceGetInterfaceType(device) == 2)
1004
+ {
1005
+ printf("Cannot debug %s over %s.\n", CFStringGetCStringPtr(device_full_name, CFStringGetSystemEncoding()), CFStringGetCStringPtr(device_interface_name, CFStringGetSystemEncoding()));
1006
+ exit(0);
1007
+ }
1008
+
1009
+ printf("Starting debug of %s connected through %s...\n", CFStringGetCStringPtr(device_full_name, CFStringGetSystemEncoding()), CFStringGetCStringPtr(device_interface_name, CFStringGetSystemEncoding()));
1010
+
1011
+ mount_developer_image(device); // put debugserver on the device
1012
+ start_remote_debug_server(device); // start debugserver
1013
+ write_lldb_prep_cmds(device, url); // dump the necessary lldb commands into a file
1014
+
1015
+ CFRelease(url);
1016
+
1017
+ printf("[100%%] Connecting to remote debug server\n");
1018
+ printf("-------------------------\n");
1019
+
1020
+ setpgid(getpid(), 0);
1021
+ signal(SIGHUP, killed);
1022
+ signal(SIGINT, killed);
1023
+ signal(SIGTERM, killed);
1024
+ // Need this before fork to avoid race conditions. For child process we remove this right after fork.
1025
+ signal(SIGLLDB, lldb_finished_handler);
1026
+
1027
+ parent = getpid();
1028
+ }
1029
+
1030
+ void launch_debugger(AMDeviceRef device, CFURLRef url) {
1031
+ setup_lldb(device, url);
1032
+ int pid = fork();
1033
+ if (pid == 0) {
1034
+ signal(SIGHUP, SIG_DFL);
1035
+ signal(SIGLLDB, SIG_DFL);
1036
+ child = getpid();
1037
+
1038
+ int pfd[2] = {-1, -1};
1039
+ if (isatty(STDIN_FILENO))
1040
+ // If we are running on a terminal, then we need to bring process to foreground for input
1041
+ // to work correctly on lldb's end.
1042
+ bring_process_to_foreground();
1043
+ else
1044
+ // If lldb is running in a non terminal environment, then it freaks out spamming "^D" and
1045
+ // "quit". It seems this is caused by read() on stdin returning EOF in lldb. To hack around
1046
+ // this we setup a dummy pipe on stdin, so read() would block expecting "user's" input.
1047
+ setup_dummy_pipe_on_stdin(pfd);
1048
+
1049
+ char lldb_shell[400];
1050
+ sprintf(lldb_shell, LLDB_SHELL);
1051
+ if(device_id != NULL)
1052
+ strcat(lldb_shell, device_id);
1053
+
1054
+ int status = system(lldb_shell); // launch lldb
1055
+ if (status == -1)
1056
+ perror("failed launching lldb");
1057
+
1058
+ close(pfd[0]);
1059
+ close(pfd[1]);
1060
+
1061
+ // Notify parent we're exiting
1062
+ kill(parent, SIGLLDB);
1063
+ // Pass lldb exit code
1064
+ _exit(WEXITSTATUS(status));
1065
+ } else if (pid > 0) {
1066
+ child = pid;
1067
+ } else {
1068
+ perror("fork failed");
1069
+ exit(exitcode_error);
1070
+ }
1071
+ }
1072
+
1073
+ void launch_debugger_and_exit(AMDeviceRef device, CFURLRef url) {
1074
+ setup_lldb(device,url);
1075
+ int pfd[2] = {-1, -1};
1076
+ if (pipe(pfd) == -1)
1077
+ perror("Pipe failed");
1078
+ int pid = fork();
1079
+ if (pid == 0) {
1080
+ signal(SIGHUP, SIG_DFL);
1081
+ signal(SIGLLDB, SIG_DFL);
1082
+ child = getpid();
1083
+
1084
+ if (dup2(pfd[0],STDIN_FILENO) == -1)
1085
+ perror("dup2 failed");
1086
+
1087
+ char lldb_shell[400];
1088
+ sprintf(lldb_shell, LLDB_SHELL);
1089
+ if(device_id != NULL)
1090
+ strcat(lldb_shell, device_id);
1091
+
1092
+ int status = system(lldb_shell); // launch lldb
1093
+ if (status == -1)
1094
+ perror("failed launching lldb");
1095
+
1096
+ close(pfd[0]);
1097
+
1098
+ // Notify parent we're exiting
1099
+ kill(parent, SIGLLDB);
1100
+ // Pass lldb exit code
1101
+ _exit(WEXITSTATUS(status));
1102
+ } else if (pid > 0) {
1103
+ child = pid;
1104
+ if (verbose)
1105
+ printf("Waiting for child [Child: %d][Parent: %d]\n", child, parent);
1106
+ } else {
1107
+ perror("fork failed");
1108
+ exit(exitcode_error);
1109
+ }
1110
+ }
1111
+
1112
+ CFStringRef get_bundle_id(CFURLRef app_url)
1113
+ {
1114
+ if (app_url == NULL)
1115
+ return NULL;
1116
+
1117
+ CFURLRef url = CFURLCreateCopyAppendingPathComponent(NULL, app_url, CFSTR("Info.plist"), false);
1118
+
1119
+ if (url == NULL)
1120
+ return NULL;
1121
+
1122
+ CFReadStreamRef stream = CFReadStreamCreateWithFile(NULL, url);
1123
+ CFRelease(url);
1124
+
1125
+ if (stream == NULL)
1126
+ return NULL;
1127
+
1128
+ CFPropertyListRef plist = NULL;
1129
+ if (CFReadStreamOpen(stream) == TRUE) {
1130
+ plist = CFPropertyListCreateWithStream(NULL, stream, 0,
1131
+ kCFPropertyListImmutable, NULL, NULL);
1132
+ }
1133
+ CFReadStreamClose(stream);
1134
+ CFRelease(stream);
1135
+
1136
+ if (plist == NULL)
1137
+ return NULL;
1138
+
1139
+ const void *value = CFDictionaryGetValue(plist, CFSTR("CFBundleIdentifier"));
1140
+ CFStringRef bundle_id = NULL;
1141
+ if (value != NULL)
1142
+ bundle_id = CFRetain(value);
1143
+
1144
+ CFRelease(plist);
1145
+ return bundle_id;
1146
+ }
1147
+
1148
+
1149
+ void read_dir(service_conn_t afcFd, afc_connection* afc_conn_p, const char* dir,
1150
+ void(*callback)(afc_connection *conn,const char *dir,int file))
1151
+ {
1152
+ char *dir_ent;
1153
+
1154
+ afc_connection afc_conn;
1155
+ if (!afc_conn_p) {
1156
+ afc_conn_p = &afc_conn;
1157
+ AFCConnectionOpen(afcFd, 0, &afc_conn_p);
1158
+ }
1159
+
1160
+ printf("%s\n", dir);
1161
+
1162
+ afc_dictionary* afc_dict_p;
1163
+ char *key, *val;
1164
+ int not_dir = 0;
1165
+
1166
+ unsigned int code = AFCFileInfoOpen(afc_conn_p, dir, &afc_dict_p);
1167
+ if (code != 0) {
1168
+ // there was a problem reading or opening the file to get info on it, abort
1169
+ return;
1170
+ }
1171
+
1172
+ while((AFCKeyValueRead(afc_dict_p,&key,&val) == 0) && key && val) {
1173
+ if (strcmp(key,"st_ifmt")==0) {
1174
+ not_dir = strcmp(val,"S_IFDIR");
1175
+ break;
1176
+ }
1177
+ }
1178
+ AFCKeyValueClose(afc_dict_p);
1179
+
1180
+ if (not_dir) {
1181
+ if (callback) (*callback)(afc_conn_p, dir, not_dir);
1182
+ return;
1183
+ }
1184
+
1185
+ afc_directory* afc_dir_p;
1186
+ afc_error_t err = AFCDirectoryOpen(afc_conn_p, dir, &afc_dir_p);
1187
+
1188
+ if (err != 0) {
1189
+ // Couldn't open dir - was probably a file
1190
+ return;
1191
+ } else {
1192
+ if (callback) (*callback)(afc_conn_p, dir, not_dir);
1193
+ }
1194
+
1195
+ while(true) {
1196
+ err = AFCDirectoryRead(afc_conn_p, afc_dir_p, &dir_ent);
1197
+
1198
+ if (err != 0 || !dir_ent)
1199
+ break;
1200
+
1201
+ if (strcmp(dir_ent, ".") == 0 || strcmp(dir_ent, "..") == 0)
1202
+ continue;
1203
+
1204
+ char* dir_joined = malloc(strlen(dir) + strlen(dir_ent) + 2);
1205
+ strcpy(dir_joined, dir);
1206
+ if (dir_joined[strlen(dir)-1] != '/')
1207
+ strcat(dir_joined, "/");
1208
+ strcat(dir_joined, dir_ent);
1209
+ read_dir(afcFd, afc_conn_p, dir_joined, callback);
1210
+ free(dir_joined);
1211
+ }
1212
+
1213
+ AFCDirectoryClose(afc_conn_p, afc_dir_p);
1214
+ }
1215
+
1216
+
1217
+ // Used to send files to app-specific sandbox (Documents dir)
1218
+ service_conn_t start_house_arrest_service(AMDeviceRef device) {
1219
+ AMDeviceConnect(device);
1220
+ assert(AMDeviceIsPaired(device));
1221
+ assert(AMDeviceValidatePairing(device) == 0);
1222
+ assert(AMDeviceStartSession(device) == 0);
1223
+
1224
+ service_conn_t houseFd;
1225
+
1226
+ if (bundle_id == NULL) {
1227
+ printf("Bundle id is not specified\n");
1228
+ exit(1);
1229
+ }
1230
+
1231
+ CFStringRef cf_bundle_id = CFStringCreateWithCString(NULL, bundle_id, kCFStringEncodingASCII);
1232
+ if (AMDeviceStartHouseArrestService(device, cf_bundle_id, 0, &houseFd, 0) != 0)
1233
+ {
1234
+ printf("Unable to find bundle with id: %s\n", bundle_id);
1235
+ exit(1);
1236
+ }
1237
+
1238
+ assert(AMDeviceStopSession(device) == 0);
1239
+ assert(AMDeviceDisconnect(device) == 0);
1240
+ CFRelease(cf_bundle_id);
1241
+
1242
+ return houseFd;
1243
+ }
1244
+
1245
+ char* get_filename_from_path(char* path)
1246
+ {
1247
+ char *ptr = path + strlen(path);
1248
+ while (ptr > path)
1249
+ {
1250
+ if (*ptr == '/')
1251
+ break;
1252
+ --ptr;
1253
+ }
1254
+ if (ptr+1 >= path+strlen(path))
1255
+ return NULL;
1256
+ if (ptr == path)
1257
+ return ptr;
1258
+ return ptr+1;
1259
+ }
1260
+
1261
+ void* read_file_to_memory(char * path, size_t* file_size)
1262
+ {
1263
+ struct stat buf;
1264
+ int err = stat(path, &buf);
1265
+ if (err < 0)
1266
+ {
1267
+ return NULL;
1268
+ }
1269
+
1270
+ *file_size = buf.st_size;
1271
+ FILE* fd = fopen(path, "r");
1272
+ char* content = malloc(*file_size);
1273
+ if (fread(content, *file_size, 1, fd) != 1)
1274
+ {
1275
+ fclose(fd);
1276
+ return NULL;
1277
+ }
1278
+ fclose(fd);
1279
+ return content;
1280
+ }
1281
+
1282
+ void list_files(AMDeviceRef device)
1283
+ {
1284
+ service_conn_t houseFd = start_house_arrest_service(device);
1285
+
1286
+ afc_connection* afc_conn_p;
1287
+ if (AFCConnectionOpen(houseFd, 0, &afc_conn_p) == 0) {
1288
+ read_dir(houseFd, afc_conn_p, list_root?list_root:"/", NULL);
1289
+ AFCConnectionClose(afc_conn_p);
1290
+ }
1291
+ }
1292
+
1293
+ void copy_file_callback(afc_connection* afc_conn_p, const char *name,int file)
1294
+ {
1295
+ const char *local_name=name;
1296
+
1297
+ if (*local_name=='/') local_name++;
1298
+
1299
+ if (*local_name=='\0') return;
1300
+
1301
+ if (file) {
1302
+ afc_file_ref fref;
1303
+ int err = AFCFileRefOpen(afc_conn_p,name,1,&fref);
1304
+
1305
+ if (err) {
1306
+ fprintf(stderr,"AFCFileRefOpen(\"%s\") failed: %d\n",name,err);
1307
+ return;
1308
+ }
1309
+
1310
+ FILE *fp = fopen(local_name,"w");
1311
+
1312
+ if (fp==NULL) {
1313
+ fprintf(stderr,"fopen(\"%s\",\"w\") failer: %s\n",local_name,strerror(errno));
1314
+ AFCFileRefClose(afc_conn_p,fref);
1315
+ return;
1316
+ }
1317
+
1318
+ char buf[4096];
1319
+ size_t sz=sizeof(buf);
1320
+
1321
+ while (AFCFileRefRead(afc_conn_p,fref,buf,&sz)==0 && sz) {
1322
+ fwrite(buf,sz,1,fp);
1323
+ sz = sizeof(buf);
1324
+ }
1325
+
1326
+ AFCFileRefClose(afc_conn_p,fref);
1327
+ fclose(fp);
1328
+ } else {
1329
+ if (mkdir(local_name,0777) && errno!=EEXIST)
1330
+ fprintf(stderr,"mkdir(\"%s\") failed: %s\n",local_name,strerror(errno));
1331
+ }
1332
+ }
1333
+
1334
+ void mkdirhier(char *path)
1335
+ {
1336
+ char *slash;
1337
+ struct stat buf;
1338
+
1339
+ if (path[0]=='.' && path[1]=='/') path+=2;
1340
+
1341
+ if ((slash = strrchr(path,'/'))) {
1342
+ *slash = '\0';
1343
+ if (stat(path,&buf)==0) {
1344
+ *slash = '/';
1345
+ return;
1346
+ }
1347
+ mkdirhier(path);
1348
+ mkdir (path,0777);
1349
+ *slash = '/';
1350
+ }
1351
+
1352
+ return;
1353
+ }
1354
+
1355
+ void download_tree(AMDeviceRef device)
1356
+ {
1357
+ service_conn_t houseFd = start_house_arrest_service(device);
1358
+ afc_connection* afc_conn_p = NULL;
1359
+ char *dirname = NULL;
1360
+
1361
+ if (AFCConnectionOpen(houseFd, 0, &afc_conn_p) == 0) do {
1362
+
1363
+ if (target_filename) {
1364
+ dirname = strdup(target_filename);
1365
+ mkdirhier(dirname);
1366
+ if (mkdir(dirname,0777) && errno!=EEXIST) {
1367
+ fprintf(stderr,"mkdir(\"%s\") failed: %s\n",dirname,strerror(errno));
1368
+ break;
1369
+ }
1370
+ if (chdir(dirname)) {
1371
+ fprintf(stderr,"chdir(\"%s\") failed: %s\n",dirname,strerror(errno));
1372
+ break;
1373
+ }
1374
+ }
1375
+
1376
+ read_dir(houseFd, afc_conn_p, list_root?list_root:"/", copy_file_callback);
1377
+
1378
+ } while(0);
1379
+
1380
+ if (dirname) free(dirname);
1381
+ if (afc_conn_p) AFCConnectionClose(afc_conn_p);
1382
+ }
1383
+
1384
+ void upload_file(AMDeviceRef device) {
1385
+ service_conn_t houseFd = start_house_arrest_service(device);
1386
+
1387
+ afc_file_ref file_ref;
1388
+
1389
+ afc_connection afc_conn;
1390
+ afc_connection* afc_conn_p = &afc_conn;
1391
+ AFCConnectionOpen(houseFd, 0, &afc_conn_p);
1392
+
1393
+ // read_dir(houseFd, NULL, "/", NULL);
1394
+
1395
+ if (!target_filename)
1396
+ {
1397
+ target_filename = get_filename_from_path(upload_pathname);
1398
+ }
1399
+
1400
+ size_t file_size;
1401
+ void* file_content = read_file_to_memory(upload_pathname, &file_size);
1402
+
1403
+ if (!file_content)
1404
+ {
1405
+ printf("Could not open file: %s\n", upload_pathname);
1406
+ exit(-1);
1407
+ }
1408
+
1409
+ // Make sure the directory was created
1410
+ {
1411
+ char *dirpath = strdup(target_filename);
1412
+ char *c = dirpath, *lastSlash = dirpath;
1413
+ while(*c) {
1414
+ if(*c == '/') {
1415
+ lastSlash = c;
1416
+ }
1417
+ c++;
1418
+ }
1419
+ *lastSlash = '\0';
1420
+ assert(AFCDirectoryCreate(afc_conn_p, dirpath) == 0);
1421
+ }
1422
+
1423
+
1424
+ int ret = AFCFileRefOpen(afc_conn_p, target_filename, 3, &file_ref);
1425
+ if (ret == 0x000a) {
1426
+ printf("Cannot write to %s. Permission error.\n", target_filename);
1427
+ exit(1);
1428
+ }
1429
+ if (ret == 0x0009) {
1430
+ printf("Target %s is a directory.\n", target_filename);
1431
+ exit(1);
1432
+ }
1433
+ assert(ret == 0);
1434
+ assert(AFCFileRefWrite(afc_conn_p, file_ref, file_content, file_size) == 0);
1435
+ assert(AFCFileRefClose(afc_conn_p, file_ref) == 0);
1436
+ assert(AFCConnectionClose(afc_conn_p) == 0);
1437
+
1438
+ free(file_content);
1439
+ }
1440
+
1441
+ void handle_device(AMDeviceRef device) {
1442
+ //if (found_device)
1443
+ // return; // handle one device only
1444
+
1445
+ CFStringRef found_device_id = AMDeviceCopyDeviceIdentifier(device),
1446
+ device_full_name = get_device_full_name(device),
1447
+ device_interface_name = get_device_interface_name(device);
1448
+
1449
+ if (detect_only) {
1450
+ printf("[....] Found %s connected through %s.\n", CFStringGetCStringPtr(device_full_name, CFStringGetSystemEncoding()), CFStringGetCStringPtr(device_interface_name, CFStringGetSystemEncoding()));
1451
+ found_device = true;
1452
+ return;
1453
+ }
1454
+ if (device_id != NULL) {
1455
+ if(strcmp(device_id, CFStringGetCStringPtr(found_device_id, CFStringGetSystemEncoding())) == 0) {
1456
+ found_device = true;
1457
+ } else {
1458
+ printf("Skipping %s.\n", CFStringGetCStringPtr(device_full_name, CFStringGetSystemEncoding()));
1459
+ return;
1460
+ }
1461
+ } else {
1462
+ device_id = MYCFStringCopyUTF8String(found_device_id);
1463
+ found_device = true;
1464
+ }
1465
+
1466
+ printf("[....] Using %s (%s).\n", CFStringGetCStringPtr(device_full_name, CFStringGetSystemEncoding()), CFStringGetCStringPtr(found_device_id, CFStringGetSystemEncoding()));
1467
+
1468
+ if (command_only) {
1469
+ if (strcmp("list", command) == 0) {
1470
+ list_files(device);
1471
+ } else if (strcmp("upload", command) == 0) {
1472
+ upload_file(device);
1473
+ } else if (strcmp("download", command) == 0) {
1474
+ download_tree(device);
1475
+ }
1476
+ exit(0);
1477
+ }
1478
+
1479
+
1480
+ CFRetain(device); // don't know if this is necessary?
1481
+
1482
+ CFStringRef path = CFStringCreateWithCString(NULL, app_path, kCFStringEncodingASCII);
1483
+ CFURLRef relative_url = CFURLCreateWithFileSystemPath(NULL, path, kCFURLPOSIXPathStyle, false);
1484
+ CFURLRef url = CFURLCopyAbsoluteURL(relative_url);
1485
+
1486
+ CFRelease(relative_url);
1487
+
1488
+ if (uninstall) {
1489
+ printf("------ Uninstall phase ------\n");
1490
+
1491
+ CFStringRef bundle_id = get_bundle_id(url);
1492
+ if (bundle_id == NULL) {
1493
+ printf("Error: Unable to get bundle id from package %s\n Uninstall failed\n", app_path);
1494
+ } else {
1495
+ AMDeviceConnect(device);
1496
+ assert(AMDeviceIsPaired(device));
1497
+ assert(AMDeviceValidatePairing(device) == 0);
1498
+ assert(AMDeviceStartSession(device) == 0);
1499
+
1500
+ int code = AMDeviceSecureUninstallApplication(0, device, bundle_id, 0, NULL, 0);
1501
+ if (code == 0) {
1502
+ printf("[ OK ] Uninstalled package with bundle id %s\n", CFStringGetCStringPtr(bundle_id, CFStringGetSystemEncoding()));
1503
+ } else {
1504
+ printf("[ ERROR ] Could not uninstall package with bundle id %s\n", CFStringGetCStringPtr(bundle_id, CFStringGetSystemEncoding()));
1505
+ }
1506
+ assert(AMDeviceStopSession(device) == 0);
1507
+ assert(AMDeviceDisconnect(device) == 0);
1508
+ }
1509
+ }
1510
+
1511
+ if(install) {
1512
+ printf("------ Install phase ------\n");
1513
+ printf("[ 0%%] Found %s connected through %s, beginning install\n", CFStringGetCStringPtr(device_full_name, CFStringGetSystemEncoding()), CFStringGetCStringPtr(device_interface_name, CFStringGetSystemEncoding()));
1514
+
1515
+ AMDeviceConnect(device);
1516
+ assert(AMDeviceIsPaired(device));
1517
+ assert(AMDeviceValidatePairing(device) == 0);
1518
+ assert(AMDeviceStartSession(device) == 0);
1519
+
1520
+
1521
+ // NOTE: the secure version doesn't seem to require us to start the AFC service
1522
+ service_conn_t afcFd;
1523
+ assert(AMDeviceSecureStartService(device, CFSTR("com.apple.afc"), NULL, &afcFd) == 0);
1524
+ assert(AMDeviceStopSession(device) == 0);
1525
+ assert(AMDeviceDisconnect(device) == 0);
1526
+
1527
+ CFStringRef keys[] = { CFSTR("PackageType") };
1528
+ CFStringRef values[] = { CFSTR("Developer") };
1529
+ CFDictionaryRef options = CFDictionaryCreate(NULL, (const void **)&keys, (const void **)&values, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1530
+
1531
+ //assert(AMDeviceTransferApplication(afcFd, path, NULL, transfer_callback, NULL) == 0);
1532
+ assert(AMDeviceSecureTransferPath(0, device, url, options, transfer_callback, 0)==0);
1533
+
1534
+ close(afcFd);
1535
+
1536
+
1537
+
1538
+ AMDeviceConnect(device);
1539
+ assert(AMDeviceIsPaired(device));
1540
+ assert(AMDeviceValidatePairing(device) == 0);
1541
+ assert(AMDeviceStartSession(device) == 0);
1542
+
1543
+ // // NOTE: the secure version doesn't seem to require us to start the installation_proxy service
1544
+ // // Although I can't find it right now, I in some code that the first param of AMDeviceSecureInstallApplication was a "dontStartInstallProxy"
1545
+ // // implying this is done for us by iOS already
1546
+
1547
+ //service_conn_t installFd;
1548
+ //assert(AMDeviceSecureStartService(device, CFSTR("com.apple.mobile.installation_proxy"), NULL, &installFd) == 0);
1549
+
1550
+ //mach_error_t result = AMDeviceInstallApplication(installFd, path, options, install_callback, NULL);
1551
+ mach_error_t result = AMDeviceSecureInstallApplication(0, device, url, options, install_callback, 0);
1552
+ if (result != 0)
1553
+ {
1554
+ char* error = "Unknown error.";
1555
+ if (result == 0xe8008015)
1556
+ error = "Your application failed code-signing checks. Check your certificates, provisioning profiles, and bundle ids.";
1557
+ printf("AMDeviceInstallApplication failed: 0x%X: %s\n", result, error);
1558
+ exit(exitcode_error);
1559
+ }
1560
+
1561
+ // close(installFd);
1562
+
1563
+ assert(AMDeviceStopSession(device) == 0);
1564
+ assert(AMDeviceDisconnect(device) == 0);
1565
+
1566
+ CFRelease(path);
1567
+ CFRelease(options);
1568
+
1569
+ printf("[100%%] Installed package %s\n", app_path);
1570
+ }
1571
+
1572
+ if (!debug)
1573
+ exit(0); // no debug phase
1574
+
1575
+ if (justlaunch)
1576
+ launch_debugger_and_exit(device, url);
1577
+ else
1578
+ launch_debugger(device, url);
1579
+ }
1580
+
1581
+ void device_callback(struct am_device_notification_callback_info *info, void *arg) {
1582
+ switch (info->msg) {
1583
+ case ADNCI_MSG_CONNECTED:
1584
+ if(device_id != NULL || !debug || AMDeviceGetInterfaceType(info->dev) != 2) {
1585
+ handle_device(info->dev);
1586
+ } else if(best_device_match == NULL) {
1587
+ best_device_match = info->dev;
1588
+ CFRetain(best_device_match);
1589
+ }
1590
+ default:
1591
+ break;
1592
+ }
1593
+ }
1594
+
1595
+ void timeout_callback(CFRunLoopTimerRef timer, void *info) {
1596
+ if ((!found_device) && (!detect_only)) {
1597
+ if(best_device_match != NULL) {
1598
+ handle_device(best_device_match);
1599
+
1600
+ CFRelease(best_device_match);
1601
+ best_device_match = NULL;
1602
+ }
1603
+
1604
+ if(!found_device) {
1605
+ printf("[....] Timed out waiting for device.\n");
1606
+ exit(exitcode_error);
1607
+ }
1608
+ }
1609
+ else
1610
+ {
1611
+ if (!debug) {
1612
+ printf("[....] No more devices found.\n");
1613
+ }
1614
+
1615
+ if (detect_only && !found_device) {
1616
+ exit(exitcode_error);
1617
+ return;
1618
+ } else {
1619
+ int mypid = getpid();
1620
+ if ((parent != 0) && (parent == mypid) && (child != 0))
1621
+ {
1622
+ if (verbose)
1623
+ {
1624
+ printf("Timeout. Killing child (%d) tree\n", child);
1625
+ }
1626
+ kill_ptree(child, SIGHUP);
1627
+ }
1628
+ }
1629
+ exit(0);
1630
+ }
1631
+ }
1632
+
1633
+ void usage(const char* app) {
1634
+ printf(
1635
+ "Usage: %s [OPTION]...\n"
1636
+ " -d, --debug launch the app in lldb after installation\n"
1637
+ " -i, --id <device_id> the id of the device to connect to\n"
1638
+ " -c, --detect only detect if the device is connected\n"
1639
+ " -b, --bundle <bundle.app> the path to the app bundle to be installed\n"
1640
+ " -a, --args <args> command line arguments to pass to the app when launching it\n"
1641
+ " -t, --timeout <timeout> number of seconds to wait for a device to be connected\n"
1642
+ " -u, --unbuffered don't buffer stdout\n"
1643
+ " -n, --nostart do not start the app when debugging\n"
1644
+ " -I, --noninteractive start in non interactive mode (quit when app crashes or exits)\n"
1645
+ " -L, --justlaunch just launch the app and exit lldb\n"
1646
+ " -v, --verbose enable verbose output\n"
1647
+ " -m, --noinstall directly start debugging without app install (-d not required)\n"
1648
+ " -p, --port <number> port used for device, default: 12345 \n"
1649
+ " -r, --uninstall uninstall the app before install (do not use with -m; app cache and data are cleared) \n"
1650
+ " -1, --bundle_id <bundle id> specify bundle id for list and upload\n"
1651
+ " -l, --list list files\n"
1652
+ " -o, --upload <file> upload file\n"
1653
+ " -w, --download download app tree\n"
1654
+ " -2, --to <target pathname> use together with up/download file/tree. specify target\n"
1655
+ " -V, --version print the executable version \n",
1656
+ app);
1657
+ }
1658
+
1659
+ void show_version() {
1660
+ printf("%s\n", APP_VERSION);
1661
+ }
1662
+
1663
+ int main(int argc, char *argv[]) {
1664
+ static struct option longopts[] = {
1665
+ { "debug", no_argument, NULL, 'd' },
1666
+ { "id", required_argument, NULL, 'i' },
1667
+ { "bundle", required_argument, NULL, 'b' },
1668
+ { "args", required_argument, NULL, 'a' },
1669
+ { "verbose", no_argument, NULL, 'v' },
1670
+ { "timeout", required_argument, NULL, 't' },
1671
+ { "unbuffered", no_argument, NULL, 'u' },
1672
+ { "nostart", no_argument, NULL, 'n' },
1673
+ { "noninteractive", no_argument, NULL, 'I' },
1674
+ { "justlaunch", no_argument, NULL, 'L' },
1675
+ { "detect", no_argument, NULL, 'c' },
1676
+ { "version", no_argument, NULL, 'V' },
1677
+ { "noinstall", no_argument, NULL, 'm' },
1678
+ { "port", required_argument, NULL, 'p' },
1679
+ { "uninstall", no_argument, NULL, 'r' },
1680
+ { "list", optional_argument, NULL, 'l' },
1681
+ { "bundle_id", required_argument, NULL, '1'},
1682
+ { "upload", required_argument, NULL, 'o'},
1683
+ { "download", optional_argument, NULL, 'w'},
1684
+ { "to", required_argument, NULL, '2'},
1685
+ { NULL, 0, NULL, 0 },
1686
+ };
1687
+ char ch;
1688
+
1689
+ while ((ch = getopt_long(argc, argv, "VmcdvunrILi:b:a:t:g:x:p:1:2:o:l::w::", longopts, NULL)) != -1)
1690
+ {
1691
+ switch (ch) {
1692
+ case 'm':
1693
+ install = 0;
1694
+ debug = 1;
1695
+ break;
1696
+ case 'd':
1697
+ debug = 1;
1698
+ break;
1699
+ case 'i':
1700
+ device_id = optarg;
1701
+ break;
1702
+ case 'b':
1703
+ app_path = optarg;
1704
+ break;
1705
+ case 'a':
1706
+ args = optarg;
1707
+ break;
1708
+ case 'v':
1709
+ verbose = 1;
1710
+ break;
1711
+ case 't':
1712
+ timeout = atoi(optarg);
1713
+ break;
1714
+ case 'u':
1715
+ unbuffered = 1;
1716
+ break;
1717
+ case 'n':
1718
+ nostart = 1;
1719
+ break;
1720
+ case 'I':
1721
+ interactive = false;
1722
+ break;
1723
+ case 'L':
1724
+ interactive = false;
1725
+ justlaunch = true;
1726
+ break;
1727
+ case 'c':
1728
+ detect_only = true;
1729
+ debug = 1;
1730
+ break;
1731
+ case 'V':
1732
+ show_version();
1733
+ return exitcode_error;
1734
+ case 'p':
1735
+ port = atoi(optarg);
1736
+ break;
1737
+ case 'r':
1738
+ uninstall = 1;
1739
+ break;
1740
+ case '1':
1741
+ bundle_id = optarg;
1742
+ break;
1743
+ case '2':
1744
+ target_filename = optarg;
1745
+ break;
1746
+ case 'o':
1747
+ command_only = true;
1748
+ upload_pathname = optarg;
1749
+ command = "upload";
1750
+ break;
1751
+ case 'l':
1752
+ command_only = true;
1753
+ command = "list";
1754
+ list_root = optarg;
1755
+ break;
1756
+ case 'w':
1757
+ command_only = true;
1758
+ command = "download";
1759
+ list_root = optarg;
1760
+ break;
1761
+ default:
1762
+ usage(argv[0]);
1763
+ return exitcode_error;
1764
+ }
1765
+ }
1766
+
1767
+ if (!app_path && !detect_only && !command_only) {
1768
+ usage(argv[0]);
1769
+ exit(exitcode_error);
1770
+ }
1771
+
1772
+ if (unbuffered) {
1773
+ setbuf(stdout, NULL);
1774
+ setbuf(stderr, NULL);
1775
+ }
1776
+
1777
+ if (detect_only && timeout == 0) {
1778
+ timeout = 5;
1779
+ }
1780
+
1781
+ if (app_path) {
1782
+ assert(access(app_path, F_OK) == 0);
1783
+ }
1784
+
1785
+ AMDSetLogLevel(5); // otherwise syslog gets flooded with crap
1786
+ if (timeout > 0)
1787
+ {
1788
+ CFRunLoopTimerRef timer = CFRunLoopTimerCreate(NULL, CFAbsoluteTimeGetCurrent() + timeout, 0, 0, 0, timeout_callback, NULL);
1789
+ CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopCommonModes);
1790
+ printf("[....] Waiting up to %d seconds for iOS device to be connected\n", timeout);
1791
+ }
1792
+ else
1793
+ {
1794
+ printf("[....] Waiting for iOS device to be connected\n");
1795
+ }
1796
+
1797
+ struct am_device_notification *notify;
1798
+ AMDeviceNotificationSubscribe(&device_callback, 0, 0, NULL, &notify);
1799
+ CFRunLoopRun();
1800
+ }
1801
+