Merge branch 'master' into deva/semver
diff --git a/README.md b/README.md
index 1376e4f..e712b0e 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,7 @@
* Mac OS X. Tested on 10.11 El Capitan, 10.12 Sierra, iOS 9.0 and iOS 10.0
* You need to have a valid iOS Development certificate installed.
-* Xcode 7 or greater should be installed (**NOT** Command Line Tools!)
+* Xcode 7 or greater should be installed (**NOT** just Command Line Tools!)
## Roadmap
@@ -65,6 +65,7 @@
-c, --detect only detect if the device is connected
-b, --bundle <bundle.app> the path to the app bundle to be installed
-a, --args <args> command line arguments to pass to the app when launching it
+ -s, --envs <envs> environment variables, space separated key-value pairs, to pass to the app when launching it
-t, --timeout <timeout> number of seconds to wait for a device to be connected
-u, --unbuffered don't buffer stdout
-n, --nostart do not start the app when debugging
@@ -76,9 +77,9 @@
-r, --uninstall uninstall the app before install (do not use with -m; app cache and data are cleared)
-9, --uninstall_only uninstall the app ONLY. Use only with -1 <bundle_id>
-1, --bundle_id <bundle id> specify bundle id for list and upload
- -l, --list list files
+ -l, --list[=<dir>] list all app files or the specified directory
-o, --upload <file> upload file
- -w, --download download app tree
+ -w, --download[=<path>] download app tree or the specified file/directory
-2, --to <target pathname> use together with up/download file/tree. specify target
-D, --mkdir <dir> make directory on device
-R, --rm <path> remove file or directory on device (directories must be empty)
@@ -86,6 +87,7 @@
-e, --exists check if the app with given bundle_id is installed or not
-B, --list_bundle_id list bundle_id
-W, --no-wifi ignore wifi devices
+ -O, --output <file> write stdout and stderr to this file
--detect_deadlocks <sec> start printing backtraces for all threads periodically after specific amount of seconds
## Examples
@@ -95,6 +97,9 @@
// deploy and debug your app to a connected device
ios-deploy --debug --bundle my.app
+ // deploy, debug and pass environment variables to a connected device
+ ios-deploy --debug --envs DYLD_PRINT_STATISTICS=1 --bundle my.app
+
// deploy and debug your app to a connected device, skipping any wi-fi connection (use USB)
ios-deploy --debug --bundle my.app --no-wifi
diff --git a/RELEASING.md b/RELEASING.md
new file mode 100644
index 0000000..f322b13
--- /dev/null
+++ b/RELEASING.md
@@ -0,0 +1,20 @@
+## 1. Increment a version
+
+```
+export PKG_VER=YOUR_VERSION_HERE
+./increment_version.sh $PKG_VER
+git commit -m "Incremented version to $PKG_VER" package.json src/ios-deploy/version.h
+```
+
+## 2. Tag a version
+
+```
+git tag $PKG_VER
+```
+
+## 3. Push version and tag
+
+```
+git push origin master
+git push origin $PKG_VER
+```
diff --git a/demo/Makefile b/demo/Makefile
index 319e9f8..43fae0c 100644
--- a/demo/Makefile
+++ b/demo/Makefile
@@ -1,7 +1,5 @@
-IOS_SDK_VERSION = 9.1
-
-IOS_CC = gcc -ObjC
-IOS_SDK = $(shell xcode-select --print-path)/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS$(IOS_SDK_VERSION).sdk
+IOS_CC = clang -ObjC
+IOS_SDK = $(shell xcrun --sdk iphoneos --show-sdk-path)
all: clean demo.app
diff --git a/increment_version.sh b/increment_version.sh
new file mode 100755
index 0000000..70c1cf6
--- /dev/null
+++ b/increment_version.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+if [ -z "$1" ]
+then
+ echo "Usage: $0 [version_to_set]"
+ exit 1
+fi
+
+echo "\"$1\"" > src/ios-deploy/version.h
+npm version --no-git-tag-version $1
diff --git a/ios-deploy.xcodeproj/project.pbxproj b/ios-deploy.xcodeproj/project.pbxproj
index 834978f..e549251 100644
--- a/ios-deploy.xcodeproj/project.pbxproj
+++ b/ios-deploy.xcodeproj/project.pbxproj
@@ -180,7 +180,6 @@
C0CD3D981F59D20100F954DB /* ShellScript */,
7E70898B1B587BF3004D23AA /* Frameworks */,
7EDCC3CC1C45DC89002F9851 /* Sources */,
- C0CD3D9B1F59DA8300F954DB /* Run Script */,
);
buildRules = (
);
@@ -251,7 +250,7 @@
};
buildConfigurationList = 7E7089891B587BF3004D23AA /* Build configuration list for PBXProject "ios-deploy" */;
compatibilityVersion = "Xcode 3.2";
- developmentRegion = English;
+ developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
@@ -330,21 +329,7 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
- shellScript = "mkdir -p \"${PROJECT_DIR}/_Frameworks\"\nrsync -r /System/Library/PrivateFrameworks/MobileDevice.framework \"${PROJECT_DIR}/_Frameworks\" --exclude=XPCServices --links\n";
- };
- C0CD3D9B1F59DA8300F954DB /* Run Script */ = {
- isa = PBXShellScriptBuildPhase;
- buildActionMask = 2147483647;
- files = (
- );
- inputPaths = (
- );
- name = "Run Script";
- outputPaths = (
- );
- runOnlyForDeploymentPostprocessing = 0;
- shellPath = /bin/sh;
- shellScript = "rm -rf \"${PROJECT_DIR}/_Frameworks\"\n";
+ shellScript = "mkdir -p \"${PROJECT_DIR}/_Frameworks\"\n\n# copy over MobileDevice.framework from its location\nMD_FRAMEWORK_LOCATION=/System/Library/PrivateFrameworks/MobileDevice.framework\nif [ -d \"$MD_FRAMEWORK_LOCATION\" ]; then\n rsync -r $MD_FRAMEWORK_LOCATION \"${PROJECT_DIR}/_Frameworks\" --exclude=XPCServices --links\nfi\n\n# check if this new location exists, if it does, copy it instead\nNEW_MD_FRAMEWORK_LOCATION=/Library/Apple/System/Library/PrivateFrameworks/MobileDevice.framework\nif [ -d \"$NEW_MD_FRAMEWORK_LOCATION\" ]; then\nrsync -r $NEW_MD_FRAMEWORK_LOCATION \"${PROJECT_DIR}/_Frameworks\" --exclude=XPCServices --links\nfi\n\n";
};
/* End PBXShellScriptBuildPhase section */
diff --git a/package.json b/package.json
index c937352..70a7c96 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "ios-deploy",
- "version": "1.9.4",
+ "version": "1.10.0-beta.1",
"os": [
"darwin"
],
@@ -26,7 +26,8 @@
"build-test": "npm run pycompile && xcodebuild -target ios-deploy-lib && xcodebuild test -scheme ios-deploy-tests",
"eslint": "eslint src/scripts/*.js",
"test": "npm run eslint && npm run build-test",
- "pycompile": "python -m py_compile src/scripts/*.py"
+ "pycompile": "python -m py_compile src/scripts/*.py",
+ "postversion": "echo \\\"$npm_package_version\\\" > src/ios-deploy/version.h"
},
"keywords": [
"ios-deploy",
diff --git a/src/ios-deploy/MobileDevice.h b/src/ios-deploy/MobileDevice.h
index 89b7d5a..2646bcd 100644
--- a/src/ios-deploy/MobileDevice.h
+++ b/src/ios-deploy/MobileDevice.h
@@ -66,6 +66,7 @@
typedef unsigned int afc_error_t;
typedef unsigned int usbmux_error_t;
typedef unsigned int service_conn_t;
+typedef service_conn_t * ServiceConnRef;
struct am_recovery_device;
@@ -142,6 +143,8 @@
unsigned int context; /* 40 */
} __attribute__ ((packed)) afc_connection;
+typedef struct afc_connection * AFCConnectionRef;
+
typedef struct afc_directory {
unsigned char unknown[0]; /* size unknown */
} __attribute__ ((packed)) afc_directory;
@@ -258,10 +261,10 @@
*/
mach_error_t AMDeviceStartService(struct am_device *device, CFStringRef
- service_name, service_conn_t *handle, unsigned int *
+ service_name, ServiceConnRef handle, unsigned int *
unknown);
-mach_error_t AMDeviceStartHouseArrestService(struct am_device *device, CFStringRef identifier, void *unknown, service_conn_t *handle, unsigned int *what);
+mach_error_t AMDeviceStartHouseArrestService(struct am_device *device, CFStringRef identifier, void *unknown, ServiceConnRef handle, unsigned int *what);
/* Stops a session. You should do this before accessing services.
*
@@ -281,10 +284,10 @@
*/
afc_error_t AFCConnectionOpen(service_conn_t handle, unsigned int io_timeout,
- struct afc_connection **conn);
+ AFCConnectionRef *conn);
/* Pass in a pointer to an afc_device_info structure. It will be filled. */
-afc_error_t AFCDeviceInfoOpen(afc_connection *conn, struct
+afc_error_t AFCDeviceInfoOpen(AFCConnectionRef conn, struct
afc_dictionary **info);
/* Turns debug mode on if the environment variable AFCDEBUG is set to a numeric
@@ -300,7 +303,7 @@
* MDERR_OK if successful
*/
-afc_error_t AFCDirectoryOpen(afc_connection *conn, const char *path,
+afc_error_t AFCDirectoryOpen(AFCConnectionRef conn, const char *path,
struct afc_directory **dir);
/* Acquires the next entry in a directory previously opened with
@@ -313,30 +316,30 @@
* MDERR_OK if successful, even if no entries remain
*/
-afc_error_t AFCDirectoryRead(afc_connection *conn/*unsigned int unused*/, struct afc_directory *dir,
+afc_error_t AFCDirectoryRead(AFCConnectionRef conn/*unsigned int unused*/, struct afc_directory *dir,
char **dirent);
-afc_error_t AFCDirectoryClose(afc_connection *conn, struct afc_directory *dir);
-afc_error_t AFCDirectoryCreate(afc_connection *conn, const char *dirname);
-afc_error_t AFCRemovePath(afc_connection *conn, const char *dirname);
-afc_error_t AFCRenamePath(afc_connection *conn, const char *from, const char *to);
-afc_error_t AFCLinkPath(afc_connection *conn, long long int linktype, const char *target, const char *linkname);
+afc_error_t AFCDirectoryClose(AFCConnectionRef conn, struct afc_directory *dir);
+afc_error_t AFCDirectoryCreate(AFCConnectionRef conn, const char *dirname);
+afc_error_t AFCRemovePath(AFCConnectionRef conn, const char *dirname);
+afc_error_t AFCRenamePath(AFCConnectionRef conn, const char *from, const char *to);
+afc_error_t AFCLinkPath(AFCConnectionRef conn, long long int linktype, const char *target, const char *linkname);
/* Returns the context field of the given AFC connection. */
-unsigned int AFCConnectionGetContext(afc_connection *conn);
+unsigned int AFCConnectionGetContext(AFCConnectionRef conn);
/* Returns the fs_block_size field of the given AFC connection. */
-unsigned int AFCConnectionGetFSBlockSize(afc_connection *conn);
+unsigned int AFCConnectionGetFSBlockSize(AFCConnectionRef conn);
/* Returns the io_timeout field of the given AFC connection. In iTunes this is
* 0. */
-unsigned int AFCConnectionGetIOTimeout(afc_connection *conn);
+unsigned int AFCConnectionGetIOTimeout(AFCConnectionRef conn);
/* Returns the sock_block_size field of the given AFC connection. */
-unsigned int AFCConnectionGetSocketBlockSize(afc_connection *conn);
+unsigned int AFCConnectionGetSocketBlockSize(AFCConnectionRef conn);
/* Closes the given AFC connection. */
-afc_error_t AFCConnectionClose(afc_connection *conn);
+afc_error_t AFCConnectionClose(AFCConnectionRef conn);
/* Registers for device notifications related to the restore process. unknown0
* is zero when iTunes calls this. In iTunes,
@@ -392,19 +395,19 @@
* ------------------------------------------------------------------------- */
/* mode 2 = read, mode 3 = write */
-afc_error_t AFCFileRefOpen(afc_connection *conn, const char *path,
+afc_error_t AFCFileRefOpen(AFCConnectionRef conn, const char *path,
unsigned long long mode, afc_file_ref *ref);
-afc_error_t AFCFileRefSeek(afc_connection *conn, afc_file_ref ref,
+afc_error_t AFCFileRefSeek(AFCConnectionRef conn, afc_file_ref ref,
unsigned long long offset1, unsigned long long offset2);
-afc_error_t AFCFileRefRead(afc_connection *conn, afc_file_ref ref,
+afc_error_t AFCFileRefRead(AFCConnectionRef conn, afc_file_ref ref,
void *buf, size_t *len);
-afc_error_t AFCFileRefSetFileSize(afc_connection *conn, afc_file_ref ref,
+afc_error_t AFCFileRefSetFileSize(AFCConnectionRef conn, afc_file_ref ref,
unsigned long long offset);
-afc_error_t AFCFileRefWrite(afc_connection *conn, afc_file_ref ref,
+afc_error_t AFCFileRefWrite(AFCConnectionRef conn, afc_file_ref ref,
const void *buf, size_t len);
-afc_error_t AFCFileRefClose(afc_connection *conn, afc_file_ref ref);
+afc_error_t AFCFileRefClose(AFCConnectionRef conn, afc_file_ref ref);
-afc_error_t AFCFileInfoOpen(afc_connection *conn, const char *path, struct
+afc_error_t AFCFileInfoOpen(AFCConnectionRef conn, const char *path, struct
afc_dictionary **info);
afc_error_t AFCKeyValueRead(struct afc_dictionary *dict, char **key, char **
val);
@@ -429,7 +432,7 @@
mach_error_t AMDeviceDisconnect(struct am_device *device);
mach_error_t AMDeviceRetain(struct am_device *device);
mach_error_t AMDeviceRelease(struct am_device *device);
-CFStringRef AMDeviceCopyValue(struct am_device *device, unsigned int, CFStringRef cfstring);
+CFStringRef AMDeviceCopyValue(struct am_device *device, void*, CFStringRef cfstring);
CFStringRef AMDeviceCopyDeviceIdentifier(struct am_device *device);
typedef void (*notify_callback)(CFStringRef notification, void *data);
diff --git a/src/ios-deploy/device_db.h b/src/ios-deploy/device_db.h
index 489cf36..3758138 100644
--- a/src/ios-deploy/device_db.h
+++ b/src/ios-deploy/device_db.h
@@ -31,6 +31,7 @@
ADD_DEVICE("N78AP", "iPod Touch 5G", "iphoneos", "armv7"),
ADD_DEVICE("N78AAP", "iPod Touch 5G", "iphoneos", "armv7"),
ADD_DEVICE("N102AP", "iPod Touch 6G", "iphoneos", "arm64"),
+ ADD_DEVICE("N112AP", "iPod Touch 7G", "iphoneos", "arm64"),
// iPad
@@ -45,6 +46,7 @@
ADD_DEVICE("P101AP", "iPad 4", "iphoneos", "armv7s"),
ADD_DEVICE("P102AP", "iPad 4 (GSM)", "iphoneos", "armv7s"),
ADD_DEVICE("P103AP", "iPad 4 (CDMA)", "iphoneos", "armv7s"),
+ ADD_DEVICE("J71bAP", "iPad 6", "iphoneos", "arm64"),
ADD_DEVICE("J71AP", "iPad Air", "iphoneos", "arm64"),
ADD_DEVICE("J72AP", "iPad Air (GSM)", "iphoneos", "arm64"),
ADD_DEVICE("J73AP", "iPad Air (CDMA)", "iphoneos", "arm64"),
@@ -55,6 +57,10 @@
ADD_DEVICE("J71tAP", "iPad (2017)", "iphoneos", "arm64"),
ADD_DEVICE("J72sAP", "iPad (2017)", "iphoneos", "arm64"),
ADD_DEVICE("J72tAP", "iPad (2017)", "iphoneos", "arm64"),
+ ADD_DEVICE("J71bAP", "iPad (2018)", "iphoneos", "arm64"),
+ ADD_DEVICE("J72bAP", "iPad (2018)", "iphoneos", "arm64"),
+ ADD_DEVICE("J217AP", "iPad Air 3", "iphoneos", "arm64e"),
+ ADD_DEVICE("J218AP", "iPad Air 3 (Cellular)", "iphoneos", "arm64e"),
// iPad Pro
@@ -66,6 +72,22 @@
ADD_DEVICE("J128AP", "iPad Pro (9.7\")", "iphoneos", "arm64"),
ADD_DEVICE("J207AP", "iPad Pro (10.5\")", "iphoneos", "arm64"),
ADD_DEVICE("J208AP", "iPad Pro (10.5\")", "iphoneos", "arm64"),
+ ADD_DEVICE("J317AP", "iPad Pro (11\")", "iphoneos", "arm64e"),
+ ADD_DEVICE("J317xAP", "iPad Pro (11\")", "iphoneos", "arm64e"),
+ ADD_DEVICE("J318AP", "iPad Pro (11\")", "iphoneos", "arm64e"),
+ ADD_DEVICE("J318xAP", "iPad Pro (11\")", "iphoneos", "arm64e"),
+ ADD_DEVICE("J318AP", "iPad Pro (11\")", "iphoneos", "arm64e"),
+ ADD_DEVICE("J318xAP", "iPad Pro (11\")", "iphoneos", "arm64e"),
+ ADD_DEVICE("J318AP", "iPad Pro (11\")", "iphoneos", "arm64e"),
+ ADD_DEVICE("J318xAP", "iPad Pro (11\")", "iphoneos", "arm64e"),
+ ADD_DEVICE("J320AP", "iPad Pro 3G (12.9\")", "iphoneos", "arm64e"),
+ ADD_DEVICE("J320xAP", "iPad Pro 3G (12.9\")", "iphoneos", "arm64e"),
+ ADD_DEVICE("J321AP", "iPad Pro 3G (12.9\")", "iphoneos", "arm64e"),
+ ADD_DEVICE("J321xAP", "iPad Pro 3G (12.9\")", "iphoneos", "arm64e"),
+ ADD_DEVICE("J321AP", "iPad Pro 3G (12.9\")", "iphoneos", "arm64e"),
+ ADD_DEVICE("J321xAP", "iPad Pro 3G (12.9\")", "iphoneos", "arm64e"),
+ ADD_DEVICE("J321AP", "iPad Pro 3G (12.9\")", "iphoneos", "arm64e"),
+ ADD_DEVICE("J321xAP", "iPad Pro 3G (12.9\")", "iphoneos", "arm64e"),
// iPad Mini
@@ -80,6 +102,8 @@
ADD_DEVICE("J87MAP", "iPad mini 3 (CDMA)", "iphoneos", "arm64"),
ADD_DEVICE("J96AP", "iPad mini 4", "iphoneos", "arm64"),
ADD_DEVICE("J97AP", "iPad mini 4 (GSM)", "iphoneos", "arm64"),
+ ADD_DEVICE("J210AP", "iPad mini 5", "iphoneos", "arm64e"),
+ ADD_DEVICE("J211AP", "iPad mini 5 (Cellular)", "iphoneos", "arm64e"),
// iPhone
@@ -108,6 +132,19 @@
ADD_DEVICE("D101AP", "iPhone 7", "iphoneos", "arm64"),
ADD_DEVICE("D11AP", "iPhone 7 Plus", "iphoneos", "arm64"),
ADD_DEVICE("D111AP", "iPhone 7 Plus", "iphoneos", "arm64"),
+ ADD_DEVICE("D20AP", "iPhone 8", "iphoneos", "arm64"),
+ ADD_DEVICE("D20AAP", "iPhone 8", "iphoneos", "arm64"),
+ ADD_DEVICE("D201AP", "iPhone 8", "iphoneos", "arm64"),
+ ADD_DEVICE("D201AAP","iPhone 8", "iphoneos", "arm64"),
+ ADD_DEVICE("D21AP", "iPhone 8 Plus", "iphoneos", "arm64"),
+ ADD_DEVICE("D21AAP", "iPhone 8 Plus", "iphoneos", "arm64"),
+ ADD_DEVICE("D211AP", "iPhone 8 Plus", "iphoneos", "arm64"),
+ ADD_DEVICE("D211AAP","iPhone 8 Plus", "iphoneos", "arm64"),
+ ADD_DEVICE("D22AP", "iPhone X", "iphoneos", "arm64"),
+ ADD_DEVICE("D221AP", "iPhone X", "iphoneos", "arm64"),
+ ADD_DEVICE("N841AP", "iPhone XR", "iphoneos", "arm64e"),
+ ADD_DEVICE("D321AP", "iPhone XS", "iphoneos", "arm64e"),
+ ADD_DEVICE("D331pAP","iPhone XS Max", "iphoneos", "arm64e"),
// Apple TV
@@ -115,4 +152,5 @@
ADD_DEVICE("J33AP", "Apple TV 3G", "appletvos", "armv7"),
ADD_DEVICE("J33IAP", "Apple TV 3.1G", "appletvos", "armv7"),
ADD_DEVICE("J42dAP", "Apple TV 4G", "appletvos", "arm64"),
+ ADD_DEVICE("J105aAP","Apple TV 4K", "appletvos", "arm64"),
};
diff --git a/src/ios-deploy/ios-deploy.m b/src/ios-deploy/ios-deploy.m
index 3efd346..29c2c33 100644
--- a/src/ios-deploy/ios-deploy.m
+++ b/src/ios-deploy/ios-deploy.m
@@ -20,7 +20,7 @@
#import "device_db.h"
#define PREP_CMDS_PATH @"/tmp/%@/fruitstrap-lldb-prep-cmds-"
-#define LLDB_SHELL @"lldb -s %@"
+#define LLDB_SHELL @"PATH=/usr/bin /usr/bin/lldb -s %@"
/*
* Startup script passed to lldb.
* To see how xcode interacts with lldb, put this into .lldbinit:
@@ -32,6 +32,8 @@
target create \"{disk_app}\"\n\
script fruitstrap_device_app=\"{device_app}\"\n\
script fruitstrap_connect_url=\"connect://127.0.0.1:{device_port}\"\n\
+ script fruitstrap_output_path=\"{output_path}\"\n\
+ script fruitstrap_error_path=\"{error_path}\"\n\
target modules search-paths add {modules_search_paths_pairs}\n\
command script import \"{python_file_path}\"\n\
command script add -f {python_command}.connect_command connect\n\
@@ -66,14 +68,18 @@
#include "lldb.py.h"
;
+const char* output_path = NULL;
+const char* error_path = NULL;
typedef struct am_device * AMDeviceRef;
-mach_error_t AMDeviceSecureStartService(struct am_device *device, CFStringRef service_name, unsigned int *unknown, service_conn_t *handle);
+mach_error_t AMDeviceSecureStartService(AMDeviceRef device, CFStringRef service_name, unsigned int *unknown, ServiceConnRef * handle);
+mach_error_t AMDeviceCreateHouseArrestService(AMDeviceRef device, CFStringRef identifier, void * unknown, AFCConnectionRef * handle);
+CFSocketNativeHandle AMDServiceConnectionGetSocket(ServiceConnRef con);
int AMDeviceSecureTransferPath(int zero, AMDeviceRef device, CFURLRef url, CFDictionaryRef options, void *callback, int cbarg);
int AMDeviceSecureInstallApplication(int zero, AMDeviceRef device, CFURLRef url, CFDictionaryRef options, void *callback, int cbarg);
int AMDeviceMountImage(AMDeviceRef device, CFStringRef image, CFDictionaryRef options, void *callback, int cbarg);
mach_error_t AMDeviceLookupApplications(AMDeviceRef device, CFDictionaryRef options, CFDictionaryRef *result);
-int AMDeviceGetInterfaceType(struct am_device *device);
+int AMDeviceGetInterfaceType(AMDeviceRef device);
bool found_device = false, debug = false, verbose = false, unbuffered = false, nostart = false, debugserver_only = false, detect_only = false, install = true, uninstall = false, no_wifi = false;
bool command_only = false;
@@ -86,9 +92,11 @@
char *app_path = NULL;
char *device_id = NULL;
char *args = NULL;
+char *envs = NULL;
char *list_root = NULL;
int _timeout = 0;
int _detectDeadlockTimeout = 0;
+bool _json_output = false;
int port = 0; // 0 means "dynamically assigned"
CFStringRef last_path = NULL;
service_conn_t gdbfd;
@@ -97,7 +105,6 @@
pid_t child = 0;
// Signal sent from child to parent process when LLDB finishes.
const int SIGLLDB = SIGUSR1;
-AMDeviceRef best_device_match = NULL;
NSString* tmpUUID;
struct am_device_notification *notify;
@@ -126,7 +133,9 @@
NSString* str = [[[NSString alloc] initWithFormat:format arguments:valist] autorelease];
va_end(valist);
- NSLog(@"[ !! ] %@", str);
+ if (!_json_output) {
+ NSLog(@"[ !! ] %@", str);
+ }
exit(exitcode_error);
}
@@ -149,14 +158,7 @@
}
void NSLogOut(NSString* format, ...) {
- va_list valist;
- va_start(valist, format);
- __NSLogOut(format, valist);
- va_end(valist);
-}
-
-void NSLogVerbose(NSString* format, ...) {
- if (verbose) {
+ if (!_json_output) {
va_list valist;
va_start(valist, format);
__NSLogOut(format, valist);
@@ -164,6 +166,30 @@
}
}
+void NSLogVerbose(NSString* format, ...) {
+ if (verbose && !_json_output) {
+ va_list valist;
+ va_start(valist, format);
+ __NSLogOut(format, valist);
+ va_end(valist);
+ }
+}
+
+void NSLogJSON(NSDictionary* jsonDict) {
+ if (_json_output) {
+ NSError *error;
+ NSData *data = [NSJSONSerialization dataWithJSONObject:jsonDict
+ options:NSJSONWritingPrettyPrinted
+ error:&error];
+ assert(data != NULL);
+ NSString *jsonString = @"{\"error\": \"JSON error\"}";
+ if (data) {
+ jsonString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
+ }
+ [jsonString writeToFile:@"/dev/stdout" atomically:NO encoding:NSUTF8StringEncoding error:nil];
+ }
+}
+
BOOL mkdirp(NSString* path) {
NSError* error = nil;
@@ -313,23 +339,6 @@
return res;
}
-char * MYCFStringCopyUTF8String(CFStringRef aString) {
- if (aString == NULL) {
- return NULL;
- }
-
- CFIndex length = CFStringGetLength(aString);
- CFIndex maxSize =
- CFStringGetMaximumSizeForEncoding(length,
- kCFStringEncodingUTF8);
- char *buffer = (char *)malloc(maxSize);
- if (CFStringGetCString(aString, buffer, maxSize,
- kCFStringEncodingUTF8)) {
- return buffer;
- }
- return NULL;
-}
-
CFStringRef get_device_full_name(const AMDeviceRef device) {
CFStringRef full_name = NULL,
device_udid = AMDeviceCopyDeviceIdentifier(device),
@@ -379,6 +388,49 @@
return full_name;
}
+NSDictionary* get_device_json_dict(const AMDeviceRef device) {
+ NSMutableDictionary *json_dict = [NSMutableDictionary new];
+ AMDeviceConnect(device);
+
+ CFStringRef device_udid = AMDeviceCopyDeviceIdentifier(device);
+ if (device_udid) {
+ [json_dict setValue:(__bridge NSString *)device_udid forKey:@"DeviceIdentifier"];
+ CFRelease(device_udid);
+ }
+
+ CFStringRef device_hardware_model = AMDeviceCopyValue(device, 0, CFSTR("HardwareModel"));
+ if (device_hardware_model) {
+ [json_dict setValue:(NSString*)device_hardware_model forKey:@"HardwareModel"];
+ size_t device_db_length = sizeof(device_db) / sizeof(device_desc);
+ for (size_t i = 0; i < device_db_length; i ++) {
+ if (CFStringCompare(device_hardware_model, device_db[i].model, kCFCompareNonliteral | kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
+ device_desc dev = device_db[i];
+ [json_dict setValue:(__bridge NSString *)dev.name forKey:@"modelName"];
+ [json_dict setValue:(__bridge NSString *)dev.sdk forKey:@"modelSDK"];
+ [json_dict setValue:(__bridge NSString *)dev.arch forKey:@"modelArch"];
+ break;
+ }
+ }
+ CFRelease(device_hardware_model);
+ }
+
+ for (NSString *deviceValue in @[@"DeviceName",
+ @"BuildVersion",
+ @"DeviceClass",
+ @"ProductType",
+ @"ProductVersion"]) {
+ CFStringRef cf_value = AMDeviceCopyValue(device, 0, (__bridge CFStringRef)deviceValue);
+ if (cf_value) {
+ [json_dict setValue:(__bridge NSString *)cf_value forKey:deviceValue];
+ CFRelease(cf_value);
+ }
+ }
+
+ AMDeviceDisconnect(device);
+
+ return json_dict;
+}
+
CFStringRef get_device_interface_name(const AMDeviceRef device) {
// AMDeviceGetInterfaceType(device) 0=Unknown, 1 = Direct/USB, 2 = Indirect/WIFI
switch(AMDeviceGetInterfaceType(device)) {
@@ -489,7 +541,8 @@
FILE* sig = fopen(CFStringGetCStringPtr(sig_path, kCFStringEncodingMacRoman), "rb");
void *sig_buf = malloc(128);
- assert(fread(sig_buf, 1, 128, sig) == 128);
+ size_t bytes_read = fread(sig_buf, 1, 128, sig);
+ assert( bytes_read == 128);
fclose(sig);
CFDataRef sig_data = CFDataCreateWithBytesNoCopy(NULL, sig_buf, 128, NULL);
CFRelease(sig_path);
@@ -505,6 +558,12 @@
} else if (result == 0xe8000076 /* already mounted */) {
NSLogOut(@"[ 95%%] Developer disk image already mounted");
} else {
+ if (result == 0xe80000e2 /* device locked */) {
+ NSLogOut(@"The device is locked.");
+ NSLogJSON(@{@"Event": @"Error",
+ @"Status": @"DeviceLocked"
+ });
+ }
on_error(@"Unable to mount developer disk image. (%x)", result);
}
@@ -521,7 +580,13 @@
CFStringRef path = CFDictionaryGetValue(dict, CFSTR("Path"));
if ((last_path == NULL || !CFEqual(path, last_path)) && !CFStringHasSuffix(path, CFSTR(".ipa"))) {
- NSLogOut(@"[%3d%%] Copying %@ to device", percent / 2, path);
+ int overall_percent = percent / 2;
+ NSLogOut(@"[%3d%%] Copying %@ to device", overall_percent, path);
+ NSLogJSON(@{@"Event": @"BundleCopy",
+ @"OverallPercent": @(overall_percent),
+ @"Percent": @(percent),
+ @"Path": (__bridge NSString *)path
+ });
}
if (last_path != NULL) {
@@ -538,7 +603,13 @@
CFStringRef status = CFDictionaryGetValue(dict, CFSTR("Status"));
CFNumberGetValue(CFDictionaryGetValue(dict, CFSTR("PercentComplete")), kCFNumberSInt32Type, &percent);
- NSLogOut(@"[%3d%%] %@", (percent / 2) + 50, status);
+ int overall_percent = (percent / 2) + 50;
+ NSLogOut(@"[%3d%%] %@", overall_percent, status);
+ NSLogJSON(@{@"Event": @"BundleInstall",
+ @"OverallPercent": @(overall_percent),
+ @"Percent": @(percent),
+ @"Status": (__bridge NSString *)status
+ });
return 0;
}
@@ -651,6 +722,22 @@
//printf("write_lldb_prep_cmds: [%s][%s]\n", CFStringGetCStringPtr (cmds,kCFStringEncodingMacRoman),
// CFStringGetCStringPtr(pmodule, kCFStringEncodingMacRoman));
}
+
+ if (envs) {
+ CFStringRef cf_envs = CFStringCreateWithCString(NULL, envs, kCFStringEncodingUTF8);
+ CFStringFindAndReplace(cmds, CFSTR("{envs}"), cf_envs, range, 0);
+ rangeLLDB.length = CFStringGetLength(pmodule);
+ CFStringFindAndReplace(pmodule, CFSTR("{envs}"), cf_envs, rangeLLDB, 0);
+
+ //printf("write_lldb_prep_cmds:envs: [%s][%s]\n", CFStringGetCStringPtr (cmds,kCFStringEncodingMacRoman),
+ // CFStringGetCStringPtr(pmodule, kCFStringEncodingMacRoman));
+ CFRelease(cf_envs);
+ } else {
+ CFStringFindAndReplace(cmds, CFSTR("{envs}"), CFSTR(""), range, 0);
+ CFStringFindAndReplace(pmodule, CFSTR("{envs}"), CFSTR(""), rangeLLDB, 0);
+ //printf("write_lldb_prep_cmds: [%s][%s]\n", CFStringGetCStringPtr (cmds,kCFStringEncodingMacRoman),
+ // CFStringGetCStringPtr(pmodule, kCFStringEncodingMacRoman));
+ }
range.length = CFStringGetLength(cmds);
CFStringRef bundle_identifier = copy_disk_app_identifier(disk_app_url);
@@ -667,6 +754,21 @@
CFStringFindAndReplace(cmds, CFSTR("{device_port}"), device_port, range, 0);
range.length = CFStringGetLength(cmds);
+ if (output_path) {
+ CFStringRef output_path_str = CFStringCreateWithFormat(NULL, NULL, CFSTR("%s"), output_path);
+ CFStringFindAndReplace(cmds, CFSTR("{output_path}"), output_path_str, range, 0);
+ } else {
+ CFStringFindAndReplace(cmds, CFSTR("{output_path}"), CFSTR(""), range, 0);
+ }
+ range.length = CFStringGetLength(cmds);
+ if (error_path) {
+ CFStringRef error_path_str = CFStringCreateWithFormat(NULL, NULL, CFSTR("%s"), error_path);
+ CFStringFindAndReplace(cmds, CFSTR("{error_path}"), error_path_str, range, 0);
+ } else {
+ CFStringFindAndReplace(cmds, CFSTR("{error_path}"), CFSTR(""), range, 0);
+ }
+ range.length = CFStringGetLength(cmds);
+
CFURLRef device_container_url = CFURLCreateCopyDeletingLastPathComponent(NULL, device_app_url);
CFStringRef device_container_path = CFURLCopyFileSystemPath(device_container_url, kCFURLPOSIXPathStyle);
CFMutableStringRef dcp_noprivate = CFStringCreateMutableCopy(NULL, 0, device_container_path);
@@ -795,9 +897,11 @@
void start_remote_debug_server(AMDeviceRef device) {
- check_error(AMDeviceStartService(device, CFSTR("com.apple.debugserver"), &gdbfd, NULL));
- assert(gdbfd > 0);
+ ServiceConnRef con;
+ check_error(AMDeviceSecureStartService(device, CFSTR("com.apple.debugserver"), NULL, &con));
+ assert(con != NULL);
+ gdbfd = AMDServiceConnectionGetSocket(con);
/*
* The debugserver connection is through a fd handle, while lldb requires a host/port to connect, so create an intermediate
* socket to transfer data.
@@ -911,12 +1015,6 @@
NSLogOut(@"------ Debug phase ------");
- if(AMDeviceGetInterfaceType(device) == 2)
- {
- NSLogOut(@"Cannot debug %@ over %@.", device_full_name, device_interface_name);
- exit(0);
- }
-
NSLogOut(@"Starting debug of %@ connected through %@...", device_full_name, device_interface_name);
mount_developer_image(device); // put debugserver on the device
@@ -1035,6 +1133,10 @@
NSLogOut(@"debugserver port: %d", port);
NSLogOut(@"App path: %@", device_app_path);
+ NSLogJSON(@{@"Event": @"DebugServerLaunched",
+ @"Port": @(port),
+ @"Path": (__bridge NSString *)device_app_path
+ });
}
CFStringRef get_bundle_id(CFURLRef app_url)
@@ -1074,17 +1176,11 @@
}
-void read_dir(service_conn_t afcFd, afc_connection* afc_conn_p, const char* dir,
- void(*callback)(afc_connection *conn,const char *dir,int file))
+void read_dir(AFCConnectionRef afc_conn_p, const char* dir,
+ void(*callback)(AFCConnectionRef conn, const char *dir, int file))
{
char *dir_ent;
- afc_connection afc_conn;
- if (!afc_conn_p) {
- afc_conn_p = &afc_conn;
- AFCConnectionOpen(afcFd, 0, &afc_conn_p);
- }
-
afc_dictionary* afc_dict_p;
char *key, *val;
int not_dir = 0;
@@ -1138,7 +1234,7 @@
if (dir_joined[strlen(dir)-1] != '/')
strcat(dir_joined, "/");
strcat(dir_joined, dir_ent);
- read_dir(afcFd, afc_conn_p, dir_joined, callback);
+ read_dir(afc_conn_p, dir_joined, callback);
free(dir_joined);
}
@@ -1147,20 +1243,20 @@
// Used to send files to app-specific sandbox (Documents dir)
-service_conn_t start_house_arrest_service(AMDeviceRef device) {
+AFCConnectionRef start_house_arrest_service(AMDeviceRef device) {
AMDeviceConnect(device);
assert(AMDeviceIsPaired(device));
check_error(AMDeviceValidatePairing(device));
check_error(AMDeviceStartSession(device));
- service_conn_t houseFd;
+ AFCConnectionRef conn = NULL;
if (bundle_id == NULL) {
on_error(@"Bundle id is not specified");
}
CFStringRef cf_bundle_id = CFStringCreateWithCString(NULL, bundle_id, kCFStringEncodingUTF8);
- if (AMDeviceStartHouseArrestService(device, cf_bundle_id, 0, &houseFd, 0) != 0)
+ if (AMDeviceCreateHouseArrestService(device, cf_bundle_id, 0, &conn) != 0)
{
on_error(@"Unable to find bundle with id: %@", [NSString stringWithUTF8String:bundle_id]);
}
@@ -1169,7 +1265,7 @@
check_error(AMDeviceDisconnect(device));
CFRelease(cf_bundle_id);
- return houseFd;
+ return conn;
}
char const* get_filename_from_path(char const* path)
@@ -1208,16 +1304,12 @@
fclose(fd);
return content;
}
-
void list_files(AMDeviceRef device)
{
- service_conn_t houseFd = start_house_arrest_service(device);
-
- afc_connection* afc_conn_p;
- if (AFCConnectionOpen(houseFd, 0, &afc_conn_p) == 0) {
- read_dir(houseFd, afc_conn_p, list_root?list_root:"/", NULL);
- AFCConnectionClose(afc_conn_p);
- }
+ AFCConnectionRef afc_conn_p = start_house_arrest_service(device);
+ assert(afc_conn_p);
+ read_dir(afc_conn_p, list_root?list_root:"/", NULL);
+ check_error(AFCConnectionClose(afc_conn_p));
}
int app_exists(AMDeviceRef device)
@@ -1250,6 +1342,22 @@
return -1;
}
+void get_battery_level(AMDeviceRef device)
+{
+
+ AMDeviceConnect(device);
+ assert(AMDeviceIsPaired(device));
+ check_error(AMDeviceValidatePairing(device));
+ check_error(AMDeviceStartSession(device));
+
+ CFStringRef result = AMDeviceCopyValue(device, (void*)@"com.apple.mobile.battery", (__bridge CFStringRef)@"BatteryCurrentCapacity");
+ NSLogOut(@"BatteryCurrentCapacity:%@",result);
+ CFRelease(result);
+
+ check_error(AMDeviceStopSession(device));
+ check_error(AMDeviceDisconnect(device));
+}
+
void list_bundle_id(AMDeviceRef device)
{
AMDeviceConnect(device);
@@ -1275,7 +1383,7 @@
check_error(AMDeviceDisconnect(device));
}
-void copy_file_callback(afc_connection* afc_conn_p, const char *name,int file)
+void copy_file_callback(AFCConnectionRef afc_conn_p, const char *name,int file)
{
const char *local_name=name;
@@ -1318,8 +1426,8 @@
void download_tree(AMDeviceRef device)
{
- service_conn_t houseFd = start_house_arrest_service(device);
- afc_connection* afc_conn_p = NULL;
+ AFCConnectionRef afc_conn_p = start_house_arrest_service(device);
+ assert(afc_conn_p);
char *dirname = NULL;
list_root = list_root? list_root : "/";
@@ -1328,40 +1436,33 @@
NSString* targetPath = [NSString pathWithComponents:@[ @(target_filename), @(list_root)] ];
mkdirp([targetPath stringByDeletingLastPathComponent]);
- if (AFCConnectionOpen(houseFd, 0, &afc_conn_p) == 0) do {
-
- if (target_filename) {
- dirname = strdup(target_filename);
- mkdirp(@(dirname));
- if (mkdir(dirname,0777) && errno!=EEXIST) {
- fprintf(stderr,"mkdir(\"%s\") failed: %s\n",dirname,strerror(errno));
- break;
+ do {
+ if (target_filename) {
+ dirname = strdup(target_filename);
+ mkdirp(@(dirname));
+ if (mkdir(dirname,0777) && errno!=EEXIST) {
+ fprintf(stderr,"mkdir(\"%s\") failed: %s\n",dirname,strerror(errno));
+ break;
+ }
+ if (chdir(dirname)) {
+ fprintf(stderr,"chdir(\"%s\") failed: %s\n",dirname,strerror(errno));
+ break;
+ }
}
- if (chdir(dirname)) {
- fprintf(stderr,"chdir(\"%s\") failed: %s\n",dirname,strerror(errno));
- break;
- }
- }
-
- read_dir(houseFd, afc_conn_p, list_root, copy_file_callback);
-
+ read_dir(afc_conn_p, list_root, copy_file_callback);
} while(0);
if (dirname) free(dirname);
if (afc_conn_p) AFCConnectionClose(afc_conn_p);
}
-void upload_dir(AMDeviceRef device, afc_connection* afc_conn_p, NSString* source, NSString* destination);
-void upload_single_file(AMDeviceRef device, afc_connection* afc_conn_p, NSString* sourcePath, NSString* destinationPath);
+void upload_dir(AMDeviceRef device, AFCConnectionRef afc_conn_p, NSString* source, NSString* destination);
+void upload_single_file(AMDeviceRef device, AFCConnectionRef afc_conn_p, NSString* sourcePath, NSString* destinationPath);
void upload_file(AMDeviceRef device)
{
- service_conn_t houseFd = start_house_arrest_service(device);
-
- afc_connection afc_conn;
- afc_connection* afc_conn_p = &afc_conn;
- AFCConnectionOpen(houseFd, 0, &afc_conn_p);
-
+ AFCConnectionRef afc_conn_p = start_house_arrest_service(device);
+ assert(afc_conn_p);
// read_dir(houseFd, NULL, "/", NULL);
if (!target_filename)
@@ -1386,10 +1487,10 @@
{
upload_single_file(device, afc_conn_p, sourcePath, destinationPath);
}
- assert(AFCConnectionClose(afc_conn_p) == 0);
+ check_error(AFCConnectionClose(afc_conn_p));
}
-void upload_single_file(AMDeviceRef device, afc_connection* afc_conn_p, NSString* sourcePath, NSString* destinationPath) {
+void upload_single_file(AMDeviceRef device, AFCConnectionRef afc_conn_p, NSString* sourcePath, NSString* destinationPath) {
afc_file_ref file_ref;
@@ -1418,13 +1519,13 @@
on_error(@"Target %@ is a directory.", destinationPath);
}
assert(ret == 0);
- assert(AFCFileRefWrite(afc_conn_p, file_ref, file_content, file_size) == 0);
- assert(AFCFileRefClose(afc_conn_p, file_ref) == 0);
+ check_error(AFCFileRefWrite(afc_conn_p, file_ref, file_content, file_size));
+ check_error(AFCFileRefClose(afc_conn_p, file_ref));
free(file_content);
}
-void upload_dir(AMDeviceRef device, afc_connection* afc_conn_p, NSString* source, NSString* destination)
+void upload_dir(AMDeviceRef device, AFCConnectionRef afc_conn_p, NSString* source, NSString* destination)
{
check_error(AFCDirectoryCreate(afc_conn_p, [destination fileSystemRepresentation]));
destination = [destination copy];
@@ -1446,26 +1547,17 @@
}
void make_directory(AMDeviceRef device) {
- service_conn_t houseFd = start_house_arrest_service(device);
-
- afc_connection afc_conn;
- afc_connection* afc_conn_p = &afc_conn;
- AFCConnectionOpen(houseFd, 0, &afc_conn_p);
-
- assert(AFCDirectoryCreate(afc_conn_p, target_filename) == 0);
- assert(AFCConnectionClose(afc_conn_p) == 0);
+ AFCConnectionRef afc_conn_p = start_house_arrest_service(device);
+ assert(afc_conn_p);
+ check_error(AFCDirectoryCreate(afc_conn_p, target_filename));
+ check_error(AFCConnectionClose(afc_conn_p));
}
void remove_path(AMDeviceRef device) {
- service_conn_t houseFd = start_house_arrest_service(device);
-
- afc_connection afc_conn;
- afc_connection* afc_conn_p = &afc_conn;
- AFCConnectionOpen(houseFd, 0, &afc_conn_p);
-
-
- assert(AFCRemovePath(afc_conn_p, target_filename) == 0);
- assert(AFCConnectionClose(afc_conn_p) == 0);
+ AFCConnectionRef afc_conn_p = start_house_arrest_service(device);
+ assert(afc_conn_p);
+ check_error(AFCRemovePath(afc_conn_p, target_filename));
+ check_error(AFCConnectionClose(afc_conn_p));
}
void uninstall_app(AMDeviceRef device) {
@@ -1509,7 +1601,13 @@
device_interface_name = get_device_interface_name(device);
if (detect_only) {
- NSLogOut(@"[....] Found %@ connected through %@.", device_full_name, device_interface_name);
+ if (_json_output) {
+ NSLogJSON(@{@"Event": @"DeviceDetected",
+ @"Device": get_device_json_dict(device)
+ });
+ } else {
+ NSLogOut(@"[....] Found %@ connected through %@.", device_full_name, device_interface_name);
+ }
found_device = true;
return;
}
@@ -1523,7 +1621,8 @@
return;
}
} else {
- device_id = MYCFStringCopyUTF8String(found_device_id);
+ // Use the first device we find if a device_id wasn't specified.
+ device_id = strdup(CFStringGetCStringPtr(found_device_id, kCFStringEncodingUTF8));
found_device = true;
}
@@ -1546,6 +1645,8 @@
uninstall_app(device);
} else if (strcmp("list_bundle_id", command) == 0) {
list_bundle_id(device);
+ } else if (strcmp("get_battery_level", command) == 0) {
+ get_battery_level(device);
}
exit(0);
}
@@ -1601,7 +1702,7 @@
// NOTE: the secure version doesn't seem to require us to start the AFC service
- service_conn_t afcFd;
+ ServiceConnRef afcFd;
check_error(AMDeviceSecureStartService(device, CFSTR("com.apple.afc"), NULL, &afcFd));
check_error(AMDeviceStopSession(device));
check_error(AMDeviceDisconnect(device));
@@ -1612,8 +1713,7 @@
//assert(AMDeviceTransferApplication(afcFd, path, NULL, transfer_callback, NULL) == 0);
check_error(AMDeviceSecureTransferPath(0, device, url, options, transfer_callback, 0));
-
- close(afcFd);
+ close(*afcFd);
@@ -1641,6 +1741,11 @@
CFRelease(options);
NSLogOut(@"[100%%] Installed package %@", [NSString stringWithUTF8String:app_path]);
+ NSLogJSON(@{@"Event": @"BundleInstall",
+ @"OverallPercent": @(100),
+ @"Percent": @(100),
+ @"Status": @"Complete"
+ });
}
if (!debug)
@@ -1658,20 +1763,14 @@
void device_callback(struct am_device_notification_callback_info *info, void *arg) {
switch (info->msg) {
case ADNCI_MSG_CONNECTED:
- if(device_id != NULL || !debug || AMDeviceGetInterfaceType(info->dev) != 2) {
- if (no_wifi && AMDeviceGetInterfaceType(info->dev) == 2)
- {
- NSLogVerbose(@"Skipping wifi device (type: %d)", AMDeviceGetInterfaceType(info->dev));
- }
- else
- {
- NSLogVerbose(@"Handling device type: %d", AMDeviceGetInterfaceType(info->dev));
- handle_device(info->dev);
- }
- } else if(best_device_match == NULL) {
- NSLogVerbose(@"Best device match: %d", AMDeviceGetInterfaceType(info->dev));
- best_device_match = info->dev;
- CFRetain(best_device_match);
+ if (no_wifi && AMDeviceGetInterfaceType(info->dev) == 2)
+ {
+ NSLogVerbose(@"Skipping wifi device (type: %d)", AMDeviceGetInterfaceType(info->dev));
+ }
+ else
+ {
+ NSLogVerbose(@"Handling device type: %d", AMDeviceGetInterfaceType(info->dev));
+ handle_device(info->dev);
}
default:
break;
@@ -1685,21 +1784,11 @@
return;
// App running for too long
- NSLog(@"[ !! ] App is running for too long");
+ NSLogOut(@"[ !! ] App is running for too long");
exit(exitcode_timeout);
return;
} else if ((!found_device) && (!detect_only)) {
- // Device not found timeout
- if (best_device_match != NULL) {
- NSLogVerbose(@"Handling best device match.");
- handle_device(best_device_match);
-
- CFRelease(best_device_match);
- best_device_match = NULL;
- }
-
- if (!found_device)
- on_error(@"Timed out waiting for device.");
+ on_error(@"Timed out waiting for device.");
}
else
{
@@ -1731,6 +1820,7 @@
@" -c, --detect only detect if the device is connected\n"
@" -b, --bundle <bundle.app> the path to the app bundle to be installed\n"
@" -a, --args <args> command line arguments to pass to the app when launching it\n"
+ @" -s, --envs <envs> environment variables, space separated key-value pairs, to pass to the app when launching it\n"
@" -t, --timeout <timeout> number of seconds to wait for a device to be connected\n"
@" -u, --unbuffered don't buffer stdout\n"
@" -n, --nostart do not start the app when debugging\n"
@@ -1743,9 +1833,9 @@
@" -r, --uninstall uninstall the app before install (do not use with -m; app cache and data are cleared) \n"
@" -9, --uninstall_only uninstall the app ONLY. Use only with -1 <bundle_id> \n"
@" -1, --bundle_id <bundle id> specify bundle id for list and upload\n"
- @" -l, --list list files\n"
+ @" -l, --list[=<dir>] list all app files or the specified directory\n"
@" -o, --upload <file> upload file\n"
- @" -w, --download download app tree\n"
+ @" -w, --download[=<path>] download app tree or the specified file/directory\n"
@" -2, --to <target pathname> use together with up/download file/tree. specify target\n"
@" -D, --mkdir <dir> make directory on device\n"
@" -R, --rm <path> remove file or directory on device (directories must be empty)\n"
@@ -1753,7 +1843,11 @@
@" -e, --exists check if the app with given bundle_id is installed or not \n"
@" -B, --list_bundle_id list bundle_id \n"
@" -W, --no-wifi ignore wifi devices\n"
- @" --detect_deadlocks <sec> start printing backtraces for all threads periodically after specific amount of seconds\n",
+ @" -C, --get_battery_level get battery current capacity \n"
+ @" -O, --output <file> write stdout to this file\n"
+ @" -E, --error_output <file> write stderr to this file\n"
+ @" --detect_deadlocks <sec> start printing backtraces for all threads periodically after specific amount of seconds\n"
+ @" -j, --json format output as JSON\n",
[NSString stringWithUTF8String:app]);
}
@@ -1776,6 +1870,7 @@
{ "id", required_argument, NULL, 'i' },
{ "bundle", required_argument, NULL, 'b' },
{ "args", required_argument, NULL, 'a' },
+ { "envs", required_argument, NULL, 's' },
{ "verbose", no_argument, NULL, 'v' },
{ "timeout", required_argument, NULL, 't' },
{ "unbuffered", no_argument, NULL, 'u' },
@@ -1799,12 +1894,16 @@
{ "exists", no_argument, NULL, 'e'},
{ "list_bundle_id", no_argument, NULL, 'B'},
{ "no-wifi", no_argument, NULL, 'W'},
+ { "get_battery_level", no_argument, NULL, 'C'},
+ { "output", required_argument, NULL, 'O' },
+ { "error_output", required_argument, NULL, 'E' },
{ "detect_deadlocks", required_argument, NULL, 1000 },
+ { "json", no_argument, NULL, 'j'},
{ NULL, 0, NULL, 0 },
};
int ch;
- while ((ch = getopt_long(argc, argv, "VmcdvunNrILeD:R:i:b:a:t:g:x:p:1:2:o:l::w::9::B::W", longopts, NULL)) != -1)
+ while ((ch = getopt_long(argc, argv, "VmcdvunrILeD:R:i:b:a:t:p:1:2:o:l:w:9BWjNs:OE:C", longopts, NULL)) != -1)
{
switch (ch) {
case 'm':
@@ -1823,6 +1922,9 @@
case 'a':
args = optarg;
break;
+ case 's':
+ envs = optarg;
+ break;
case 'v':
verbose = true;
break;
@@ -1907,9 +2009,22 @@
case 'W':
no_wifi = true;
break;
+ case 'C':
+ command_only = true;
+ command = "get_battery_level";
+ break;
+ case 'O':
+ output_path = optarg;
+ break;
+ case 'E':
+ error_path = optarg;
+ break;
case 1000:
_detectDeadlockTimeout = atoi(optarg);
break;
+ case 'j':
+ _json_output = true;
+ break;
default:
usage(argv[0]);
return exitcode_error;
diff --git a/src/ios-deploy/version.h b/src/ios-deploy/version.h
index 65be40a..84c8e7f 100644
--- a/src/ios-deploy/version.h
+++ b/src/ios-deploy/version.h
@@ -1 +1 @@
-"1.9.4"
+"1.10.0-beta.1"
diff --git a/src/scripts/check_reqs.js b/src/scripts/check_reqs.js
index 34e00db..d1c7b67 100755
--- a/src/scripts/check_reqs.js
+++ b/src/scripts/check_reqs.js
@@ -5,16 +5,10 @@
var child_process = require('child_process');
var semver = require('semver')
-var XCODEBUILD_MIN_VERSION = 7.0;
-var XCODEBUILD_NOT_FOUND_MESSAGE = util.format('Please install Xcode version %s or greater from the Mac App Store.', XCODEBUILD_MIN_VERSION);
+var XCODEBUILD_NOT_FOUND_MESSAGE = 'Please install Xcode from the Mac App Store.';
var TOOL = 'xcodebuild';
-var xcode_version = child_process.spawn(TOOL, ['-version']),
- version_string = '';
-
-xcode_version.stdout.on('data', function (data) {
- version_string += data;
-});
+var xcode_version = child_process.spawn(TOOL, ['-version']);
xcode_version.stderr.on('data', function (data) {
console.log('stderr: ' + data);
@@ -44,7 +38,7 @@
console.log('!!!! WARNING: `--unsafe-perm=true` flag when running `npm install`');
console.log('!!!! WARNING: or else it will fail.');
console.log('!!!! WARNING: link:');
- console.log('!!!! WARNING: https://github.com/phonegap/ios-deploy#os-x-1011-el-capitan-or-greater');
+ console.log('!!!! WARNING: https://github.com/ios-control/ios-deploy#os-x-1011-el-capitan-or-greater');
console.log('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!');
}
diff --git a/src/scripts/lldb.py b/src/scripts/lldb.py
index 3e149aa..04e6aca 100644
--- a/src/scripts/lldb.py
+++ b/src/scripts/lldb.py
@@ -5,6 +5,7 @@
import lldb
listener = None
+startup_error = lldb.SBError()
def connect_command(debugger, command, result, internal_dict):
# These two are passed in by the script which loads us
@@ -41,7 +42,6 @@
def run_command(debugger, command, result, internal_dict):
device_app = internal_dict['fruitstrap_device_app']
args = command.split('--',1)
- error = lldb.SBError()
lldb.target.modules[0].SetPlatformFileSpec(lldb.SBFileSpec(device_app))
args_arr = []
if len(args) > 1:
@@ -55,14 +55,20 @@
#This env variable makes NSLog, CFLog and os_log messages get mirrored to stderr
#https://stackoverflow.com/a/39581193
launchInfo.SetEnvironmentEntries(['OS_ACTIVITY_DT_MODE=enable'], True)
+
+ envs_arr = []
+ if len(args) > 1:
+ envs_arr = shlex.split(args[1])
+ envs_arr = envs_arr + shlex.split('{envs}')
+ launchInfo.SetEnvironmentEntries(envs_arr, True)
- lldb.target.Launch(launchInfo, error)
+ lldb.target.Launch(launchInfo, startup_error)
lockedstr = ': Locked'
- if lockedstr in str(error):
+ if lockedstr in str(startup_error):
print('\\nDevice Locked\\n')
os._exit(254)
else:
- print(str(error))
+ print(str(startup_error))
def safequit_command(debugger, command, result, internal_dict):
process = lldb.target.process
@@ -79,6 +85,19 @@
def autoexit_command(debugger, command, result, internal_dict):
global listener
process = lldb.target.process
+ if not startup_error.Success():
+ print('\\nPROCESS_NOT_STARTED\\n')
+ os._exit({exitcode_app_crash})
+
+ output_path = internal_dict['fruitstrap_output_path']
+ out = None
+ if output_path:
+ out = open(output_path, 'w')
+
+ error_path = internal_dict['fruitstrap_error_path']
+ err = None
+ if error_path:
+ err = open(error_path, 'w')
detectDeadlockTimeout = {detect_deadlock_timeout}
printBacktraceTime = time.time() + detectDeadlockTimeout if detectDeadlockTimeout > 0 else None
@@ -91,14 +110,26 @@
def ProcessSTDOUT():
stdout = process.GetSTDOUT(1024)
while stdout:
- sys.stdout.write(stdout)
+ if out:
+ out.write(stdout)
+ else:
+ sys.stdout.write(stdout)
stdout = process.GetSTDOUT(1024)
def ProcessSTDERR():
stderr = process.GetSTDERR(1024)
while stderr:
- sys.stdout.write(stderr)
+ if err:
+ err.write(stderr)
+ else:
+ sys.stdout.write(stderr)
stderr = process.GetSTDERR(1024)
+
+ def CloseOut():
+ if (out):
+ out.close()
+ if (err):
+ err.close()
while True:
if listener.WaitForEvent(1, event) and lldb.SBProcess.EventIsProcessEvent(event):
@@ -121,17 +152,21 @@
if state == lldb.eStateExited:
sys.stdout.write( '\\nPROCESS_EXITED\\n' )
+ CloseOut()
os._exit(process.GetExitStatus())
elif printBacktraceTime is None and state == lldb.eStateStopped:
sys.stdout.write( '\\nPROCESS_STOPPED\\n' )
debugger.HandleCommand('bt')
+ CloseOut()
os._exit({exitcode_app_crash})
elif state == lldb.eStateCrashed:
sys.stdout.write( '\\nPROCESS_CRASHED\\n' )
debugger.HandleCommand('bt')
+ CloseOut()
os._exit({exitcode_app_crash})
elif state == lldb.eStateDetached:
sys.stdout.write( '\\nPROCESS_DETACHED\\n' )
+ CloseOut()
os._exit({exitcode_app_crash})
elif printBacktraceTime is not None and time.time() >= printBacktraceTime:
printBacktraceTime = None