1//===-- libdebugserver.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
18#include "DNB.h"
19#include "DNBLog.h"
20#include "DNBTimer.h"
21#include "PseudoTerminal.h"
22#include "RNBContext.h"
23#include "RNBServices.h"
24#include "RNBSocket.h"
25#include "RNBRemote.h"
26#include "SysSignal.h"
27
28//----------------------------------------------------------------------
29// Run loop modes which determine which run loop function will be called
30//----------------------------------------------------------------------
31typedef enum
32{
33    eRNBRunLoopModeInvalid = 0,
34    eRNBRunLoopModeGetStartModeFromRemoteProtocol,
35    eRNBRunLoopModeInferiorExecuting,
36    eRNBRunLoopModeExit
37} RNBRunLoopMode;
38
39
40//----------------------------------------------------------------------
41// Global Variables
42//----------------------------------------------------------------------
43RNBRemoteSP g_remoteSP;
44int g_disable_aslr = 0;
45int g_isatty = 0;
46
47#define RNBLogSTDOUT(fmt, ...) do { if (g_isatty) { fprintf(stdout, fmt, ## __VA_ARGS__); } else { _DNBLog(0, fmt, ## __VA_ARGS__); } } while (0)
48#define RNBLogSTDERR(fmt, ...) do { if (g_isatty) { fprintf(stderr, fmt, ## __VA_ARGS__); } else { _DNBLog(0, fmt, ## __VA_ARGS__); } } while (0)
49
50
51//----------------------------------------------------------------------
52// Get our program path and arguments from the remote connection.
53// We will need to start up the remote connection without a PID, get the
54// arguments, wait for the new process to finish launching and hit its
55// entry point,  and then return the run loop mode that should come next.
56//----------------------------------------------------------------------
57RNBRunLoopMode
58RNBRunLoopGetStartModeFromRemote (RNBRemoteSP &remoteSP)
59{
60    std::string packet;
61
62    if (remoteSP.get() != NULL)
63    {
64        RNBRemote* remote = remoteSP.get();
65        RNBContext& ctx = remote->Context();
66        uint32_t event_mask = RNBContext::event_read_packet_available;
67
68        // Spin waiting to get the A packet.
69        while (1)
70        {
71            DNBLogThreadedIf (LOG_RNB_MAX, "%s ctx.Events().WaitForSetEvents( 0x%08x ) ...",__FUNCTION__, event_mask);
72            nub_event_t set_events = ctx.Events().WaitForSetEvents(event_mask);
73            DNBLogThreadedIf (LOG_RNB_MAX, "%s ctx.Events().WaitForSetEvents( 0x%08x ) => 0x%08x", __FUNCTION__, event_mask, set_events);
74
75            if (set_events & RNBContext::event_read_packet_available)
76            {
77                rnb_err_t err = rnb_err;
78                RNBRemote::PacketEnum type;
79
80                err = remote->HandleReceivedPacket (&type);
81
82                // check if we tried to attach to a process
83                if (type == RNBRemote::vattach || type == RNBRemote::vattachwait)
84                {
85                    if (err == rnb_success)
86                        return eRNBRunLoopModeInferiorExecuting;
87                    else
88                    {
89                        RNBLogSTDERR ("error: attach failed.");
90                        return eRNBRunLoopModeExit;
91                    }
92                }
93
94
95                if (err == rnb_success)
96                {
97                    DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s Got success...",__FUNCTION__);
98					continue;
99				}
100				else if (err == rnb_not_connected)
101                {
102                    RNBLogSTDERR ("error: connection lost.");
103                    return eRNBRunLoopModeExit;
104                }
105                else
106                {
107                    // a catch all for any other gdb remote packets that failed
108                    DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s Error getting packet.",__FUNCTION__);
109                    continue;
110                }
111
112                DNBLogThreadedIf (LOG_RNB_MINIMAL, "#### %s", __FUNCTION__);
113            }
114            else
115            {
116                DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s Connection closed before getting \"A\" packet.", __FUNCTION__);
117                return eRNBRunLoopModeExit;
118            }
119        }
120    }
121    return eRNBRunLoopModeExit;
122}
123
124
125//----------------------------------------------------------------------
126// Watch for signals:
127// SIGINT: so we can halt our inferior. (disabled for now)
128// SIGPIPE: in case our child process dies
129//----------------------------------------------------------------------
130nub_process_t g_pid;
131int g_sigpipe_received = 0;
132void
133signal_handler(int signo)
134{
135    DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s (%s)", __FUNCTION__, SysSignal::Name(signo));
136
137    switch (signo)
138    {
139			//  case SIGINT:
140			//      DNBProcessKill (g_pid, signo);
141			//      break;
142
143		case SIGPIPE:
144			g_sigpipe_received = 1;
145			break;
146    }
147}
148
149// Return the new run loop mode based off of the current process state
150RNBRunLoopMode
151HandleProcessStateChange (RNBRemoteSP &remote, bool initialize)
152{
153    RNBContext& ctx = remote->Context();
154    nub_process_t pid = ctx.ProcessID();
155
156    if (pid == INVALID_NUB_PROCESS)
157    {
158        DNBLogThreadedIf (LOG_RNB_MINIMAL, "#### %s error: pid invalid, exiting...", __FUNCTION__);
159        return eRNBRunLoopModeExit;
160    }
161    nub_state_t pid_state = DNBProcessGetState (pid);
162
163    DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s (&remote, initialize=%i)  pid_state = %s", __FUNCTION__, (int)initialize, DNBStateAsString (pid_state));
164
165    switch (pid_state)
166    {
167		case eStateInvalid:
168		case eStateUnloaded:
169			// Something bad happened
170			return eRNBRunLoopModeExit;
171			break;
172
173		case eStateAttaching:
174		case eStateLaunching:
175			return eRNBRunLoopModeInferiorExecuting;
176
177		case eStateSuspended:
178		case eStateCrashed:
179		case eStateStopped:
180			if (initialize == false)
181			{
182				// Compare the last stop count to our current notion of a stop count
183				// to make sure we don't notify more than once for a given stop.
184				nub_size_t prev_pid_stop_count = ctx.GetProcessStopCount();
185				bool pid_stop_count_changed = ctx.SetProcessStopCount(DNBProcessGetStopCount(pid));
186				if (pid_stop_count_changed)
187				{
188					remote->FlushSTDIO();
189
190					if (ctx.GetProcessStopCount() == 1)
191					{
192						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);
193					}
194					else
195					{
196
197						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);
198						remote->NotifyThatProcessStopped ();
199					}
200				}
201				else
202				{
203					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);
204				}
205			}
206			return eRNBRunLoopModeInferiorExecuting;
207
208		case eStateStepping:
209		case eStateRunning:
210			return eRNBRunLoopModeInferiorExecuting;
211
212		case eStateExited:
213			remote->HandlePacket_last_signal(NULL);
214			return eRNBRunLoopModeExit;
215		case eStateDetached:
216            return eRNBRunLoopModeExit;
217
218    }
219
220    // Catch all...
221    return eRNBRunLoopModeExit;
222}
223// This function handles the case where our inferior program is stopped and
224// we are waiting for gdb remote protocol packets. When a packet occurs that
225// makes the inferior run, we need to leave this function with a new state
226// as the return code.
227RNBRunLoopMode
228RNBRunLoopInferiorExecuting (RNBRemoteSP &remote)
229{
230    DNBLogThreadedIf (LOG_RNB_MINIMAL, "#### %s", __FUNCTION__);
231    RNBContext& ctx = remote->Context();
232
233    // Init our mode and set 'is_running' based on the current process state
234    RNBRunLoopMode mode = HandleProcessStateChange (remote, true);
235
236    while (ctx.ProcessID() != INVALID_NUB_PROCESS)
237    {
238
239        std::string set_events_str;
240        uint32_t event_mask = ctx.NormalEventBits();
241
242        if (!ctx.ProcessStateRunning())
243        {
244            // Clear the stdio bits if we are not running so we don't send any async packets
245            event_mask &= ~RNBContext::event_proc_stdio_available;
246        }
247
248        // We want to make sure we consume all process state changes and have
249        // whomever is notifying us to wait for us to reset the event bit before
250        // continuing.
251        //ctx.Events().SetResetAckMask (RNBContext::event_proc_state_changed);
252
253        DNBLogThreadedIf (LOG_RNB_EVENTS, "%s ctx.Events().WaitForSetEvents(0x%08x) ...",__FUNCTION__, event_mask);
254        nub_event_t set_events = ctx.Events().WaitForSetEvents(event_mask);
255        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));
256
257        if (set_events)
258        {
259            if ((set_events & RNBContext::event_proc_thread_exiting) ||
260                (set_events & RNBContext::event_proc_stdio_available))
261            {
262                remote->FlushSTDIO();
263            }
264
265            if (set_events & RNBContext::event_read_packet_available)
266            {
267                // handleReceivedPacket will take care of resetting the
268                // event_read_packet_available events when there are no more...
269                set_events ^= RNBContext::event_read_packet_available;
270
271                if (ctx.ProcessStateRunning())
272                {
273                    if (remote->HandleAsyncPacket() == rnb_not_connected)
274                    {
275                        // TODO: connect again? Exit?
276                    }
277                }
278                else
279                {
280                    if (remote->HandleReceivedPacket() == rnb_not_connected)
281                    {
282                        // TODO: connect again? Exit?
283                    }
284                }
285            }
286
287            if (set_events & RNBContext::event_proc_state_changed)
288            {
289                mode = HandleProcessStateChange (remote, false);
290                ctx.Events().ResetEvents(RNBContext::event_proc_state_changed);
291                set_events ^= RNBContext::event_proc_state_changed;
292            }
293
294            if (set_events & RNBContext::event_proc_thread_exiting)
295            {
296                mode = eRNBRunLoopModeExit;
297            }
298
299            if (set_events & RNBContext::event_read_thread_exiting)
300            {
301                // Out remote packet receiving thread exited, exit for now.
302                if (ctx.HasValidProcessID())
303                {
304                    // TODO: We should add code that will leave the current process
305                    // in its current state and listen for another connection...
306                    if (ctx.ProcessStateRunning())
307                    {
308                        DNBProcessKill (ctx.ProcessID(), SIGINT);
309                    }
310                }
311                mode = eRNBRunLoopModeExit;
312            }
313        }
314
315        // Reset all event bits that weren't reset for now...
316        if (set_events != 0)
317			ctx.Events().ResetEvents(set_events);
318
319        if (mode != eRNBRunLoopModeInferiorExecuting)
320			break;
321    }
322
323    return mode;
324}
325
326void
327ASLLogCallback(void *baton, uint32_t flags, const char *format, va_list args)
328{
329#if 0
330	vprintf(format, args);
331#endif
332}
333
334extern "C" int
335debug_server_main(int fd)
336{
337#if 1
338	g_isatty = 0;
339#else
340	g_isatty = ::isatty (STDIN_FILENO);
341
342	DNBLogSetDebug(1);
343	DNBLogSetVerbose(1);
344	DNBLogSetLogMask(-1);
345	DNBLogSetLogCallback(ASLLogCallback, NULL);
346#endif
347
348    signal (SIGPIPE, signal_handler);
349
350    g_remoteSP.reset (new RNBRemote);
351
352    RNBRemote *remote = g_remoteSP.get();
353    if (remote == NULL)
354    {
355        RNBLogSTDERR ("error: failed to create a remote connection class\n");
356        return -1;
357    }
358
359
360    RNBRunLoopMode mode = eRNBRunLoopModeGetStartModeFromRemoteProtocol;
361
362    while (mode != eRNBRunLoopModeExit)
363    {
364        switch (mode)
365        {
366			case eRNBRunLoopModeGetStartModeFromRemoteProtocol:
367				if (g_remoteSP->Comm().useFD(fd) == rnb_success) {
368					RNBLogSTDOUT("Starting remote data thread.\n");
369					g_remoteSP->StartReadRemoteDataThread();
370
371					RNBLogSTDOUT("Waiting for start mode from remote.\n");
372					mode = RNBRunLoopGetStartModeFromRemote(g_remoteSP);
373				}
374				else
375				{
376					mode = eRNBRunLoopModeExit;
377				}
378				break;
379
380			case eRNBRunLoopModeInferiorExecuting:
381				mode = RNBRunLoopInferiorExecuting(g_remoteSP);
382				break;
383
384			default:
385				mode = eRNBRunLoopModeExit;
386				break;
387
388			case eRNBRunLoopModeExit:
389				break;
390        }
391    }
392
393    g_remoteSP->StopReadRemoteDataThread ();
394    g_remoteSP->Context().SetProcessID(INVALID_NUB_PROCESS);
395
396    return 0;
397}
398