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