2010-06-08 16:52:24 +00:00
//===-- debugserver.cpp -----------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
# include <sys/socket.h>
# include <sys/types.h>
# include <errno.h>
# include <getopt.h>
# include <netinet/in.h>
# include <sys/select.h>
# include <sys/sysctl.h>
# include <string>
# include <vector>
# include <asl.h>
2011-04-12 05:54:46 +00:00
# include <arpa/inet.h>
# include <netdb.h>
# include <netinet/in.h>
# include <netinet/tcp.h>
# include <sys/un.h>
# include <sys/types.h>
2014-06-18 18:26:50 +00:00
# include <crt_externs.h> // for _NSGetEnviron()
2010-06-08 16:52:24 +00:00
2014-07-24 01:36:24 +00:00
# if defined (__APPLE__)
# include <sched.h>
2015-07-07 04:15:43 +00:00
extern " C " int proc_set_wakemon_params ( pid_t , int , int ) ; // <libproc_internal.h> SPI
2014-07-24 01:36:24 +00:00
# endif
2010-06-08 16:52:24 +00:00
# include "CFString.h"
# include "DNB.h"
# include "DNBLog.h"
# include "DNBTimer.h"
# include "PseudoTerminal.h"
# include "RNBContext.h"
# include "RNBServices.h"
# include "RNBSocket.h"
# include "RNBRemote.h"
# include "SysSignal.h"
// Global PID in case we get a signal and need to stop the process...
nub_process_t g_pid = INVALID_NUB_PROCESS ;
//----------------------------------------------------------------------
// Run loop modes which determine which run loop function will be called
//----------------------------------------------------------------------
typedef enum
{
eRNBRunLoopModeInvalid = 0 ,
eRNBRunLoopModeGetStartModeFromRemoteProtocol ,
eRNBRunLoopModeInferiorAttaching ,
eRNBRunLoopModeInferiorLaunching ,
eRNBRunLoopModeInferiorExecuting ,
2011-03-20 04:57:14 +00:00
eRNBRunLoopModePlatformMode ,
2010-06-08 16:52:24 +00:00
eRNBRunLoopModeExit
} RNBRunLoopMode ;
//----------------------------------------------------------------------
// Global Variables
//----------------------------------------------------------------------
RNBRemoteSP g_remoteSP ;
static int g_lockdown_opt = 0 ;
static int g_applist_opt = 0 ;
static nub_launch_flavor_t g_launch_flavor = eLaunchFlavorDefault ;
2010-09-09 06:32:46 +00:00
int g_disable_aslr = 0 ;
2010-06-08 16:52:24 +00:00
int g_isatty = 0 ;
2014-02-25 19:57:47 +00:00
bool g_detach_on_error = true ;
2010-06-08 16:52:24 +00:00
# define RNBLogSTDOUT(fmt, ...) do { if (g_isatty) { fprintf(stdout, fmt, ## __VA_ARGS__); } else { _DNBLog(0, fmt, ## __VA_ARGS__); } } while (0)
# define RNBLogSTDERR(fmt, ...) do { if (g_isatty) { fprintf(stderr, fmt, ## __VA_ARGS__); } else { _DNBLog(0, fmt, ## __VA_ARGS__); } } while (0)
//----------------------------------------------------------------------
// Get our program path and arguments from the remote connection.
// We will need to start up the remote connection without a PID, get the
// arguments, wait for the new process to finish launching and hit its
// entry point, and then return the run loop mode that should come next.
//----------------------------------------------------------------------
RNBRunLoopMode
2011-01-22 23:43:18 +00:00
RNBRunLoopGetStartModeFromRemote ( RNBRemote * remote )
2010-06-08 16:52:24 +00:00
{
std : : string packet ;
2011-01-22 23:43:18 +00:00
if ( remote )
2010-06-08 16:52:24 +00:00
{
RNBContext & ctx = remote - > Context ( ) ;
2011-02-24 22:24:29 +00:00
uint32_t event_mask = RNBContext : : event_read_packet_available |
RNBContext : : event_read_thread_exiting ;
2010-06-08 16:52:24 +00:00
// Spin waiting to get the A packet.
while ( 1 )
{
DNBLogThreadedIf ( LOG_RNB_MAX , " %s ctx.Events().WaitForSetEvents( 0x%08x ) ... " , __FUNCTION__ , event_mask ) ;
nub_event_t set_events = ctx . Events ( ) . WaitForSetEvents ( event_mask ) ;
DNBLogThreadedIf ( LOG_RNB_MAX , " %s ctx.Events().WaitForSetEvents( 0x%08x ) => 0x%08x " , __FUNCTION__ , event_mask , set_events ) ;
2011-02-24 22:24:29 +00:00
if ( set_events & RNBContext : : event_read_thread_exiting )
{
2013-02-25 19:31:37 +00:00
RNBLogSTDERR ( " error: packet read thread exited. \n " ) ;
2011-02-24 22:24:29 +00:00
return eRNBRunLoopModeExit ;
}
2010-06-08 16:52:24 +00:00
if ( set_events & RNBContext : : event_read_packet_available )
{
rnb_err_t err = rnb_err ;
RNBRemote : : PacketEnum type ;
err = remote - > HandleReceivedPacket ( & type ) ;
// check if we tried to attach to a process
2012-07-20 21:37:13 +00:00
if ( type = = RNBRemote : : vattach | | type = = RNBRemote : : vattachwait | | type = = RNBRemote : : vattachorwait )
2010-06-08 16:52:24 +00:00
{
if ( err = = rnb_success )
2013-02-25 19:31:37 +00:00
{
RNBLogSTDOUT ( " Attach succeeded, ready to debug. \n " ) ;
2010-06-08 16:52:24 +00:00
return eRNBRunLoopModeInferiorExecuting ;
2013-02-25 19:31:37 +00:00
}
2010-06-08 16:52:24 +00:00
else
{
2013-02-25 19:31:37 +00:00
RNBLogSTDERR ( " error: attach failed. \n " ) ;
2010-06-08 16:52:24 +00:00
return eRNBRunLoopModeExit ;
}
}
if ( err = = rnb_success )
{
// If we got our arguments we are ready to launch using the arguments
// and any environment variables we received.
if ( type = = RNBRemote : : set_argv )
{
return eRNBRunLoopModeInferiorLaunching ;
}
}
else if ( err = = rnb_not_connected )
{
2013-02-25 19:31:37 +00:00
RNBLogSTDERR ( " error: connection lost. \n " ) ;
2010-06-08 16:52:24 +00:00
return eRNBRunLoopModeExit ;
}
else
{
// a catch all for any other gdb remote packets that failed
DNBLogThreadedIf ( LOG_RNB_MINIMAL , " %s Error getting packet. " , __FUNCTION__ ) ;
continue ;
}
DNBLogThreadedIf ( LOG_RNB_MINIMAL , " #### %s " , __FUNCTION__ ) ;
}
else
{
DNBLogThreadedIf ( LOG_RNB_MINIMAL , " %s Connection closed before getting \" A \" packet. " , __FUNCTION__ ) ;
return eRNBRunLoopModeExit ;
}
}
}
return eRNBRunLoopModeExit ;
}
//----------------------------------------------------------------------
// This run loop mode will wait for the process to launch and hit its
// entry point. It will currently ignore all events except for the
// process state changed event, where it watches for the process stopped
// or crash process state.
//----------------------------------------------------------------------
RNBRunLoopMode
2011-01-22 23:43:18 +00:00
RNBRunLoopLaunchInferior ( RNBRemote * remote , const char * stdin_path , const char * stdout_path , const char * stderr_path , bool no_stdio )
2010-06-08 16:52:24 +00:00
{
RNBContext & ctx = remote - > Context ( ) ;
// The Process stuff takes a c array, the RNBContext has a vector...
// So make up a c array.
DNBLogThreadedIf ( LOG_RNB_MINIMAL , " %s Launching '%s'... " , __FUNCTION__ , ctx . ArgumentAtIndex ( 0 ) ) ;
size_t inferior_argc = ctx . ArgumentCount ( ) ;
// Initialize inferior_argv with inferior_argc + 1 NULLs
std : : vector < const char * > inferior_argv ( inferior_argc + 1 , NULL ) ;
size_t i ;
for ( i = 0 ; i < inferior_argc ; i + + )
inferior_argv [ i ] = ctx . ArgumentAtIndex ( i ) ;
// Pass the environment array the same way:
size_t inferior_envc = ctx . EnvironmentCount ( ) ;
// Initialize inferior_argv with inferior_argc + 1 NULLs
std : : vector < const char * > inferior_envp ( inferior_envc + 1 , NULL ) ;
for ( i = 0 ; i < inferior_envc ; i + + )
inferior_envp [ i ] = ctx . EnvironmentAtIndex ( i ) ;
// Our launch type hasn't been set to anything concrete, so we need to
// figure our how we are going to launch automatically.
nub_launch_flavor_t launch_flavor = g_launch_flavor ;
if ( launch_flavor = = eLaunchFlavorDefault )
{
// Our default launch method is posix spawn
launch_flavor = eLaunchFlavorPosixSpawn ;
2014-03-29 18:54:20 +00:00
# if defined WITH_BKS
// Check if we have an app bundle, if so launch using BackBoard Services.
if ( strstr ( inferior_argv [ 0 ] , " .app " ) )
{
launch_flavor = eLaunchFlavorBKS ;
}
# elif defined WITH_SPRINGBOARD
2010-06-08 16:52:24 +00:00
// Check if we have an app bundle, if so launch using SpringBoard.
if ( strstr ( inferior_argv [ 0 ] , " .app " ) )
{
launch_flavor = eLaunchFlavorSpringBoard ;
}
# endif
}
ctx . SetLaunchFlavor ( launch_flavor ) ;
char resolved_path [ PATH_MAX ] ;
// If we fail to resolve the path to our executable, then just use what we
// were given and hope for the best
if ( ! DNBResolveExecutablePath ( inferior_argv [ 0 ] , resolved_path , sizeof ( resolved_path ) ) )
: : strncpy ( resolved_path , inferior_argv [ 0 ] , sizeof ( resolved_path ) ) ;
char launch_err_str [ PATH_MAX ] ;
launch_err_str [ 0 ] = ' \0 ' ;
2011-02-26 01:36:13 +00:00
const char * cwd = ( ctx . GetWorkingDirPath ( ) ! = NULL ? ctx . GetWorkingDirPath ( )
: ctx . GetWorkingDirectory ( ) ) ;
2014-03-29 18:54:20 +00:00
const char * process_event = ctx . GetProcessEvent ( ) ;
2010-06-08 16:52:24 +00:00
nub_process_t pid = DNBProcessLaunch ( resolved_path ,
& inferior_argv [ 0 ] ,
& inferior_envp [ 0 ] ,
2011-02-26 01:36:13 +00:00
cwd ,
2011-01-22 23:43:18 +00:00
stdin_path ,
stdout_path ,
stderr_path ,
2010-12-03 18:46:09 +00:00
no_stdio ,
2010-06-08 16:52:24 +00:00
launch_flavor ,
2010-08-31 18:35:14 +00:00
g_disable_aslr ,
2014-03-29 18:54:20 +00:00
process_event ,
2010-06-08 16:52:24 +00:00
launch_err_str ,
sizeof ( launch_err_str ) ) ;
g_pid = pid ;
2011-07-08 00:00:32 +00:00
if ( pid = = INVALID_NUB_PROCESS & & strlen ( launch_err_str ) > 0 )
2010-06-08 16:52:24 +00:00
{
2010-07-30 23:14:42 +00:00
DNBLogThreaded ( " %s DNBProcessLaunch() returned error: '%s' " , __FUNCTION__ , launch_err_str ) ;
2010-06-08 16:52:24 +00:00
ctx . LaunchStatus ( ) . SetError ( - 1 , DNBError : : Generic ) ;
ctx . LaunchStatus ( ) . SetErrorString ( launch_err_str ) ;
}
2011-07-08 00:00:32 +00:00
else if ( pid = = INVALID_NUB_PROCESS )
{
DNBLogThreaded ( " %s DNBProcessLaunch() failed to launch process, unknown failure " , __FUNCTION__ ) ;
ctx . LaunchStatus ( ) . SetError ( - 1 , DNBError : : Generic ) ;
2015-03-04 21:28:55 +00:00
ctx . LaunchStatus ( ) . SetErrorString ( " <unknown failure> " ) ;
2011-07-08 00:00:32 +00:00
}
2010-06-08 16:52:24 +00:00
else
2011-02-24 22:24:29 +00:00
{
2010-06-08 16:52:24 +00:00
ctx . LaunchStatus ( ) . Clear ( ) ;
2011-02-24 22:24:29 +00:00
}
2010-06-08 16:52:24 +00:00
if ( remote - > Comm ( ) . IsConnected ( ) )
{
// It we are connected already, the next thing gdb will do is ask
// whether the launch succeeded, and if not, whether there is an
// error code. So we need to fetch one packet from gdb before we wait
// on the stop from the target.
uint32_t event_mask = RNBContext : : event_read_packet_available ;
nub_event_t set_events = ctx . Events ( ) . WaitForSetEvents ( event_mask ) ;
if ( set_events & RNBContext : : event_read_packet_available )
{
rnb_err_t err = rnb_err ;
RNBRemote : : PacketEnum type ;
err = remote - > HandleReceivedPacket ( & type ) ;
if ( err ! = rnb_success )
{
DNBLogThreadedIf ( LOG_RNB_MINIMAL , " %s Error getting packet. " , __FUNCTION__ ) ;
return eRNBRunLoopModeExit ;
}
if ( type ! = RNBRemote : : query_launch_success )
{
DNBLogThreadedIf ( LOG_RNB_MINIMAL , " %s Didn't get the expected qLaunchSuccess packet. " , __FUNCTION__ ) ;
}
}
}
while ( pid ! = INVALID_NUB_PROCESS )
{
// Wait for process to start up and hit entry point
DNBLogThreadedIf ( LOG_RNB_EVENTS , " %s DNBProcessWaitForEvent (%4.4x, eEventProcessRunningStateChanged | eEventProcessStoppedStateChanged, true, INFINITE)... " , __FUNCTION__ , pid ) ;
nub_event_t set_events = DNBProcessWaitForEvents ( pid , eEventProcessRunningStateChanged | eEventProcessStoppedStateChanged , true , NULL ) ;
DNBLogThreadedIf ( LOG_RNB_EVENTS , " %s DNBProcessWaitForEvent (%4.4x, eEventProcessRunningStateChanged | eEventProcessStoppedStateChanged, true, INFINITE) => 0x%8.8x " , __FUNCTION__ , pid , set_events ) ;
if ( set_events = = 0 )
{
pid = INVALID_NUB_PROCESS ;
g_pid = pid ;
}
else
{
if ( set_events & ( eEventProcessRunningStateChanged | eEventProcessStoppedStateChanged ) )
{
nub_state_t pid_state = DNBProcessGetState ( pid ) ;
DNBLogThreadedIf ( LOG_RNB_EVENTS , " %s process %4.4x state changed (eEventProcessStateChanged): %s " , __FUNCTION__ , pid , DNBStateAsString ( pid_state ) ) ;
switch ( pid_state )
{
default :
case eStateInvalid :
case eStateUnloaded :
case eStateAttaching :
case eStateLaunching :
case eStateSuspended :
break ; // Ignore
case eStateRunning :
case eStateStepping :
// Still waiting to stop at entry point...
break ;
case eStateStopped :
case eStateCrashed :
ctx . SetProcessID ( pid ) ;
return eRNBRunLoopModeInferiorExecuting ;
case eStateDetached :
case eStateExited :
pid = INVALID_NUB_PROCESS ;
g_pid = pid ;
return eRNBRunLoopModeExit ;
}
}
DNBProcessResetEvents ( pid , set_events ) ;
}
}
return eRNBRunLoopModeExit ;
}
//----------------------------------------------------------------------
// This run loop mode will wait for the process to launch and hit its
// entry point. It will currently ignore all events except for the
// process state changed event, where it watches for the process stopped
// or crash process state.
//----------------------------------------------------------------------
RNBRunLoopMode
2011-01-22 23:43:18 +00:00
RNBRunLoopLaunchAttaching ( RNBRemote * remote , nub_process_t attach_pid , nub_process_t & pid )
2010-06-08 16:52:24 +00:00
{
RNBContext & ctx = remote - > Context ( ) ;
DNBLogThreadedIf ( LOG_RNB_MINIMAL , " %s Attaching to pid %i... " , __FUNCTION__ , attach_pid ) ;
char err_str [ 1024 ] ;
pid = DNBProcessAttach ( attach_pid , NULL , err_str , sizeof ( err_str ) ) ;
g_pid = pid ;
if ( pid = = INVALID_NUB_PROCESS )
{
ctx . LaunchStatus ( ) . SetError ( - 1 , DNBError : : Generic ) ;
if ( err_str [ 0 ] )
ctx . LaunchStatus ( ) . SetErrorString ( err_str ) ;
return eRNBRunLoopModeExit ;
}
else
{
ctx . SetProcessID ( pid ) ;
return eRNBRunLoopModeInferiorExecuting ;
}
}
//----------------------------------------------------------------------
// Watch for signals:
// SIGINT: so we can halt our inferior. (disabled for now)
// SIGPIPE: in case our child process dies
//----------------------------------------------------------------------
int g_sigint_received = 0 ;
int g_sigpipe_received = 0 ;
void
signal_handler ( int signo )
{
DNBLogThreadedIf ( LOG_RNB_MINIMAL , " %s (%s) " , __FUNCTION__ , SysSignal : : Name ( signo ) ) ;
switch ( signo )
{
case SIGINT :
g_sigint_received + + ;
if ( g_pid ! = INVALID_NUB_PROCESS )
{
// Only send a SIGINT once...
if ( g_sigint_received = = 1 )
{
switch ( DNBProcessGetState ( g_pid ) )
{
case eStateRunning :
case eStateStepping :
DNBProcessSignal ( g_pid , SIGSTOP ) ;
return ;
2011-04-01 00:29:43 +00:00
default :
break ;
2010-06-08 16:52:24 +00:00
}
}
}
exit ( SIGINT ) ;
break ;
case SIGPIPE :
g_sigpipe_received = 1 ;
break ;
}
}
// Return the new run loop mode based off of the current process state
RNBRunLoopMode
2011-01-22 23:43:18 +00:00
HandleProcessStateChange ( RNBRemote * remote , bool initialize )
2010-06-08 16:52:24 +00:00
{
RNBContext & ctx = remote - > Context ( ) ;
nub_process_t pid = ctx . ProcessID ( ) ;
if ( pid = = INVALID_NUB_PROCESS )
{
DNBLogThreadedIf ( LOG_RNB_MINIMAL , " #### %s error: pid invalid, exiting... " , __FUNCTION__ ) ;
return eRNBRunLoopModeExit ;
}
nub_state_t pid_state = DNBProcessGetState ( pid ) ;
DNBLogThreadedIf ( LOG_RNB_MINIMAL , " %s (&remote, initialize=%i) pid_state = %s " , __FUNCTION__ , ( int ) initialize , DNBStateAsString ( pid_state ) ) ;
switch ( pid_state )
{
case eStateInvalid :
case eStateUnloaded :
// Something bad happened
return eRNBRunLoopModeExit ;
break ;
case eStateAttaching :
case eStateLaunching :
return eRNBRunLoopModeInferiorExecuting ;
case eStateSuspended :
case eStateCrashed :
case eStateStopped :
// If we stop due to a signal, so clear the fact that we got a SIGINT
// so we can stop ourselves again (but only while our inferior
// process is running..)
g_sigint_received = 0 ;
if ( initialize = = false )
{
// Compare the last stop count to our current notion of a stop count
// to make sure we don't notify more than once for a given stop.
nub_size_t prev_pid_stop_count = ctx . GetProcessStopCount ( ) ;
bool pid_stop_count_changed = ctx . SetProcessStopCount ( DNBProcessGetStopCount ( pid ) ) ;
if ( pid_stop_count_changed )
{
remote - > FlushSTDIO ( ) ;
if ( ctx . GetProcessStopCount ( ) = = 1 )
{
2012-09-18 18:04:04 +00:00
DNBLogThreadedIf ( LOG_RNB_MINIMAL , " %s (&remote, initialize=%i) pid_state = %s pid_stop_count %llu (old %llu)) Notify??? no, first stop... " , __FUNCTION__ , ( int ) initialize , DNBStateAsString ( pid_state ) , ( uint64_t ) ctx . GetProcessStopCount ( ) , ( uint64_t ) prev_pid_stop_count ) ;
2010-06-08 16:52:24 +00:00
}
else
{
2012-09-18 18:04:04 +00:00
DNBLogThreadedIf ( LOG_RNB_MINIMAL , " %s (&remote, initialize=%i) pid_state = %s pid_stop_count %llu (old %llu)) Notify??? YES!!! " , __FUNCTION__ , ( int ) initialize , DNBStateAsString ( pid_state ) , ( uint64_t ) ctx . GetProcessStopCount ( ) , ( uint64_t ) prev_pid_stop_count ) ;
2010-06-08 16:52:24 +00:00
remote - > NotifyThatProcessStopped ( ) ;
}
}
else
{
2012-09-18 18:04:04 +00:00
DNBLogThreadedIf ( LOG_RNB_MINIMAL , " %s (&remote, initialize=%i) pid_state = %s pid_stop_count %llu (old %llu)) Notify??? skipping... " , __FUNCTION__ , ( int ) initialize , DNBStateAsString ( pid_state ) , ( uint64_t ) ctx . GetProcessStopCount ( ) , ( uint64_t ) prev_pid_stop_count ) ;
2010-06-08 16:52:24 +00:00
}
}
return eRNBRunLoopModeInferiorExecuting ;
case eStateStepping :
case eStateRunning :
return eRNBRunLoopModeInferiorExecuting ;
case eStateExited :
remote - > HandlePacket_last_signal ( NULL ) ;
2011-04-01 00:29:43 +00:00
case eStateDetached :
2010-06-08 16:52:24 +00:00
return eRNBRunLoopModeExit ;
}
// Catch all...
return eRNBRunLoopModeExit ;
}
// This function handles the case where our inferior program is stopped and
// we are waiting for gdb remote protocol packets. When a packet occurs that
// makes the inferior run, we need to leave this function with a new state
// as the return code.
RNBRunLoopMode
2011-01-22 23:43:18 +00:00
RNBRunLoopInferiorExecuting ( RNBRemote * remote )
2010-06-08 16:52:24 +00:00
{
DNBLogThreadedIf ( LOG_RNB_MINIMAL , " #### %s " , __FUNCTION__ ) ;
RNBContext & ctx = remote - > Context ( ) ;
// Init our mode and set 'is_running' based on the current process state
RNBRunLoopMode mode = HandleProcessStateChange ( remote , true ) ;
while ( ctx . ProcessID ( ) ! = INVALID_NUB_PROCESS )
{
std : : string set_events_str ;
uint32_t event_mask = ctx . NormalEventBits ( ) ;
if ( ! ctx . ProcessStateRunning ( ) )
{
2012-11-17 00:21:04 +00:00
// Clear some bits if we are not running so we don't send any async packets
2010-06-08 16:52:24 +00:00
event_mask & = ~ RNBContext : : event_proc_stdio_available ;
2012-11-17 00:21:04 +00:00
event_mask & = ~ RNBContext : : event_proc_profile_data ;
2010-06-08 16:52:24 +00:00
}
// We want to make sure we consume all process state changes and have
// whomever is notifying us to wait for us to reset the event bit before
// continuing.
//ctx.Events().SetResetAckMask (RNBContext::event_proc_state_changed);
DNBLogThreadedIf ( LOG_RNB_EVENTS , " %s ctx.Events().WaitForSetEvents(0x%08x) ... " , __FUNCTION__ , event_mask ) ;
nub_event_t set_events = ctx . Events ( ) . WaitForSetEvents ( event_mask ) ;
DNBLogThreadedIf ( LOG_RNB_EVENTS , " %s ctx.Events().WaitForSetEvents(0x%08x) => 0x%08x (%s) " , __FUNCTION__ , event_mask , set_events , ctx . EventsAsString ( set_events , set_events_str ) ) ;
if ( set_events )
{
if ( ( set_events & RNBContext : : event_proc_thread_exiting ) | |
( set_events & RNBContext : : event_proc_stdio_available ) )
{
remote - > FlushSTDIO ( ) ;
}
2012-11-17 00:21:04 +00:00
if ( set_events & RNBContext : : event_proc_profile_data )
{
remote - > SendAsyncProfileData ( ) ;
}
2010-06-08 16:52:24 +00:00
if ( set_events & RNBContext : : event_read_packet_available )
{
// handleReceivedPacket will take care of resetting the
// event_read_packet_available events when there are no more...
set_events ^ = RNBContext : : event_read_packet_available ;
if ( ctx . ProcessStateRunning ( ) )
{
if ( remote - > HandleAsyncPacket ( ) = = rnb_not_connected )
{
// TODO: connect again? Exit?
}
}
else
{
if ( remote - > HandleReceivedPacket ( ) = = rnb_not_connected )
{
// TODO: connect again? Exit?
}
}
}
if ( set_events & RNBContext : : event_proc_state_changed )
{
mode = HandleProcessStateChange ( remote , false ) ;
ctx . Events ( ) . ResetEvents ( RNBContext : : event_proc_state_changed ) ;
set_events ^ = RNBContext : : event_proc_state_changed ;
}
if ( set_events & RNBContext : : event_proc_thread_exiting )
{
mode = eRNBRunLoopModeExit ;
}
if ( set_events & RNBContext : : event_read_thread_exiting )
{
// Out remote packet receiving thread exited, exit for now.
if ( ctx . HasValidProcessID ( ) )
{
// TODO: We should add code that will leave the current process
// in its current state and listen for another connection...
if ( ctx . ProcessStateRunning ( ) )
{
2014-02-25 04:53:13 +00:00
if ( ctx . GetDetachOnError ( ) )
{
DNBLog ( " debugserver's event read thread is exiting, detaching from the inferior process. " ) ;
DNBProcessDetach ( ctx . ProcessID ( ) ) ;
}
else
{
DNBLog ( " debugserver's event read thread is exiting, killing the inferior process. " ) ;
DNBProcessKill ( ctx . ProcessID ( ) ) ;
}
}
else
{
if ( ctx . GetDetachOnError ( ) )
{
DNBLog ( " debugserver's event read thread is exiting, detaching from the inferior process. " ) ;
DNBProcessDetach ( ctx . ProcessID ( ) ) ;
}
2010-06-08 16:52:24 +00:00
}
}
mode = eRNBRunLoopModeExit ;
}
}
// Reset all event bits that weren't reset for now...
if ( set_events ! = 0 )
ctx . Events ( ) . ResetEvents ( set_events ) ;
if ( mode ! = eRNBRunLoopModeInferiorExecuting )
break ;
}
return mode ;
}
2011-03-20 04:57:14 +00:00
RNBRunLoopMode
RNBRunLoopPlatform ( RNBRemote * remote )
{
RNBRunLoopMode mode = eRNBRunLoopModePlatformMode ;
RNBContext & ctx = remote - > Context ( ) ;
while ( mode = = eRNBRunLoopModePlatformMode )
{
std : : string set_events_str ;
const uint32_t event_mask = RNBContext : : event_read_packet_available |
RNBContext : : event_read_thread_exiting ;
DNBLogThreadedIf ( LOG_RNB_EVENTS , " %s ctx.Events().WaitForSetEvents(0x%08x) ... " , __FUNCTION__ , event_mask ) ;
nub_event_t set_events = ctx . Events ( ) . WaitForSetEvents ( event_mask ) ;
DNBLogThreadedIf ( LOG_RNB_EVENTS , " %s ctx.Events().WaitForSetEvents(0x%08x) => 0x%08x (%s) " , __FUNCTION__ , event_mask , set_events , ctx . EventsAsString ( set_events , set_events_str ) ) ;
if ( set_events )
{
if ( set_events & RNBContext : : event_read_packet_available )
{
if ( remote - > HandleReceivedPacket ( ) = = rnb_not_connected )
mode = eRNBRunLoopModeExit ;
}
if ( set_events & RNBContext : : event_read_thread_exiting )
{
mode = eRNBRunLoopModeExit ;
}
ctx . Events ( ) . ResetEvents ( set_events ) ;
}
}
return eRNBRunLoopModeExit ;
}
2013-12-10 19:36:45 +00:00
//----------------------------------------------------------------------
// Convenience function to set up the remote listening port
// Returns 1 for success 0 for failure.
//----------------------------------------------------------------------
static void
PortWasBoundCallbackUnixSocket ( const void * baton , in_port_t port )
{
//::printf ("PortWasBoundCallbackUnixSocket (baton = %p, port = %u)\n", baton, port);
const char * unix_socket_name = ( const char * ) baton ;
if ( unix_socket_name & & unix_socket_name [ 0 ] )
{
// We were given a unix socket name to use to communicate the port
// that we ended up binding to back to our parent process
struct sockaddr_un saddr_un ;
int s = : : socket ( AF_UNIX , SOCK_STREAM , 0 ) ;
if ( s < 0 )
{
perror ( " error: socket (AF_UNIX, SOCK_STREAM, 0) " ) ;
exit ( 1 ) ;
}
saddr_un . sun_family = AF_UNIX ;
: : strncpy ( saddr_un . sun_path , unix_socket_name , sizeof ( saddr_un . sun_path ) - 1 ) ;
saddr_un . sun_path [ sizeof ( saddr_un . sun_path ) - 1 ] = ' \0 ' ;
saddr_un . sun_len = SUN_LEN ( & saddr_un ) ;
2015-03-09 19:45:23 +00:00
if ( : : connect ( s , ( struct sockaddr * ) & saddr_un , static_cast < socklen_t > ( SUN_LEN ( & saddr_un ) ) ) < 0 )
2013-12-10 19:36:45 +00:00
{
perror ( " error: connect (socket, &saddr_un, saddr_un_len) " ) ;
exit ( 1 ) ;
}
//::printf ("connect () sucess!!\n");
// We were able to connect to the socket, now write our PID so whomever
// launched us will know this process's ID
RNBLogSTDOUT ( " Listening to port %i... \n " , port ) ;
char pid_str [ 64 ] ;
const int pid_str_len = : : snprintf ( pid_str , sizeof ( pid_str ) , " %u " , port ) ;
2015-03-09 19:45:23 +00:00
const ssize_t bytes_sent = : : send ( s , pid_str , pid_str_len , 0 ) ;
2013-12-10 19:36:45 +00:00
if ( pid_str_len ! = bytes_sent )
{
perror ( " error: send (s, pid_str, pid_str_len, 0) " ) ;
exit ( 1 ) ;
}
//::printf ("send () sucess!!\n");
// We are done with the socket
close ( s ) ;
}
}
2011-04-12 05:54:46 +00:00
static void
2013-12-06 17:46:35 +00:00
PortWasBoundCallbackNamedPipe ( const void * baton , uint16_t port )
2011-04-12 05:54:46 +00:00
{
2013-12-04 19:19:12 +00:00
const char * named_pipe = ( const char * ) baton ;
if ( named_pipe & & named_pipe [ 0 ] )
2011-04-12 05:54:46 +00:00
{
2013-12-04 19:19:12 +00:00
int fd = : : open ( named_pipe , O_WRONLY ) ;
if ( fd > - 1 )
2011-04-12 05:54:46 +00:00
{
2013-12-04 19:19:12 +00:00
char port_str [ 64 ] ;
const ssize_t port_str_len = : : snprintf ( port_str , sizeof ( port_str ) , " %u " , port ) ;
// Write the port number as a C string with the NULL terminator
: : write ( fd , port_str , port_str_len + 1 ) ;
close ( fd ) ;
2011-04-12 05:54:46 +00:00
}
}
}
2010-06-08 16:52:24 +00:00
static int
2013-12-10 19:36:45 +00:00
ConnectRemote ( RNBRemote * remote ,
const char * host ,
int port ,
bool reverse_connect ,
const char * named_pipe_path ,
const char * unix_socket_name )
2010-06-08 16:52:24 +00:00
{
2011-01-22 23:43:18 +00:00
if ( ! remote - > Comm ( ) . IsConnected ( ) )
2010-06-08 16:52:24 +00:00
{
2013-12-05 22:58:22 +00:00
if ( reverse_connect )
2010-06-08 16:52:24 +00:00
{
2013-12-05 22:58:22 +00:00
if ( port = = 0 )
{
DNBLogThreaded ( " error: invalid port supplied for reverse connection: %i. \n " , port ) ;
return 0 ;
}
if ( remote - > Comm ( ) . Connect ( host , port ) ! = rnb_success )
{
DNBLogThreaded ( " Failed to reverse connect to %s:%i. \n " , host , port ) ;
return 0 ;
}
2010-06-08 16:52:24 +00:00
}
else
{
2013-12-05 22:58:22 +00:00
if ( port ! = 0 )
2014-02-27 19:38:18 +00:00
RNBLogSTDOUT ( " Listening to port %i for a connection from %s... \n " , port , host ? host : " 127.0.0.1 " ) ;
2013-12-10 19:36:45 +00:00
if ( unix_socket_name & & unix_socket_name [ 0 ] )
2013-12-05 22:58:22 +00:00
{
2013-12-10 19:36:45 +00:00
if ( remote - > Comm ( ) . Listen ( host , port , PortWasBoundCallbackUnixSocket , unix_socket_name ) ! = rnb_success )
{
RNBLogSTDERR ( " Failed to get connection from a remote gdb process. \n " ) ;
return 0 ;
}
}
else
{
if ( remote - > Comm ( ) . Listen ( host , port , PortWasBoundCallbackNamedPipe , named_pipe_path ) ! = rnb_success )
{
RNBLogSTDERR ( " Failed to get connection from a remote gdb process. \n " ) ;
return 0 ;
}
2013-12-05 22:58:22 +00:00
}
2010-06-08 16:52:24 +00:00
}
2013-12-05 22:58:22 +00:00
remote - > StartReadRemoteDataThread ( ) ;
2010-06-08 16:52:24 +00:00
}
return 1 ;
}
//----------------------------------------------------------------------
// ASL Logging callback that can be registered with DNBLogSetLogCallback
//----------------------------------------------------------------------
void
ASLLogCallback ( void * baton , uint32_t flags , const char * format , va_list args )
{
if ( format = = NULL )
return ;
static aslmsg g_aslmsg = NULL ;
if ( g_aslmsg = = NULL )
{
g_aslmsg = : : asl_new ( ASL_TYPE_MSG ) ;
char asl_key_sender [ PATH_MAX ] ;
2014-03-13 18:30:04 +00:00
snprintf ( asl_key_sender , sizeof ( asl_key_sender ) , " com.apple.%s-%s " , DEBUGSERVER_PROGRAM_NAME , DEBUGSERVER_VERSION_STR ) ;
2010-06-08 16:52:24 +00:00
: : asl_set ( g_aslmsg , ASL_KEY_SENDER , asl_key_sender ) ;
}
int asl_level ;
if ( flags & DNBLOG_FLAG_FATAL ) asl_level = ASL_LEVEL_CRIT ;
else if ( flags & DNBLOG_FLAG_ERROR ) asl_level = ASL_LEVEL_ERR ;
else if ( flags & DNBLOG_FLAG_WARNING ) asl_level = ASL_LEVEL_WARNING ;
else if ( flags & DNBLOG_FLAG_VERBOSE ) asl_level = ASL_LEVEL_WARNING ; //ASL_LEVEL_INFO;
else asl_level = ASL_LEVEL_WARNING ; //ASL_LEVEL_DEBUG;
: : asl_vlog ( NULL , g_aslmsg , asl_level , format , args ) ;
}
//----------------------------------------------------------------------
// FILE based Logging callback that can be registered with
// DNBLogSetLogCallback
//----------------------------------------------------------------------
void
FileLogCallback ( void * baton , uint32_t flags , const char * format , va_list args )
{
if ( baton = = NULL | | format = = NULL )
return ;
: : vfprintf ( ( FILE * ) baton , format , args ) ;
: : fprintf ( ( FILE * ) baton , " \n " ) ;
}
void
show_usage_and_exit ( int exit_code )
{
RNBLogSTDERR ( " Usage: \n %s host:port [program-name program-arg1 program-arg2 ...] \n " , DEBUGSERVER_PROGRAM_NAME ) ;
RNBLogSTDERR ( " %s /path/file [program-name program-arg1 program-arg2 ...] \n " , DEBUGSERVER_PROGRAM_NAME ) ;
RNBLogSTDERR ( " %s host:port --attach=<pid> \n " , DEBUGSERVER_PROGRAM_NAME ) ;
RNBLogSTDERR ( " %s /path/file --attach=<pid> \n " , DEBUGSERVER_PROGRAM_NAME ) ;
RNBLogSTDERR ( " %s host:port --attach=<process_name> \n " , DEBUGSERVER_PROGRAM_NAME ) ;
RNBLogSTDERR ( " %s /path/file --attach=<process_name> \n " , DEBUGSERVER_PROGRAM_NAME ) ;
exit ( exit_code ) ;
}
//----------------------------------------------------------------------
2013-04-04 20:35:24 +00:00
// option descriptors for getopt_long_only()
2010-06-08 16:52:24 +00:00
//----------------------------------------------------------------------
static struct option g_long_options [ ] =
{
{ " attach " , required_argument , NULL , ' a ' } ,
2010-11-18 05:57:03 +00:00
{ " arch " , required_argument , NULL , ' A ' } ,
2010-06-08 16:52:24 +00:00
{ " debug " , no_argument , NULL , ' g ' } ,
2014-02-25 19:57:47 +00:00
{ " kill-on-error " , no_argument , NULL , ' K ' } ,
2010-06-08 16:52:24 +00:00
{ " verbose " , no_argument , NULL , ' v ' } ,
{ " lockdown " , no_argument , & g_lockdown_opt , 1 } , // short option "-k"
{ " applist " , no_argument , & g_applist_opt , 1 } , // short option "-t"
{ " log-file " , required_argument , NULL , ' l ' } ,
{ " log-flags " , required_argument , NULL , ' f ' } ,
{ " launch " , required_argument , NULL , ' x ' } , // Valid values are "auto", "posix-spawn", "fork-exec", "springboard" (arm only)
{ " waitfor " , required_argument , NULL , ' w ' } , // Wait for a process whose name starts with ARG
{ " waitfor-interval " , required_argument , NULL , ' i ' } , // Time in usecs to wait between sampling the pid list when waiting for a process by name
{ " waitfor-duration " , required_argument , NULL , ' d ' } , // The time in seconds to wait for a process to show up by name
{ " native-regs " , no_argument , NULL , ' r ' } , // Specify to use the native registers instead of the gdb defaults for the architecture.
2011-01-23 05:56:20 +00:00
{ " stdio-path " , required_argument , NULL , ' s ' } , // Set the STDIO path to be used when launching applications (STDIN, STDOUT and STDERR) (only if debugserver launches the process)
{ " stdin-path " , required_argument , NULL , ' I ' } , // Set the STDIN path to be used when launching applications (only if debugserver launches the process)
2011-01-25 16:56:01 +00:00
{ " stdout-path " , required_argument , NULL , ' O ' } , // Set the STDOUT path to be used when launching applications (only if debugserver launches the process)
{ " stderr-path " , required_argument , NULL , ' E ' } , // Set the STDERR path to be used when launching applications (only if debugserver launches the process)
2011-01-23 05:56:20 +00:00
{ " no-stdio " , no_argument , NULL , ' n ' } , // Do not set up any stdio (perhaps the program is a GUI program) (only if debugserver launches the process)
{ " setsid " , no_argument , NULL , ' S ' } , // call setsid() to make debugserver run in its own session
2010-08-31 18:35:14 +00:00
{ " disable-aslr " , no_argument , NULL , ' D ' } , // Use _POSIX_SPAWN_DISABLE_ASLR to avoid shared library randomization
2011-01-23 05:56:20 +00:00
{ " working-dir " , required_argument , NULL , ' W ' } , // The working directory that the inferior process should have (only if debugserver launches the process)
2011-03-20 04:57:14 +00:00
{ " platform " , required_argument , NULL , ' p ' } , // Put this executable into a remote platform mode
2013-12-10 19:36:45 +00:00
{ " unix-socket " , required_argument , NULL , ' u ' } , // If we need to handshake with our parent process, an option will be passed down that specifies a unix socket name to use
2013-12-04 19:19:12 +00:00
{ " named-pipe " , required_argument , NULL , ' P ' } ,
2013-12-05 22:58:22 +00:00
{ " reverse-connect " , no_argument , NULL , ' R ' } ,
2014-06-18 18:26:50 +00:00
{ " env " , required_argument , NULL , ' e ' } , // When debugserver launches the process, set a single environment entry as specified by the option value ("./debugserver -e FOO=1 -e BAR=2 localhost:1234 -- /bin/ls")
{ " forward-env " , no_argument , NULL , ' F ' } , // When debugserver launches the process, forward debugserver's current environment variables to the child process ("./debugserver -F localhost:1234 -- /bin/ls"
2010-06-08 16:52:24 +00:00
{ NULL , 0 , NULL , 0 }
} ;
//----------------------------------------------------------------------
// main
//----------------------------------------------------------------------
int
main ( int argc , char * argv [ ] )
{
2012-11-01 02:02:59 +00:00
const char * argv_sub_zero = argv [ 0 ] ; // save a copy of argv[0] for error reporting post-launch
2014-07-24 01:36:24 +00:00
# if defined (__APPLE__)
pthread_setname_np ( " main thread " ) ;
# if defined (__arm__) || defined (__arm64__) || defined (__aarch64__)
struct sched_param thread_param ;
int thread_sched_policy ;
if ( pthread_getschedparam ( pthread_self ( ) , & thread_sched_policy , & thread_param ) = = 0 )
{
thread_param . sched_priority = 47 ;
pthread_setschedparam ( pthread_self ( ) , thread_sched_policy , & thread_param ) ;
}
2015-07-07 04:15:43 +00:00
: : proc_set_wakemon_params ( getpid ( ) , 500 , 0 ) ; // Allow up to 500 wakeups/sec to avoid EXC_RESOURCE for normal use.
2014-07-24 01:36:24 +00:00
# endif
# endif
2010-06-08 16:52:24 +00:00
g_isatty = : : isatty ( STDIN_FILENO ) ;
// ::printf ("uid=%u euid=%u gid=%u egid=%u\n",
// getuid(),
// geteuid(),
// getgid(),
// getegid());
// signal (SIGINT, signal_handler);
signal ( SIGPIPE , signal_handler ) ;
2010-07-30 23:14:42 +00:00
signal ( SIGHUP , signal_handler ) ;
2014-03-29 18:54:20 +00:00
// We're always sitting in waitpid or kevent waiting on our target process' death,
// we don't need no stinking SIGCHLD's...
sigset_t sigset ;
sigemptyset ( & sigset ) ;
sigaddset ( & sigset , SIGCHLD ) ;
sigprocmask ( SIG_BLOCK , & sigset , NULL ) ;
2010-06-08 16:52:24 +00:00
2011-02-24 22:24:29 +00:00
g_remoteSP . reset ( new RNBRemote ( ) ) ;
RNBRemote * remote = g_remoteSP . get ( ) ;
if ( remote = = NULL )
{
RNBLogSTDERR ( " error: failed to create a remote connection class \n " ) ;
return - 1 ;
}
RNBContext & ctx = remote - > Context ( ) ;
2010-06-08 16:52:24 +00:00
int i ;
int attach_pid = INVALID_NUB_PROCESS ;
FILE * log_file = NULL ;
uint32_t log_flags = 0 ;
// Parse our options
int ch ;
int long_option_index = 0 ;
int debug = 0 ;
std : : string compile_options ;
std : : string waitfor_pid_name ; // Wait for a process that starts with this name
std : : string attach_pid_name ;
2010-11-18 05:57:03 +00:00
std : : string arch_name ;
2011-04-12 05:54:46 +00:00
std : : string working_dir ; // The new working directory to use for the inferior
2013-12-10 19:36:45 +00:00
std : : string unix_socket_name ; // If we need to handshake with our parent process, an option will be passed down that specifies a unix socket name to use
2013-12-04 19:19:12 +00:00
std : : string named_pipe_path ; // If we need to handshake with our parent process, an option will be passed down that specifies a named pipe to use
2010-06-08 16:52:24 +00:00
useconds_t waitfor_interval = 1000 ; // Time in usecs between process lists polls when waiting for a process by name, default 1 msec.
useconds_t waitfor_duration = 0 ; // Time in seconds to wait for a process by name, 0 means wait forever.
2010-12-03 18:46:09 +00:00
bool no_stdio = false ;
2013-12-05 22:58:22 +00:00
bool reverse_connect = false ; // Set to true by an option to indicate we should reverse connect to the host:port supplied as the first debugserver argument
2010-06-08 16:52:24 +00:00
# if !defined (DNBLOG_ENABLED)
compile_options + = " (no-logging) " ;
# endif
RNBRunLoopMode start_mode = eRNBRunLoopModeExit ;
2011-05-23 18:04:09 +00:00
char short_options [ 512 ] ;
uint32_t short_options_idx = 0 ;
// Handle the two case that don't have short options in g_long_options
short_options [ short_options_idx + + ] = ' k ' ;
short_options [ short_options_idx + + ] = ' t ' ;
for ( i = 0 ; g_long_options [ i ] . name ! = NULL ; + + i )
{
if ( isalpha ( g_long_options [ i ] . val ) )
{
short_options [ short_options_idx + + ] = g_long_options [ i ] . val ;
switch ( g_long_options [ i ] . has_arg )
{
default :
case no_argument :
break ;
case optional_argument :
short_options [ short_options_idx + + ] = ' : ' ;
// Fall through to required_argument case below...
case required_argument :
short_options [ short_options_idx + + ] = ' : ' ;
break ;
}
}
}
// NULL terminate the short option string.
short_options [ short_options_idx + + ] = ' \0 ' ;
2013-11-22 18:55:04 +00:00
# if __GLIBC__
optind = 0 ;
# else
optreset = 1 ;
optind = 1 ;
# endif
2013-04-04 20:35:24 +00:00
while ( ( ch = getopt_long_only ( argc , argv , short_options , g_long_options , & long_option_index ) ) ! = - 1 )
2010-06-08 16:52:24 +00:00
{
DNBLogDebug ( " option: ch == %c (0x%2.2x) --%s%c%s \n " ,
ch , ( uint8_t ) ch ,
g_long_options [ long_option_index ] . name ,
g_long_options [ long_option_index ] . has_arg ? ' = ' : ' ' ,
optarg ? optarg : " " ) ;
switch ( ch )
{
case 0 : // Any optional that auto set themselves will return 0
break ;
2010-11-18 05:57:03 +00:00
case ' A ' :
if ( optarg & & optarg [ 0 ] )
arch_name . assign ( optarg ) ;
break ;
2010-06-08 16:52:24 +00:00
case ' a ' :
if ( optarg & & optarg [ 0 ] )
{
if ( isdigit ( optarg [ 0 ] ) )
{
char * end = NULL ;
2015-03-09 19:45:23 +00:00
attach_pid = static_cast < int > ( strtoul ( optarg , & end , 0 ) ) ;
2010-06-08 16:52:24 +00:00
if ( end = = NULL | | * end ! = ' \0 ' )
{
RNBLogSTDERR ( " error: invalid pid option '%s' \n " , optarg ) ;
exit ( 4 ) ;
}
}
else
{
attach_pid_name = optarg ;
}
start_mode = eRNBRunLoopModeInferiorAttaching ;
}
break ;
// --waitfor=NAME
case ' w ' :
if ( optarg & & optarg [ 0 ] )
{
waitfor_pid_name = optarg ;
start_mode = eRNBRunLoopModeInferiorAttaching ;
}
break ;
// --waitfor-interval=USEC
case ' i ' :
if ( optarg & & optarg [ 0 ] )
{
char * end = NULL ;
2015-03-09 19:45:23 +00:00
waitfor_interval = static_cast < useconds_t > ( strtoul ( optarg , & end , 0 ) ) ;
2010-06-08 16:52:24 +00:00
if ( end = = NULL | | * end ! = ' \0 ' )
{
RNBLogSTDERR ( " error: invalid waitfor-interval option value '%s'. \n " , optarg ) ;
exit ( 6 ) ;
}
}
break ;
// --waitfor-duration=SEC
case ' d ' :
if ( optarg & & optarg [ 0 ] )
{
char * end = NULL ;
2015-03-09 19:45:23 +00:00
waitfor_duration = static_cast < useconds_t > ( strtoul ( optarg , & end , 0 ) ) ;
2010-06-08 16:52:24 +00:00
if ( end = = NULL | | * end ! = ' \0 ' )
{
RNBLogSTDERR ( " error: invalid waitfor-duration option value '%s'. \n " , optarg ) ;
exit ( 7 ) ;
}
}
break ;
2014-02-25 04:53:13 +00:00
2014-02-25 19:57:47 +00:00
case ' K ' :
g_detach_on_error = false ;
2010-06-08 16:52:24 +00:00
2011-01-23 05:56:20 +00:00
case ' W ' :
2011-01-22 23:43:18 +00:00
if ( optarg & & optarg [ 0 ] )
2011-01-23 05:56:20 +00:00
working_dir . assign ( optarg ) ;
2011-01-22 23:43:18 +00:00
break ;
2010-06-08 16:52:24 +00:00
case ' x ' :
if ( optarg & & optarg [ 0 ] )
{
if ( strcasecmp ( optarg , " auto " ) = = 0 )
g_launch_flavor = eLaunchFlavorDefault ;
else if ( strcasestr ( optarg , " posix " ) = = optarg )
g_launch_flavor = eLaunchFlavorPosixSpawn ;
else if ( strcasestr ( optarg , " fork " ) = = optarg )
g_launch_flavor = eLaunchFlavorForkExec ;
2012-02-22 02:18:59 +00:00
# ifdef WITH_SPRINGBOARD
2010-06-08 16:52:24 +00:00
else if ( strcasestr ( optarg , " spring " ) = = optarg )
g_launch_flavor = eLaunchFlavorSpringBoard ;
# endif
2014-03-29 18:54:20 +00:00
# ifdef WITH_BKS
else if ( strcasestr ( optarg , " backboard " ) = = optarg )
g_launch_flavor = eLaunchFlavorBKS ;
# endif
2010-06-08 16:52:24 +00:00
else
{
RNBLogSTDERR ( " error: invalid TYPE for the --launch=TYPE (-x TYPE) option: '%s' \n " , optarg ) ;
RNBLogSTDERR ( " Valid values TYPE are: \n " ) ;
2014-03-29 18:54:20 +00:00
RNBLogSTDERR ( " auto Auto-detect the best launch method to use. \n " ) ;
RNBLogSTDERR ( " posix Launch the executable using posix_spawn. \n " ) ;
RNBLogSTDERR ( " fork Launch the executable using fork and exec. \n " ) ;
2012-02-22 02:18:59 +00:00
# ifdef WITH_SPRINGBOARD
2014-03-29 18:54:20 +00:00
RNBLogSTDERR ( " spring Launch the executable through Springboard. \n " ) ;
# endif
# ifdef WITH_BKS
RNBLogSTDERR ( " backboard Launch the executable through BackBoard Services. \n " ) ;
2010-06-08 16:52:24 +00:00
# endif
exit ( 5 ) ;
}
}
break ;
case ' l ' : // Set Log File
if ( optarg & & optarg [ 0 ] )
{
if ( strcasecmp ( optarg , " stdout " ) = = 0 )
log_file = stdout ;
else if ( strcasecmp ( optarg , " stderr " ) = = 0 )
log_file = stderr ;
else
2011-01-24 03:46:59 +00:00
{
2011-01-23 05:56:20 +00:00
log_file = fopen ( optarg , " w " ) ;
2011-01-24 03:46:59 +00:00
if ( log_file ! = NULL )
setlinebuf ( log_file ) ;
}
2010-06-08 16:52:24 +00:00
if ( log_file = = NULL )
{
const char * errno_str = strerror ( errno ) ;
RNBLogSTDERR ( " Failed to open log file '%s' for writing: errno = %i (%s) " , optarg , errno , errno_str ? errno_str : " unknown error " ) ;
}
}
break ;
case ' f ' : // Log Flags
if ( optarg & & optarg [ 0 ] )
2015-03-09 19:45:23 +00:00
log_flags = static_cast < uint32_t > ( strtoul ( optarg , NULL , 0 ) ) ;
2010-06-08 16:52:24 +00:00
break ;
case ' g ' :
debug = 1 ;
2011-08-10 23:01:39 +00:00
DNBLogSetDebug ( debug ) ;
2010-06-08 16:52:24 +00:00
break ;
case ' t ' :
g_applist_opt = 1 ;
break ;
case ' k ' :
g_lockdown_opt = 1 ;
break ;
case ' r ' :
2013-11-09 00:33:46 +00:00
// Do nothing, native regs is the default these days
2010-06-08 16:52:24 +00:00
break ;
2013-12-05 22:58:22 +00:00
case ' R ' :
reverse_connect = true ;
break ;
2010-06-08 16:52:24 +00:00
case ' v ' :
DNBLogSetVerbose ( 1 ) ;
break ;
case ' s ' :
2011-02-24 22:24:29 +00:00
ctx . GetSTDIN ( ) . assign ( optarg ) ;
ctx . GetSTDOUT ( ) . assign ( optarg ) ;
ctx . GetSTDERR ( ) . assign ( optarg ) ;
2011-01-22 23:43:18 +00:00
break ;
case ' I ' :
2011-02-24 22:24:29 +00:00
ctx . GetSTDIN ( ) . assign ( optarg ) ;
2011-01-22 23:43:18 +00:00
break ;
case ' O ' :
2011-02-24 22:24:29 +00:00
ctx . GetSTDOUT ( ) . assign ( optarg ) ;
2011-01-22 23:43:18 +00:00
break ;
case ' E ' :
2011-02-24 22:24:29 +00:00
ctx . GetSTDERR ( ) . assign ( optarg ) ;
2010-06-08 16:52:24 +00:00
break ;
2010-12-03 18:46:09 +00:00
case ' n ' :
no_stdio = true ;
break ;
2010-06-08 16:52:24 +00:00
case ' S ' :
// Put debugserver into a new session. Terminals group processes
// into sessions and when a special terminal key sequences
// (like control+c) are typed they can cause signals to go out to
// all processes in a session. Using this --setsid (-S) option
// will cause debugserver to run in its own sessions and be free
// from such issues.
//
// This is useful when debugserver is spawned from a command
// line application that uses debugserver to do the debugging,
// yet that application doesn't want debugserver receiving the
// signals sent to the session (i.e. dying when anyone hits ^C).
setsid ( ) ;
break ;
2010-08-31 18:35:14 +00:00
case ' D ' :
g_disable_aslr = 1 ;
break ;
2011-03-20 04:57:14 +00:00
case ' p ' :
start_mode = eRNBRunLoopModePlatformMode ;
break ;
2011-04-12 05:54:46 +00:00
2013-12-10 19:36:45 +00:00
case ' u ' :
unix_socket_name . assign ( optarg ) ;
break ;
2013-12-04 19:19:12 +00:00
case ' P ' :
named_pipe_path . assign ( optarg ) ;
2011-04-12 05:54:46 +00:00
break ;
2013-12-10 19:36:45 +00:00
2014-06-18 18:26:50 +00:00
case ' e ' :
// Pass a single specified environment variable down to the process that gets launched
remote - > Context ( ) . PushEnvironment ( optarg ) ;
break ;
case ' F ' :
// Pass the current environment down to the process that gets launched
{
char * * host_env = * _NSGetEnviron ( ) ;
char * env_entry ;
size_t i ;
for ( i = 0 ; ( env_entry = host_env [ i ] ) ! = NULL ; + + i )
remote - > Context ( ) . PushEnvironment ( env_entry ) ;
}
break ;
2010-06-08 16:52:24 +00:00
}
}
2010-11-18 05:57:03 +00:00
if ( arch_name . empty ( ) )
{
2011-02-24 22:24:29 +00:00
# if defined (__arm__)
2010-11-18 05:57:03 +00:00
arch_name . assign ( " arm " ) ;
# endif
}
2010-12-01 22:45:40 +00:00
else
{
DNBSetArchitecture ( arch_name . c_str ( ) ) ;
}
2010-06-08 16:52:24 +00:00
2011-02-24 22:24:29 +00:00
// if (arch_name.empty())
// {
// fprintf(stderr, "error: no architecture was specified\n");
// exit (8);
// }
2013-04-04 20:35:24 +00:00
// Skip any options we consumed with getopt_long_only
2010-06-08 16:52:24 +00:00
argc - = optind ;
argv + = optind ;
2011-01-23 05:56:20 +00:00
if ( ! working_dir . empty ( ) )
2011-01-22 23:43:18 +00:00
{
2011-01-23 05:56:20 +00:00
if ( remote - > Context ( ) . SetWorkingDirectory ( working_dir . c_str ( ) ) = = false )
2011-01-22 23:43:18 +00:00
{
2011-01-23 05:56:20 +00:00
RNBLogSTDERR ( " error: working directory doesn't exist '%s'. \n " , working_dir . c_str ( ) ) ;
2011-01-22 23:43:18 +00:00
exit ( 8 ) ;
}
}
2014-02-25 04:53:13 +00:00
remote - > Context ( ) . SetDetachOnError ( g_detach_on_error ) ;
2011-01-22 23:43:18 +00:00
remote - > Initialize ( ) ;
2010-11-18 05:57:03 +00:00
2010-06-08 16:52:24 +00:00
// It is ok for us to set NULL as the logfile (this will disable any logging)
if ( log_file ! = NULL )
{
DNBLogSetLogCallback ( FileLogCallback , log_file ) ;
// If our log file was set, yet we have no log flags, log everything!
if ( log_flags = = 0 )
log_flags = LOG_ALL | LOG_RNB_ALL ;
DNBLogSetLogMask ( log_flags ) ;
}
else
{
// Enable DNB logging
DNBLogSetLogCallback ( ASLLogCallback , NULL ) ;
DNBLogSetLogMask ( log_flags ) ;
}
if ( DNBLogEnabled ( ) )
{
for ( i = 0 ; i < argc ; i + + )
DNBLogDebug ( " argv[%i] = %s " , i , argv [ i ] ) ;
}
// as long as we're dropping remotenub in as a replacement for gdbserver,
// explicitly note that this is not gdbserver.
2014-03-13 18:30:04 +00:00
RNBLogSTDOUT ( " %s-%s %sfor %s. \n " ,
2010-06-08 16:52:24 +00:00
DEBUGSERVER_PROGRAM_NAME ,
2014-03-13 18:30:04 +00:00
DEBUGSERVER_VERSION_STR ,
2010-06-08 16:52:24 +00:00
compile_options . c_str ( ) ,
RNB_ARCH ) ;
2013-12-05 22:58:22 +00:00
std : : string host ;
int port = INT32_MAX ;
2010-06-08 16:52:24 +00:00
char str [ PATH_MAX ] ;
2012-07-17 03:23:13 +00:00
str [ 0 ] = ' \0 ' ;
2010-06-08 16:52:24 +00:00
if ( g_lockdown_opt = = 0 & & g_applist_opt = = 0 )
{
// Make sure we at least have port
if ( argc < 1 )
{
show_usage_and_exit ( 1 ) ;
}
// accept 'localhost:' prefix on port number
2013-12-05 22:58:22 +00:00
int items_scanned = : : sscanf ( argv [ 0 ] , " %[^:]:%i " , str , & port ) ;
2010-06-08 16:52:24 +00:00
if ( items_scanned = = 2 )
{
2013-12-05 22:58:22 +00:00
host = str ;
DNBLogDebug ( " host = '%s' port = %i " , host . c_str ( ) , port ) ;
2010-06-08 16:52:24 +00:00
}
else
{
2013-06-06 22:44:19 +00:00
// No hostname means "localhost"
2013-12-05 22:58:22 +00:00
int items_scanned = : : sscanf ( argv [ 0 ] , " %i " , & port ) ;
2013-06-06 22:44:19 +00:00
if ( items_scanned = = 1 )
{
2014-02-27 19:38:18 +00:00
host = " 127.0.0.1 " ;
2013-12-05 22:58:22 +00:00
DNBLogDebug ( " host = '%s' port = %i " , host . c_str ( ) , port ) ;
2013-06-06 22:44:19 +00:00
}
else if ( argv [ 0 ] [ 0 ] = = ' / ' )
{
2013-12-05 22:58:22 +00:00
port = INT32_MAX ;
2013-06-06 22:44:19 +00:00
strncpy ( str , argv [ 0 ] , sizeof ( str ) ) ;
}
else
{
show_usage_and_exit ( 2 ) ;
}
2010-06-08 16:52:24 +00:00
}
// We just used the 'host:port' or the '/path/file' arg...
argc - - ;
argv + + ;
}
// If we know we're waiting to attach, we don't need any of this other info.
2011-03-20 04:57:14 +00:00
if ( start_mode ! = eRNBRunLoopModeInferiorAttaching & &
start_mode ! = eRNBRunLoopModePlatformMode )
2010-06-08 16:52:24 +00:00
{
if ( argc = = 0 | | g_lockdown_opt )
{
if ( g_lockdown_opt ! = 0 )
{
// Work around for SIGPIPE crashes due to posix_spawn issue.
// We have to close STDOUT and STDERR, else the first time we
// try and do any, we get SIGPIPE and die as posix_spawn is
// doing bad things with our file descriptors at the moment.
int null = open ( " /dev/null " , O_RDWR ) ;
dup2 ( null , STDOUT_FILENO ) ;
dup2 ( null , STDERR_FILENO ) ;
}
else if ( g_applist_opt ! = 0 )
{
// List all applications we are able to see
std : : string applist_plist ;
int err = ListApplications ( applist_plist , false , false ) ;
if ( err = = 0 )
{
fputs ( applist_plist . c_str ( ) , stdout ) ;
}
else
{
RNBLogSTDERR ( " error: ListApplications returned error %i \n " , err ) ;
}
// Exit with appropriate error if we were asked to list the applications
// with no other args were given (and we weren't trying to do this over
// lockdown)
return err ;
}
DNBLogDebug ( " Get args from remote protocol... " ) ;
start_mode = eRNBRunLoopModeGetStartModeFromRemoteProtocol ;
}
else
{
start_mode = eRNBRunLoopModeInferiorLaunching ;
// Fill in the argv array in the context from the rest of our args.
// Skip the name of this executable and the port number
for ( int i = 0 ; i < argc ; i + + )
{
DNBLogDebug ( " inferior_argv[%i] = '%s' " , i , argv [ i ] ) ;
ctx . PushArgument ( argv [ i ] ) ;
}
}
}
if ( start_mode = = eRNBRunLoopModeExit )
return - 1 ;
RNBRunLoopMode mode = start_mode ;
char err_str [ 1024 ] = { ' \0 ' } ;
while ( mode ! = eRNBRunLoopModeExit )
{
switch ( mode )
{
case eRNBRunLoopModeGetStartModeFromRemoteProtocol :
2012-02-22 02:18:59 +00:00
# ifdef WITH_LOCKDOWN
2010-06-08 16:52:24 +00:00
if ( g_lockdown_opt )
{
2011-01-22 23:43:18 +00:00
if ( ! remote - > Comm ( ) . IsConnected ( ) )
2010-06-08 16:52:24 +00:00
{
2011-01-22 23:43:18 +00:00
if ( remote - > Comm ( ) . ConnectToService ( ) ! = rnb_success )
2010-06-08 16:52:24 +00:00
{
RNBLogSTDERR ( " Failed to get connection from a remote gdb process. \n " ) ;
mode = eRNBRunLoopModeExit ;
}
else if ( g_applist_opt ! = 0 )
{
// List all applications we are able to see
std : : string applist_plist ;
if ( ListApplications ( applist_plist , false , false ) = = 0 )
{
DNBLogDebug ( " Task list: %s " , applist_plist . c_str ( ) ) ;
2011-01-22 23:43:18 +00:00
remote - > Comm ( ) . Write ( applist_plist . c_str ( ) , applist_plist . size ( ) ) ;
2010-06-08 16:52:24 +00:00
// Issue a read that will never yield any data until the other side
// closes the socket so this process doesn't just exit and cause the
// socket to close prematurely on the other end and cause data loss.
std : : string buf ;
2011-01-22 23:43:18 +00:00
remote - > Comm ( ) . Read ( buf ) ;
2010-06-08 16:52:24 +00:00
}
2011-01-22 23:43:18 +00:00
remote - > Comm ( ) . Disconnect ( false ) ;
2010-06-08 16:52:24 +00:00
mode = eRNBRunLoopModeExit ;
break ;
}
else
{
// Start watching for remote packets
2011-01-22 23:43:18 +00:00
remote - > StartReadRemoteDataThread ( ) ;
2010-06-08 16:52:24 +00:00
}
}
}
else
# endif
2013-12-05 22:58:22 +00:00
if ( port ! = INT32_MAX )
2011-03-20 04:57:14 +00:00
{
2013-12-10 19:36:45 +00:00
if ( ! ConnectRemote ( remote , host . c_str ( ) , port , reverse_connect , named_pipe_path . c_str ( ) , unix_socket_name . c_str ( ) ) )
2011-03-20 04:57:14 +00:00
mode = eRNBRunLoopModeExit ;
}
else if ( str [ 0 ] = = ' / ' )
{
if ( remote - > Comm ( ) . OpenFile ( str ) )
mode = eRNBRunLoopModeExit ;
}
2010-06-08 16:52:24 +00:00
if ( mode ! = eRNBRunLoopModeExit )
{
RNBLogSTDOUT ( " Got a connection, waiting for process information for launching or attaching. \n " ) ;
2011-01-22 23:43:18 +00:00
mode = RNBRunLoopGetStartModeFromRemote ( remote ) ;
2010-06-08 16:52:24 +00:00
}
break ;
case eRNBRunLoopModeInferiorAttaching :
if ( ! waitfor_pid_name . empty ( ) )
{
// Set our end wait time if we are using a waitfor-duration
// option that may have been specified
struct timespec attach_timeout_abstime , * timeout_ptr = NULL ;
if ( waitfor_duration ! = 0 )
{
DNBTimer : : OffsetTimeOfDay ( & attach_timeout_abstime , waitfor_duration , 0 ) ;
timeout_ptr = & attach_timeout_abstime ;
}
nub_launch_flavor_t launch_flavor = g_launch_flavor ;
if ( launch_flavor = = eLaunchFlavorDefault )
{
// Our default launch method is posix spawn
launch_flavor = eLaunchFlavorPosixSpawn ;
2014-03-29 18:54:20 +00:00
# if defined WITH_BKS
// Check if we have an app bundle, if so launch using SpringBoard.
if ( waitfor_pid_name . find ( " .app " ) ! = std : : string : : npos )
{
launch_flavor = eLaunchFlavorBKS ;
}
# elif defined WITH_SPRINGBOARD
2010-06-08 16:52:24 +00:00
// Check if we have an app bundle, if so launch using SpringBoard.
if ( waitfor_pid_name . find ( " .app " ) ! = std : : string : : npos )
{
launch_flavor = eLaunchFlavorSpringBoard ;
}
# endif
}
ctx . SetLaunchFlavor ( launch_flavor ) ;
2012-07-20 21:37:13 +00:00
bool ignore_existing = false ;
2013-02-25 19:31:37 +00:00
RNBLogSTDOUT ( " Waiting to attach to process %s... \n " , waitfor_pid_name . c_str ( ) ) ;
2012-07-20 21:37:13 +00:00
nub_process_t pid = DNBProcessAttachWait ( waitfor_pid_name . c_str ( ) , launch_flavor , ignore_existing , timeout_ptr , waitfor_interval , err_str , sizeof ( err_str ) ) ;
2010-06-08 16:52:24 +00:00
g_pid = pid ;
if ( pid = = INVALID_NUB_PROCESS )
{
ctx . LaunchStatus ( ) . SetError ( - 1 , DNBError : : Generic ) ;
if ( err_str [ 0 ] )
ctx . LaunchStatus ( ) . SetErrorString ( err_str ) ;
2013-02-25 19:31:37 +00:00
RNBLogSTDERR ( " error: failed to attach to process named: \" %s \" %s \n " , waitfor_pid_name . c_str ( ) , err_str ) ;
2010-06-08 16:52:24 +00:00
mode = eRNBRunLoopModeExit ;
}
else
{
ctx . SetProcessID ( pid ) ;
mode = eRNBRunLoopModeInferiorExecuting ;
}
}
else if ( attach_pid ! = INVALID_NUB_PROCESS )
{
RNBLogSTDOUT ( " Attaching to process %i... \n " , attach_pid ) ;
nub_process_t attached_pid ;
2011-01-22 23:43:18 +00:00
mode = RNBRunLoopLaunchAttaching ( remote , attach_pid , attached_pid ) ;
2010-06-08 16:52:24 +00:00
if ( mode ! = eRNBRunLoopModeInferiorExecuting )
{
const char * error_str = remote - > Context ( ) . LaunchStatus ( ) . AsString ( ) ;
RNBLogSTDERR ( " error: failed to attach process %i: %s \n " , attach_pid , error_str ? error_str : " unknown error. " ) ;
mode = eRNBRunLoopModeExit ;
}
}
else if ( ! attach_pid_name . empty ( ) )
{
struct timespec attach_timeout_abstime , * timeout_ptr = NULL ;
if ( waitfor_duration ! = 0 )
{
DNBTimer : : OffsetTimeOfDay ( & attach_timeout_abstime , waitfor_duration , 0 ) ;
timeout_ptr = & attach_timeout_abstime ;
}
2013-02-25 19:31:37 +00:00
RNBLogSTDOUT ( " Attaching to process %s... \n " , attach_pid_name . c_str ( ) ) ;
2010-06-08 16:52:24 +00:00
nub_process_t pid = DNBProcessAttachByName ( attach_pid_name . c_str ( ) , timeout_ptr , err_str , sizeof ( err_str ) ) ;
g_pid = pid ;
if ( pid = = INVALID_NUB_PROCESS )
{
ctx . LaunchStatus ( ) . SetError ( - 1 , DNBError : : Generic ) ;
if ( err_str [ 0 ] )
ctx . LaunchStatus ( ) . SetErrorString ( err_str ) ;
2013-02-25 19:31:37 +00:00
RNBLogSTDERR ( " error: failed to attach to process named: \" %s \" %s \n " , waitfor_pid_name . c_str ( ) , err_str ) ;
2010-06-08 16:52:24 +00:00
mode = eRNBRunLoopModeExit ;
}
else
{
ctx . SetProcessID ( pid ) ;
mode = eRNBRunLoopModeInferiorExecuting ;
}
}
else
{
2013-02-25 19:31:37 +00:00
RNBLogSTDERR ( " error: asked to attach with empty name and invalid PID. \n " ) ;
2010-06-08 16:52:24 +00:00
mode = eRNBRunLoopModeExit ;
}
if ( mode ! = eRNBRunLoopModeExit )
{
2013-12-05 22:58:22 +00:00
if ( port ! = INT32_MAX )
2010-06-08 16:52:24 +00:00
{
2013-12-10 19:36:45 +00:00
if ( ! ConnectRemote ( remote , host . c_str ( ) , port , reverse_connect , named_pipe_path . c_str ( ) , unix_socket_name . c_str ( ) ) )
2010-06-08 16:52:24 +00:00
mode = eRNBRunLoopModeExit ;
}
else if ( str [ 0 ] = = ' / ' )
{
2011-01-22 23:43:18 +00:00
if ( remote - > Comm ( ) . OpenFile ( str ) )
2010-06-08 16:52:24 +00:00
mode = eRNBRunLoopModeExit ;
}
if ( mode ! = eRNBRunLoopModeExit )
2013-02-25 19:31:37 +00:00
RNBLogSTDOUT ( " Waiting for debugger instructions for process %d. \n " , attach_pid ) ;
2010-06-08 16:52:24 +00:00
}
break ;
case eRNBRunLoopModeInferiorLaunching :
{
2011-01-22 23:43:18 +00:00
mode = RNBRunLoopLaunchInferior ( remote ,
2011-02-24 22:24:29 +00:00
ctx . GetSTDINPath ( ) ,
ctx . GetSTDOUTPath ( ) ,
ctx . GetSTDERRPath ( ) ,
2011-01-22 23:43:18 +00:00
no_stdio ) ;
2010-06-08 16:52:24 +00:00
2011-01-22 23:43:18 +00:00
if ( mode = = eRNBRunLoopModeInferiorExecuting )
{
2013-12-05 22:58:22 +00:00
if ( port ! = INT32_MAX )
2011-01-22 23:43:18 +00:00
{
2013-12-10 19:36:45 +00:00
if ( ! ConnectRemote ( remote , host . c_str ( ) , port , reverse_connect , named_pipe_path . c_str ( ) , unix_socket_name . c_str ( ) ) )
2011-01-22 23:43:18 +00:00
mode = eRNBRunLoopModeExit ;
}
else if ( str [ 0 ] = = ' / ' )
{
if ( remote - > Comm ( ) . OpenFile ( str ) )
mode = eRNBRunLoopModeExit ;
}
if ( mode ! = eRNBRunLoopModeExit )
2014-02-25 19:57:47 +00:00
{
const char * proc_name = " <unknown> " ;
if ( ctx . ArgumentCount ( ) > 0 )
proc_name = ctx . ArgumentAtIndex ( 0 ) ;
RNBLogSTDOUT ( " Got a connection, launched process %s (pid = %d). \n " , proc_name , ctx . ProcessID ( ) ) ;
}
2011-01-22 23:43:18 +00:00
}
else
{
const char * error_str = remote - > Context ( ) . LaunchStatus ( ) . AsString ( ) ;
2012-11-01 02:02:59 +00:00
RNBLogSTDERR ( " error: failed to launch process %s: %s \n " , argv_sub_zero , error_str ? error_str : " unknown error. " ) ;
2011-01-22 23:43:18 +00:00
}
2010-06-08 16:52:24 +00:00
}
break ;
case eRNBRunLoopModeInferiorExecuting :
2011-01-22 23:43:18 +00:00
mode = RNBRunLoopInferiorExecuting ( remote ) ;
2010-06-08 16:52:24 +00:00
break ;
2011-03-20 04:57:14 +00:00
case eRNBRunLoopModePlatformMode :
2013-12-05 22:58:22 +00:00
if ( port ! = INT32_MAX )
2011-03-20 04:57:14 +00:00
{
2013-12-10 19:36:45 +00:00
if ( ! ConnectRemote ( remote , host . c_str ( ) , port , reverse_connect , named_pipe_path . c_str ( ) , unix_socket_name . c_str ( ) ) )
2011-03-20 04:57:14 +00:00
mode = eRNBRunLoopModeExit ;
}
else if ( str [ 0 ] = = ' / ' )
{
if ( remote - > Comm ( ) . OpenFile ( str ) )
mode = eRNBRunLoopModeExit ;
}
if ( mode ! = eRNBRunLoopModeExit )
mode = RNBRunLoopPlatform ( remote ) ;
break ;
2010-06-08 16:52:24 +00:00
default :
mode = eRNBRunLoopModeExit ;
case eRNBRunLoopModeExit :
break ;
}
}
2011-01-22 23:43:18 +00:00
remote - > StopReadRemoteDataThread ( ) ;
remote - > Context ( ) . SetProcessID ( INVALID_NUB_PROCESS ) ;
2013-02-25 19:31:37 +00:00
RNBLogSTDOUT ( " Exiting. \n " ) ;
2010-06-08 16:52:24 +00:00
return 0 ;
}