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