From 10e008f4a97d5657f8a2fd294a21ee4e56a7d3f6 Mon Sep 17 00:00:00 2001 From: Laurent Sansonetti Date: Tue, 19 Jul 2011 23:51:27 -0700 Subject: [PATCH] add device deployment support --- Rakefile | 7 +- data/Rakefile | 10 +- data/src/MobileDevice.h | 512 ++++++++++++++++++++++++++++++++++++ data/src/deploy.m | 173 ++++++++++++ lib/rubixir/rake.rb | 8 +- lib/rubixir/rake/builder.rb | 2 +- lib/rubixir/rake/config.rb | 4 + sample/hello/app/main.rb | 2 +- 8 files changed, 708 insertions(+), 10 deletions(-) create mode 100644 data/src/MobileDevice.h create mode 100644 data/src/deploy.m diff --git a/Rakefile b/Rakefile index 74e7ce37..cb911194 100644 --- a/Rakefile +++ b/Rakefile @@ -1,6 +1,6 @@ PLATFORMS_DIR = '/Developer/Platforms' SDK_VERSION = '4.3' -PROJECT_VERSION = '0.0.5' +PROJECT_VERSION = '0.0.6' verbose(true) @@ -49,7 +49,10 @@ require 'rake/gempackagetask' gem_spec = Gem::Specification.new do |spec| files = [] files.concat(Dir.glob('./lib/**/*')) - files.concat(Dir.glob('./data/**/*').reject { |path| File.basename(path) =~ /(Rakefile|_stubs.m)/ }) + files.concat(Dir.glob('./data/BridgeSupport/*.bridgesupport')) + files.concat(%w{./data/deploy ./data/llc ./data/ruby}) + files.concat(Dir.glob('./data/iPhoneOS/*')) + files.concat(Dir.glob('./data/iPhoneSimulator/*')) files.concat(Dir.glob('./doc/html/**/*')) files.concat(Dir.glob('./sample/**/*').reject { |path| path =~ /build/ }) files.reject! { |path| /^\./.match(File.basename(path)) } diff --git a/data/Rakefile b/data/Rakefile index 269cabe8..6896120c 100644 --- a/data/Rakefile +++ b/data/Rakefile @@ -4,7 +4,7 @@ SDK_VERSION = ENV['sdk_version'] verbose(true) task :default => :all -task :all => [:vm_files, :bridgesupport_files, :bridgesupport_static_stubs] +task :all => [:vm_files, :bridgesupport_files, :bridgesupport_static_stubs, :deploy] task :vm_files do install '../vm/miniruby', 'ruby' @@ -81,6 +81,10 @@ task :bridgesupport_static_stubs do end end -task :clean do - %w{ruby llc iPhoneOS iPhoneSimulator BridgeSupport}.each { |path| rm_rf(path) } +task :deploy do + sh "/usr/bin/gcc -I./src -Wall -O3 src/deploy.m -o deploy /System/Library/PrivateFrameworks/MobileDevice.framework/MobileDevice -framework Foundation" +end + +task :clean do + %w{ruby llc iPhoneOS iPhoneSimulator BridgeSupport deploy}.each { |path| rm_rf(path) } end diff --git a/data/src/MobileDevice.h b/data/src/MobileDevice.h new file mode 100644 index 00000000..f720a20e --- /dev/null +++ b/data/src/MobileDevice.h @@ -0,0 +1,512 @@ +/* ---------------------------------------------------------------------------- + * MobileDevice.h - interface to MobileDevice.framework + * $LastChangedDate: 2007-01-08 07:54:51$ + * ------------------------------------------------------------------------- */ + +#if !defined(MOBILEDEVICE_H) +#define MOBILEDEVICE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(WIN32) +#include +typedef unsigned int mach_error_t; +#elif defined(__APPLE__) +#include +#include +#endif + +/* Error codes */ +#define MDERR_APPLE_MOBILE (err_system(0x3a)) +#define MDERR_IPHONE (err_sub(0)) + +/* Apple Mobile (AM*) errors */ +#define MDERR_OK ERR_SUCCESS +#define MDERR_SYSCALL (ERR_MOBILE_DEVICE | 0x01) +#define MDERR_OUT_OF_MEMORY (ERR_MOBILE_DEVICE | 0x03) +#define MDERR_QUERY_FAILED (ERR_MOBILE_DEVICE | 0x04) +#define MDERR_INVALID_ARGUMENT (ERR_MOBILE_DEVICE | 0x0b) +#define MDERR_DICT_NOT_LOADED (ERR_MOBILE_DEVICE | 0x25) + +/* Apple File Connection (AFC*) errors */ +#define MDERR_AFC_OUT_OF_MEMORY 0x03 + +/* USBMux errors */ +#define MDERR_USBMUX_ARG_NULL 0x16 +#define MDERR_USBMUX_FAILED 0xffffffff + +/* Messages passed to device notification callbacks: passed as part of + * am_device_notification_callback_info. */ +#define ADNCI_MSG_CONNECTED 1 +#define ADNCI_MSG_DISCONNECTED 2 +#define ADNCI_MSG_UNKNOWN 3 + +#define AMD_IPHONE_PRODUCT_ID 0x1290 +//#define AMD_IPHONE_SERIAL "" + +/* Services, found in /System/Library/Lockdown/Services.plist */ +#define AMSVC_AFC CFSTR("com.apple.afc") +#define AMSVC_BACKUP CFSTR("com.apple.mobilebackup") +#define AMSVC_CRASH_REPORT_COPY CFSTR("com.apple.crashreportcopy") +#define AMSVC_DEBUG_IMAGE_MOUNT CFSTR("com.apple.mobile.debug_image_mount") +#define AMSVC_NOTIFICATION_PROXY CFSTR("com.apple.mobile.notification_proxy") +#define AMSVC_PURPLE_TEST CFSTR("com.apple.purpletestr") +#define AMSVC_SOFTWARE_UPDATE CFSTR("com.apple.mobile.software_update") +#define AMSVC_SYNC CFSTR("com.apple.mobilesync") +#define AMSVC_SCREENSHOT CFSTR("com.apple.screenshotr") +#define AMSVC_SYSLOG_RELAY CFSTR("com.apple.syslog_relay") +#define AMSVC_SYSTEM_PROFILER CFSTR("com.apple.mobile.system_profiler") + +typedef unsigned int afc_error_t; +typedef unsigned int usbmux_error_t; + +struct am_recovery_device; + +struct am_device_notification_callback_info { + struct am_device *dev; /* 0 device */ + unsigned int msg; /* 4 one of ADNCI_MSG_* */ +} __attribute__ ((packed)); + +/* The type of the device restore notification callback functions. + * TODO: change to correct type. */ +typedef void (*am_restore_device_notification_callback)(struct + am_recovery_device *); + +/* This is a CoreFoundation object of class AMRecoveryModeDevice. */ +struct am_recovery_device { + unsigned char unknown0[8]; /* 0 */ + am_restore_device_notification_callback callback; /* 8 */ + void *user_info; /* 12 */ + unsigned char unknown1[12]; /* 16 */ + unsigned int readwrite_pipe; /* 28 */ + unsigned char read_pipe; /* 32 */ + unsigned char write_ctrl_pipe; /* 33 */ + unsigned char read_unknown_pipe; /* 34 */ + unsigned char write_file_pipe; /* 35 */ + unsigned char write_input_pipe; /* 36 */ +} __attribute__ ((packed)); + +/* A CoreFoundation object of class AMRestoreModeDevice. */ +struct am_restore_device { + unsigned char unknown[32]; + int port; +} __attribute__ ((packed)); + +/* The type of the device notification callback function. */ +typedef void(*am_device_notification_callback)(struct + am_device_notification_callback_info *); + +/* The type of the _AMDDeviceAttached function. + * TODO: change to correct type. */ +typedef void *amd_device_attached_callback; + +/* The type of the device restore notification callback functions. + * TODO: change to correct type. */ +//typedef void (*am_restore_device_notification_callback)(struct +// am_recovery_device *); + +struct am_device { + unsigned char unknown0[16]; /* 0 - zero */ + unsigned int device_id; /* 16 */ + unsigned int product_id; /* 20 - set to AMD_IPHONE_PRODUCT_ID */ + char *serial; /* 24 - set to AMD_IPHONE_SERIAL */ + unsigned int unknown1; /* 28 */ + unsigned char unknown2[4]; /* 32 */ + unsigned int lockdown_conn; /* 36 */ + unsigned char unknown3[8]; /* 40 */ +} __attribute__ ((packed)); + +struct am_device_notification { + unsigned int unknown0; /* 0 */ + unsigned int unknown1; /* 4 */ + unsigned int unknown2; /* 8 */ + am_device_notification_callback callback; /* 12 */ + unsigned int unknown3; /* 16 */ +} __attribute__ ((packed)); + +struct afc_connection { + unsigned int handle; /* 0 */ + unsigned int unknown0; /* 4 */ + unsigned char unknown1; /* 8 */ + unsigned char padding[3]; /* 9 */ + unsigned int unknown2; /* 12 */ + unsigned int unknown3; /* 16 */ + unsigned int unknown4; /* 20 */ + unsigned int fs_block_size; /* 24 */ + unsigned int sock_block_size; /* 28: always 0x3c */ + unsigned int io_timeout; /* 32: from AFCConnectionOpen, usu. 0 */ + void *afc_lock; /* 36 */ + unsigned int context; /* 40 */ +} __attribute__ ((packed)); + +struct afc_device_info { + unsigned char unknown[12]; /* 0 */ +} __attribute__ ((packed)); + +struct afc_directory { + unsigned char unknown[0]; /* size unknown */ +} __attribute__ ((packed)); + +struct afc_dictionary { + unsigned char unknown[0]; /* size unknown */ +} __attribute__ ((packed)); + +typedef unsigned long long afc_file_ref; + +struct usbmux_listener_1 { /* offset value in iTunes */ + unsigned int unknown0; /* 0 1 */ + unsigned char *unknown1; /* 4 ptr, maybe device? */ + amd_device_attached_callback callback; /* 8 _AMDDeviceAttached */ + unsigned int unknown3; /* 12 */ + unsigned int unknown4; /* 16 */ + unsigned int unknown5; /* 20 */ +} __attribute__ ((packed)); + +struct usbmux_listener_2 { + unsigned char unknown0[4144]; +} __attribute__ ((packed)); + +struct am_bootloader_control_packet { + unsigned char opcode; /* 0 */ + unsigned char length; /* 1 */ + unsigned char magic[2]; /* 2: 0x34, 0x12 */ + unsigned char payload[0]; /* 4 */ +} __attribute__ ((packed)); + +/* ---------------------------------------------------------------------------- + * Public routines + * ------------------------------------------------------------------------- */ + +/* Registers a notification with the current run loop. The callback gets + * copied into the notification struct, as well as being registered with the + * current run loop. dn_unknown3 gets copied into unknown3 in the same. + * (Maybe dn_unknown3 is a user info parameter that gets passed as an arg to + * the callback?) unused0 and unused1 are both 0 when iTunes calls this. + * In iTunes the callback is located from $3db78e-$3dbbaf. + * + * Returns: + * MDERR_OK if successful + * MDERR_SYSCALL if CFRunLoopAddSource() failed + * MDERR_OUT_OF_MEMORY if we ran out of memory + */ + +mach_error_t AMDeviceNotificationSubscribe(am_device_notification_callback + callback, unsigned int unused0, unsigned int unused1, unsigned int + dn_unknown3, struct am_device_notification **notification); + +/* Connects to the iPhone. Pass in the am_device structure that the + * notification callback will give to you. + * + * Returns: + * MDERR_OK if successfully connected + * MDERR_SYSCALL if setsockopt() failed + * MDERR_QUERY_FAILED if the daemon query failed + * MDERR_INVALID_ARGUMENT if USBMuxConnectByPort returned 0xffffffff + */ + +mach_error_t AMDeviceConnect(struct am_device *device); + +/* Calls PairingRecordPath() on the given device, than tests whether the path + * which that function returns exists. During the initial connect, the path + * returned by that function is '/', and so this returns 1. + * + * Returns: + * 0 if the path did not exist + * 1 if it did + */ + +int AMDeviceIsPaired(struct am_device *device); + +/* iTunes calls this function immediately after testing whether the device is + * paired. It creates a pairing file and establishes a Lockdown connection. + * + * Returns: + * MDERR_OK if successful + * MDERR_INVALID_ARGUMENT if the supplied device is null + * MDERR_DICT_NOT_LOADED if the load_dict() call failed + */ + +mach_error_t AMDeviceValidatePairing(struct am_device *device); + +/* Creates a Lockdown session and adjusts the device structure appropriately + * to indicate that the session has been started. iTunes calls this function + * after validating pairing. + * + * Returns: + * MDERR_OK if successful + * MDERR_INVALID_ARGUMENT if the Lockdown conn has not been established + * MDERR_DICT_NOT_LOADED if the load_dict() call failed + */ + +mach_error_t AMDeviceStartSession(struct am_device *device); + +/* Starts a service and returns a handle that can be used in order to further + * access the service. You should stop the session and disconnect before using + * the service. iTunes calls this function after starting a session. It starts + * the service and the SSL connection. unknown may safely be + * NULL (it is when iTunes calls this), but if it is not, then it will be + * filled upon function exit. service_name should be one of the AMSVC_* + * constants. If the service is AFC (AMSVC_AFC), then the handle is the handle + * that will be used for further AFC* calls. + * + * Returns: + * MDERR_OK if successful + * MDERR_SYSCALL if the setsockopt() call failed + * MDERR_INVALID_ARGUMENT if the Lockdown conn has not been established + */ + +mach_error_t AMDeviceStartService(struct am_device *device, CFStringRef + service_name, struct afc_connection **handle, unsigned int * + unknown); + +/* Stops a session. You should do this before accessing services. + * + * Returns: + * MDERR_OK if successful + * MDERR_INVALID_ARGUMENT if the Lockdown conn has not been established + */ + +mach_error_t AMDeviceStopSession(struct am_device *device); + +/* Opens an Apple File Connection. You must start the appropriate service + * first with AMDeviceStartService(). In iTunes, io_timeout is 0. + * + * Returns: + * MDERR_OK if successful + * MDERR_AFC_OUT_OF_MEMORY if malloc() failed + */ + +afc_error_t AFCConnectionOpen(struct afc_connection *handle, unsigned int io_timeout, + struct afc_connection **conn); + +/* Pass in a pointer to an afc_device_info structure. It will be filled. */ +afc_error_t AFCDeviceInfoOpen(struct afc_connection *conn, struct + afc_dictionary **info); + +/* Turns debug mode on if the environment variable AFCDEBUG is set to a numeric + * value, or if the file '/AFCDEBUG' is present and contains a value. */ +#if defined(__APPLE__) +void AFCPlatformInit(); +#endif + +/* Opens a directory on the iPhone. Pass in a pointer in dir to be filled in. + * Note that this normally only accesses the iTunes sandbox/partition as the + * root, which is /var/root/Media. Pathnames are specified with '/' delimiters + * as in Unix style. + * + * Returns: + * MDERR_OK if successful + */ + +afc_error_t AFCDirectoryOpen(struct afc_connection *conn, char *path, struct + afc_directory **dir); + +/* Acquires the next entry in a directory previously opened with + * AFCDirectoryOpen(). When dirent is filled with a NULL value, then the end + * of the directory has been reached. '.' and '..' will be returned as the + * first two entries in each directory except the root; you may want to skip + * over them. + * + * Returns: + * MDERR_OK if successful, even if no entries remain + */ + +afc_error_t AFCDirectoryRead(struct afc_connection *conn/*unsigned int unused*/, struct afc_directory *dir, + char **dirent); + +afc_error_t AFCDirectoryClose(struct afc_connection *conn, struct afc_directory *dir); +afc_error_t AFCDirectoryCreate(struct afc_connection *conn, char *dirname); +afc_error_t AFCRemovePath(struct afc_connection *conn, char *dirname); + +/* Returns the context field of the given AFC connection. */ +unsigned int AFCConnectionGetContext(struct afc_connection *conn); + +/* Returns the fs_block_size field of the given AFC connection. */ +unsigned int AFCConnectionGetFSBlockSize(struct afc_connection *conn); + +/* Returns the io_timeout field of the given AFC connection. In iTunes this is + * 0. */ +unsigned int AFCConnectionGetIOTimeout(struct afc_connection *conn); + +/* Returns the sock_block_size field of the given AFC connection. */ +unsigned int AFCConnectionGetSocketBlockSize(struct afc_connection *conn); + +/* Closes the given AFC connection. */ +afc_error_t AFCConnectionClose(struct afc_connection *conn); + +/* Registers for device notifications related to the restore process. unknown0 + * is zero when iTunes calls this. In iTunes, + * the callbacks are located at: + * 1: $3ac68e-$3ac6b1, calls $3ac542(unknown1, arg, 0) + * 2: $3ac66a-$3ac68d, calls $3ac542(unknown1, 0, arg) + * 3: $3ac762-$3ac785, calls $3ac6b2(unknown1, arg, 0) + * 4: $3ac73e-$3ac761, calls $3ac6b2(unknown1, 0, arg) + */ + +unsigned int AMRestoreRegisterForDeviceNotifications( + am_restore_device_notification_callback dfu_connect_callback, + am_restore_device_notification_callback recovery_connect_callback, + am_restore_device_notification_callback dfu_disconnect_callback, + am_restore_device_notification_callback recovery_disconnect_callback, + unsigned int unknown0, + void *user_info); + +/* Causes the restore functions to spit out (unhelpful) progress messages to + * the file specified by the given path. iTunes always calls this right before + * restoring with a path of + * "$HOME/Library/Logs/iPhone Updater Logs/iPhoneUpdater X.log", where X is an + * unused number. + */ + +unsigned int AMRestoreEnableFileLogging(char *path); + +/* Initializes a new option dictionary to default values. Pass the constant + * kCFAllocatorDefault as the allocator. The option dictionary looks as + * follows: + * { + * NORImageType => 'production', + * AutoBootDelay => 0, + * KernelCacheType => 'Release', + * UpdateBaseband => true, + * DFUFileType => 'RELEASE', + * SystemImageType => 'User', + * CreateFilesystemPartitions => true, + * FlashNOR => true, + * RestoreBootArgs => 'rd=md0 nand-enable-reformat=1 -progress' + * BootImageType => 'User' + * } + * + * Returns: + * the option dictionary if successful + * NULL if out of memory + */ + +CFMutableDictionaryRef AMRestoreCreateDefaultOptions(CFAllocatorRef allocator); + +/* ---------------------------------------------------------------------------- + * Less-documented public routines + * ------------------------------------------------------------------------- */ + +/* mode 2 = read, mode 3 = write; unknown = 0 */ +afc_error_t AFCFileRefOpen(struct afc_connection *conn, char *path, unsigned + long long int mode, afc_file_ref *ref); +afc_error_t AFCFileRefRead(struct afc_connection *conn, afc_file_ref ref, + void *buf, unsigned int *len); +afc_error_t AFCFileRefWrite(struct afc_connection *conn, afc_file_ref ref, + void *buf, unsigned int len); +afc_error_t AFCFileRefClose(struct afc_connection *conn, afc_file_ref ref); + +afc_error_t AFCFileInfoOpen(struct afc_connection *conn, char *path, struct + afc_dictionary **info); +afc_error_t AFCKeyValueRead(struct afc_dictionary *dict, char **key, char ** + val); +afc_error_t AFCKeyValueClose(struct afc_dictionary *dict); + +unsigned int AMRestorePerformRecoveryModeRestore(struct am_recovery_device * + rdev, CFDictionaryRef opts, void *callback, void *user_info); +unsigned int AMRestorePerformRestoreModeRestore(struct am_restore_device * + rdev, CFDictionaryRef opts, void *callback, void *user_info); + +struct am_restore_device *AMRestoreModeDeviceCreate(unsigned int unknown0, + unsigned int connection_id, unsigned int unknown1); + +unsigned int AMRestoreCreatePathsForBundle(CFStringRef restore_bundle_path, + CFStringRef kernel_cache_type, CFStringRef boot_image_type, unsigned int + unknown0, CFStringRef *firmware_dir_path, CFStringRef * + kernelcache_restore_path, unsigned int unknown1, CFStringRef * + ramdisk_path); + +unsigned int AMDeviceGetConnectionID(struct am_device *device); +mach_error_t AMDeviceEnterRecovery(struct am_device *device); +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); +mach_error_t AMDeviceCopyDeviceIdentifier(struct am_device *device); + +mach_error_t AMDShutdownNotificationProxy(void *); + +/*edits by geohot*/ +mach_error_t AMDeviceDeactivate(struct am_device *device); +mach_error_t AMDeviceActivate(struct am_device *device, CFMutableDictionaryRef); +mach_error_t AMDeviceRemoveValue(struct am_device *device, unsigned int, CFStringRef cfstring); + +/* ---------------------------------------------------------------------------- + * Semi-private routines + * ------------------------------------------------------------------------- */ + +/* Pass in a usbmux_listener_1 structure and a usbmux_listener_2 structure + * pointer, which will be filled with the resulting usbmux_listener_2. + * + * Returns: + * MDERR_OK if completed successfully + * MDERR_USBMUX_ARG_NULL if one of the arguments was NULL + * MDERR_USBMUX_FAILED if the listener was not created successfully + */ + +usbmux_error_t USBMuxListenerCreate(struct usbmux_listener_1 *esi_fp8, struct + usbmux_listener_2 **eax_fp12); + +/* ---------------------------------------------------------------------------- + * Less-documented semi-private routines + * ------------------------------------------------------------------------- */ + +usbmux_error_t USBMuxListenerHandleData(void *); + +/* ---------------------------------------------------------------------------- + * Private routines - here be dragons + * ------------------------------------------------------------------------- */ + +/* AMRestorePerformRestoreModeRestore() calls this function with a dictionary + * in order to perform certain special restore operations + * (RESTORED_OPERATION_*). It is thought that this function might enable + * significant access to the phone. */ + +/* +typedef unsigned int (*t_performOperation)(struct am_restore_device *rdev, + CFDictionaryRef op) __attribute__ ((regparm(2))); + t_performOperation _performOperation = (t_performOperation)0x3c39fa4b; +*/ + +/* ---------------------------------------------------------------------------- + * Less-documented private routines + * ------------------------------------------------------------------------- */ + + +/* +typedef int (*t_socketForPort)(struct am_restore_device *rdev, unsigned int port) + __attribute__ ((regparm(2))); +t_socketForPort _socketForPort = (t_socketForPort)(void *)0x3c39f36c; + +typedef void (*t_restored_send_message)(int port, CFDictionaryRef msg); +t_restored_send_message _restored_send_message = (t_restored_send_message)0x3c3a4e40; + +typedef CFDictionaryRef (*t_restored_receive_message)(int port); +t_restored_receive_message _restored_receive_message = (t_restored_receive_message)0x3c3a4d40; + +typedef unsigned int (*t_sendControlPacket)(struct am_recovery_device *rdev, unsigned + int msg1, unsigned int msg2, unsigned int unknown0, unsigned int *unknown1, + unsigned char *unknown2) __attribute__ ((regparm(3))); +t_sendControlPacket _sendControlPacket = (t_sendControlPacket)0x3c3a3da3;; + +typedef unsigned int (*t_sendCommandToDevice)(struct am_recovery_device *rdev, + CFStringRef cmd) __attribute__ ((regparm(2))); +t_sendCommandToDevice _sendCommandToDevice = (t_sendCommandToDevice)0x3c3a3e3b; + +typedef unsigned int (*t_AMRUSBInterfaceReadPipe)(unsigned int readwrite_pipe, unsigned + int read_pipe, unsigned char *data, unsigned int *len); +t_AMRUSBInterfaceReadPipe _AMRUSBInterfaceReadPipe = (t_AMRUSBInterfaceReadPipe)0x3c3a27e8; + +typedef unsigned int (*t_AMRUSBInterfaceWritePipe)(unsigned int readwrite_pipe, unsigned + int write_pipe, void *data, unsigned int len); +t_AMRUSBInterfaceWritePipe _AMRUSBInterfaceWritePipe = (t_AMRUSBInterfaceWritePipe)0x3c3a27cb; +*/ + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/data/src/deploy.m b/data/src/deploy.m new file mode 100644 index 00000000..e564e608 --- /dev/null +++ b/data/src/deploy.m @@ -0,0 +1,173 @@ +#import +#include "MobileDevice.h" + +CFStringRef AMDeviceGetName(struct am_device *dev); + +static void +die(const char *func, int retcode) +{ + printf("%s() error: code %d\n", func, retcode); + exit(1); +} + +static void +send_plist(NSFileHandle *handle, id plist) +{ + NSData *data = [NSPropertyListSerialization dataWithPropertyList:plist format:NSPropertyListXMLFormat_v1_0 options:0 error:nil]; + uint32_t nlen = CFSwapInt32HostToBig([data length]); + [handle writeData:[[[NSData alloc] initWithBytes:&nlen length:sizeof(nlen)] autorelease]]; + [handle writeData:data]; +} + +static id +read_plist(NSFileHandle *handle) +{ + NSData *datalen = [handle availableData]; + if ([datalen length] < 4) { + printf("error: datalen packet not found\n"); + exit(1); + } + uint32_t *lenp = (uint32_t *)[datalen bytes]; + uint32_t len = CFSwapInt32BigToHost(*lenp); + + NSMutableData *data = [NSMutableData data]; + while (true) { + NSData *chunk = [handle availableData]; + if (chunk == nil || [chunk length] == 0) { + break; + } + [data appendData:chunk]; + if ([data length] >= len) { + break; + } + } + + return [NSPropertyListSerialization propertyListWithData:data options:0 format:NULL error:nil]; +} + +static NSString *app_package_path = nil; +static NSData *app_package_data = nil; + +static void +device_go(struct am_device *dev) +{ + printf("connecting to device\n"); + int retcode = AMDeviceConnect(dev); + if (retcode != 0) { + die("AMDeviceConnect", retcode); + } + + printf("pairing device\n"); + retcode = AMDeviceValidatePairing(dev); + if (retcode != 0) { + die("AMDeviceValidatePairing", retcode); + } + + printf("creating lockdown session\n"); + retcode = AMDeviceStartSession(dev); + if (retcode != 0) { + die("AMDeviceStartSession", retcode); + } + + printf("starting afc service\n"); + struct afc_connection *conn = NULL; + retcode = AMDeviceStartService(dev, CFSTR("com.apple.afc"), &conn, NULL); + if (retcode != 0) { + die("AMDeviceStartService", retcode); + } + assert(conn != NULL); + + printf("opening afc connection\n"); + struct afc_connection *afc = NULL; + retcode = AFCConnectionOpen(conn, 0, &afc); + if (retcode != 0) { + die("AFCConnectionOpen", retcode); + } + assert(afc != NULL); + + printf("copying package into public staging directory\n"); + NSString *remote_pkg_path = [NSString stringWithFormat:@"PublicStaging/%@", [app_package_path lastPathComponent]]; + AFCDirectoryCreate(afc, "PublicStaging"); + afc_file_ref fileref; + retcode = AFCFileRefOpen(afc, (char *)[remote_pkg_path fileSystemRepresentation], 0x3 /* write */, &fileref); + if (retcode != 0) { + die("AFCFileRefOpen", retcode); + } + retcode = AFCFileRefWrite(afc, fileref, (void *)[app_package_data bytes], [app_package_data length]); + if (retcode != 0) { + die("AFCFileRefWrite", retcode); + } + retcode = AFCFileRefClose(afc, fileref); + if (retcode != 0) { + die("AFCFileRefClose", retcode); + } + + printf("starting installer proxy service\n"); + struct afc_connection *ipc = NULL; + retcode = AMDeviceStartService(dev, CFSTR("com.apple.mobile.installation_proxy"), &ipc, NULL); + if (retcode != 0) { + die("AMDeviceStartService", retcode); + } + assert(ipc != NULL); + NSFileHandle *handle = [[NSFileHandle alloc] initWithFileDescriptor:(int)ipc closeOnDealloc:NO]; + + printf("send install command\n"); + send_plist(handle, [NSDictionary dictionaryWithObjectsAndKeys: + @"Install", @"Command", + remote_pkg_path, @"PackagePath", + nil]); + + while (true) { + id plist = read_plist(handle); + if (plist == nil) { + break; + } + id percent = [plist objectForKey:@"PercentComplete"]; + if (percent == nil) { + break; + } + printf("progress... %d%%...\n", [percent intValue]); + } + + printf("complete!\n"); + [handle release]; +} + +static void +device_subscribe_cb(struct am_device_notification_callback_info *info) +{ + CFStringRef name = AMDeviceGetName(info->dev); + if (name != NULL) { + printf("found usb mobile device %s\n", [(id)name UTF8String]); + + device_go(info->dev); + exit(0); + } +} + +int +main(int argc, char **argv) +{ + if (argc != 2) { + printf("usage: %s \n", argv[0]); + exit(1); + } + + [[NSAutoreleasePool alloc] init]; + app_package_path = [[NSString stringWithUTF8String:argv[1]] retain]; + app_package_data = [[NSData dataWithContentsOfFile:app_package_path] retain]; + if (app_package_data == nil) { + printf("can't read data from %s\n", [app_package_path fileSystemRepresentation]); + exit(1); + } + + struct am_device_notification *notif = NULL; + const int retcode = AMDeviceNotificationSubscribe(device_subscribe_cb, 0, 0, 0, ¬if); + if (retcode != 0) { + die("AMDeviceNotificationSubscribe", retcode); + } + + printf("waiting for devices to show up\n"); + [[NSRunLoop mainRunLoop] run]; + return 0; +} diff --git a/lib/rubixir/rake.rb b/lib/rubixir/rake.rb index 22cb4d6f..72a67e21 100644 --- a/lib/rubixir/rake.rb +++ b/lib/rubixir/rake.rb @@ -27,8 +27,9 @@ end desc "Run the simulator" task :simulator => ['build:simulator'] do + app = Rubixir::CONFIG.app_bundle('iPhoneSimulator', true) sim = File.join(Rubixir::CONFIG.platform_dir('iPhoneSimulator'), '/Developer/Applications/iPhone Simulator.app/Contents/MacOS/iPhone Simulator') - sh "\"#{sim}\" -SimulateApplication \"#{Rubixir::CONFIG.app_bundle('iPhoneSimulator', true)}\"" + sh "\"#{sim}\" -SimulateApplication \"#{app}\"" end desc "Create an .ipa package" @@ -45,8 +46,9 @@ task :package => ['build:ios'] do end desc "Deploy on the device" -task :deploy do - # TODO +task :deploy => :package do + deploy = File.join(Rubixir::CONFIG.datadir, 'deploy') + sh "#{deploy} #{Rubixir::CONFIG.archive}" end desc "Clear build objects" diff --git a/lib/rubixir/rake/builder.rb b/lib/rubixir/rake/builder.rb index dc41fbaf..5b2aaa1f 100644 --- a/lib/rubixir/rake/builder.rb +++ b/lib/rubixir/rake/builder.rb @@ -1,7 +1,7 @@ module Rubixir class Builder def build(config, platform) - datadir = File.join(File.dirname(__FILE__), '../../../data') + datadir = config.datadir libstatic = File.join(datadir, 'libmacruby-static.a') archs = Dir.glob(File.join(datadir, platform, '*.bc')).map do |path| path.scan(/kernel-(.+).bc$/)[0][0] diff --git a/lib/rubixir/rake/config.rb b/lib/rubixir/rake/config.rb index 7651829b..56829300 100644 --- a/lib/rubixir/rake/config.rb +++ b/lib/rubixir/rake/config.rb @@ -16,6 +16,10 @@ module Rubixir @bundle_signature = '????' end + def datadir + File.expand_path(File.join(File.dirname(__FILE__), '../../../data')) + end + def platform_dir(platform) File.join(@platforms_dir, platform + '.platform') end diff --git a/sample/hello/app/main.rb b/sample/hello/app/main.rb index ef19a7da..33d2cc69 100644 --- a/sample/hello/app/main.rb +++ b/sample/hello/app/main.rb @@ -6,7 +6,7 @@ class MyView < UIView text = "ZOMG!" else bgcolor = UIColor.blackColor - text = @touches ? "Touched #{@touches} times!" : "Hello Ruby!" + text = @touches ? "Touched #{@touches} times!" : "Hello Rubixir!" end bgcolor.set