ios-deploy 1.3.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+