diff options
Diffstat (limited to 'lldb/tools/debugserver/source/MacOSX/MachProcess.mm')
-rw-r--r-- | lldb/tools/debugserver/source/MacOSX/MachProcess.mm | 840 |
1 files changed, 589 insertions, 251 deletions
diff --git a/lldb/tools/debugserver/source/MacOSX/MachProcess.mm b/lldb/tools/debugserver/source/MacOSX/MachProcess.mm index c4ba9e21921..b9e06307a4a 100644 --- a/lldb/tools/debugserver/source/MacOSX/MachProcess.mm +++ b/lldb/tools/debugserver/source/MacOSX/MachProcess.mm @@ -44,58 +44,137 @@ #include "CFData.h" #include "CFString.h" -#if defined (WITH_SPRINGBOARD) || defined (WITH_BKS) +#ifdef WITH_SPRINGBOARD + +#include <CoreFoundation/CoreFoundation.h> +#include <SpringBoardServices/SpringBoardServer.h> +#include <SpringBoardServices/SBSWatchdogAssertion.h> + +static bool +IsSBProcess (nub_process_t pid) +{ + CFReleaser<CFArrayRef> appIdsForPID (::SBSCopyDisplayIdentifiersForProcessID(pid)); + return appIdsForPID.get() != NULL; +} + +#endif // WITH_SPRINGBOARD + +#if defined (WITH_SPRINGBOARD) || defined (WITH_BKS) || defined (WITH_FBS) // This returns a CFRetained pointer to the Bundle ID for app_bundle_path, // or NULL if there was some problem getting the bundle id. -static CFStringRef -CopyBundleIDForPath (const char *app_bundle_path, DNBError &err_str) +static CFStringRef CopyBundleIDForPath (const char *app_bundle_path, DNBError &err_str); +#endif + +#if defined(WITH_BKS) || defined(WITH_FBS) +#import <Foundation/Foundation.h> +static const int OPEN_APPLICATION_TIMEOUT_ERROR = 111; +typedef void (*SetErrorFunction) (NSInteger, DNBError &); +typedef bool (*CallOpenApplicationFunction) (NSString *bundleIDNSStr, NSDictionary *options, DNBError &error, pid_t *return_pid); +// This function runs the BKSSystemService (or FBSSystemService) method openApplication:options:clientPort:withResult, +// messaging the app passed in bundleIDNSStr. +// The function should be run inside of an NSAutoReleasePool. +// +// It will use the "options" dictionary passed in, and fill the error passed in if there is an error. +// If return_pid is not NULL, we'll fetch the pid that was made for the bundleID. +// If bundleIDNSStr is NULL, then the system application will be messaged. + +template <typename OpenFlavor, typename ErrorFlavor, ErrorFlavor no_error_enum_value, SetErrorFunction error_function> +static bool +CallBoardSystemServiceOpenApplication (NSString *bundleIDNSStr, NSDictionary *options, DNBError &error, pid_t *return_pid) { - CFBundle bundle(app_bundle_path); - CFStringRef bundleIDCFStr = bundle.GetIdentifier(); - std::string bundleID; - if (CFString::UTF8(bundleIDCFStr, bundleID) == NULL) + // Now make our systemService: + OpenFlavor *system_service = [[OpenFlavor alloc] init]; + + if (bundleIDNSStr == nil) { - struct stat app_bundle_stat; - char err_msg[PATH_MAX]; + bundleIDNSStr = [system_service systemApplicationBundleIdentifier]; + if (bundleIDNSStr == nil) + { + // Okay, no system app... + error.SetErrorString("No system application to message."); + return false; + } + } - if (::stat (app_bundle_path, &app_bundle_stat) < 0) + mach_port_t client_port = [system_service createClientPort]; + __block dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); + __block ErrorFlavor open_app_error = no_error_enum_value; + bool wants_pid = (return_pid != NULL); + __block pid_t pid_in_block; + + const char *cstr = [bundleIDNSStr UTF8String]; + if (!cstr) + cstr = "<Unknown Bundle ID>"; + + DNBLog ("About to launch process for bundle ID: %s", cstr); + [system_service openApplication: bundleIDNSStr + options: options + clientPort: client_port + withResult: ^(NSError *bks_error) { - err_str.SetError(errno, DNBError::POSIX); - snprintf(err_msg, sizeof(err_msg), "%s: \"%s\"", err_str.AsString(), app_bundle_path); - err_str.SetErrorString(err_msg); - DNBLogThreadedIf(LOG_PROCESS, "%s() error: %s", __FUNCTION__, err_msg); + // The system service will cleanup the client port we created for us. + if (bks_error) + open_app_error = (ErrorFlavor)[bks_error code]; + + if (open_app_error == no_error_enum_value) + { + if (wants_pid) + { + pid_in_block = [system_service pidForApplication: bundleIDNSStr]; + DNBLog("In completion handler, got pid for bundle id, pid: %d.", pid_in_block); + DNBLogThreadedIf(LOG_PROCESS, "In completion handler, got pid for bundle id, pid: %d.", pid_in_block); + } + else + DNBLogThreadedIf (LOG_PROCESS, "In completion handler: success."); } else { - err_str.SetError(-1, DNBError::Generic); - snprintf(err_msg, sizeof(err_msg), "failed to extract CFBundleIdentifier from %s", app_bundle_path); - err_str.SetErrorString(err_msg); - DNBLogThreadedIf(LOG_PROCESS, "%s() error: failed to extract CFBundleIdentifier from '%s'", __FUNCTION__, app_bundle_path); + const char *error_str = [(NSString *)[bks_error localizedDescription] UTF8String]; + DNBLogThreadedIf(LOG_PROCESS, "In completion handler for send event, got error \"%s\"(%ld).", + error_str ? error_str : "<unknown error>", + open_app_error); + // REMOVE ME + DNBLogError ("In completion handler for send event, got error \"%s\"(%ld).", + error_str ? error_str : "<unknown error>", + open_app_error); } - return NULL; + + [system_service release]; + dispatch_semaphore_signal(semaphore); } - DNBLogThreadedIf(LOG_PROCESS, "%s() extracted CFBundleIdentifier: %s", __FUNCTION__, bundleID.c_str()); - CFRetain (bundleIDCFStr); + ]; - return bundleIDCFStr; -} -#endif // #if defined 9WITH_SPRINGBOARD) || defined (WITH_BKS) + const uint32_t timeout_secs = 9; -#ifdef WITH_SPRINGBOARD + dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, timeout_secs * NSEC_PER_SEC); -#include <CoreFoundation/CoreFoundation.h> -#include <SpringBoardServices/SpringBoardServer.h> -#include <SpringBoardServices/SBSWatchdogAssertion.h> + long success = dispatch_semaphore_wait(semaphore, timeout) == 0; -static bool -IsSBProcess (nub_process_t pid) + dispatch_release(semaphore); + + if (!success) { - CFReleaser<CFArrayRef> appIdsForPID (::SBSCopyDisplayIdentifiersForProcessID(pid)); - return appIdsForPID.get() != NULL; + DNBLogError("timed out trying to send openApplication to %s.", cstr); + error.SetError (OPEN_APPLICATION_TIMEOUT_ERROR, DNBError::Generic); + error.SetErrorString ("timed out trying to launch app"); + } + else if (open_app_error != no_error_enum_value) + { + error_function (open_app_error, error); + DNBLogError("unable to launch the application with CFBundleIdentifier '%s' bks_error = %u", cstr, open_app_error); + success = false; + } + else if (wants_pid) + { + *return_pid = pid_in_block; + DNBLogThreadedIf (LOG_PROCESS, "Out of completion handler, pid from block %d and passing out: %d", pid_in_block, *return_pid); } -#endif // WITH_SPRINGBOARD + + return success; +} +#endif #ifdef WITH_BKS #import <Foundation/Foundation.h> @@ -115,10 +194,10 @@ IsBKSProcess (nub_process_t pid) } static void -SetBKSError (BKSOpenApplicationErrorCode error_code, DNBError &error) +SetBKSError (NSInteger error_code, DNBError &error) { error.SetError (error_code, DNBError::BackBoard); - NSString *err_nsstr = ::BKSOpenApplicationErrorCodeToString(error_code); + NSString *err_nsstr = ::BKSOpenApplicationErrorCodeToString((BKSOpenApplicationErrorCode) error_code); const char *err_str = NULL; if (err_nsstr == NULL) err_str = "unknown BKS error"; @@ -131,8 +210,164 @@ SetBKSError (BKSOpenApplicationErrorCode error_code, DNBError &error) error.SetErrorString(err_str); } -static const int BKS_OPEN_APPLICATION_TIMEOUT_ERROR = 111; +static bool +BKSAddEventDataToOptions (NSMutableDictionary *options, const char *event_data, DNBError &option_error) +{ + if (strcmp (event_data, "BackgroundContentFetching") == 0) + { + DNBLog("Setting ActivateForEvent key in options dictionary."); + NSDictionary *event_details = [NSDictionary dictionary]; + NSDictionary *event_dictionary = [NSDictionary dictionaryWithObject:event_details forKey:BKSActivateForEventOptionTypeBackgroundContentFetching]; + [options setObject: event_dictionary forKey: BKSOpenApplicationOptionKeyActivateForEvent]; + return true; + } + else + { + DNBLogError ("Unrecognized event type: %s. Ignoring.", event_data); + option_error.SetErrorString("Unrecognized event data."); + return false; + } + +} + +static NSMutableDictionary * +BKSCreateOptionsDictionary(const char *app_bundle_path, NSMutableArray *launch_argv, NSMutableDictionary *launch_envp, NSString *stdio_path, bool disable_aslr, const char *event_data) +{ + NSMutableDictionary *debug_options = [NSMutableDictionary dictionary]; + if (launch_argv != nil) + [debug_options setObject: launch_argv forKey: BKSDebugOptionKeyArguments]; + if (launch_envp != nil) + [debug_options setObject: launch_envp forKey: BKSDebugOptionKeyEnvironment]; + + [debug_options setObject: stdio_path forKey: BKSDebugOptionKeyStandardOutPath]; + [debug_options setObject: stdio_path forKey: BKSDebugOptionKeyStandardErrorPath]; + [debug_options setObject: [NSNumber numberWithBool: YES] forKey: BKSDebugOptionKeyWaitForDebugger]; + if (disable_aslr) + [debug_options setObject: [NSNumber numberWithBool: YES] forKey: BKSDebugOptionKeyDisableASLR]; + + // That will go in the overall dictionary: + + NSMutableDictionary *options = [NSMutableDictionary dictionary]; + [options setObject: debug_options forKey: BKSOpenApplicationOptionKeyDebuggingOptions]; + // And there are some other options at the top level in this dictionary: + [options setObject: [NSNumber numberWithBool: YES] forKey: BKSOpenApplicationOptionKeyUnlockDevice]; + + DNBError error; + BKSAddEventDataToOptions (options, event_data, error); + + return options; +} + +static CallOpenApplicationFunction BKSCallOpenApplicationFunction = CallBoardSystemServiceOpenApplication<BKSSystemService, BKSOpenApplicationErrorCode, BKSOpenApplicationErrorCodeNone, SetBKSError>; #endif // WITH_BKS + +#ifdef WITH_FBS +#import <Foundation/Foundation.h> +extern "C" +{ +#import <FrontBoardServices/FrontBoardServices.h> +#import <FrontBoardServices/FBSSystemService_LaunchServices.h> +#import <FrontBoardServices/FBSOpenApplicationConstants_Private.h> +#import <MobileCoreServices/MobileCoreServices.h> +#import <MobileCoreServices/LSResourceProxy.h> +} + +#ifdef WITH_BKS +static bool +IsFBSProcess (nub_process_t pid) +{ + BKSApplicationStateMonitor *state_monitor = [[BKSApplicationStateMonitor alloc] init]; + BKSApplicationState app_state = [state_monitor mostElevatedApplicationStateForPID: pid]; + return app_state != BKSApplicationStateUnknown; +} +#else +static bool +IsFBSProcess (nub_process_t pid) +{ + // FIXME: What is the FBS equivalent of BKSApplicationStateMonitor + return true; +} +#endif + +static void +SetFBSError (NSInteger error_code, DNBError &error) +{ + error.SetError ((DNBError::ValueType) error_code, DNBError::FrontBoard); + NSString *err_nsstr = ::FBSOpenApplicationErrorCodeToString((FBSOpenApplicationErrorCode) error_code); + const char *err_str = NULL; + if (err_nsstr == NULL) + err_str = "unknown FBS error"; + else + { + err_str = [err_nsstr UTF8String]; + if (err_str == NULL) + err_str = "unknown FBS error"; + } + error.SetErrorString(err_str); +} + +static bool +FBSAddEventDataToOptions (NSMutableDictionary *options, const char *event_data, DNBError &option_error) +{ + if (strcmp (event_data, "BackgroundContentFetching") == 0) + { + DNBLog("Setting ActivateForEvent key in options dictionary."); + NSDictionary *event_details = [NSDictionary dictionary]; + NSDictionary *event_dictionary = [NSDictionary dictionaryWithObject:event_details forKey:FBSActivateForEventOptionTypeBackgroundContentFetching]; + [options setObject: event_dictionary forKey: FBSOpenApplicationOptionKeyActivateForEvent]; + return true; + } + else + { + DNBLogError ("Unrecognized event type: %s. Ignoring.", event_data); + option_error.SetErrorString("Unrecognized event data."); + return false; + } + +} + +static NSMutableDictionary * +FBSCreateOptionsDictionary(const char *app_bundle_path, NSMutableArray *launch_argv, NSDictionary *launch_envp, NSString *stdio_path, bool disable_aslr, const char *event_data) +{ + NSMutableDictionary *debug_options = [NSMutableDictionary dictionary]; + + if (launch_argv != nil) + [debug_options setObject: launch_argv forKey: FBSDebugOptionKeyArguments]; + if (launch_envp != nil) + [debug_options setObject: launch_envp forKey: FBSDebugOptionKeyEnvironment]; + + [debug_options setObject: stdio_path forKey: FBSDebugOptionKeyStandardOutPath]; + [debug_options setObject: stdio_path forKey: FBSDebugOptionKeyStandardErrorPath]; + [debug_options setObject: [NSNumber numberWithBool: YES] forKey: FBSDebugOptionKeyWaitForDebugger]; + if (disable_aslr) + [debug_options setObject: [NSNumber numberWithBool: YES] forKey: FBSDebugOptionKeyDisableASLR]; + + // That will go in the overall dictionary: + + NSMutableDictionary *options = [NSMutableDictionary dictionary]; + [options setObject: debug_options forKey: FBSOpenApplicationOptionKeyDebuggingOptions]; + // And there are some other options at the top level in this dictionary: + [options setObject: [NSNumber numberWithBool: YES] forKey: FBSOpenApplicationOptionKeyUnlockDevice]; + + // We have to get the "sequence ID & UUID" for this app bundle path and send them to FBS: + + NSURL *app_bundle_url = [NSURL fileURLWithPath: [NSString stringWithUTF8String: app_bundle_path] isDirectory: YES]; + LSApplicationProxy *app_proxy = [LSApplicationProxy applicationProxyForBundleURL: app_bundle_url]; + if (app_proxy) + { + DNBLog("Sending AppProxy info: sequence no: %lu, GUID: %s.", app_proxy.sequenceNumber, [app_proxy.cacheGUID.UUIDString UTF8String]); + [options setObject: [NSNumber numberWithUnsignedInteger: app_proxy.sequenceNumber] forKey: FBSOpenApplicationOptionKeyLSSequenceNumber]; + [options setObject: app_proxy.cacheGUID.UUIDString forKey: FBSOpenApplicationOptionKeyLSCacheGUID]; + } + + DNBError error; + FBSAddEventDataToOptions (options, event_data, error); + + return options; +} +static CallOpenApplicationFunction FBSCallOpenApplicationFunction = CallBoardSystemServiceOpenApplication<FBSSystemService, FBSOpenApplicationErrorCode, FBSOpenApplicationErrorCodeNone, SetFBSError>; +#endif // WITH_FBS + #if 0 #define DEBUG_LOG(fmt, ...) printf(fmt, ## __VA_ARGS__) #else @@ -863,8 +1098,9 @@ MachProcess::SendEvent (const char *event, DNBError &send_err) DNBLogThreadedIf(LOG_PROCESS, "MachProcess::SendEvent (event = %s) to pid: %d", event, m_pid); if (m_pid == INVALID_NUB_PROCESS) return false; -#if WITH_BKS - return BKSSendEvent (event, send_err); + // FIXME: Shouldn't we use the launch flavor we were started with? +#if defined(WITH_FBS) || defined(WITH_BKS) + return BoardServiceSendEvent (event, send_err); #endif return true; } @@ -1970,9 +2206,22 @@ MachProcess::AttachForDebug (pid_t pid, char *err_str, size_t err_len) SetState(eStateAttaching); m_pid = pid; // Let ourselves know we are going to be using SBS or BKS if the correct flag bit is set... -#if defined (WITH_BKS) - if (IsBKSProcess (pid)) +#if defined (WITH_FBS) || defined (WITH_BKS) + bool found_app_flavor = false; +#endif + +#if defined (WITH_FBS) + if (!found_app_flavor && IsFBSProcess (pid)) + { + found_app_flavor = true; + m_flags |= eMachProcessFlagsUsingFBS; + } +#elif defined (WITH_BKS) + if (!found_app_flavor && IsBKSProcess (pid)) + { + found_app_flavor = true; m_flags |= eMachProcessFlagsUsingBKS; + } #elif defined (WITH_SPRINGBOARD) if (IsSBProcess(pid)) m_flags |= eMachProcessFlagsUsingSBS; @@ -2058,7 +2307,7 @@ MachProcess::GetOSVersionNumbers (uint64_t *major, uint64_t *minor, uint64_t *pa const void * MachProcess::PrepareForAttach (const char *path, nub_launch_flavor_t launch_flavor, bool waitfor, DNBError &attach_err) { -#if defined (WITH_SPRINGBOARD) || defined (WITH_BKS) +#if defined (WITH_SPRINGBOARD) || defined (WITH_BKS) || defined (WITH_FBS) // Tell SpringBoard to halt the next launch of this application on startup. if (!waitfor) @@ -2074,7 +2323,12 @@ MachProcess::PrepareForAttach (const char *path, nub_launch_flavor_t launch_flav return NULL; } -#if defined (WITH_BKS) +#if defined (WITH_FBS) + if (launch_flavor == eLaunchFlavorDefault) + launch_flavor = eLaunchFlavorFBS; + if (launch_flavor != eLaunchFlavorFBS) + return NULL; +#elif defined (WITH_BKS) if (launch_flavor == eLaunchFlavorDefault) launch_flavor = eLaunchFlavorBKS; if (launch_flavor != eLaunchFlavorBKS) @@ -2101,6 +2355,76 @@ MachProcess::PrepareForAttach (const char *path, nub_launch_flavor_t launch_flav return NULL; } +#if defined (WITH_FBS) + if (launch_flavor == eLaunchFlavorFBS) + { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + NSString *stdio_path = nil; + NSFileManager *file_manager = [NSFileManager defaultManager]; + const char *null_path = "/dev/null"; + stdio_path = [file_manager stringWithFileSystemRepresentation: null_path length: strlen(null_path)]; + + NSMutableDictionary *debug_options = [NSMutableDictionary dictionary]; + NSMutableDictionary *options = [NSMutableDictionary dictionary]; + + DNBLogThreadedIf(LOG_PROCESS, "Calling BKSSystemService openApplication: @\"%s\",options include stdio path: \"%s\", " + "BKSDebugOptionKeyDebugOnNextLaunch & BKSDebugOptionKeyWaitForDebugger )", + bundleIDStr.c_str(), + null_path); + + [debug_options setObject: stdio_path forKey: FBSDebugOptionKeyStandardOutPath]; + [debug_options setObject: stdio_path forKey: FBSDebugOptionKeyStandardErrorPath]; + [debug_options setObject: [NSNumber numberWithBool: YES] forKey: FBSDebugOptionKeyWaitForDebugger]; + [debug_options setObject: [NSNumber numberWithBool: YES] forKey: FBSDebugOptionKeyDebugOnNextLaunch]; + + [options setObject: debug_options forKey: FBSOpenApplicationOptionKeyDebuggingOptions]; + + FBSSystemService *system_service = [[FBSSystemService alloc] init]; + + mach_port_t client_port = [system_service createClientPort]; + __block dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); + __block FBSOpenApplicationErrorCode attach_error_code = FBSOpenApplicationErrorCodeNone; + + NSString *bundleIDNSStr = (NSString *) bundleIDCFStr; + + [system_service openApplication: bundleIDNSStr + options: options + clientPort: client_port + withResult: ^(NSError *error) + { + // The system service will cleanup the client port we created for us. + if (error) + attach_error_code = (FBSOpenApplicationErrorCode)[error code]; + + [system_service release]; + dispatch_semaphore_signal(semaphore); + } + ]; + + const uint32_t timeout_secs = 9; + + dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, timeout_secs * NSEC_PER_SEC); + + long success = dispatch_semaphore_wait(semaphore, timeout) == 0; + + if (!success) + { + DNBLogError("timed out trying to launch %s.", bundleIDStr.c_str()); + attach_err.SetErrorString("debugserver timed out waiting for openApplication to complete."); + attach_err.SetError (OPEN_APPLICATION_TIMEOUT_ERROR, DNBError::Generic); + } + else if (attach_error_code != FBSOpenApplicationErrorCodeNone) + { + SetFBSError (attach_error_code, attach_err); + DNBLogError("unable to launch the application with CFBundleIdentifier '%s' bks_error = %ld", + bundleIDStr.c_str(), + (NSInteger) attach_error_code); + } + dispatch_release(semaphore); + [pool drain]; + } +#endif #if defined (WITH_BKS) if (launch_flavor == eLaunchFlavorBKS) { @@ -2158,19 +2482,21 @@ MachProcess::PrepareForAttach (const char *path, nub_launch_flavor_t launch_flav { DNBLogError("timed out trying to launch %s.", bundleIDStr.c_str()); attach_err.SetErrorString("debugserver timed out waiting for openApplication to complete."); - attach_err.SetError (BKS_OPEN_APPLICATION_TIMEOUT_ERROR, DNBError::Generic); + attach_err.SetError (OPEN_APPLICATION_TIMEOUT_ERROR, DNBError::Generic); } else if (attach_error_code != BKSOpenApplicationErrorCodeNone) { SetBKSError (attach_error_code, attach_err); - DNBLogError("unable to launch the application with CFBundleIdentifier '%s' bks_error = %u", + DNBLogError("unable to launch the application with CFBundleIdentifier '%s' bks_error = %ld", bundleIDStr.c_str(), attach_error_code); } dispatch_release(semaphore); [pool drain]; } -#elif defined (WITH_SPRINGBOARD) +#endif + +#if defined (WITH_SPRINGBOARD) if (launch_flavor == eLaunchFlavorSpringBoard) { SBSApplicationLaunchError sbs_error = 0; @@ -2203,7 +2529,7 @@ MachProcess::PrepareForAttach (const char *path, nub_launch_flavor_t launch_flav DNBLogThreadedIf(LOG_PROCESS, "Successfully set DebugOnNextLaunch."); return bundleIDCFStr; -# else // defined (WITH_SPRINGBOARD) || defined (WITH_BKS) +# else // !(defined (WITH_SPRINGBOARD) || defined (WITH_BKS) || defined (WITH_FBS)) return NULL; #endif } @@ -2213,12 +2539,28 @@ MachProcess::PrepareForAttach (const char *path, nub_launch_flavor_t launch_flav // will be returned. nub_process_t -MachProcess::CheckForProcess (const void *attach_token) +MachProcess::CheckForProcess (const void *attach_token, nub_launch_flavor_t launch_flavor) { if (attach_token == NULL) return INVALID_NUB_PROCESS; +#if defined (WITH_FBS) + if (launch_flavor == eLaunchFlavorFBS) + { + NSString *bundleIDNSStr = (NSString *) attach_token; + FBSSystemService *systemService = [[FBSSystemService alloc] init]; + pid_t pid = [systemService pidForApplication: bundleIDNSStr]; + [systemService release]; + if (pid == 0) + return INVALID_NUB_PROCESS; + else + return pid; + } +#endif + #if defined (WITH_BKS) + if (launch_flavor == eLaunchFlavorBKS) + { NSString *bundleIDNSStr = (NSString *) attach_token; BKSSystemService *systemService = [[BKSSystemService alloc] init]; pid_t pid = [systemService pidForApplication: bundleIDNSStr]; @@ -2227,7 +2569,12 @@ MachProcess::CheckForProcess (const void *attach_token) return INVALID_NUB_PROCESS; else return pid; -#elif defined (WITH_SPRINGBOARD) + } +#endif + +#if defined (WITH_SPRINGBOARD) + if (launch_flavor == eLaunchFlavorSpringBoard) + { CFStringRef bundleIDCFStr = (CFStringRef) attach_token; Boolean got_it; nub_process_t attach_pid; @@ -2236,9 +2583,9 @@ MachProcess::CheckForProcess (const void *attach_token) return attach_pid; else return INVALID_NUB_PROCESS; -#else - return INVALID_NUB_PROCESS; + } #endif + return INVALID_NUB_PROCESS; } // Call this to clean up after you have either attached or given up on the attach. @@ -2247,22 +2594,39 @@ MachProcess::CheckForProcess (const void *attach_token) // this method. void -MachProcess::CleanupAfterAttach (const void *attach_token, bool success, DNBError &err_str) +MachProcess::CleanupAfterAttach (const void *attach_token, nub_launch_flavor_t launch_flavor, bool success, DNBError &err_str) { if (attach_token == NULL) return; +#if defined (WITH_FBS) + if (launch_flavor == eLaunchFlavorFBS) + { + if (!success) + { + FBSCleanupAfterAttach (attach_token, err_str); + } + CFRelease((CFStringRef) attach_token); + } +#endif + #if defined (WITH_BKS) + if (launch_flavor == eLaunchFlavorBKS) + { if (!success) { BKSCleanupAfterAttach (attach_token, err_str); } CFRelease((CFStringRef) attach_token); + } +#endif -#elif defined (WITH_SPRINGBOARD) +#if defined (WITH_SPRINGBOARD) // Tell SpringBoard to cancel the debug on next launch of this application // if we failed to attach + if (launch_flavor == eMachProcessFlagsUsingSpringBoard) + { if (!success) { SBSApplicationLaunchError sbs_error = 0; @@ -2284,6 +2648,7 @@ MachProcess::CleanupAfterAttach (const void *attach_token, bool success, DNBErro } CFRelease((CFStringRef) attach_token); + } #endif } @@ -2317,6 +2682,22 @@ MachProcess::LaunchForDebug case eLaunchFlavorForkExec: m_pid = MachProcess::ForkChildForPTraceDebugging (path, argv, envp, this, launch_err); break; +#ifdef WITH_FBS + case eLaunchFlavorFBS: + { + const char *app_ext = strstr(path, ".app"); + if (app_ext && (app_ext[4] == '\0' || app_ext[4] == '/')) + { + std::string app_bundle_path(path, app_ext + strlen(".app")); + m_flags |= eMachProcessFlagsUsingFBS; + if (BoardServiceLaunchForDebug (app_bundle_path.c_str(), argv, envp, no_stdio, disable_aslr, event_data, launch_err) != 0) + return m_pid; // A successful SBLaunchForDebug() returns and assigns a non-zero m_pid. + else + break; // We tried a FBS launch, but didn't succeed lets get out + } + } + break; +#endif #ifdef WITH_BKS case eLaunchFlavorBKS: { @@ -2324,7 +2705,8 @@ MachProcess::LaunchForDebug if (app_ext && (app_ext[4] == '\0' || app_ext[4] == '/')) { std::string app_bundle_path(path, app_ext + strlen(".app")); - if (BKSLaunchForDebug (app_bundle_path.c_str(), argv, envp, no_stdio, disable_aslr, event_data, launch_err) != 0) + m_flags |= eMachProcessFlagsUsingBKS; + if (BoardServiceLaunchForDebug (app_bundle_path.c_str(), argv, envp, no_stdio, disable_aslr, event_data, launch_err) != 0) return m_pid; // A successful SBLaunchForDebug() returns and assigns a non-zero m_pid. else break; // We tried a BKS launch, but didn't succeed lets get out @@ -2719,6 +3101,43 @@ MachProcess::ForkChildForPTraceDebugging return pid; } +#if defined (WITH_SPRINGBOARD) || defined (WITH_BKS) || defined (WITH_FBS) +// This returns a CFRetained pointer to the Bundle ID for app_bundle_path, +// or NULL if there was some problem getting the bundle id. +static CFStringRef +CopyBundleIDForPath (const char *app_bundle_path, DNBError &err_str) +{ + CFBundle bundle(app_bundle_path); + CFStringRef bundleIDCFStr = bundle.GetIdentifier(); + std::string bundleID; + if (CFString::UTF8(bundleIDCFStr, bundleID) == NULL) + { + struct stat app_bundle_stat; + char err_msg[PATH_MAX]; + + if (::stat (app_bundle_path, &app_bundle_stat) < 0) + { + err_str.SetError(errno, DNBError::POSIX); + snprintf(err_msg, sizeof(err_msg), "%s: \"%s\"", err_str.AsString(), app_bundle_path); + err_str.SetErrorString(err_msg); + DNBLogThreadedIf(LOG_PROCESS, "%s() error: %s", __FUNCTION__, err_msg); + } + else + { + err_str.SetError(-1, DNBError::Generic); + snprintf(err_msg, sizeof(err_msg), "failed to extract CFBundleIdentifier from %s", app_bundle_path); + err_str.SetErrorString(err_msg); + DNBLogThreadedIf(LOG_PROCESS, "%s() error: failed to extract CFBundleIdentifier from '%s'", __FUNCTION__, app_bundle_path); + } + return NULL; + } + + DNBLogThreadedIf(LOG_PROCESS, "%s() extracted CFBundleIdentifier: %s", __FUNCTION__, bundleID.c_str()); + CFRetain (bundleIDCFStr); + + return bundleIDCFStr; +} +#endif // #if defined (WITH_SPRINGBOARD) || defined (WITH_BKS) || defined (WITH_FBS) #ifdef WITH_SPRINGBOARD pid_t @@ -2929,178 +3348,19 @@ MachProcess::SBForkChildForPTraceDebugging (const char *app_bundle_path, char co #endif // #ifdef WITH_SPRINGBOARD -#ifdef WITH_BKS - - -// This function runs the BKSSystemService method openApplication:options:clientPort:withResult, -// messaging the app passed in bundleIDNSStr. -// The function should be run inside of an NSAutoReleasePool. -// -// It will use the "options" dictionary passed in, and fill the error passed in if there is an error. -// If return_pid is not NULL, we'll fetch the pid that was made for the bundleID. -// If bundleIDNSStr is NULL, then the system application will be messaged. - -static bool -CallBKSSystemServiceOpenApplication (NSString *bundleIDNSStr, NSDictionary *options, DNBError &error, pid_t *return_pid) -{ - // Now make our systemService: - BKSSystemService *system_service = [[BKSSystemService alloc] init]; - - if (bundleIDNSStr == nil) - { - bundleIDNSStr = [system_service systemApplicationBundleIdentifier]; - if (bundleIDNSStr == nil) - { - // Okay, no system app... - error.SetErrorString("No system application to message."); - return false; - } - } - - mach_port_t client_port = [system_service createClientPort]; - __block dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); - __block BKSOpenApplicationErrorCode open_app_error = BKSOpenApplicationErrorCodeNone; - bool wants_pid = (return_pid != NULL); - __block pid_t pid_in_block; - - const char *cstr = [bundleIDNSStr UTF8String]; - if (!cstr) - cstr = "<Unknown Bundle ID>"; - - DNBLog ("About to launch process for bundle ID: %s", cstr); - [system_service openApplication: bundleIDNSStr - options: options - clientPort: client_port - withResult: ^(NSError *bks_error) - { - // The system service will cleanup the client port we created for us. - if (bks_error) - open_app_error = (BKSOpenApplicationErrorCode)[bks_error code]; - - if (open_app_error == BKSOpenApplicationErrorCodeNone) - { - if (wants_pid) - { - pid_in_block = [system_service pidForApplication: bundleIDNSStr]; - DNBLog("In completion handler, got pid for bundle id, pid: %d.", pid_in_block); - DNBLogThreadedIf(LOG_PROCESS, "In completion handler, got pid for bundle id, pid: %d.", pid_in_block); - } - else - DNBLogThreadedIf (LOG_PROCESS, "In completion handler: success."); - } - else - { - const char *error_str = [[bks_error localizedDescription] UTF8String]; - DNBLogThreadedIf(LOG_PROCESS, "In completion handler for send event, got error \"%s\"(%d).", - error_str ? error_str : "<unknown error>", - open_app_error); - // REMOVE ME - DNBLogError ("In completion handler for send event, got error \"%s\"(%d).", - error_str ? error_str : "<unknown error>", - open_app_error); - } - - [system_service release]; - dispatch_semaphore_signal(semaphore); - } - - ]; - - const uint32_t timeout_secs = 9; - - dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, timeout_secs * NSEC_PER_SEC); - - long success = dispatch_semaphore_wait(semaphore, timeout) == 0; - - dispatch_release(semaphore); - - if (!success) - { - DNBLogError("timed out trying to send openApplication to %s.", cstr); - error.SetError (BKS_OPEN_APPLICATION_TIMEOUT_ERROR, DNBError::Generic); - error.SetErrorString ("timed out trying to launch app"); - } - else if (open_app_error != BKSOpenApplicationErrorCodeNone) - { - SetBKSError (open_app_error, error); - DNBLogError("unable to launch the application with CFBundleIdentifier '%s' bks_error = %u", cstr, open_app_error); - success = false; - } - else if (wants_pid) - { - *return_pid = pid_in_block; - DNBLogThreadedIf (LOG_PROCESS, "Out of completion handler, pid from block %d and passing out: %d", pid_in_block, *return_pid); - } - - - return success; -} - -void -MachProcess::BKSCleanupAfterAttach (const void *attach_token, DNBError &err_str) -{ - bool success; - - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - - // Instead of rewriting CopyBundleIDForPath for NSStrings, we'll just use toll-free bridging here: - NSString *bundleIDNSStr = (NSString *) attach_token; - - // Okay, now let's assemble all these goodies into the BackBoardServices options mega-dictionary: - - // First we have the debug sub-dictionary: - NSMutableDictionary *debug_options = [NSMutableDictionary dictionary]; - [debug_options setObject: [NSNumber numberWithBool: YES] forKey: BKSDebugOptionKeyCancelDebugOnNextLaunch]; - - // That will go in the overall dictionary: - - NSMutableDictionary *options = [NSMutableDictionary dictionary]; - [options setObject: debug_options forKey: BKSOpenApplicationOptionKeyDebuggingOptions]; - - success = CallBKSSystemServiceOpenApplication(bundleIDNSStr, options, err_str, NULL); - if (!success) - { - DNBLogError ("error trying to cancel debug on next launch for %s: %s", [bundleIDNSStr UTF8String], err_str.AsString()); - } - - [pool drain]; -} - -bool -AddEventDataToOptions (NSMutableDictionary *options, const char *event_data, DNBError &option_error) -{ - if (strcmp (event_data, "BackgroundContentFetching") == 0) - { - DNBLog("Setting ActivateForEvent key in options dictionary."); - NSDictionary *event_details = [NSDictionary dictionary]; - NSDictionary *event_dictionary = [NSDictionary dictionaryWithObject:event_details forKey:BKSActivateForEventOptionTypeBackgroundContentFetching]; - [options setObject: event_dictionary forKey: BKSOpenApplicationOptionKeyActivateForEvent]; - return true; - } - else - { - DNBLogError ("Unrecognized event type: %s. Ignoring.", event_data); - option_error.SetErrorString("Unrecognized event data."); - return false; - } - -} +#if defined (WITH_BKS) || defined (WITH_FBS) pid_t -MachProcess::BKSLaunchForDebug (const char *path, char const *argv[], char const *envp[], bool no_stdio, bool disable_aslr, const char *event_data, DNBError &launch_err) +MachProcess::BoardServiceLaunchForDebug (const char *path, char const *argv[], char const *envp[], bool no_stdio, bool disable_aslr, const char *event_data, DNBError &launch_err) { - // Clear out and clean up from any current state - Clear(); - DNBLogThreadedIf(LOG_PROCESS, "%s( '%s', argv)", __FUNCTION__, path); // Fork a child process for debugging SetState(eStateLaunching); - m_pid = BKSForkChildForPTraceDebugging(path, argv, envp, no_stdio, disable_aslr, event_data, launch_err); + m_pid = BoardServiceForkChildForPTraceDebugging(path, argv, envp, no_stdio, disable_aslr, event_data, launch_err); if (m_pid != 0) { - m_flags |= eMachProcessFlagsUsingBKS; m_path = path; size_t i; char const *arg; @@ -3136,7 +3396,7 @@ MachProcess::BKSLaunchForDebug (const char *path, char const *argv[], char const } pid_t -MachProcess::BKSForkChildForPTraceDebugging (const char *app_bundle_path, +MachProcess::BoardServiceForkChildForPTraceDebugging (const char *app_bundle_path, char const *argv[], char const *envp[], bool no_stdio, @@ -3242,40 +3502,24 @@ MachProcess::BKSForkChildForPTraceDebugging (const char *app_bundle_path, // Okay, now let's assemble all these goodies into the BackBoardServices options mega-dictionary: - // First we have the debug sub-dictionary: - NSMutableDictionary *debug_options = [NSMutableDictionary dictionary]; - if (launch_argv != nil) - [debug_options setObject: launch_argv forKey: BKSDebugOptionKeyArguments]; - if (launch_envp != nil) - [debug_options setObject: launch_envp forKey: BKSDebugOptionKeyEnvironment]; - - [debug_options setObject: stdio_path forKey: BKSDebugOptionKeyStandardOutPath]; - [debug_options setObject: stdio_path forKey: BKSDebugOptionKeyStandardErrorPath]; - [debug_options setObject: [NSNumber numberWithBool: YES] forKey: BKSDebugOptionKeyWaitForDebugger]; - if (disable_aslr) - [debug_options setObject: [NSNumber numberWithBool: YES] forKey: BKSDebugOptionKeyDisableASLR]; - - // That will go in the overall dictionary: - - NSMutableDictionary *options = [NSMutableDictionary dictionary]; - [options setObject: debug_options forKey: BKSOpenApplicationOptionKeyDebuggingOptions]; + NSMutableDictionary *options = nullptr; + pid_t return_pid = INVALID_NUB_PROCESS; + bool success = false; - // For now we only support one kind of event: the "fetch" event, which is indicated by the fact that its data - // is an empty dictionary. - if (event_data != NULL && *event_data != '\0') - { - if (!AddEventDataToOptions(options, event_data, launch_err)) +#ifdef WITH_BKS + if (m_flags & eMachProcessFlagsUsingBKS) { - [pool drain]; - return INVALID_NUB_PROCESS; + options = BKSCreateOptionsDictionary(app_bundle_path, launch_argv, launch_envp, stdio_path, disable_aslr, event_data); + success = BKSCallOpenApplicationFunction (bundleIDNSStr, options, launch_err, &return_pid); } +#endif +#ifdef WITH_FBS + if (m_flags & eMachProcessFlagsUsingFBS) + { + options = FBSCreateOptionsDictionary(app_bundle_path, launch_argv, launch_envp, stdio_path, disable_aslr, event_data); + success = FBSCallOpenApplicationFunction (bundleIDNSStr, options, launch_err, &return_pid); } - - // And there are some other options at the top level in this dictionary: - [options setObject: [NSNumber numberWithBool: YES] forKey: BKSOpenApplicationOptionKeyUnlockDevice]; - - pid_t return_pid = INVALID_NUB_PROCESS; - bool success = CallBKSSystemServiceOpenApplication(bundleIDNSStr, options, launch_err, &return_pid); +#endif if (success) { @@ -3290,7 +3534,7 @@ MachProcess::BKSForkChildForPTraceDebugging (const char *app_bundle_path, } bool -MachProcess::BKSSendEvent (const char *event_data, DNBError &send_err) +MachProcess::BoardServiceSendEvent (const char *event_data, DNBError &send_err) { bool return_value = true; @@ -3306,7 +3550,18 @@ MachProcess::BKSSendEvent (const char *event_data, DNBError &send_err) if (strcmp (event_data, "BackgroundApplication") == 0) { // This is an event I cooked up. What you actually do is foreground the system app, so: - return_value = CallBKSSystemServiceOpenApplication(nil, nil, send_err, NULL); +#ifdef WITH_BKS + if (m_flags & eMachProcessFlagsUsingBKS) + { + return_value = BKSCallOpenApplicationFunction(nil, nil, send_err, NULL); + } +#endif +#ifdef WITH_FBS + if (m_flags & eMachProcessFlagsUsingFBS) + { + return_value = FBSCallOpenApplicationFunction(nil, nil, send_err, NULL); + } +#endif if (!return_value) { DNBLogError ("Failed to background application, error: %s.", send_err.AsString()); @@ -3326,14 +3581,31 @@ MachProcess::BKSSendEvent (const char *event_data, DNBError &send_err) NSMutableDictionary *options = [NSMutableDictionary dictionary]; - if (!AddEventDataToOptions(options, event_data, send_err)) +#ifdef WITH_BKS + if (m_flags & eMachProcessFlagsUsingBKS) + { + if (!BKSAddEventDataToOptions(options, event_data, send_err)) { [pool drain]; return false; } + return_value = BKSCallOpenApplicationFunction (bundleIDNSStr, options, send_err, NULL); + DNBLogThreadedIf (LOG_PROCESS, "Called BKSCallOpenApplicationFunction to send event."); - - return_value = CallBKSSystemServiceOpenApplication(bundleIDNSStr, options, send_err, NULL); + } +#endif +#ifdef WITH_FBS + if (m_flags & eMachProcessFlagsUsingFBS) + { + if (!FBSAddEventDataToOptions(options, event_data, send_err)) + { + [pool drain]; + return false; + } + return_value = FBSCallOpenApplicationFunction (bundleIDNSStr, options, send_err, NULL); + DNBLogThreadedIf (LOG_PROCESS, "Called FBSCallOpenApplicationFunction to send event."); + } +#endif if (!return_value) { @@ -3344,4 +3616,70 @@ MachProcess::BKSSendEvent (const char *event_data, DNBError &send_err) [pool drain]; return return_value; } +#endif // defined(WITH_BKS) || defined (WITH_FBS) + +#ifdef WITH_BKS +void +MachProcess::BKSCleanupAfterAttach (const void *attach_token, DNBError &err_str) +{ + bool success; + + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + // Instead of rewriting CopyBundleIDForPath for NSStrings, we'll just use toll-free bridging here: + NSString *bundleIDNSStr = (NSString *) attach_token; + + // Okay, now let's assemble all these goodies into the BackBoardServices options mega-dictionary: + + // First we have the debug sub-dictionary: + NSMutableDictionary *debug_options = [NSMutableDictionary dictionary]; + [debug_options setObject: [NSNumber numberWithBool: YES] forKey: BKSDebugOptionKeyCancelDebugOnNextLaunch]; + + // That will go in the overall dictionary: + + NSMutableDictionary *options = [NSMutableDictionary dictionary]; + [options setObject: debug_options forKey: BKSOpenApplicationOptionKeyDebuggingOptions]; + + success = BKSCallOpenApplicationFunction (bundleIDNSStr, options, err_str, NULL); + + if (!success) + { + DNBLogError ("error trying to cancel debug on next launch for %s: %s", [bundleIDNSStr UTF8String], err_str.AsString()); + } + + [pool drain]; +} #endif // WITH_BKS + +#ifdef WITH_FBS +void +MachProcess::FBSCleanupAfterAttach (const void *attach_token, DNBError &err_str) +{ + bool success; + + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + // Instead of rewriting CopyBundleIDForPath for NSStrings, we'll just use toll-free bridging here: + NSString *bundleIDNSStr = (NSString *) attach_token; + + // Okay, now let's assemble all these goodies into the BackBoardServices options mega-dictionary: + + // First we have the debug sub-dictionary: + NSMutableDictionary *debug_options = [NSMutableDictionary dictionary]; + [debug_options setObject: [NSNumber numberWithBool: YES] forKey: FBSDebugOptionKeyCancelDebugOnNextLaunch]; + + // That will go in the overall dictionary: + + NSMutableDictionary *options = [NSMutableDictionary dictionary]; + [options setObject: debug_options forKey: FBSOpenApplicationOptionKeyDebuggingOptions]; + + success = FBSCallOpenApplicationFunction (bundleIDNSStr, options, err_str, NULL); + + if (!success) + { + DNBLogError ("error trying to cancel debug on next launch for %s: %s", [bundleIDNSStr UTF8String], err_str.AsString()); + } + + [pool drain]; +} +#endif // WITH_FBS |