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