debugserver.cpp revision 4fdf7602bedd8be648f3c549074cf13d90a05f03
1//===-- debugserver.cpp -----------------------------------------*- C++ -*-===// 2// 3// The LLVM Compiler Infrastructure 4// 5// This file is distributed under the University of Illinois Open Source 6// License. See LICENSE.TXT for details. 7// 8//===----------------------------------------------------------------------===// 9 10#include <sys/socket.h> 11#include <sys/types.h> 12#include <errno.h> 13#include <getopt.h> 14#include <netinet/in.h> 15#include <sys/select.h> 16#include <sys/sysctl.h> 17#include <string> 18#include <vector> 19#include <asl.h> 20 21#include "CFString.h" 22#include "DNB.h" 23#include "DNBLog.h" 24#include "DNBTimer.h" 25#include "PseudoTerminal.h" 26#include "RNBContext.h" 27#include "RNBServices.h" 28#include "RNBSocket.h" 29#include "RNBRemote.h" 30#include "SysSignal.h" 31 32// Global PID in case we get a signal and need to stop the process... 33nub_process_t g_pid = INVALID_NUB_PROCESS; 34 35//---------------------------------------------------------------------- 36// Run loop modes which determine which run loop function will be called 37//---------------------------------------------------------------------- 38typedef enum 39{ 40 eRNBRunLoopModeInvalid = 0, 41 eRNBRunLoopModeGetStartModeFromRemoteProtocol, 42 eRNBRunLoopModeInferiorAttaching, 43 eRNBRunLoopModeInferiorLaunching, 44 eRNBRunLoopModeInferiorExecuting, 45 eRNBRunLoopModePlatformMode, 46 eRNBRunLoopModeExit 47} RNBRunLoopMode; 48 49 50//---------------------------------------------------------------------- 51// Global Variables 52//---------------------------------------------------------------------- 53RNBRemoteSP g_remoteSP; 54static int g_lockdown_opt = 0; 55static int g_applist_opt = 0; 56static nub_launch_flavor_t g_launch_flavor = eLaunchFlavorDefault; 57int g_disable_aslr = 0; 58 59int g_isatty = 0; 60 61#define RNBLogSTDOUT(fmt, ...) do { if (g_isatty) { fprintf(stdout, fmt, ## __VA_ARGS__); } else { _DNBLog(0, fmt, ## __VA_ARGS__); } } while (0) 62#define RNBLogSTDERR(fmt, ...) do { if (g_isatty) { fprintf(stderr, fmt, ## __VA_ARGS__); } else { _DNBLog(0, fmt, ## __VA_ARGS__); } } while (0) 63 64//---------------------------------------------------------------------- 65// Get our program path and arguments from the remote connection. 66// We will need to start up the remote connection without a PID, get the 67// arguments, wait for the new process to finish launching and hit its 68// entry point, and then return the run loop mode that should come next. 69//---------------------------------------------------------------------- 70RNBRunLoopMode 71RNBRunLoopGetStartModeFromRemote (RNBRemote* remote) 72{ 73 std::string packet; 74 75 if (remote) 76 { 77 RNBContext& ctx = remote->Context(); 78 uint32_t event_mask = RNBContext::event_read_packet_available | 79 RNBContext::event_read_thread_exiting; 80 81 // Spin waiting to get the A packet. 82 while (1) 83 { 84 DNBLogThreadedIf (LOG_RNB_MAX, "%s ctx.Events().WaitForSetEvents( 0x%08x ) ...",__FUNCTION__, event_mask); 85 nub_event_t set_events = ctx.Events().WaitForSetEvents(event_mask); 86 DNBLogThreadedIf (LOG_RNB_MAX, "%s ctx.Events().WaitForSetEvents( 0x%08x ) => 0x%08x", __FUNCTION__, event_mask, set_events); 87 88 if (set_events & RNBContext::event_read_thread_exiting) 89 { 90 RNBLogSTDERR ("error: packet read thread exited."); 91 return eRNBRunLoopModeExit; 92 } 93 94 if (set_events & RNBContext::event_read_packet_available) 95 { 96 rnb_err_t err = rnb_err; 97 RNBRemote::PacketEnum type; 98 99 err = remote->HandleReceivedPacket (&type); 100 101 // check if we tried to attach to a process 102 if (type == RNBRemote::vattach || type == RNBRemote::vattachwait) 103 { 104 if (err == rnb_success) 105 return eRNBRunLoopModeInferiorExecuting; 106 else 107 { 108 RNBLogSTDERR ("error: attach failed."); 109 return eRNBRunLoopModeExit; 110 } 111 } 112 113 if (err == rnb_success) 114 { 115 // If we got our arguments we are ready to launch using the arguments 116 // and any environment variables we received. 117 if (type == RNBRemote::set_argv) 118 { 119 return eRNBRunLoopModeInferiorLaunching; 120 } 121 } 122 else if (err == rnb_not_connected) 123 { 124 RNBLogSTDERR ("error: connection lost."); 125 return eRNBRunLoopModeExit; 126 } 127 else 128 { 129 // a catch all for any other gdb remote packets that failed 130 DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s Error getting packet.",__FUNCTION__); 131 continue; 132 } 133 134 DNBLogThreadedIf (LOG_RNB_MINIMAL, "#### %s", __FUNCTION__); 135 } 136 else 137 { 138 DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s Connection closed before getting \"A\" packet.", __FUNCTION__); 139 return eRNBRunLoopModeExit; 140 } 141 } 142 } 143 return eRNBRunLoopModeExit; 144} 145 146 147//---------------------------------------------------------------------- 148// This run loop mode will wait for the process to launch and hit its 149// entry point. It will currently ignore all events except for the 150// process state changed event, where it watches for the process stopped 151// or crash process state. 152//---------------------------------------------------------------------- 153RNBRunLoopMode 154RNBRunLoopLaunchInferior (RNBRemote *remote, const char *stdin_path, const char *stdout_path, const char *stderr_path, bool no_stdio) 155{ 156 RNBContext& ctx = remote->Context(); 157 158 // The Process stuff takes a c array, the RNBContext has a vector... 159 // So make up a c array. 160 161 DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s Launching '%s'...", __FUNCTION__, ctx.ArgumentAtIndex(0)); 162 163 size_t inferior_argc = ctx.ArgumentCount(); 164 // Initialize inferior_argv with inferior_argc + 1 NULLs 165 std::vector<const char *> inferior_argv(inferior_argc + 1, NULL); 166 167 size_t i; 168 for (i = 0; i < inferior_argc; i++) 169 inferior_argv[i] = ctx.ArgumentAtIndex(i); 170 171 // Pass the environment array the same way: 172 173 size_t inferior_envc = ctx.EnvironmentCount(); 174 // Initialize inferior_argv with inferior_argc + 1 NULLs 175 std::vector<const char *> inferior_envp(inferior_envc + 1, NULL); 176 177 for (i = 0; i < inferior_envc; i++) 178 inferior_envp[i] = ctx.EnvironmentAtIndex(i); 179 180 // Our launch type hasn't been set to anything concrete, so we need to 181 // figure our how we are going to launch automatically. 182 183 nub_launch_flavor_t launch_flavor = g_launch_flavor; 184 if (launch_flavor == eLaunchFlavorDefault) 185 { 186 // Our default launch method is posix spawn 187 launch_flavor = eLaunchFlavorPosixSpawn; 188 189#if defined (__arm__) 190 // Check if we have an app bundle, if so launch using SpringBoard. 191 if (strstr(inferior_argv[0], ".app")) 192 { 193 launch_flavor = eLaunchFlavorSpringBoard; 194 } 195#endif 196 } 197 198 ctx.SetLaunchFlavor(launch_flavor); 199 char resolved_path[PATH_MAX]; 200 201 // If we fail to resolve the path to our executable, then just use what we 202 // were given and hope for the best 203 if ( !DNBResolveExecutablePath (inferior_argv[0], resolved_path, sizeof(resolved_path)) ) 204 ::strncpy(resolved_path, inferior_argv[0], sizeof(resolved_path)); 205 206 char launch_err_str[PATH_MAX]; 207 launch_err_str[0] = '\0'; 208 const char * cwd = (ctx.GetWorkingDirPath() != NULL ? ctx.GetWorkingDirPath() 209 : ctx.GetWorkingDirectory()); 210 nub_process_t pid = DNBProcessLaunch (resolved_path, 211 &inferior_argv[0], 212 &inferior_envp[0], 213 cwd, 214 stdin_path, 215 stdout_path, 216 stderr_path, 217 no_stdio, 218 launch_flavor, 219 g_disable_aslr, 220 launch_err_str, 221 sizeof(launch_err_str)); 222 223 g_pid = pid; 224 225 if (pid == INVALID_NUB_PROCESS && strlen(launch_err_str) > 0) 226 { 227 DNBLogThreaded ("%s DNBProcessLaunch() returned error: '%s'", __FUNCTION__, launch_err_str); 228 ctx.LaunchStatus().SetError(-1, DNBError::Generic); 229 ctx.LaunchStatus().SetErrorString(launch_err_str); 230 } 231 else 232 { 233 ctx.LaunchStatus().Clear(); 234 } 235 236 if (remote->Comm().IsConnected()) 237 { 238 // It we are connected already, the next thing gdb will do is ask 239 // whether the launch succeeded, and if not, whether there is an 240 // error code. So we need to fetch one packet from gdb before we wait 241 // on the stop from the target. 242 243 uint32_t event_mask = RNBContext::event_read_packet_available; 244 nub_event_t set_events = ctx.Events().WaitForSetEvents(event_mask); 245 246 if (set_events & RNBContext::event_read_packet_available) 247 { 248 rnb_err_t err = rnb_err; 249 RNBRemote::PacketEnum type; 250 251 err = remote->HandleReceivedPacket (&type); 252 253 if (err != rnb_success) 254 { 255 DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s Error getting packet.", __FUNCTION__); 256 return eRNBRunLoopModeExit; 257 } 258 if (type != RNBRemote::query_launch_success) 259 { 260 DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s Didn't get the expected qLaunchSuccess packet.", __FUNCTION__); 261 } 262 } 263 } 264 265 while (pid != INVALID_NUB_PROCESS) 266 { 267 // Wait for process to start up and hit entry point 268 DNBLogThreadedIf (LOG_RNB_EVENTS, "%s DNBProcessWaitForEvent (%4.4x, eEventProcessRunningStateChanged | eEventProcessStoppedStateChanged, true, INFINITE)...", __FUNCTION__, pid); 269 nub_event_t set_events = DNBProcessWaitForEvents (pid, eEventProcessRunningStateChanged | eEventProcessStoppedStateChanged, true, NULL); 270 DNBLogThreadedIf (LOG_RNB_EVENTS, "%s DNBProcessWaitForEvent (%4.4x, eEventProcessRunningStateChanged | eEventProcessStoppedStateChanged, true, INFINITE) => 0x%8.8x", __FUNCTION__, pid, set_events); 271 272 if (set_events == 0) 273 { 274 pid = INVALID_NUB_PROCESS; 275 g_pid = pid; 276 } 277 else 278 { 279 if (set_events & (eEventProcessRunningStateChanged | eEventProcessStoppedStateChanged)) 280 { 281 nub_state_t pid_state = DNBProcessGetState (pid); 282 DNBLogThreadedIf (LOG_RNB_EVENTS, "%s process %4.4x state changed (eEventProcessStateChanged): %s", __FUNCTION__, pid, DNBStateAsString(pid_state)); 283 284 switch (pid_state) 285 { 286 default: 287 case eStateInvalid: 288 case eStateUnloaded: 289 case eStateAttaching: 290 case eStateLaunching: 291 case eStateSuspended: 292 break; // Ignore 293 294 case eStateRunning: 295 case eStateStepping: 296 // Still waiting to stop at entry point... 297 break; 298 299 case eStateStopped: 300 case eStateCrashed: 301 ctx.SetProcessID(pid); 302 return eRNBRunLoopModeInferiorExecuting; 303 304 case eStateDetached: 305 case eStateExited: 306 pid = INVALID_NUB_PROCESS; 307 g_pid = pid; 308 return eRNBRunLoopModeExit; 309 } 310 } 311 312 DNBProcessResetEvents(pid, set_events); 313 } 314 } 315 316 return eRNBRunLoopModeExit; 317} 318 319 320//---------------------------------------------------------------------- 321// This run loop mode will wait for the process to launch and hit its 322// entry point. It will currently ignore all events except for the 323// process state changed event, where it watches for the process stopped 324// or crash process state. 325//---------------------------------------------------------------------- 326RNBRunLoopMode 327RNBRunLoopLaunchAttaching (RNBRemote *remote, nub_process_t attach_pid, nub_process_t& pid) 328{ 329 RNBContext& ctx = remote->Context(); 330 331 DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s Attaching to pid %i...", __FUNCTION__, attach_pid); 332 char err_str[1024]; 333 pid = DNBProcessAttach (attach_pid, NULL, err_str, sizeof(err_str)); 334 g_pid = pid; 335 336 if (pid == INVALID_NUB_PROCESS) 337 { 338 ctx.LaunchStatus().SetError(-1, DNBError::Generic); 339 if (err_str[0]) 340 ctx.LaunchStatus().SetErrorString(err_str); 341 return eRNBRunLoopModeExit; 342 } 343 else 344 { 345 ctx.SetProcessID(pid); 346 return eRNBRunLoopModeInferiorExecuting; 347 } 348} 349 350//---------------------------------------------------------------------- 351// Watch for signals: 352// SIGINT: so we can halt our inferior. (disabled for now) 353// SIGPIPE: in case our child process dies 354//---------------------------------------------------------------------- 355int g_sigint_received = 0; 356int g_sigpipe_received = 0; 357void 358signal_handler(int signo) 359{ 360 DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s (%s)", __FUNCTION__, SysSignal::Name(signo)); 361 362 switch (signo) 363 { 364 case SIGINT: 365 g_sigint_received++; 366 if (g_pid != INVALID_NUB_PROCESS) 367 { 368 // Only send a SIGINT once... 369 if (g_sigint_received == 1) 370 { 371 switch (DNBProcessGetState (g_pid)) 372 { 373 case eStateRunning: 374 case eStateStepping: 375 DNBProcessSignal (g_pid, SIGSTOP); 376 return; 377 } 378 } 379 } 380 exit (SIGINT); 381 break; 382 383 case SIGPIPE: 384 g_sigpipe_received = 1; 385 break; 386 } 387} 388 389// Return the new run loop mode based off of the current process state 390RNBRunLoopMode 391HandleProcessStateChange (RNBRemote *remote, bool initialize) 392{ 393 RNBContext& ctx = remote->Context(); 394 nub_process_t pid = ctx.ProcessID(); 395 396 if (pid == INVALID_NUB_PROCESS) 397 { 398 DNBLogThreadedIf (LOG_RNB_MINIMAL, "#### %s error: pid invalid, exiting...", __FUNCTION__); 399 return eRNBRunLoopModeExit; 400 } 401 nub_state_t pid_state = DNBProcessGetState (pid); 402 403 DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s (&remote, initialize=%i) pid_state = %s", __FUNCTION__, (int)initialize, DNBStateAsString (pid_state)); 404 405 switch (pid_state) 406 { 407 case eStateInvalid: 408 case eStateUnloaded: 409 // Something bad happened 410 return eRNBRunLoopModeExit; 411 break; 412 413 case eStateAttaching: 414 case eStateLaunching: 415 return eRNBRunLoopModeInferiorExecuting; 416 417 case eStateSuspended: 418 case eStateCrashed: 419 case eStateStopped: 420 // If we stop due to a signal, so clear the fact that we got a SIGINT 421 // so we can stop ourselves again (but only while our inferior 422 // process is running..) 423 g_sigint_received = 0; 424 if (initialize == false) 425 { 426 // Compare the last stop count to our current notion of a stop count 427 // to make sure we don't notify more than once for a given stop. 428 nub_size_t prev_pid_stop_count = ctx.GetProcessStopCount(); 429 bool pid_stop_count_changed = ctx.SetProcessStopCount(DNBProcessGetStopCount(pid)); 430 if (pid_stop_count_changed) 431 { 432 remote->FlushSTDIO(); 433 434 if (ctx.GetProcessStopCount() == 1) 435 { 436 DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s (&remote, initialize=%i) pid_state = %s pid_stop_count %u (old %u)) Notify??? no, first stop...", __FUNCTION__, (int)initialize, DNBStateAsString (pid_state), ctx.GetProcessStopCount(), prev_pid_stop_count); 437 } 438 else 439 { 440 441 DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s (&remote, initialize=%i) pid_state = %s pid_stop_count %u (old %u)) Notify??? YES!!!", __FUNCTION__, (int)initialize, DNBStateAsString (pid_state), ctx.GetProcessStopCount(), prev_pid_stop_count); 442 remote->NotifyThatProcessStopped (); 443 } 444 } 445 else 446 { 447 DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s (&remote, initialize=%i) pid_state = %s pid_stop_count %u (old %u)) Notify??? skipping...", __FUNCTION__, (int)initialize, DNBStateAsString (pid_state), ctx.GetProcessStopCount(), prev_pid_stop_count); 448 } 449 } 450 return eRNBRunLoopModeInferiorExecuting; 451 452 case eStateStepping: 453 case eStateRunning: 454 return eRNBRunLoopModeInferiorExecuting; 455 456 case eStateExited: 457 remote->HandlePacket_last_signal(NULL); 458 return eRNBRunLoopModeExit; 459 460 } 461 462 // Catch all... 463 return eRNBRunLoopModeExit; 464} 465// This function handles the case where our inferior program is stopped and 466// we are waiting for gdb remote protocol packets. When a packet occurs that 467// makes the inferior run, we need to leave this function with a new state 468// as the return code. 469RNBRunLoopMode 470RNBRunLoopInferiorExecuting (RNBRemote *remote) 471{ 472 DNBLogThreadedIf (LOG_RNB_MINIMAL, "#### %s", __FUNCTION__); 473 RNBContext& ctx = remote->Context(); 474 475 // Init our mode and set 'is_running' based on the current process state 476 RNBRunLoopMode mode = HandleProcessStateChange (remote, true); 477 478 while (ctx.ProcessID() != INVALID_NUB_PROCESS) 479 { 480 481 std::string set_events_str; 482 uint32_t event_mask = ctx.NormalEventBits(); 483 484 if (!ctx.ProcessStateRunning()) 485 { 486 // Clear the stdio bits if we are not running so we don't send any async packets 487 event_mask &= ~RNBContext::event_proc_stdio_available; 488 } 489 490 // We want to make sure we consume all process state changes and have 491 // whomever is notifying us to wait for us to reset the event bit before 492 // continuing. 493 //ctx.Events().SetResetAckMask (RNBContext::event_proc_state_changed); 494 495 DNBLogThreadedIf (LOG_RNB_EVENTS, "%s ctx.Events().WaitForSetEvents(0x%08x) ...",__FUNCTION__, event_mask); 496 nub_event_t set_events = ctx.Events().WaitForSetEvents(event_mask); 497 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)); 498 499 if (set_events) 500 { 501 if ((set_events & RNBContext::event_proc_thread_exiting) || 502 (set_events & RNBContext::event_proc_stdio_available)) 503 { 504 remote->FlushSTDIO(); 505 } 506 507 if (set_events & RNBContext::event_read_packet_available) 508 { 509 // handleReceivedPacket will take care of resetting the 510 // event_read_packet_available events when there are no more... 511 set_events ^= RNBContext::event_read_packet_available; 512 513 if (ctx.ProcessStateRunning()) 514 { 515 if (remote->HandleAsyncPacket() == rnb_not_connected) 516 { 517 // TODO: connect again? Exit? 518 } 519 } 520 else 521 { 522 if (remote->HandleReceivedPacket() == rnb_not_connected) 523 { 524 // TODO: connect again? Exit? 525 } 526 } 527 } 528 529 if (set_events & RNBContext::event_proc_state_changed) 530 { 531 mode = HandleProcessStateChange (remote, false); 532 ctx.Events().ResetEvents(RNBContext::event_proc_state_changed); 533 set_events ^= RNBContext::event_proc_state_changed; 534 } 535 536 if (set_events & RNBContext::event_proc_thread_exiting) 537 { 538 mode = eRNBRunLoopModeExit; 539 } 540 541 if (set_events & RNBContext::event_read_thread_exiting) 542 { 543 // Out remote packet receiving thread exited, exit for now. 544 if (ctx.HasValidProcessID()) 545 { 546 // TODO: We should add code that will leave the current process 547 // in its current state and listen for another connection... 548 if (ctx.ProcessStateRunning()) 549 { 550 DNBProcessKill (ctx.ProcessID()); 551 } 552 } 553 mode = eRNBRunLoopModeExit; 554 } 555 } 556 557 // Reset all event bits that weren't reset for now... 558 if (set_events != 0) 559 ctx.Events().ResetEvents(set_events); 560 561 if (mode != eRNBRunLoopModeInferiorExecuting) 562 break; 563 } 564 565 return mode; 566} 567 568 569RNBRunLoopMode 570RNBRunLoopPlatform (RNBRemote *remote) 571{ 572 RNBRunLoopMode mode = eRNBRunLoopModePlatformMode; 573 RNBContext& ctx = remote->Context(); 574 575 while (mode == eRNBRunLoopModePlatformMode) 576 { 577 std::string set_events_str; 578 const uint32_t event_mask = RNBContext::event_read_packet_available | 579 RNBContext::event_read_thread_exiting; 580 581 DNBLogThreadedIf (LOG_RNB_EVENTS, "%s ctx.Events().WaitForSetEvents(0x%08x) ...",__FUNCTION__, event_mask); 582 nub_event_t set_events = ctx.Events().WaitForSetEvents(event_mask); 583 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)); 584 585 if (set_events) 586 { 587 if (set_events & RNBContext::event_read_packet_available) 588 { 589 if (remote->HandleReceivedPacket() == rnb_not_connected) 590 mode = eRNBRunLoopModeExit; 591 } 592 593 if (set_events & RNBContext::event_read_thread_exiting) 594 { 595 mode = eRNBRunLoopModeExit; 596 } 597 ctx.Events().ResetEvents(set_events); 598 } 599 } 600 return eRNBRunLoopModeExit; 601} 602 603//---------------------------------------------------------------------- 604// Convenience function to set up the remote listening port 605// Returns 1 for success 0 for failure. 606//---------------------------------------------------------------------- 607 608static int 609StartListening (RNBRemote *remote, int listen_port) 610{ 611 if (!remote->Comm().IsConnected()) 612 { 613 RNBLogSTDOUT ("Listening to port %i...\n", listen_port); 614 if (remote->Comm().Listen(listen_port) != rnb_success) 615 { 616 RNBLogSTDERR ("Failed to get connection from a remote gdb process.\n"); 617 return 0; 618 } 619 else 620 { 621 remote->StartReadRemoteDataThread(); 622 } 623 } 624 return 1; 625} 626 627//---------------------------------------------------------------------- 628// ASL Logging callback that can be registered with DNBLogSetLogCallback 629//---------------------------------------------------------------------- 630void 631ASLLogCallback(void *baton, uint32_t flags, const char *format, va_list args) 632{ 633 if (format == NULL) 634 return; 635 static aslmsg g_aslmsg = NULL; 636 if (g_aslmsg == NULL) 637 { 638 g_aslmsg = ::asl_new (ASL_TYPE_MSG); 639 char asl_key_sender[PATH_MAX]; 640 snprintf(asl_key_sender, sizeof(asl_key_sender), "com.apple.%s-%g", DEBUGSERVER_PROGRAM_NAME, DEBUGSERVER_VERSION_NUM); 641 ::asl_set (g_aslmsg, ASL_KEY_SENDER, asl_key_sender); 642 } 643 644 int asl_level; 645 if (flags & DNBLOG_FLAG_FATAL) asl_level = ASL_LEVEL_CRIT; 646 else if (flags & DNBLOG_FLAG_ERROR) asl_level = ASL_LEVEL_ERR; 647 else if (flags & DNBLOG_FLAG_WARNING) asl_level = ASL_LEVEL_WARNING; 648 else if (flags & DNBLOG_FLAG_VERBOSE) asl_level = ASL_LEVEL_WARNING; //ASL_LEVEL_INFO; 649 else asl_level = ASL_LEVEL_WARNING; //ASL_LEVEL_DEBUG; 650 651 ::asl_vlog (NULL, g_aslmsg, asl_level, format, args); 652} 653 654//---------------------------------------------------------------------- 655// FILE based Logging callback that can be registered with 656// DNBLogSetLogCallback 657//---------------------------------------------------------------------- 658void 659FileLogCallback(void *baton, uint32_t flags, const char *format, va_list args) 660{ 661 if (baton == NULL || format == NULL) 662 return; 663 664 ::vfprintf ((FILE *)baton, format, args); 665 ::fprintf ((FILE *)baton, "\n"); 666} 667 668 669void 670show_usage_and_exit (int exit_code) 671{ 672 RNBLogSTDERR ("Usage:\n %s host:port [program-name program-arg1 program-arg2 ...]\n", DEBUGSERVER_PROGRAM_NAME); 673 RNBLogSTDERR (" %s /path/file [program-name program-arg1 program-arg2 ...]\n", DEBUGSERVER_PROGRAM_NAME); 674 RNBLogSTDERR (" %s host:port --attach=<pid>\n", DEBUGSERVER_PROGRAM_NAME); 675 RNBLogSTDERR (" %s /path/file --attach=<pid>\n", DEBUGSERVER_PROGRAM_NAME); 676 RNBLogSTDERR (" %s host:port --attach=<process_name>\n", DEBUGSERVER_PROGRAM_NAME); 677 RNBLogSTDERR (" %s /path/file --attach=<process_name>\n", DEBUGSERVER_PROGRAM_NAME); 678 exit (exit_code); 679} 680 681 682//---------------------------------------------------------------------- 683// option descriptors for getopt_long() 684//---------------------------------------------------------------------- 685static struct option g_long_options[] = 686{ 687 { "attach", required_argument, NULL, 'a' }, 688 { "arch", required_argument, NULL, 'A' }, 689 { "debug", no_argument, NULL, 'g' }, 690 { "verbose", no_argument, NULL, 'v' }, 691 { "lockdown", no_argument, &g_lockdown_opt, 1 }, // short option "-k" 692 { "applist", no_argument, &g_applist_opt, 1 }, // short option "-t" 693 { "log-file", required_argument, NULL, 'l' }, 694 { "log-flags", required_argument, NULL, 'f' }, 695 { "launch", required_argument, NULL, 'x' }, // Valid values are "auto", "posix-spawn", "fork-exec", "springboard" (arm only) 696 { "waitfor", required_argument, NULL, 'w' }, // Wait for a process whose name starts with ARG 697 { "waitfor-interval", required_argument, NULL, 'i' }, // Time in usecs to wait between sampling the pid list when waiting for a process by name 698 { "waitfor-duration", required_argument, NULL, 'd' }, // The time in seconds to wait for a process to show up by name 699 { "native-regs", no_argument, NULL, 'r' }, // Specify to use the native registers instead of the gdb defaults for the architecture. 700 { "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) 701 { "stdin-path", required_argument, NULL, 'I' }, // Set the STDIN path to be used when launching applications (only if debugserver launches the process) 702 { "stdout-path", required_argument, NULL, 'O' }, // Set the STDOUT path to be used when launching applications (only if debugserver launches the process) 703 { "stderr-path", required_argument, NULL, 'E' }, // Set the STDERR path to be used when launching applications (only if debugserver launches the process) 704 { "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) 705 { "setsid", no_argument, NULL, 'S' }, // call setsid() to make debugserver run in its own session 706 { "disable-aslr", no_argument, NULL, 'D' }, // Use _POSIX_SPAWN_DISABLE_ASLR to avoid shared library randomization 707 { "working-dir", required_argument, NULL, 'W' }, // The working directory that the inferior process should have (only if debugserver launches the process) 708 { "platform", required_argument, NULL, 'p' }, // Put this executable into a remote platform mode 709 { NULL, 0, NULL, 0 } 710}; 711 712 713//---------------------------------------------------------------------- 714// main 715//---------------------------------------------------------------------- 716int 717main (int argc, char *argv[]) 718{ 719 g_isatty = ::isatty (STDIN_FILENO); 720 721 // ::printf ("uid=%u euid=%u gid=%u egid=%u\n", 722 // getuid(), 723 // geteuid(), 724 // getgid(), 725 // getegid()); 726 727 728 // signal (SIGINT, signal_handler); 729 signal (SIGPIPE, signal_handler); 730 signal (SIGHUP, signal_handler); 731 732 g_remoteSP.reset (new RNBRemote ()); 733 734 735 RNBRemote *remote = g_remoteSP.get(); 736 if (remote == NULL) 737 { 738 RNBLogSTDERR ("error: failed to create a remote connection class\n"); 739 return -1; 740 } 741 742 RNBContext& ctx = remote->Context(); 743 744 int i; 745 int attach_pid = INVALID_NUB_PROCESS; 746 747 FILE* log_file = NULL; 748 uint32_t log_flags = 0; 749 // Parse our options 750 int ch; 751 int long_option_index = 0; 752 int debug = 0; 753 std::string compile_options; 754 std::string waitfor_pid_name; // Wait for a process that starts with this name 755 std::string attach_pid_name; 756 std::string arch_name; 757 std::string working_dir; // The new working directory to use for the inferior 758 useconds_t waitfor_interval = 1000; // Time in usecs between process lists polls when waiting for a process by name, default 1 msec. 759 useconds_t waitfor_duration = 0; // Time in seconds to wait for a process by name, 0 means wait forever. 760 bool no_stdio = false; 761 762#if !defined (DNBLOG_ENABLED) 763 compile_options += "(no-logging) "; 764#endif 765 766 RNBRunLoopMode start_mode = eRNBRunLoopModeExit; 767 768 while ((ch = getopt_long(argc, argv, "a:A:d:gi:vktl:f:w:x:rs:n", g_long_options, &long_option_index)) != -1) 769 { 770 DNBLogDebug("option: ch == %c (0x%2.2x) --%s%c%s\n", 771 ch, (uint8_t)ch, 772 g_long_options[long_option_index].name, 773 g_long_options[long_option_index].has_arg ? '=' : ' ', 774 optarg ? optarg : ""); 775 switch (ch) 776 { 777 case 0: // Any optional that auto set themselves will return 0 778 break; 779 780 case 'A': 781 if (optarg && optarg[0]) 782 arch_name.assign(optarg); 783 break; 784 785 case 'a': 786 if (optarg && optarg[0]) 787 { 788 if (isdigit(optarg[0])) 789 { 790 char *end = NULL; 791 attach_pid = strtoul(optarg, &end, 0); 792 if (end == NULL || *end != '\0') 793 { 794 RNBLogSTDERR ("error: invalid pid option '%s'\n", optarg); 795 exit (4); 796 } 797 } 798 else 799 { 800 attach_pid_name = optarg; 801 } 802 start_mode = eRNBRunLoopModeInferiorAttaching; 803 } 804 break; 805 806 // --waitfor=NAME 807 case 'w': 808 if (optarg && optarg[0]) 809 { 810 waitfor_pid_name = optarg; 811 start_mode = eRNBRunLoopModeInferiorAttaching; 812 } 813 break; 814 815 // --waitfor-interval=USEC 816 case 'i': 817 if (optarg && optarg[0]) 818 { 819 char *end = NULL; 820 waitfor_interval = strtoul(optarg, &end, 0); 821 if (end == NULL || *end != '\0') 822 { 823 RNBLogSTDERR ("error: invalid waitfor-interval option value '%s'.\n", optarg); 824 exit (6); 825 } 826 } 827 break; 828 829 // --waitfor-duration=SEC 830 case 'd': 831 if (optarg && optarg[0]) 832 { 833 char *end = NULL; 834 waitfor_duration = strtoul(optarg, &end, 0); 835 if (end == NULL || *end != '\0') 836 { 837 RNBLogSTDERR ("error: invalid waitfor-duration option value '%s'.\n", optarg); 838 exit (7); 839 } 840 } 841 break; 842 843 case 'W': 844 if (optarg && optarg[0]) 845 working_dir.assign(optarg); 846 break; 847 848 case 'x': 849 if (optarg && optarg[0]) 850 { 851 if (strcasecmp(optarg, "auto") == 0) 852 g_launch_flavor = eLaunchFlavorDefault; 853 else if (strcasestr(optarg, "posix") == optarg) 854 g_launch_flavor = eLaunchFlavorPosixSpawn; 855 else if (strcasestr(optarg, "fork") == optarg) 856 g_launch_flavor = eLaunchFlavorForkExec; 857#if defined (__arm__) 858 else if (strcasestr(optarg, "spring") == optarg) 859 g_launch_flavor = eLaunchFlavorSpringBoard; 860#endif 861 else 862 { 863 RNBLogSTDERR ("error: invalid TYPE for the --launch=TYPE (-x TYPE) option: '%s'\n", optarg); 864 RNBLogSTDERR ("Valid values TYPE are:\n"); 865 RNBLogSTDERR (" auto Auto-detect the best launch method to use.\n"); 866 RNBLogSTDERR (" posix Launch the executable using posix_spawn.\n"); 867 RNBLogSTDERR (" fork Launch the executable using fork and exec.\n"); 868#if defined (__arm__) 869 RNBLogSTDERR (" spring Launch the executable through Springboard.\n"); 870#endif 871 exit (5); 872 } 873 } 874 break; 875 876 case 'l': // Set Log File 877 if (optarg && optarg[0]) 878 { 879 if (strcasecmp(optarg, "stdout") == 0) 880 log_file = stdout; 881 else if (strcasecmp(optarg, "stderr") == 0) 882 log_file = stderr; 883 else 884 { 885 log_file = fopen(optarg, "w"); 886 if (log_file != NULL) 887 setlinebuf(log_file); 888 } 889 890 if (log_file == NULL) 891 { 892 const char *errno_str = strerror(errno); 893 RNBLogSTDERR ("Failed to open log file '%s' for writing: errno = %i (%s)", optarg, errno, errno_str ? errno_str : "unknown error"); 894 } 895 } 896 break; 897 898 case 'f': // Log Flags 899 if (optarg && optarg[0]) 900 log_flags = strtoul(optarg, NULL, 0); 901 break; 902 903 case 'g': 904 debug = 1; 905 DNBLogSetDebug(1); 906 break; 907 908 case 't': 909 g_applist_opt = 1; 910 break; 911 912 case 'k': 913 g_lockdown_opt = 1; 914 break; 915 916 case 'r': 917 remote->SetUseNativeRegisters (true); 918 break; 919 920 case 'v': 921 DNBLogSetVerbose(1); 922 break; 923 924 case 's': 925 ctx.GetSTDIN().assign(optarg); 926 ctx.GetSTDOUT().assign(optarg); 927 ctx.GetSTDERR().assign(optarg); 928 break; 929 930 case 'I': 931 ctx.GetSTDIN().assign(optarg); 932 break; 933 934 case 'O': 935 ctx.GetSTDOUT().assign(optarg); 936 break; 937 938 case 'E': 939 ctx.GetSTDERR().assign(optarg); 940 break; 941 942 case 'n': 943 no_stdio = true; 944 break; 945 946 case 'S': 947 // Put debugserver into a new session. Terminals group processes 948 // into sessions and when a special terminal key sequences 949 // (like control+c) are typed they can cause signals to go out to 950 // all processes in a session. Using this --setsid (-S) option 951 // will cause debugserver to run in its own sessions and be free 952 // from such issues. 953 // 954 // This is useful when debugserver is spawned from a command 955 // line application that uses debugserver to do the debugging, 956 // yet that application doesn't want debugserver receiving the 957 // signals sent to the session (i.e. dying when anyone hits ^C). 958 setsid(); 959 break; 960 case 'D': 961 g_disable_aslr = 1; 962 break; 963 964 case 'p': 965 start_mode = eRNBRunLoopModePlatformMode; 966 break; 967 } 968 } 969 970 if (arch_name.empty()) 971 { 972#if defined (__arm__) 973 arch_name.assign ("arm"); 974#endif 975 } 976 else 977 { 978 DNBSetArchitecture (arch_name.c_str()); 979 } 980 981// if (arch_name.empty()) 982// { 983// fprintf(stderr, "error: no architecture was specified\n"); 984// exit (8); 985// } 986 // Skip any options we consumed with getopt_long 987 argc -= optind; 988 argv += optind; 989 990 991 if (!working_dir.empty()) 992 { 993 if (remote->Context().SetWorkingDirectory (working_dir.c_str()) == false) 994 { 995 RNBLogSTDERR ("error: working directory doesn't exist '%s'.\n", working_dir.c_str()); 996 exit (8); 997 } 998 } 999 1000 remote->Initialize(); 1001 1002 // It is ok for us to set NULL as the logfile (this will disable any logging) 1003 1004 if (log_file != NULL) 1005 { 1006 DNBLogSetLogCallback(FileLogCallback, log_file); 1007 // If our log file was set, yet we have no log flags, log everything! 1008 if (log_flags == 0) 1009 log_flags = LOG_ALL | LOG_RNB_ALL; 1010 1011 DNBLogSetLogMask (log_flags); 1012 } 1013 else 1014 { 1015 // Enable DNB logging 1016 DNBLogSetLogCallback(ASLLogCallback, NULL); 1017 DNBLogSetLogMask (log_flags); 1018 1019 } 1020 1021 if (DNBLogEnabled()) 1022 { 1023 for (i=0; i<argc; i++) 1024 DNBLogDebug("argv[%i] = %s", i, argv[i]); 1025 } 1026 1027 // as long as we're dropping remotenub in as a replacement for gdbserver, 1028 // explicitly note that this is not gdbserver. 1029 1030 RNBLogSTDOUT ("%s-%g %sfor %s.\n", 1031 DEBUGSERVER_PROGRAM_NAME, 1032 DEBUGSERVER_VERSION_NUM, 1033 compile_options.c_str(), 1034 RNB_ARCH); 1035 1036 int listen_port = INT32_MAX; 1037 char str[PATH_MAX]; 1038 1039 if (g_lockdown_opt == 0 && g_applist_opt == 0) 1040 { 1041 // Make sure we at least have port 1042 if (argc < 1) 1043 { 1044 show_usage_and_exit (1); 1045 } 1046 // accept 'localhost:' prefix on port number 1047 1048 int items_scanned = ::sscanf (argv[0], "%[^:]:%i", str, &listen_port); 1049 if (items_scanned == 2) 1050 { 1051 DNBLogDebug("host = '%s' port = %i", str, listen_port); 1052 } 1053 else if (argv[0][0] == '/') 1054 { 1055 listen_port = INT32_MAX; 1056 strncpy(str, argv[0], sizeof(str)); 1057 } 1058 else 1059 { 1060 show_usage_and_exit (2); 1061 } 1062 1063 // We just used the 'host:port' or the '/path/file' arg... 1064 argc--; 1065 argv++; 1066 1067 } 1068 1069 // If we know we're waiting to attach, we don't need any of this other info. 1070 if (start_mode != eRNBRunLoopModeInferiorAttaching && 1071 start_mode != eRNBRunLoopModePlatformMode) 1072 { 1073 if (argc == 0 || g_lockdown_opt) 1074 { 1075 if (g_lockdown_opt != 0) 1076 { 1077 // Work around for SIGPIPE crashes due to posix_spawn issue. 1078 // We have to close STDOUT and STDERR, else the first time we 1079 // try and do any, we get SIGPIPE and die as posix_spawn is 1080 // doing bad things with our file descriptors at the moment. 1081 int null = open("/dev/null", O_RDWR); 1082 dup2(null, STDOUT_FILENO); 1083 dup2(null, STDERR_FILENO); 1084 } 1085 else if (g_applist_opt != 0) 1086 { 1087 // List all applications we are able to see 1088 std::string applist_plist; 1089 int err = ListApplications(applist_plist, false, false); 1090 if (err == 0) 1091 { 1092 fputs (applist_plist.c_str(), stdout); 1093 } 1094 else 1095 { 1096 RNBLogSTDERR ("error: ListApplications returned error %i\n", err); 1097 } 1098 // Exit with appropriate error if we were asked to list the applications 1099 // with no other args were given (and we weren't trying to do this over 1100 // lockdown) 1101 return err; 1102 } 1103 1104 DNBLogDebug("Get args from remote protocol..."); 1105 start_mode = eRNBRunLoopModeGetStartModeFromRemoteProtocol; 1106 } 1107 else 1108 { 1109 start_mode = eRNBRunLoopModeInferiorLaunching; 1110 // Fill in the argv array in the context from the rest of our args. 1111 // Skip the name of this executable and the port number 1112 for (int i = 0; i < argc; i++) 1113 { 1114 DNBLogDebug("inferior_argv[%i] = '%s'", i, argv[i]); 1115 ctx.PushArgument (argv[i]); 1116 } 1117 } 1118 } 1119 1120 if (start_mode == eRNBRunLoopModeExit) 1121 return -1; 1122 1123 RNBRunLoopMode mode = start_mode; 1124 char err_str[1024] = {'\0'}; 1125 1126 while (mode != eRNBRunLoopModeExit) 1127 { 1128 switch (mode) 1129 { 1130 case eRNBRunLoopModeGetStartModeFromRemoteProtocol: 1131#if defined (__arm__) 1132 if (g_lockdown_opt) 1133 { 1134 if (!remote->Comm().IsConnected()) 1135 { 1136 if (remote->Comm().ConnectToService () != rnb_success) 1137 { 1138 RNBLogSTDERR ("Failed to get connection from a remote gdb process.\n"); 1139 mode = eRNBRunLoopModeExit; 1140 } 1141 else if (g_applist_opt != 0) 1142 { 1143 // List all applications we are able to see 1144 std::string applist_plist; 1145 if (ListApplications(applist_plist, false, false) == 0) 1146 { 1147 DNBLogDebug("Task list: %s", applist_plist.c_str()); 1148 1149 remote->Comm().Write(applist_plist.c_str(), applist_plist.size()); 1150 // Issue a read that will never yield any data until the other side 1151 // closes the socket so this process doesn't just exit and cause the 1152 // socket to close prematurely on the other end and cause data loss. 1153 std::string buf; 1154 remote->Comm().Read(buf); 1155 } 1156 remote->Comm().Disconnect(false); 1157 mode = eRNBRunLoopModeExit; 1158 break; 1159 } 1160 else 1161 { 1162 // Start watching for remote packets 1163 remote->StartReadRemoteDataThread(); 1164 } 1165 } 1166 } 1167 else 1168#endif 1169 if (listen_port != INT32_MAX) 1170 { 1171 if (!StartListening (remote, listen_port)) 1172 mode = eRNBRunLoopModeExit; 1173 } 1174 else if (str[0] == '/') 1175 { 1176 if (remote->Comm().OpenFile (str)) 1177 mode = eRNBRunLoopModeExit; 1178 } 1179 1180 if (mode != eRNBRunLoopModeExit) 1181 { 1182 RNBLogSTDOUT ("Got a connection, waiting for process information for launching or attaching.\n"); 1183 1184 mode = RNBRunLoopGetStartModeFromRemote (remote); 1185 } 1186 break; 1187 1188 case eRNBRunLoopModeInferiorAttaching: 1189 if (!waitfor_pid_name.empty()) 1190 { 1191 // Set our end wait time if we are using a waitfor-duration 1192 // option that may have been specified 1193 struct timespec attach_timeout_abstime, *timeout_ptr = NULL; 1194 if (waitfor_duration != 0) 1195 { 1196 DNBTimer::OffsetTimeOfDay(&attach_timeout_abstime, waitfor_duration, 0); 1197 timeout_ptr = &attach_timeout_abstime; 1198 } 1199 nub_launch_flavor_t launch_flavor = g_launch_flavor; 1200 if (launch_flavor == eLaunchFlavorDefault) 1201 { 1202 // Our default launch method is posix spawn 1203 launch_flavor = eLaunchFlavorPosixSpawn; 1204 1205#if defined (__arm__) 1206 // Check if we have an app bundle, if so launch using SpringBoard. 1207 if (waitfor_pid_name.find (".app") != std::string::npos) 1208 { 1209 launch_flavor = eLaunchFlavorSpringBoard; 1210 } 1211#endif 1212 } 1213 1214 ctx.SetLaunchFlavor(launch_flavor); 1215 1216 nub_process_t pid = DNBProcessAttachWait (waitfor_pid_name.c_str(), launch_flavor, timeout_ptr, waitfor_interval, err_str, sizeof(err_str)); 1217 g_pid = pid; 1218 1219 if (pid == INVALID_NUB_PROCESS) 1220 { 1221 ctx.LaunchStatus().SetError(-1, DNBError::Generic); 1222 if (err_str[0]) 1223 ctx.LaunchStatus().SetErrorString(err_str); 1224 RNBLogSTDERR ("error: failed to attach to process named: \"%s\" %s", waitfor_pid_name.c_str(), err_str); 1225 mode = eRNBRunLoopModeExit; 1226 } 1227 else 1228 { 1229 ctx.SetProcessID(pid); 1230 mode = eRNBRunLoopModeInferiorExecuting; 1231 } 1232 } 1233 else if (attach_pid != INVALID_NUB_PROCESS) 1234 { 1235 1236 RNBLogSTDOUT ("Attaching to process %i...\n", attach_pid); 1237 nub_process_t attached_pid; 1238 mode = RNBRunLoopLaunchAttaching (remote, attach_pid, attached_pid); 1239 if (mode != eRNBRunLoopModeInferiorExecuting) 1240 { 1241 const char *error_str = remote->Context().LaunchStatus().AsString(); 1242 RNBLogSTDERR ("error: failed to attach process %i: %s\n", attach_pid, error_str ? error_str : "unknown error."); 1243 mode = eRNBRunLoopModeExit; 1244 } 1245 } 1246 else if (!attach_pid_name.empty ()) 1247 { 1248 struct timespec attach_timeout_abstime, *timeout_ptr = NULL; 1249 if (waitfor_duration != 0) 1250 { 1251 DNBTimer::OffsetTimeOfDay(&attach_timeout_abstime, waitfor_duration, 0); 1252 timeout_ptr = &attach_timeout_abstime; 1253 } 1254 1255 nub_process_t pid = DNBProcessAttachByName (attach_pid_name.c_str(), timeout_ptr, err_str, sizeof(err_str)); 1256 g_pid = pid; 1257 if (pid == INVALID_NUB_PROCESS) 1258 { 1259 ctx.LaunchStatus().SetError(-1, DNBError::Generic); 1260 if (err_str[0]) 1261 ctx.LaunchStatus().SetErrorString(err_str); 1262 RNBLogSTDERR ("error: failed to attach to process named: \"%s\" %s", waitfor_pid_name.c_str(), err_str); 1263 mode = eRNBRunLoopModeExit; 1264 } 1265 else 1266 { 1267 ctx.SetProcessID(pid); 1268 mode = eRNBRunLoopModeInferiorExecuting; 1269 } 1270 1271 } 1272 else 1273 { 1274 RNBLogSTDERR ("error: asked to attach with empty name and invalid PID."); 1275 mode = eRNBRunLoopModeExit; 1276 } 1277 1278 if (mode != eRNBRunLoopModeExit) 1279 { 1280 if (listen_port != INT32_MAX) 1281 { 1282 if (!StartListening (remote, listen_port)) 1283 mode = eRNBRunLoopModeExit; 1284 } 1285 else if (str[0] == '/') 1286 { 1287 if (remote->Comm().OpenFile (str)) 1288 mode = eRNBRunLoopModeExit; 1289 } 1290 if (mode != eRNBRunLoopModeExit) 1291 RNBLogSTDOUT ("Got a connection, waiting for debugger instructions for process %d.\n", attach_pid); 1292 } 1293 break; 1294 1295 case eRNBRunLoopModeInferiorLaunching: 1296 { 1297 mode = RNBRunLoopLaunchInferior (remote, 1298 ctx.GetSTDINPath(), 1299 ctx.GetSTDOUTPath(), 1300 ctx.GetSTDERRPath(), 1301 no_stdio); 1302 1303 if (mode == eRNBRunLoopModeInferiorExecuting) 1304 { 1305 if (listen_port != INT32_MAX) 1306 { 1307 if (!StartListening (remote, listen_port)) 1308 mode = eRNBRunLoopModeExit; 1309 } 1310 else if (str[0] == '/') 1311 { 1312 if (remote->Comm().OpenFile (str)) 1313 mode = eRNBRunLoopModeExit; 1314 } 1315 1316 if (mode != eRNBRunLoopModeExit) 1317 RNBLogSTDOUT ("Got a connection, waiting for debugger instructions.\n"); 1318 } 1319 else 1320 { 1321 const char *error_str = remote->Context().LaunchStatus().AsString(); 1322 RNBLogSTDERR ("error: failed to launch process %s: %s\n", argv[0], error_str ? error_str : "unknown error."); 1323 } 1324 } 1325 break; 1326 1327 case eRNBRunLoopModeInferiorExecuting: 1328 mode = RNBRunLoopInferiorExecuting(remote); 1329 break; 1330 1331 case eRNBRunLoopModePlatformMode: 1332 if (listen_port != INT32_MAX) 1333 { 1334 if (!StartListening (remote, listen_port)) 1335 mode = eRNBRunLoopModeExit; 1336 } 1337 else if (str[0] == '/') 1338 { 1339 if (remote->Comm().OpenFile (str)) 1340 mode = eRNBRunLoopModeExit; 1341 } 1342 1343 if (mode != eRNBRunLoopModeExit) 1344 mode = RNBRunLoopPlatform (remote); 1345 break; 1346 1347 default: 1348 mode = eRNBRunLoopModeExit; 1349 case eRNBRunLoopModeExit: 1350 break; 1351 } 1352 } 1353 1354 remote->StopReadRemoteDataThread (); 1355 remote->Context().SetProcessID(INVALID_NUB_PROCESS); 1356 1357 return 0; 1358} 1359