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