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.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +22 -0
- data/README.md +12 -0
- data/Rakefile +2 -0
- data/bin/ios-deploy +6 -0
- data/ios-deploy.gemspec +23 -0
- data/lib/ios-deploy.rb +5 -0
- data/lib/ios-deploy/version.rb +3 -0
- data/node_modules/.bin/ios-deploy +0 -0
- data/node_modules/ios-deploy/.npmignore +6 -0
- data/node_modules/ios-deploy/CONTRIBUTING.md +31 -0
- data/node_modules/ios-deploy/Entitlements.plist +8 -0
- data/node_modules/ios-deploy/Info.plist +24 -0
- data/node_modules/ios-deploy/LICENSE +2 -0
- data/node_modules/ios-deploy/Makefile +35 -0
- data/node_modules/ios-deploy/MobileDevice.h +496 -0
- data/node_modules/ios-deploy/README.md +75 -0
- data/node_modules/ios-deploy/ResourceRules.plist +25 -0
- data/node_modules/ios-deploy/demo.c +9 -0
- data/node_modules/ios-deploy/ios-deploy +0 -0
- data/node_modules/ios-deploy/ios-deploy.c +1801 -0
- data/node_modules/ios-deploy/ios-deploy.dSYM/Contents/Info.plist +20 -0
- data/node_modules/ios-deploy/ios-deploy.dSYM/Contents/Resources/DWARF/ios-deploy +0 -0
- data/node_modules/ios-deploy/package.json +50 -0
- data/node_modules/ios-deploy/resources/buildbox/build.sh +3 -0
- data/package.json +15 -0
- metadata +101 -0
@@ -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>
|
Binary file
|
@@ -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, ¬ify);
|
1799
|
+
CFRunLoopRun();
|
1800
|
+
}
|
1801
|
+
|