2011-09-16 16:06:56 +02:00
//TODO: don't copy/mount DeveloperDiskImage.dmg if it's already done - Xcode checks this somehow
# import <CoreFoundation / CoreFoundation.h>
2013-12-10 02:59:19 +01:00
# import <Foundation / Foundation.h>
2011-09-16 16:06:56 +02:00
# include <unistd.h>
# include <sys/socket.h>
2013-09-19 23:31:56 +02:00
# include <sys/types.h>
# include <sys/stat.h>
2011-09-16 16:06:56 +02:00
# include <sys/un.h>
2013-10-22 22:54:23 +02:00
# include <sys/sysctl.h>
2011-09-16 16:06:56 +02:00
# include <stdio.h>
# include <signal.h>
2012-02-28 17:51:01 +01:00
# include <getopt.h>
2013-09-19 23:31:56 +02:00
# include <pwd.h>
# include <netinet/in.h>
# include <netinet/tcp.h>
2011-09-16 16:06:56 +02:00
# include "MobileDevice.h"
2014-05-13 03:05:45 +02:00
# define APP_VERSION "1.0.8"
2013-10-22 22:54:23 +02:00
# define PREP_CMDS_PATH " / tmp / fruitstrap-lldb-prep-cmds-"
2014-03-26 17:59:01 +01:00
# define LLDB_SHELL "lldb -s " PREP_CMDS_PATH
2013-09-19 23:31:56 +02:00
/*
* Startup script passed to lldb .
* To see how xcode interacts with lldb , put this into . lldbinit :
* log enable - v - f / Users / vargaz / lldb . log lldb all
* log enable - v - f / Users / vargaz / gdb - remote . log gdb - remote all
*/
# define LLDB_PREP_CMDS CFSTR("\
2014-03-21 01:00:32 +01:00
platform select remote - ios - - sysroot { symbols_path } \ n \
2013-10-22 22:54:23 +02:00
target create \ " {disk_app} \" \n \
2013-09-19 23:31:56 +02:00
script fruitstrap_device_app = \ " {device_app} \" \n \
2013-12-20 23:38:58 +01:00
script fruitstrap_connect_url = \ " connect://127.0.0.1:{device_port} \" \n \
2013-10-22 22:54:23 +02:00
command script import \ " {python_file_path} \" \n \
2014-03-26 17:59:01 +01:00
command script add - f { python_command } . connect_command connect \ n \
command script add - s asynchronous - f { python_command } . run_command run \ n \
2014-04-01 14:37:49 +02:00
command script add - s asynchronous - f { python_command } . autoexit_command autoexit \ n \
2014-03-26 17:59:01 +01:00
connect \ n \
2013-09-19 23:31:56 +02:00
" )
2014-03-26 17:59:01 +01:00
const char * lldb_prep_no_cmds = " " ;
const char * lldb_prep_interactive_cmds = " \
run \ n \
" ;
2014-03-26 18:13:48 +01:00
const char * lldb_prep_noninteractive_cmds = " \
run \ n \
2014-04-01 14:37:49 +02:00
autoexit \ n \
2014-03-26 18:13:48 +01:00
" ;
2013-09-19 23:31:56 +02:00
/*
2013-10-22 22:54:23 +02:00
* Some things do not seem to work when using the normal commands like process connect / launch , so we invoke them
2013-09-19 23:31:56 +02:00
* through the python interface . Also , Launch ( ) doesn ' t seem to work when ran from init_module ( ) , so we add
* a command which can be used by the user to run it .
*/
# define LLDB_FRUITSTRAP_MODULE CFSTR("\
import lldb \ n \
2014-04-01 14:37:49 +02:00
import sys \ n \
2013-09-19 23:31:56 +02:00
\ n \
2014-03-26 17:59:01 +01:00
def connect_command ( debugger , command , result , internal_dict ) : \ n \
2013-09-19 23:31:56 +02:00
# These two are passed in by the script which loads us\n\
2014-03-26 17:59:01 +01:00
connect_url = internal_dict [ ' fruitstrap_connect_url ' ] \ n \
error = lldb . SBError ( ) \ n \
\ n \
process = lldb . target . ConnectRemote ( lldb . target . GetDebugger ( ) . GetListener ( ) , connect_url , None , error ) \ n \
2013-09-19 23:31:56 +02:00
\ n \
2014-03-26 17:59:01 +01:00
# Wait for connection to succeed\n\
listener = lldb . target . GetDebugger ( ) . GetListener ( ) \ n \
listener . StartListeningForEvents ( process . GetBroadcaster ( ) , lldb . SBProcess . eBroadcastBitStateChanged ) \ n \
events = [ ] \ n \
state = lldb . eStateInvalid \ n \
while state ! = lldb . eStateConnected : \ n \
event = lldb . SBEvent ( ) \ n \
if listener . WaitForEvent ( 1 , event ) : \ n \
state = process . GetStateFromEvent ( event ) \ n \
events . append ( event ) \ n \
else : \ n \
state = lldb . eStateInvalid \ n \
\ n \
# Add events back to queue, otherwise lldb freezes\n\
for event in events : \ n \
listener . AddEvent ( event ) \ n \
\ n \
def run_command ( debugger , command , result , internal_dict ) : \ n \
device_app = internal_dict [ ' fruitstrap_device_app ' ] \ n \
error = lldb . SBError ( ) \ n \
lldb . target . modules [ 0 ] . SetPlatformFileSpec ( lldb . SBFileSpec ( device_app ) ) \ n \
lldb . target . Launch ( lldb . SBLaunchInfo ( [ ' { args } ' ] ) , error ) \ n \
2013-10-22 22:54:23 +02:00
print str ( error ) \ n \
2014-04-01 14:37:49 +02:00
\ n \
def autoexit_command ( debugger , command , result , internal_dict ) : \ n \
process = lldb . target . process \ n \
listener = debugger . GetListener ( ) \ n \
listener . StartListeningForEvents ( process . GetBroadcaster ( ) , lldb . SBProcess . eBroadcastBitStateChanged | lldb . SBProcess . eBroadcastBitSTDOUT | lldb . SBProcess . eBroadcastBitSTDERR ) \ n \
event = lldb . SBEvent ( ) \ n \
while True : \ n \
if listener . WaitForEvent ( 1 , event ) : \ n \
state = process . GetStateFromEvent ( event ) \ n \
else : \ n \
state = lldb . eStateInvalid \ n \
\ n \
stdout = process . GetSTDOUT ( 1024 ) \ n \
while stdout : \ n \
sys . stdout . write ( stdout ) \ n \
stdout = process . GetSTDOUT ( 1024 ) \ n \
\ n \
stderr = process . GetSTDERR ( 1024 ) \ n \
while stderr : \ n \
sys . stdout . write ( stderr ) \ n \
stderr = process . GetSTDERR ( 1024 ) \ n \
\ n \
if lldb . SBProcess . EventIsProcessEvent ( event ) : \ n \
if state = = lldb . eStateExited : \ n \
sys . exit ( process . GetExitStatus ( ) ) \ n \
if state = = lldb . eStateStopped : \ n \
debugger . HandleCommand ( ' frame select ' ) \ n \
debugger . HandleCommand ( ' bt ' ) \ n \
sys . exit ( { exitcode_app_crash } ) \ n \
2013-09-19 23:31:56 +02:00
" )
2011-09-16 16:06:56 +02:00
typedef struct am_device * AMDeviceRef ;
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 ) ;
2013-12-10 02:59:19 +01:00
mach_error_t AMDeviceLookupApplications ( AMDeviceRef device , CFDictionaryRef options , CFDictionaryRef * result ) ;
2011-09-16 16:06:56 +02:00
2013-12-27 17:59:39 +01:00
bool found_device = false , debug = false , verbose = false , unbuffered = false , nostart = false , detect_only = false , install = true , uninstall = false ;
2014-03-26 18:13:48 +01:00
bool interactive = true ;
2011-09-16 16:06:56 +02:00
char * app_path = NULL ;
2011-11-04 11:04:46 +01:00
char * device_id = NULL ;
2012-02-28 17:51:01 +01:00
char * args = NULL ;
int timeout = 0 ;
2013-12-20 23:38:58 +01:00
int port = 12345 ;
2011-09-16 16:06:56 +02:00
CFStringRef last_path = NULL ;
service_conn_t gdbfd ;
2013-10-22 22:54:23 +02:00
pid_t parent = 0 ;
2014-04-01 14:37:49 +02:00
// PID of child process running lldb
pid_t child = 0 ;
// Signal sent from child to parent process when LLDB finishes.
const int SIGLLDB = SIGUSR1 ;
// Error codes we report on different failures, so scripts can distinguish between user app exit
// codes and our exit codes. For non app errors we use codes in reserved 128-255 range.
const int exitcode_error = 253 ;
const int exitcode_app_crash = 254 ;
2011-09-16 16:06:56 +02:00
2011-09-17 09:10:52 +02:00
Boolean path_exists ( CFTypeRef path ) {
if ( CFGetTypeID ( path ) = = CFStringGetTypeID ( ) ) {
CFURLRef url = CFURLCreateWithFileSystemPath ( NULL , path , kCFURLPOSIXPathStyle , true ) ;
Boolean result = CFURLResourceIsReachable ( url , NULL ) ;
CFRelease ( url ) ;
return result ;
} else if ( CFGetTypeID ( path ) = = CFURLGetTypeID ( ) ) {
return CFURLResourceIsReachable ( path , NULL ) ;
} else {
return false ;
}
}
2013-10-22 22:54:23 +02:00
CFStringRef find_path ( CFStringRef rootPath , CFStringRef namePattern , CFStringRef expression ) {
2013-09-19 23:31:56 +02:00
FILE * fpipe = NULL ;
2013-10-22 22:54:23 +02:00
CFStringRef quotedRootPath = rootPath ;
if ( CFStringGetCharacterAtIndex ( rootPath , 0 ) ! = ' ` ' ) {
quotedRootPath = CFStringCreateWithFormat ( NULL , NULL , CFSTR ( " '%@' " ) , rootPath ) ;
}
CFStringRef cf_command = CFStringCreateWithFormat ( NULL , NULL , CFSTR ( " find %@ -name '%@' %@ 2>/dev/null | sort | tail -n 1 " ) , quotedRootPath , namePattern , expression ) ;
if ( quotedRootPath ! = rootPath ) {
CFRelease ( quotedRootPath ) ;
}
char command [ 1024 ] = { ' \0 ' } ;
CFStringGetCString ( cf_command , command , sizeof ( command ) , kCFStringEncodingUTF8 ) ;
CFRelease ( cf_command ) ;
2013-09-19 23:31:56 +02:00
if ( ! ( fpipe = ( FILE * ) popen ( command , " r " ) ) )
{
perror ( " Error encountered while opening pipe " ) ;
2014-04-01 14:37:49 +02:00
exit ( exitcode_error ) ;
2013-09-19 23:31:56 +02:00
}
char buffer [ 256 ] = { ' \0 ' } ;
fgets ( buffer , sizeof ( buffer ) , fpipe ) ;
pclose ( fpipe ) ;
strtok ( buffer , " \n " ) ;
return CFStringCreateWithCString ( NULL , buffer , kCFStringEncodingUTF8 ) ;
}
2013-10-22 22:54:23 +02:00
CFStringRef copy_long_shot_disk_image_path ( ) {
return find_path ( CFSTR ( " `xcode-select --print-path` " ) , CFSTR ( " DeveloperDiskImage.dmg " ) , CFSTR ( " " ) ) ;
}
2013-09-19 23:31:56 +02:00
2013-10-22 22:54:23 +02:00
CFStringRef copy_xcode_dev_path ( ) {
static char xcode_dev_path [ 256 ] = { ' \0 ' } ;
if ( strlen ( xcode_dev_path ) = = 0 ) {
FILE * fpipe = NULL ;
char * command = " xcode-select -print-path " ;
if ( ! ( fpipe = ( FILE * ) popen ( command , " r " ) ) )
{
perror ( " Error encountered while opening pipe " ) ;
2014-04-01 14:37:49 +02:00
exit ( exitcode_error ) ;
2013-10-22 22:54:23 +02:00
}
2013-09-19 23:31:56 +02:00
2013-10-22 22:54:23 +02:00
char buffer [ 256 ] = { ' \0 ' } ;
2013-09-19 23:31:56 +02:00
2013-10-22 22:54:23 +02:00
fgets ( buffer , sizeof ( buffer ) , fpipe ) ;
pclose ( fpipe ) ;
2013-09-19 23:31:56 +02:00
2013-10-22 22:54:23 +02:00
strtok ( buffer , " \n " ) ;
strcpy ( xcode_dev_path , buffer ) ;
}
return CFStringCreateWithCString ( NULL , xcode_dev_path , kCFStringEncodingUTF8 ) ;
2013-09-19 23:31:56 +02:00
}
const char * get_home ( ) {
2012-02-28 17:51:01 +01:00
const char * home = getenv ( " HOME " ) ;
2013-09-19 23:31:56 +02:00
if ( ! home ) {
struct passwd * pwd = getpwuid ( getuid ( ) ) ;
home = pwd - > pw_dir ;
}
return home ;
}
2013-10-22 22:54:23 +02:00
CFStringRef copy_xcode_path_for ( CFStringRef subPath , CFStringRef search ) {
2013-09-19 23:31:56 +02:00
CFStringRef xcodeDevPath = copy_xcode_dev_path ( ) ;
2012-02-28 17:51:01 +01:00
CFStringRef path ;
bool found = false ;
2013-09-19 23:31:56 +02:00
const char * home = get_home ( ) ;
2012-02-28 17:51:01 +01:00
2013-10-22 22:54:23 +02:00
2013-09-19 23:31:56 +02:00
// Try using xcode-select --print-path
if ( ! found ) {
2013-10-22 22:54:23 +02:00
path = CFStringCreateWithFormat ( NULL , NULL , CFSTR ( " %@/%@/%@ " ) , xcodeDevPath , subPath , search ) ;
2012-02-28 17:51:01 +01:00
found = path_exists ( path ) ;
}
2013-10-22 22:54:23 +02:00
// Try find `xcode-select --print-path` with search as a name pattern
if ( ! found ) {
path = find_path ( CFStringCreateWithFormat ( NULL , NULL , CFSTR ( " %@/%@ " ) , xcodeDevPath , subPath ) , search , CFSTR ( " -maxdepth 1 " ) ) ;
found = CFStringGetLength ( path ) > 0 & & path_exists ( path ) ;
}
2013-09-19 23:31:56 +02:00
// If not look in the default xcode location (xcode-select is sometimes wrong)
if ( ! found ) {
2013-10-22 22:54:23 +02:00
path = CFStringCreateWithFormat ( NULL , NULL , CFSTR ( " /Applications/Xcode.app/Contents/Developer/%@&%@ " ) , subPath , search ) ;
2012-02-28 17:51:01 +01:00
found = path_exists ( path ) ;
}
2013-09-19 23:31:56 +02:00
// If not look in the users home directory, Xcode can store device support stuff there
if ( ! found ) {
2013-10-22 22:54:23 +02:00
path = CFStringCreateWithFormat ( NULL , NULL , CFSTR ( " %s/Library/Developer/Xcode/%@/%@ " ) , home , subPath , search ) ;
2012-02-28 17:51:01 +01:00
found = path_exists ( path ) ;
}
2013-10-22 22:54:23 +02:00
2013-09-19 23:31:56 +02:00
CFRelease ( xcodeDevPath ) ;
if ( found ) {
return path ;
} else {
CFRelease ( path ) ;
return NULL ;
2012-03-02 10:48:20 +01:00
}
2013-09-19 23:31:56 +02:00
}
2012-02-28 17:51:01 +01:00
2013-10-22 22:54:23 +02:00
CFMutableArrayRef get_device_product_version_parts ( AMDeviceRef device ) {
2013-09-19 23:31:56 +02:00
CFStringRef version = AMDeviceCopyValue ( device , 0 , CFSTR ( " ProductVersion " ) ) ;
2013-10-22 22:54:23 +02:00
CFArrayRef parts = CFStringCreateArrayBySeparatingStrings ( NULL , version , CFSTR ( " . " ) ) ;
CFMutableArrayRef result = CFArrayCreateMutableCopy ( NULL , CFArrayGetCount ( parts ) , parts ) ;
CFRelease ( version ) ;
CFRelease ( parts ) ;
return result ;
}
CFStringRef copy_device_support_path ( AMDeviceRef device ) {
CFStringRef version = NULL ;
2013-09-19 23:31:56 +02:00
CFStringRef build = AMDeviceCopyValue ( device , 0 , CFSTR ( " BuildVersion " ) ) ;
CFStringRef path = NULL ;
2013-10-22 22:54:23 +02:00
CFMutableArrayRef version_parts = get_device_product_version_parts ( device ) ;
2013-09-19 23:31:56 +02:00
2013-10-22 22:54:23 +02:00
while ( CFArrayGetCount ( version_parts ) > 0 ) {
version = CFStringCreateByCombiningStrings ( NULL , version_parts , CFSTR ( " . " ) ) ;
if ( path = = NULL ) {
path = copy_xcode_path_for ( CFSTR ( " iOS DeviceSupport " ) , CFStringCreateWithFormat ( NULL , NULL , CFSTR ( " %@ (%@) " ) , version , build ) ) ;
}
if ( path = = NULL ) {
path = copy_xcode_path_for ( CFSTR ( " Platforms/iPhoneOS.platform/DeviceSupport " ) , CFStringCreateWithFormat ( NULL , NULL , CFSTR ( " %@ (%@) " ) , version , build ) ) ;
}
if ( path = = NULL ) {
path = copy_xcode_path_for ( CFSTR ( " Platforms/iPhoneOS.platform/DeviceSupport " ) , CFStringCreateWithFormat ( NULL , NULL , CFSTR ( " %@ (*) " ) , version ) ) ;
}
if ( path = = NULL ) {
path = copy_xcode_path_for ( CFSTR ( " Platforms/iPhoneOS.platform/DeviceSupport " ) , version ) ;
}
if ( path = = NULL ) {
path = copy_xcode_path_for ( CFSTR ( " Platforms/iPhoneOS.platform/DeviceSupport/Latest " ) , CFSTR ( " " ) ) ;
}
CFRelease ( version ) ;
if ( path ! = NULL ) {
break ;
}
CFArrayRemoveValueAtIndex ( version_parts , CFArrayGetCount ( version_parts ) - 1 ) ;
2013-09-19 23:31:56 +02:00
}
2013-10-22 22:54:23 +02:00
CFRelease ( version_parts ) ;
2011-09-16 16:06:56 +02:00
CFRelease ( build ) ;
2013-09-19 23:31:56 +02:00
if ( path = = NULL )
2012-02-28 17:51:01 +01:00
{
2013-09-19 23:31:56 +02:00
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 " ) ;
2014-04-01 14:37:49 +02:00
exit ( exitcode_error ) ;
2011-09-16 16:06:56 +02:00
}
2012-02-28 17:51:01 +01:00
return path ;
}
2013-10-22 22:54:23 +02:00
CFStringRef copy_developer_disk_image_path ( CFStringRef deviceSupportPath ) {
CFStringRef path = CFStringCreateWithFormat ( NULL , NULL , CFSTR ( " %@/%@ " ) , deviceSupportPath , CFSTR ( " DeveloperDiskImage.dmg " ) ) ;
if ( ! path_exists ( path ) ) {
CFRelease ( path ) ;
path = NULL ;
2013-07-27 09:27:11 +02:00
}
2013-10-22 22:54:23 +02:00
2013-09-19 23:31:56 +02:00
if ( path = = NULL ) {
// Sometimes Latest seems to be missing in Xcode, in that case use find and hope for the best
path = copy_long_shot_disk_image_path ( ) ;
if ( CFStringGetLength ( path ) < 5 ) {
2013-10-22 22:54:23 +02:00
CFRelease ( path ) ;
2013-09-19 23:31:56 +02:00
path = NULL ;
}
}
2012-02-28 17:51:01 +01:00
2013-09-19 23:31:56 +02:00
if ( path = = NULL )
{
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 " ) ;
2014-04-01 14:37:49 +02:00
exit ( exitcode_error ) ;
2012-02-28 17:51:01 +01:00
}
return path ;
2011-09-16 16:06:56 +02:00
}
void mount_callback ( CFDictionaryRef dict , int arg ) {
CFStringRef status = CFDictionaryGetValue ( dict , CFSTR ( " Status " ) ) ;
if ( CFEqual ( status , CFSTR ( " LookingUpImage " ) ) ) {
printf ( " [ 0%%] Looking up developer disk image \n " ) ;
} else if ( CFEqual ( status , CFSTR ( " CopyingImage " ) ) ) {
printf ( " [ 30%%] Copying DeveloperDiskImage.dmg to device \n " ) ;
} else if ( CFEqual ( status , CFSTR ( " MountingImage " ) ) ) {
printf ( " [ 90%%] Mounting developer disk image \n " ) ;
}
}
void mount_developer_image ( AMDeviceRef device ) {
CFStringRef ds_path = copy_device_support_path ( device ) ;
2013-10-22 22:54:23 +02:00
CFStringRef image_path = copy_developer_disk_image_path ( ds_path ) ;
2012-02-28 17:51:01 +01:00
CFStringRef sig_path = CFStringCreateWithFormat ( NULL , NULL , CFSTR ( " %@.signature " ) , image_path ) ;
2011-09-16 16:06:56 +02:00
2012-02-28 17:51:01 +01:00
if ( verbose ) {
2013-10-22 22:54:23 +02:00
printf ( " Device support path: %s \n " , CFStringGetCStringPtr ( ds_path , CFStringGetSystemEncoding ( ) ) ) ;
printf ( " Developer disk image: %s \n " , CFStringGetCStringPtr ( image_path , CFStringGetSystemEncoding ( ) ) ) ;
2012-02-28 17:51:01 +01:00
}
2013-10-22 22:54:23 +02:00
CFRelease ( ds_path ) ;
2012-02-28 17:51:01 +01:00
2011-09-16 16:06:56 +02:00
FILE * sig = fopen ( CFStringGetCStringPtr ( sig_path , kCFStringEncodingMacRoman ) , " rb " ) ;
void * sig_buf = malloc ( 128 ) ;
assert ( fread ( sig_buf , 1 , 128 , sig ) = = 128 ) ;
fclose ( sig ) ;
CFDataRef sig_data = CFDataCreateWithBytesNoCopy ( NULL , sig_buf , 128 , NULL ) ;
CFRelease ( sig_path ) ;
CFTypeRef keys [ ] = { CFSTR ( " ImageSignature " ) , CFSTR ( " ImageType " ) } ;
CFTypeRef values [ ] = { sig_data , CFSTR ( " Developer " ) } ;
CFDictionaryRef options = CFDictionaryCreate ( NULL , ( const void * * ) & keys , ( const void * * ) & values , 2 , & kCFTypeDictionaryKeyCallBacks , & kCFTypeDictionaryValueCallBacks ) ;
CFRelease ( sig_data ) ;
int result = AMDeviceMountImage ( device , image_path , options , & mount_callback , 0 ) ;
2012-02-28 17:51:01 +01:00
if ( result = = 0 ) {
2011-09-16 16:06:56 +02:00
printf ( " [ 95%%] Developer disk image mounted successfully \n " ) ;
2012-02-28 17:51:01 +01:00
} else if ( result = = 0xe8000076 /* already mounted */ ) {
printf ( " [ 95%%] Developer disk image already mounted \n " ) ;
2011-09-16 16:06:56 +02:00
} else {
printf ( " [ !! ] Unable to mount developer disk image. (%x) \n " , result ) ;
2014-04-01 14:37:49 +02:00
exit ( exitcode_error ) ;
2011-09-16 16:06:56 +02:00
}
CFRelease ( image_path ) ;
CFRelease ( options ) ;
}
2013-09-19 23:31:56 +02:00
mach_error_t transfer_callback ( CFDictionaryRef dict , int arg ) {
2011-09-16 16:06:56 +02:00
int percent ;
CFStringRef status = CFDictionaryGetValue ( dict , CFSTR ( " Status " ) ) ;
CFNumberGetValue ( CFDictionaryGetValue ( dict , CFSTR ( " PercentComplete " ) ) , kCFNumberSInt32Type , & percent ) ;
if ( CFEqual ( status , CFSTR ( " CopyingFile " ) ) ) {
CFStringRef path = CFDictionaryGetValue ( dict , CFSTR ( " Path " ) ) ;
if ( ( last_path = = NULL | | ! CFEqual ( path , last_path ) ) & & ! CFStringHasSuffix ( path , CFSTR ( " .ipa " ) ) ) {
printf ( " [%3d%%] Copying %s to device \n " , percent / 2 , CFStringGetCStringPtr ( path , kCFStringEncodingMacRoman ) ) ;
}
if ( last_path ! = NULL ) {
CFRelease ( last_path ) ;
}
last_path = CFStringCreateCopy ( NULL , path ) ;
}
2013-09-19 23:31:56 +02:00
return 0 ;
2011-09-16 16:06:56 +02:00
}
2013-09-19 23:31:56 +02:00
mach_error_t install_callback ( CFDictionaryRef dict , int arg ) {
2011-09-16 16:06:56 +02:00
int percent ;
CFStringRef status = CFDictionaryGetValue ( dict , CFSTR ( " Status " ) ) ;
CFNumberGetValue ( CFDictionaryGetValue ( dict , CFSTR ( " PercentComplete " ) ) , kCFNumberSInt32Type , & percent ) ;
printf ( " [%3d%%] %s \n " , ( percent / 2 ) + 50 , CFStringGetCStringPtr ( status , kCFStringEncodingMacRoman ) ) ;
2013-09-19 23:31:56 +02:00
return 0 ;
2011-09-16 16:06:56 +02:00
}
CFURLRef copy_device_app_url ( AMDeviceRef device , CFStringRef identifier ) {
2013-12-10 02:59:19 +01:00
CFDictionaryRef result = nil ;
NSArray * a = [ NSArray arrayWithObjects :
@ " CFBundleIdentifier " , // absolute must
@ " ApplicationDSID " ,
@ " ApplicationType " ,
@ " CFBundleExecutable " ,
@ " CFBundleDisplayName " ,
@ " CFBundleIconFile " ,
@ " CFBundleName " ,
@ " CFBundleShortVersionString " ,
@ " CFBundleSupportedPlatforms " ,
@ " CFBundleURLTypes " ,
@ " CodeInfoIdentifier " ,
@ " Container " ,
@ " Entitlements " ,
@ " HasSettingsBundle " ,
@ " IsUpgradeable " ,
@ " MinimumOSVersion " ,
@ " Path " ,
@ " SignerIdentity " ,
@ " UIDeviceFamily " ,
@ " UIFileSharingEnabled " ,
@ " UIStatusBarHidden " ,
@ " UISupportedInterfaceOrientations " ,
nil ] ;
NSDictionary * optionsDict = [ NSDictionary dictionaryWithObject : a forKey : @ " ReturnAttributes " ] ;
CFDictionaryRef options = ( CFDictionaryRef ) optionsDict ;
afc_error_t resultStatus = AMDeviceLookupApplications ( device , options , & result ) ;
assert ( resultStatus = = 0 ) ;
2011-09-16 16:06:56 +02:00
CFDictionaryRef app_dict = CFDictionaryGetValue ( result , identifier ) ;
assert ( app_dict ! = NULL ) ;
CFStringRef app_path = CFDictionaryGetValue ( app_dict , CFSTR ( " Path " ) ) ;
assert ( app_path ! = NULL ) ;
2012-02-28 17:51:01 +01:00
2011-09-16 16:06:56 +02:00
CFURLRef url = CFURLCreateWithFileSystemPath ( NULL , app_path , kCFURLPOSIXPathStyle , true ) ;
CFRelease ( result ) ;
return url ;
}
CFStringRef copy_disk_app_identifier ( CFURLRef disk_app_url ) {
CFURLRef plist_url = CFURLCreateCopyAppendingPathComponent ( NULL , disk_app_url , CFSTR ( " Info.plist " ) , false ) ;
CFReadStreamRef plist_stream = CFReadStreamCreateWithFile ( NULL , plist_url ) ;
CFReadStreamOpen ( plist_stream ) ;
CFPropertyListRef plist = CFPropertyListCreateWithStream ( NULL , plist_stream , 0 , kCFPropertyListImmutable , NULL , NULL ) ;
CFStringRef bundle_identifier = CFRetain ( CFDictionaryGetValue ( plist , CFSTR ( " CFBundleIdentifier " ) ) ) ;
CFReadStreamClose ( plist_stream ) ;
CFRelease ( plist_url ) ;
CFRelease ( plist_stream ) ;
CFRelease ( plist ) ;
return bundle_identifier ;
}
2013-09-19 23:31:56 +02:00
void write_lldb_prep_cmds ( AMDeviceRef device , CFURLRef disk_app_url ) {
2014-03-21 01:00:32 +01:00
CFStringRef ds_path = copy_device_support_path ( device ) ;
CFStringRef symbols_path = CFStringCreateWithFormat ( NULL , NULL , CFSTR ( " '%@/Symbols' " ) , ds_path ) ;
2013-09-19 23:31:56 +02:00
CFMutableStringRef cmds = CFStringCreateMutableCopy ( NULL , 0 , LLDB_PREP_CMDS ) ;
2011-09-16 16:06:56 +02:00
CFRange range = { 0 , CFStringGetLength ( cmds ) } ;
2014-03-21 01:00:32 +01:00
CFStringFindAndReplace ( cmds , CFSTR ( " {symbols_path} " ) , symbols_path , range , 0 ) ;
2013-12-14 01:02:14 +01:00
range . length = CFStringGetLength ( cmds ) ;
2011-09-16 16:06:56 +02:00
CFStringFindAndReplace ( cmds , CFSTR ( " {ds_path} " ) , ds_path , range , 0 ) ;
2012-02-28 17:51:01 +01:00
range . length = CFStringGetLength ( cmds ) ;
2013-10-22 22:54:23 +02:00
CFMutableStringRef pmodule = CFStringCreateMutableCopy ( NULL , 0 , LLDB_FRUITSTRAP_MODULE ) ;
2014-04-01 14:37:49 +02:00
CFRange rangeLLDB = { 0 , CFStringGetLength ( pmodule ) } ;
CFStringRef exitcode_app_crash_str = CFStringCreateWithFormat ( NULL , NULL , CFSTR ( " %d " ) , exitcode_app_crash ) ;
CFStringFindAndReplace ( pmodule , CFSTR ( " {exitcode_app_crash} " ) , exitcode_app_crash_str , rangeLLDB , 0 ) ;
rangeLLDB . length = CFStringGetLength ( pmodule ) ;
2012-02-28 17:51:01 +01:00
if ( args ) {
CFStringRef cf_args = CFStringCreateWithCString ( NULL , args , kCFStringEncodingASCII ) ;
CFStringFindAndReplace ( cmds , CFSTR ( " {args} " ) , cf_args , range , 0 ) ;
2013-10-22 22:54:23 +02:00
//format the arguments 'arg1 arg2 ....' to an argument list ['arg1', 'arg2', ...]
CFMutableStringRef argsListLLDB = CFStringCreateMutableCopy ( NULL , 0 , cf_args ) ;
2014-04-01 14:37:49 +02:00
rangeLLDB . length = CFStringGetLength ( argsListLLDB ) ;
2013-10-22 22:54:23 +02:00
CFStringFindAndReplace ( argsListLLDB , CFSTR ( " " ) , CFSTR ( " " ) , rangeLLDB , 0 ) ; //remove multiple spaces
rangeLLDB . length = CFStringGetLength ( argsListLLDB ) ;
CFStringFindAndReplace ( argsListLLDB , CFSTR ( " " ) , CFSTR ( " ', ' " ) , rangeLLDB , 0 ) ;
rangeLLDB . length = CFStringGetLength ( pmodule ) ;
CFStringFindAndReplace ( pmodule , CFSTR ( " {args} " ) , argsListLLDB , rangeLLDB , 0 ) ;
2012-02-28 17:51:01 +01:00
CFRelease ( cf_args ) ;
} else {
CFStringFindAndReplace ( cmds , CFSTR ( " {args} " ) , CFSTR ( " " ) , range , 0 ) ;
}
range . length = CFStringGetLength ( cmds ) ;
2011-09-16 16:06:56 +02:00
CFStringRef bundle_identifier = copy_disk_app_identifier ( disk_app_url ) ;
CFURLRef device_app_url = copy_device_app_url ( device , bundle_identifier ) ;
CFStringRef device_app_path = CFURLCopyFileSystemPath ( device_app_url , kCFURLPOSIXPathStyle ) ;
CFStringFindAndReplace ( cmds , CFSTR ( " {device_app} " ) , device_app_path , range , 0 ) ;
2012-02-28 17:51:01 +01:00
range . length = CFStringGetLength ( cmds ) ;
2011-09-16 16:06:56 +02:00
CFStringRef disk_app_path = CFURLCopyFileSystemPath ( disk_app_url , kCFURLPOSIXPathStyle ) ;
CFStringFindAndReplace ( cmds , CFSTR ( " {disk_app} " ) , disk_app_path , range , 0 ) ;
2012-02-28 17:51:01 +01:00
range . length = CFStringGetLength ( cmds ) ;
2011-09-16 16:06:56 +02:00
2013-12-20 23:38:58 +01:00
CFStringRef device_port = CFStringCreateWithFormat ( NULL , NULL , CFSTR ( " %d " ) , port ) ;
CFStringFindAndReplace ( cmds , CFSTR ( " {device_port} " ) , device_port , range , 0 ) ;
range . length = CFStringGetLength ( cmds ) ;
2011-09-16 16:06:56 +02:00
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 ) ;
2012-02-28 17:51:01 +01:00
range . length = CFStringGetLength ( dcp_noprivate ) ;
2011-09-16 16:06:56 +02:00
CFStringFindAndReplace ( dcp_noprivate , CFSTR ( " /private/var/ " ) , CFSTR ( " /var/ " ) , range , 0 ) ;
2012-02-28 17:51:01 +01:00
range . length = CFStringGetLength ( cmds ) ;
2011-09-16 16:06:56 +02:00
CFStringFindAndReplace ( cmds , CFSTR ( " {device_container} " ) , dcp_noprivate , range , 0 ) ;
2012-02-28 17:51:01 +01:00
range . length = CFStringGetLength ( cmds ) ;
2011-09-16 16:06:56 +02:00
CFURLRef disk_container_url = CFURLCreateCopyDeletingLastPathComponent ( NULL , disk_app_url ) ;
CFStringRef disk_container_path = CFURLCopyFileSystemPath ( disk_container_url , kCFURLPOSIXPathStyle ) ;
CFStringFindAndReplace ( cmds , CFSTR ( " {disk_container} " ) , disk_container_path , range , 0 ) ;
2013-10-22 22:54:23 +02:00
char python_file_path [ 300 ] = " /tmp/fruitstrap_ " ;
char python_command [ 300 ] = " fruitstrap_ " ;
if ( device_id ! = NULL ) {
strcat ( python_file_path , device_id ) ;
strcat ( python_command , device_id ) ;
}
strcat ( python_file_path , " .py " ) ;
CFStringRef cf_python_command = CFStringCreateWithCString ( NULL , python_command , kCFStringEncodingASCII ) ;
CFStringFindAndReplace ( cmds , CFSTR ( " {python_command} " ) , cf_python_command , range , 0 ) ;
range . length = CFStringGetLength ( cmds ) ;
CFStringRef cf_python_file_path = CFStringCreateWithCString ( NULL , python_file_path , kCFStringEncodingASCII ) ;
CFStringFindAndReplace ( cmds , CFSTR ( " {python_file_path} " ) , cf_python_file_path , range , 0 ) ;
range . length = CFStringGetLength ( cmds ) ;
2011-09-16 16:06:56 +02:00
CFDataRef cmds_data = CFStringCreateExternalRepresentation ( NULL , cmds , kCFStringEncodingASCII , 0 ) ;
2013-10-22 22:54:23 +02:00
char prep_cmds_path [ 300 ] = PREP_CMDS_PATH ;
if ( device_id ! = NULL )
strcat ( prep_cmds_path , device_id ) ;
FILE * out = fopen ( prep_cmds_path , " w " ) ;
2011-09-16 16:06:56 +02:00
fwrite ( CFDataGetBytePtr ( cmds_data ) , CFDataGetLength ( cmds_data ) , 1 , out ) ;
2014-03-26 17:59:01 +01:00
// Write additional commands based on mode we're running in
const char * extra_cmds ;
2014-03-26 18:13:48 +01:00
if ( ! interactive )
extra_cmds = lldb_prep_noninteractive_cmds ;
else if ( nostart )
2014-03-26 17:59:01 +01:00
extra_cmds = lldb_prep_no_cmds ;
else
extra_cmds = lldb_prep_interactive_cmds ;
fwrite ( extra_cmds , strlen ( extra_cmds ) , 1 , out ) ;
2011-09-16 16:06:56 +02:00
fclose ( out ) ;
2013-09-19 23:31:56 +02:00
CFDataRef pmodule_data = CFStringCreateExternalRepresentation ( NULL , pmodule , kCFStringEncodingASCII , 0 ) ;
2013-10-22 22:54:23 +02:00
out = fopen ( python_file_path , " w " ) ;
2013-09-19 23:31:56 +02:00
fwrite ( CFDataGetBytePtr ( pmodule_data ) , CFDataGetLength ( pmodule_data ) , 1 , out ) ;
fclose ( out ) ;
2011-09-16 16:06:56 +02:00
CFRelease ( cmds ) ;
2011-09-17 09:10:52 +02:00
if ( ds_path ! = NULL ) CFRelease ( ds_path ) ;
2011-09-16 16:06:56 +02:00
CFRelease ( bundle_identifier ) ;
CFRelease ( device_app_url ) ;
CFRelease ( device_app_path ) ;
CFRelease ( disk_app_path ) ;
CFRelease ( device_container_url ) ;
CFRelease ( device_container_path ) ;
CFRelease ( dcp_noprivate ) ;
CFRelease ( disk_container_url ) ;
CFRelease ( disk_container_path ) ;
CFRelease ( cmds_data ) ;
2013-10-22 22:54:23 +02:00
CFRelease ( cf_python_command ) ;
CFRelease ( cf_python_file_path ) ;
2011-09-16 16:06:56 +02:00
}
2013-09-19 23:31:56 +02:00
CFSocketRef server_socket ;
CFSocketRef lldb_socket ;
CFWriteStreamRef serverWriteStream = NULL ;
CFWriteStreamRef lldbWriteStream = NULL ;
void
server_callback ( CFSocketRef s , CFSocketCallBackType callbackType , CFDataRef address , const void * data , void * info )
{
int res ;
//PRINT ("server: %s\n", CFDataGetBytePtr (data));
if ( CFDataGetLength ( data ) = = 0 ) {
// FIXME: Close the socket
//shutdown (CFSocketGetNative (lldb_socket), SHUT_RDWR);
//close (CFSocketGetNative (lldb_socket));
return ;
}
2013-10-22 22:54:23 +02:00
res = write ( CFSocketGetNative ( lldb_socket ) , CFDataGetBytePtr ( data ) , CFDataGetLength ( data ) ) ;
2013-09-19 23:31:56 +02:00
}
void lldb_callback ( CFSocketRef s , CFSocketCallBackType callbackType , CFDataRef address , const void * data , void * info )
{
//PRINT ("lldb: %s\n", CFDataGetBytePtr (data));
if ( CFDataGetLength ( data ) = = 0 )
return ;
write ( gdbfd , CFDataGetBytePtr ( data ) , CFDataGetLength ( data ) ) ;
}
void fdvendor_callback ( CFSocketRef s , CFSocketCallBackType callbackType , CFDataRef address , const void * data , void * info ) {
CFSocketNativeHandle socket = ( CFSocketNativeHandle ) ( * ( ( CFSocketNativeHandle * ) data ) ) ;
assert ( callbackType = = kCFSocketAcceptCallBack ) ;
//PRINT ("callback!\n");
lldb_socket = CFSocketCreateWithNative ( NULL , socket , kCFSocketDataCallBack , & lldb_callback , NULL ) ;
CFRunLoopAddSource ( CFRunLoopGetMain ( ) , CFSocketCreateRunLoopSource ( NULL , lldb_socket , 0 ) , kCFRunLoopCommonModes ) ;
}
2011-09-16 16:06:56 +02:00
void start_remote_debug_server ( AMDeviceRef device ) {
2013-09-19 23:31:56 +02:00
char buf [ 256 ] ;
int res , err , i ;
char msg [ 256 ] ;
int chsum , len ;
struct stat s ;
socklen_t buflen ;
struct sockaddr name ;
int namelen ;
2011-09-16 16:06:56 +02:00
assert ( AMDeviceStartService ( device , CFSTR ( " com.apple.debugserver " ) , & gdbfd , NULL ) = = 0 ) ;
2013-09-19 23:31:56 +02:00
assert ( gdbfd ) ;
/*
* The debugserver connection is through a fd handle , while lldb requires a host / port to connect , so create an intermediate
* socket to transfer data .
*/
server_socket = CFSocketCreateWithNative ( NULL , gdbfd , kCFSocketDataCallBack , & server_callback , NULL ) ;
CFRunLoopAddSource ( CFRunLoopGetMain ( ) , CFSocketCreateRunLoopSource ( NULL , server_socket , 0 ) , kCFRunLoopCommonModes ) ;
2011-09-16 16:06:56 +02:00
2013-09-19 23:31:56 +02:00
struct sockaddr_in addr4 ;
memset ( & addr4 , 0 , sizeof ( addr4 ) ) ;
addr4 . sin_len = sizeof ( addr4 ) ;
addr4 . sin_family = AF_INET ;
2013-12-20 23:38:58 +01:00
addr4 . sin_port = htons ( port ) ;
2013-09-19 23:31:56 +02:00
addr4 . sin_addr . s_addr = htonl ( INADDR_ANY ) ;
CFSocketRef fdvendor = CFSocketCreate ( NULL , PF_INET , 0 , 0 , kCFSocketAcceptCallBack , & fdvendor_callback , NULL ) ;
2011-09-16 16:06:56 +02:00
int yes = 1 ;
setsockopt ( CFSocketGetNative ( fdvendor ) , SOL_SOCKET , SO_REUSEADDR , & yes , sizeof ( yes ) ) ;
2013-10-22 22:54:23 +02:00
int flag = 1 ;
2013-09-19 23:31:56 +02:00
res = setsockopt ( CFSocketGetNative ( fdvendor ) , IPPROTO_TCP , TCP_NODELAY , ( char * ) & flag , sizeof ( int ) ) ;
assert ( res = = 0 ) ;
2011-09-16 16:06:56 +02:00
2013-09-19 23:31:56 +02:00
CFDataRef address_data = CFDataCreate ( NULL , ( const UInt8 * ) & addr4 , sizeof ( addr4 ) ) ;
2011-09-16 16:06:56 +02:00
CFSocketSetAddress ( fdvendor , address_data ) ;
CFRelease ( address_data ) ;
CFRunLoopAddSource ( CFRunLoopGetMain ( ) , CFSocketCreateRunLoopSource ( NULL , fdvendor , 0 ) , kCFRunLoopCommonModes ) ;
}
2013-10-22 22:54:23 +02:00
void kill_ptree_inner ( pid_t root , int signum , struct kinfo_proc * kp , int kp_len ) {
int i ;
for ( i = 0 ; i < kp_len ; i + + ) {
if ( kp [ i ] . kp_eproc . e_ppid = = root ) {
kill_ptree_inner ( kp [ i ] . kp_proc . p_pid , signum , kp , kp_len ) ;
}
}
if ( root ! = getpid ( ) ) {
kill ( root , signum ) ;
}
}
int kill_ptree ( pid_t root , int signum ) {
int mib [ 3 ] ;
size_t len ;
mib [ 0 ] = CTL_KERN ;
mib [ 1 ] = KERN_PROC ;
mib [ 2 ] = KERN_PROC_ALL ;
if ( sysctl ( mib , 3 , NULL , & len , NULL , 0 ) = = - 1 ) {
return - 1 ;
}
struct kinfo_proc * kp = calloc ( 1 , len ) ;
if ( ! kp ) {
return - 1 ;
}
if ( sysctl ( mib , 3 , kp , & len , NULL , 0 ) = = - 1 ) {
free ( kp ) ;
return - 1 ;
}
kill_ptree_inner ( root , signum , kp , len / sizeof ( struct kinfo_proc ) ) ;
free ( kp ) ;
return 0 ;
}
2013-09-19 23:31:56 +02:00
void killed ( int signum ) {
// SIGKILL needed to kill lldb, probably a better way to do this.
kill ( 0 , SIGKILL ) ;
_exit ( 0 ) ;
}
void lldb_finished_handler ( int signum )
2012-02-28 17:51:01 +01:00
{
2014-04-01 14:37:49 +02:00
int status = 0 ;
if ( waitpid ( child , & status , 0 ) = = - 1 )
perror ( " waitpid failed " ) ;
_exit ( WEXITSTATUS ( status ) ) ;
2012-02-28 17:51:01 +01:00
}
2014-03-27 15:14:14 +01:00
void bring_process_to_foreground ( ) {
if ( setpgid ( 0 , 0 ) = = - 1 )
perror ( " setpgid failed " ) ;
signal ( SIGTTOU , SIG_IGN ) ;
if ( tcsetpgrp ( STDIN_FILENO , getpid ( ) ) = = - 1 )
perror ( " tcsetpgrp failed " ) ;
signal ( SIGTTOU , SIG_DFL ) ;
}
2014-04-29 13:24:53 +02:00
void setup_dummy_pipe_on_stdin ( int pfd [ 2 ] ) {
if ( pipe ( pfd ) = = - 1 )
perror ( " pipe failed " ) ;
if ( dup2 ( pfd [ 0 ] , STDIN_FILENO ) = = - 1 )
perror ( " dup2 failed " ) ;
}
2013-10-22 22:54:23 +02:00
void launch_debugger ( AMDeviceRef device , CFURLRef url ) {
AMDeviceConnect ( device ) ;
assert ( AMDeviceIsPaired ( device ) ) ;
assert ( AMDeviceValidatePairing ( device ) = = 0 ) ;
assert ( AMDeviceStartSession ( device ) = = 0 ) ;
printf ( " ------ Debug phase ------ \n " ) ;
mount_developer_image ( device ) ; // put debugserver on the device
start_remote_debug_server ( device ) ; // start debugserver
write_lldb_prep_cmds ( device , url ) ; // dump the necessary lldb commands into a file
CFRelease ( url ) ;
printf ( " [100%%] Connecting to remote debug server \n " ) ;
printf ( " ------------------------- \n " ) ;
setpgid ( getpid ( ) , 0 ) ;
2014-04-01 14:37:49 +02:00
signal ( SIGHUP , killed ) ;
2013-10-22 22:54:23 +02:00
signal ( SIGINT , killed ) ;
signal ( SIGTERM , killed ) ;
2014-04-01 14:37:49 +02:00
// Need this before fork to avoid race conditions. For child process we remove this right after fork.
signal ( SIGLLDB , lldb_finished_handler ) ;
2013-10-22 22:54:23 +02:00
parent = getpid ( ) ;
int pid = fork ( ) ;
if ( pid = = 0 ) {
2014-04-01 14:37:49 +02:00
signal ( SIGHUP , SIG_DFL ) ;
signal ( SIGLLDB , SIG_DFL ) ;
child = getpid ( ) ;
2014-04-29 13:24:53 +02:00
int pfd [ 2 ] = { - 1 , - 1 } ;
if ( isatty ( STDIN_FILENO ) )
// If we are running on a terminal, then we need to bring process to foreground for input
// to work correctly on lldb's end.
bring_process_to_foreground ( ) ;
else
// If lldb is running in a non terminal environment, then it freaks out spamming "^D" and
// "quit". It seems this is caused by read() on stdin returning EOF in lldb. To hack around
// this we setup a dummy pipe on stdin, so read() would block expecting "user's" input.
setup_dummy_pipe_on_stdin ( pfd ) ;
2014-03-27 15:14:14 +01:00
2013-12-10 20:18:13 +01:00
char lldb_shell [ 400 ] ;
2014-03-26 17:59:01 +01:00
sprintf ( lldb_shell , LLDB_SHELL ) ;
2013-10-22 22:54:23 +02:00
if ( device_id ! = NULL )
strcat ( lldb_shell , device_id ) ;
2014-04-01 14:37:49 +02:00
int status = system ( lldb_shell ) ; // launch lldb
if ( status = = - 1 )
perror ( " failed launching lldb " ) ;
2014-04-29 13:24:53 +02:00
close ( pfd [ 0 ] ) ;
close ( pfd [ 1 ] ) ;
2014-04-01 14:37:49 +02:00
// Notify parent we're exiting
kill ( parent , SIGLLDB ) ;
// Pass lldb exit code
_exit ( WEXITSTATUS ( status ) ) ;
} else if ( pid > 0 ) {
child = pid ;
} else {
perror ( " fork failed " ) ;
exit ( exitcode_error ) ;
}
2013-10-22 22:54:23 +02:00
}
2013-12-27 17:59:39 +01:00
CFStringRef get_bundle_id ( CFURLRef app_url )
{
if ( app_url = = NULL )
return NULL ;
CFURLRef url = CFURLCreateCopyAppendingPathComponent ( NULL , app_url , CFSTR ( " Info.plist " ) , false ) ;
if ( url = = NULL )
return NULL ;
CFReadStreamRef stream = CFReadStreamCreateWithFile ( NULL , url ) ;
CFRelease ( url ) ;
if ( stream = = NULL )
return NULL ;
CFPropertyListRef plist = NULL ;
if ( CFReadStreamOpen ( stream ) = = TRUE ) {
plist = CFPropertyListCreateWithStream ( NULL , stream , 0 ,
kCFPropertyListImmutable , NULL , NULL ) ;
}
CFReadStreamClose ( stream ) ;
CFRelease ( stream ) ;
if ( plist = = NULL )
return NULL ;
const void * value = CFDictionaryGetValue ( plist , CFSTR ( " CFBundleIdentifier " ) ) ;
CFStringRef bundle_id = NULL ;
if ( value ! = NULL )
bundle_id = CFRetain ( value ) ;
CFRelease ( plist ) ;
return bundle_id ;
}
2011-09-16 16:06:56 +02:00
void handle_device ( AMDeviceRef device ) {
if ( found_device ) return ; // handle one device only
2011-11-04 11:04:46 +01:00
CFStringRef found_device_id = AMDeviceCopyDeviceIdentifier ( device ) ;
if ( device_id ! = NULL ) {
if ( strcmp ( device_id , CFStringGetCStringPtr ( found_device_id , CFStringGetSystemEncoding ( ) ) ) = = 0 ) {
found_device = true ;
} else {
return ;
}
} else {
found_device = true ;
}
2013-10-22 22:54:23 +02:00
if ( detect_only ) {
printf ( " [....] Found device (%s). \n " , CFStringGetCStringPtr ( found_device_id , CFStringGetSystemEncoding ( ) ) ) ;
exit ( 0 ) ;
}
2011-11-04 11:04:46 +01:00
2011-09-16 16:06:56 +02:00
2013-10-22 22:54:23 +02:00
CFRetain ( device ) ; // don't know if this is necessary?
2011-09-16 16:06:56 +02:00
CFStringRef path = CFStringCreateWithCString ( NULL , app_path , kCFStringEncodingASCII ) ;
CFURLRef relative_url = CFURLCreateWithFileSystemPath ( NULL , path , kCFURLPOSIXPathStyle , false ) ;
CFURLRef url = CFURLCopyAbsoluteURL ( relative_url ) ;
CFRelease ( relative_url ) ;
2013-12-27 17:59:39 +01:00
if ( uninstall ) {
printf ( " ------ Uninstall phase ------ \n " ) ;
CFStringRef bundle_id = get_bundle_id ( url ) ;
if ( bundle_id = = NULL ) {
printf ( " Error: Unable to get bundle id from package %s \n Uninstall failed \n " , app_path ) ;
} else {
AMDeviceConnect ( device ) ;
assert ( AMDeviceIsPaired ( device ) ) ;
assert ( AMDeviceValidatePairing ( device ) = = 0 ) ;
assert ( AMDeviceStartSession ( device ) = = 0 ) ;
assert ( AMDeviceSecureUninstallApplication ( 0 , device , bundle_id , 0 , NULL , 0 ) = = 0 ) ;
assert ( AMDeviceStopSession ( device ) = = 0 ) ;
assert ( AMDeviceDisconnect ( device ) = = 0 ) ;
printf ( " [ OK ] Uninstalled package with bundle id %s \n " , CFStringGetCStringPtr ( bundle_id , CFStringGetSystemEncoding ( ) ) ) ;
}
}
2013-10-22 22:54:23 +02:00
if ( install ) {
2013-12-27 17:59:39 +01:00
printf ( " ------ Install phase ------ \n " ) ;
2013-10-22 22:54:23 +02:00
printf ( " [ 0%%] Found device (%s), beginning install \n " , CFStringGetCStringPtr ( found_device_id , CFStringGetSystemEncoding ( ) ) ) ;
2012-02-28 17:51:01 +01:00
2013-10-22 22:54:23 +02:00
AMDeviceConnect ( device ) ;
assert ( AMDeviceIsPaired ( device ) ) ;
assert ( AMDeviceValidatePairing ( device ) = = 0 ) ;
assert ( AMDeviceStartSession ( device ) = = 0 ) ;
2012-02-28 17:51:01 +01:00
2011-09-16 16:06:56 +02:00
2012-02-28 17:51:01 +01:00
2013-10-22 22:54:23 +02:00
service_conn_t afcFd ;
assert ( AMDeviceStartService ( device , CFSTR ( " com.apple.afc " ) , & afcFd , NULL ) = = 0 ) ;
assert ( AMDeviceStopSession ( device ) = = 0 ) ;
assert ( AMDeviceDisconnect ( device ) = = 0 ) ;
assert ( AMDeviceTransferApplication ( afcFd , path , NULL , transfer_callback , NULL ) = = 0 ) ;
2012-02-28 17:51:01 +01:00
2013-10-22 22:54:23 +02:00
close ( afcFd ) ;
2011-09-16 16:06:56 +02:00
2013-10-22 22:54:23 +02:00
CFStringRef keys [ ] = { CFSTR ( " PackageType " ) } ;
CFStringRef values [ ] = { CFSTR ( " Developer " ) } ;
CFDictionaryRef options = CFDictionaryCreate ( NULL , ( const void * * ) & keys , ( const void * * ) & values , 1 , & kCFTypeDictionaryKeyCallBacks , & kCFTypeDictionaryValueCallBacks ) ;
2012-02-28 17:51:01 +01:00
2013-10-22 22:54:23 +02:00
AMDeviceConnect ( device ) ;
assert ( AMDeviceIsPaired ( device ) ) ;
assert ( AMDeviceValidatePairing ( device ) = = 0 ) ;
assert ( AMDeviceStartSession ( device ) = = 0 ) ;
2011-09-16 16:06:56 +02:00
2013-10-22 22:54:23 +02:00
service_conn_t installFd ;
assert ( AMDeviceStartService ( device , CFSTR ( " com.apple.mobile.installation_proxy " ) , & installFd , NULL ) = = 0 ) ;
2011-09-16 16:06:56 +02:00
2013-10-22 22:54:23 +02:00
assert ( AMDeviceStopSession ( device ) = = 0 ) ;
assert ( AMDeviceDisconnect ( device ) = = 0 ) ;
2011-09-16 16:06:56 +02:00
2013-10-22 22:54:23 +02:00
mach_error_t result = AMDeviceInstallApplication ( installFd , path , options , install_callback , NULL ) ;
if ( result ! = 0 )
{
2014-04-28 17:31:59 +02:00
char * error = " Unknown error. " ;
if ( result = = 0xe8008015 )
error = " Your application failed code-signing checks. Check your certificates, provisioning profiles, and bundle ids. " ;
printf ( " AMDeviceInstallApplication failed: 0x%X: %s \n " , result , error ) ;
2014-04-01 14:37:49 +02:00
exit ( exitcode_error ) ;
2013-10-22 22:54:23 +02:00
}
2011-09-16 16:06:56 +02:00
2013-10-22 22:54:23 +02:00
close ( installFd ) ;
2013-09-19 23:31:56 +02:00
2013-10-22 22:54:23 +02:00
CFRelease ( path ) ;
CFRelease ( options ) ;
2012-02-28 17:51:01 +01:00
2013-10-22 22:54:23 +02:00
printf ( " [100%%] Installed package %s \n " , app_path ) ;
2011-09-16 16:06:56 +02:00
}
2013-09-19 23:31:56 +02:00
2013-10-22 22:54:23 +02:00
if ( ! debug ) exit ( 0 ) ; // no debug phase
launch_debugger ( device , url ) ;
2011-09-16 16:06:56 +02:00
}
void device_callback ( struct am_device_notification_callback_info * info , void * arg ) {
switch ( info - > msg ) {
case ADNCI_MSG_CONNECTED :
handle_device ( info - > dev ) ;
default :
break ;
}
}
2012-02-28 17:51:01 +01:00
void timeout_callback ( CFRunLoopTimerRef timer , void * info ) {
if ( ! found_device ) {
2013-10-22 22:54:23 +02:00
printf ( " [....] Timed out waiting for device. \n " ) ;
2014-04-01 14:37:49 +02:00
exit ( exitcode_error ) ;
2011-09-16 16:06:56 +02:00
}
2012-02-28 17:51:01 +01:00
}
2011-09-16 16:06:56 +02:00
2012-02-28 17:51:01 +01:00
void usage ( const char * app ) {
2013-10-22 22:54:23 +02:00
printf (
" Usage: %s [OPTION]... \n "
" -d, --debug launch the app in GDB after installation \n "
" -i, --id <device_id> the id of the device to connect to \n "
" -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 "
" -t, --timeout <timeout> number of seconds to wait for a device to be connected \n "
" -u, --unbuffered don't buffer stdout \n "
" -g, --gdbargs <args> extra arguments to pass to GDB when starting the debugger \n "
" -x, --gdbexec <file> GDB commands script file \n "
" -n, --nostart do not start the app when debugging \n "
2014-03-26 18:13:48 +01:00
" -I, --noninteractive start in non interactive mode (quit when app crashes or exits) \n "
2013-10-22 22:54:23 +02:00
" -v, --verbose enable verbose output \n "
2014-03-26 18:13:48 +01:00
" -m, --noinstall directly start debugging without app install (-d not required) \n "
2013-12-20 23:38:58 +01:00
" -p, --port <number> port used for device, default: 12345 \n "
2013-12-27 17:59:39 +01:00
" -r, --uninstall uninstall the app before install (do not use with -m; app cache and data are cleared) \n "
2013-10-22 23:10:52 +02:00
" -V, --version print the executable version \n " ,
2013-10-22 22:54:23 +02:00
app ) ;
2012-02-28 17:51:01 +01:00
}
2013-10-22 23:10:52 +02:00
void show_version ( ) {
printf ( " %s \n " , APP_VERSION ) ;
}
2012-02-28 17:51:01 +01:00
int main ( int argc , char * argv [ ] ) {
static struct option longopts [ ] = {
{ " debug " , no_argument , NULL , ' d ' } ,
{ " id " , required_argument , NULL , ' i ' } ,
{ " bundle " , required_argument , NULL , ' b ' } ,
{ " args " , required_argument , NULL , ' a ' } ,
{ " verbose " , no_argument , NULL , ' v ' } ,
{ " timeout " , required_argument , NULL , ' t ' } ,
2013-10-22 22:54:23 +02:00
{ " gdbexec " , no_argument , NULL , ' x ' } ,
2013-09-19 23:31:56 +02:00
{ " unbuffered " , no_argument , NULL , ' u ' } ,
2013-10-22 22:54:23 +02:00
{ " nostart " , no_argument , NULL , ' n ' } ,
2014-03-26 18:13:48 +01:00
{ " noninteractive " , no_argument , NULL , ' I ' } ,
2013-10-22 22:54:23 +02:00
{ " detect " , no_argument , NULL , ' c ' } ,
2013-10-22 23:10:52 +02:00
{ " version " , no_argument , NULL , ' V ' } ,
2013-10-22 22:54:23 +02:00
{ " noinstall " , no_argument , NULL , ' m ' } ,
2013-12-20 23:38:58 +01:00
{ " port " , required_argument , NULL , ' p ' } ,
2013-12-27 17:59:39 +01:00
{ " uninstall " , no_argument , NULL , ' r ' } ,
2012-02-28 17:51:01 +01:00
{ NULL , 0 , NULL , 0 } ,
} ;
char ch ;
2013-12-27 17:59:39 +01:00
while ( ( ch = getopt_long ( argc , argv , " VmcdvunrIi:b:a:t:g:x:p: " , longopts , NULL ) ) ! = - 1 )
2012-02-28 17:51:01 +01:00
{
switch ( ch ) {
2013-10-22 22:54:23 +02:00
case ' m ' :
install = 0 ;
debug = 1 ;
break ;
2012-02-28 17:51:01 +01:00
case ' d ' :
debug = 1 ;
break ;
case ' i ' :
device_id = optarg ;
break ;
case ' b ' :
app_path = optarg ;
break ;
case ' a ' :
args = optarg ;
break ;
case ' v ' :
verbose = 1 ;
break ;
case ' t ' :
timeout = atoi ( optarg ) ;
break ;
2013-09-19 23:31:56 +02:00
case ' u ' :
unbuffered = 1 ;
break ;
2013-10-22 22:54:23 +02:00
case ' n ' :
nostart = 1 ;
break ;
2014-03-26 18:13:48 +01:00
case ' I ' :
interactive = false ;
break ;
2013-10-22 22:54:23 +02:00
case ' c ' :
detect_only = true ;
break ;
2013-10-22 23:10:52 +02:00
case ' V ' :
show_version ( ) ;
2014-04-01 14:37:49 +02:00
return exitcode_error ;
2013-12-20 23:38:58 +01:00
case ' p ' :
port = atoi ( optarg ) ;
break ;
2013-12-27 17:59:39 +01:00
case ' r ' :
uninstall = 1 ;
break ;
2012-02-28 17:51:01 +01:00
default :
usage ( argv [ 0 ] ) ;
2014-04-01 14:37:49 +02:00
return exitcode_error ;
2011-11-04 11:04:46 +01:00
}
2011-09-16 16:06:56 +02:00
}
2013-10-22 22:54:23 +02:00
if ( ! app_path & & ! detect_only ) {
2012-02-28 17:51:01 +01:00
usage ( argv [ 0 ] ) ;
2014-04-01 14:37:49 +02:00
exit ( exitcode_error ) ;
2012-02-28 17:51:01 +01:00
}
2013-10-22 22:54:23 +02:00
if ( unbuffered ) {
setbuf ( stdout , NULL ) ;
setbuf ( stderr , NULL ) ;
}
if ( detect_only & & timeout = = 0 ) {
timeout = 5 ;
}
2013-09-19 23:31:56 +02:00
2013-10-22 22:54:23 +02:00
if ( app_path ) {
assert ( access ( app_path , F_OK ) = = 0 ) ;
}
2011-09-16 16:06:56 +02:00
AMDSetLogLevel ( 5 ) ; // otherwise syslog gets flooded with crap
2012-02-28 17:51:01 +01:00
if ( timeout > 0 )
{
CFRunLoopTimerRef timer = CFRunLoopTimerCreate ( NULL , CFAbsoluteTimeGetCurrent ( ) + timeout , 0 , 0 , 0 , timeout_callback , NULL ) ;
CFRunLoopAddTimer ( CFRunLoopGetCurrent ( ) , timer , kCFRunLoopCommonModes ) ;
printf ( " [....] Waiting up to %d seconds for iOS device to be connected \n " , timeout ) ;
}
else
{
printf ( " [....] Waiting for iOS device to be connected \n " ) ;
}
2011-09-16 16:06:56 +02:00
struct am_device_notification * notify ;
2012-02-28 17:51:01 +01:00
AMDeviceNotificationSubscribe ( & device_callback , 0 , 0 , NULL , & notify ) ;
2011-09-16 16:06:56 +02:00
CFRunLoopRun ( ) ;
2013-10-22 22:54:23 +02:00
}