blob: 62d0b28545518c632c5647fddc677d949f82e720 [file] [log] [blame]
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001//===-- 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;
Greg Clayton6f35f5c2010-09-09 06:32:46 +000056int g_disable_aslr = 0;
Chris Lattner30fdc8d2010-06-08 16:52:24 +000057
58int g_isatty = 0;
59
60#define RNBLogSTDOUT(fmt, ...) do { if (g_isatty) { fprintf(stdout, fmt, ## __VA_ARGS__); } else { _DNBLog(0, fmt, ## __VA_ARGS__); } } while (0)
61#define RNBLogSTDERR(fmt, ...) do { if (g_isatty) { fprintf(stderr, fmt, ## __VA_ARGS__); } else { _DNBLog(0, fmt, ## __VA_ARGS__); } } while (0)
62
63//----------------------------------------------------------------------
Chris Lattner30fdc8d2010-06-08 16:52:24 +000064// Get our program path and arguments from the remote connection.
65// We will need to start up the remote connection without a PID, get the
66// arguments, wait for the new process to finish launching and hit its
67// entry point, and then return the run loop mode that should come next.
68//----------------------------------------------------------------------
69RNBRunLoopMode
Greg Clayton6779606a2011-01-22 23:43:18 +000070RNBRunLoopGetStartModeFromRemote (RNBRemote* remote)
Chris Lattner30fdc8d2010-06-08 16:52:24 +000071{
72 std::string packet;
73
Greg Clayton6779606a2011-01-22 23:43:18 +000074 if (remote)
Chris Lattner30fdc8d2010-06-08 16:52:24 +000075 {
Chris Lattner30fdc8d2010-06-08 16:52:24 +000076 RNBContext& ctx = remote->Context();
Greg Clayton71337622011-02-24 22:24:29 +000077 uint32_t event_mask = RNBContext::event_read_packet_available |
78 RNBContext::event_read_thread_exiting;
Chris Lattner30fdc8d2010-06-08 16:52:24 +000079
80 // Spin waiting to get the A packet.
81 while (1)
82 {
83 DNBLogThreadedIf (LOG_RNB_MAX, "%s ctx.Events().WaitForSetEvents( 0x%08x ) ...",__FUNCTION__, event_mask);
84 nub_event_t set_events = ctx.Events().WaitForSetEvents(event_mask);
85 DNBLogThreadedIf (LOG_RNB_MAX, "%s ctx.Events().WaitForSetEvents( 0x%08x ) => 0x%08x", __FUNCTION__, event_mask, set_events);
86
Greg Clayton71337622011-02-24 22:24:29 +000087 if (set_events & RNBContext::event_read_thread_exiting)
88 {
89 RNBLogSTDERR ("error: packet read thread exited.");
90 return eRNBRunLoopModeExit;
91 }
92
Chris Lattner30fdc8d2010-06-08 16:52:24 +000093 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
Greg Clayton6779606a2011-01-22 23:43:18 +0000153RNBRunLoopLaunchInferior (RNBRemote *remote, const char *stdin_path, const char *stdout_path, const char *stderr_path, bool no_stdio)
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000154{
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';
Johnny Chen725269a2011-02-26 01:36:13 +0000207 const char * cwd = (ctx.GetWorkingDirPath() != NULL ? ctx.GetWorkingDirPath()
208 : ctx.GetWorkingDirectory());
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000209 nub_process_t pid = DNBProcessLaunch (resolved_path,
210 &inferior_argv[0],
211 &inferior_envp[0],
Johnny Chen725269a2011-02-26 01:36:13 +0000212 cwd,
Greg Clayton6779606a2011-01-22 23:43:18 +0000213 stdin_path,
214 stdout_path,
215 stderr_path,
Caroline Ticef8da8632010-12-03 18:46:09 +0000216 no_stdio,
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000217 launch_flavor,
Greg Claytonf681b942010-08-31 18:35:14 +0000218 g_disable_aslr,
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000219 launch_err_str,
220 sizeof(launch_err_str));
221
222 g_pid = pid;
223
224 if (pid == INVALID_NUB_PROCESS && strlen(launch_err_str) > 0)
225 {
Greg Clayton3382c2c2010-07-30 23:14:42 +0000226 DNBLogThreaded ("%s DNBProcessLaunch() returned error: '%s'", __FUNCTION__, launch_err_str);
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000227 ctx.LaunchStatus().SetError(-1, DNBError::Generic);
228 ctx.LaunchStatus().SetErrorString(launch_err_str);
229 }
230 else
Greg Clayton71337622011-02-24 22:24:29 +0000231 {
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000232 ctx.LaunchStatus().Clear();
Greg Clayton71337622011-02-24 22:24:29 +0000233 }
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000234
235 if (remote->Comm().IsConnected())
236 {
237 // It we are connected already, the next thing gdb will do is ask
238 // whether the launch succeeded, and if not, whether there is an
239 // error code. So we need to fetch one packet from gdb before we wait
240 // on the stop from the target.
241
242 uint32_t event_mask = RNBContext::event_read_packet_available;
243 nub_event_t set_events = ctx.Events().WaitForSetEvents(event_mask);
244
245 if (set_events & RNBContext::event_read_packet_available)
246 {
247 rnb_err_t err = rnb_err;
248 RNBRemote::PacketEnum type;
249
250 err = remote->HandleReceivedPacket (&type);
251
252 if (err != rnb_success)
253 {
254 DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s Error getting packet.", __FUNCTION__);
255 return eRNBRunLoopModeExit;
256 }
257 if (type != RNBRemote::query_launch_success)
258 {
259 DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s Didn't get the expected qLaunchSuccess packet.", __FUNCTION__);
260 }
261 }
262 }
263
264 while (pid != INVALID_NUB_PROCESS)
265 {
266 // Wait for process to start up and hit entry point
267 DNBLogThreadedIf (LOG_RNB_EVENTS, "%s DNBProcessWaitForEvent (%4.4x, eEventProcessRunningStateChanged | eEventProcessStoppedStateChanged, true, INFINITE)...", __FUNCTION__, pid);
268 nub_event_t set_events = DNBProcessWaitForEvents (pid, eEventProcessRunningStateChanged | eEventProcessStoppedStateChanged, true, NULL);
269 DNBLogThreadedIf (LOG_RNB_EVENTS, "%s DNBProcessWaitForEvent (%4.4x, eEventProcessRunningStateChanged | eEventProcessStoppedStateChanged, true, INFINITE) => 0x%8.8x", __FUNCTION__, pid, set_events);
270
271 if (set_events == 0)
272 {
273 pid = INVALID_NUB_PROCESS;
274 g_pid = pid;
275 }
276 else
277 {
278 if (set_events & (eEventProcessRunningStateChanged | eEventProcessStoppedStateChanged))
279 {
280 nub_state_t pid_state = DNBProcessGetState (pid);
281 DNBLogThreadedIf (LOG_RNB_EVENTS, "%s process %4.4x state changed (eEventProcessStateChanged): %s", __FUNCTION__, pid, DNBStateAsString(pid_state));
282
283 switch (pid_state)
284 {
285 default:
286 case eStateInvalid:
287 case eStateUnloaded:
288 case eStateAttaching:
289 case eStateLaunching:
290 case eStateSuspended:
291 break; // Ignore
292
293 case eStateRunning:
294 case eStateStepping:
295 // Still waiting to stop at entry point...
296 break;
297
298 case eStateStopped:
299 case eStateCrashed:
300 ctx.SetProcessID(pid);
301 return eRNBRunLoopModeInferiorExecuting;
302
303 case eStateDetached:
304 case eStateExited:
305 pid = INVALID_NUB_PROCESS;
306 g_pid = pid;
307 return eRNBRunLoopModeExit;
308 }
309 }
310
311 DNBProcessResetEvents(pid, set_events);
312 }
313 }
314
315 return eRNBRunLoopModeExit;
316}
317
318
319//----------------------------------------------------------------------
320// This run loop mode will wait for the process to launch and hit its
321// entry point. It will currently ignore all events except for the
322// process state changed event, where it watches for the process stopped
323// or crash process state.
324//----------------------------------------------------------------------
325RNBRunLoopMode
Greg Clayton6779606a2011-01-22 23:43:18 +0000326RNBRunLoopLaunchAttaching (RNBRemote *remote, nub_process_t attach_pid, nub_process_t& pid)
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000327{
328 RNBContext& ctx = remote->Context();
329
330 DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s Attaching to pid %i...", __FUNCTION__, attach_pid);
331 char err_str[1024];
332 pid = DNBProcessAttach (attach_pid, NULL, err_str, sizeof(err_str));
333 g_pid = pid;
334
335 if (pid == INVALID_NUB_PROCESS)
336 {
337 ctx.LaunchStatus().SetError(-1, DNBError::Generic);
338 if (err_str[0])
339 ctx.LaunchStatus().SetErrorString(err_str);
340 return eRNBRunLoopModeExit;
341 }
342 else
343 {
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000344 ctx.SetProcessID(pid);
345 return eRNBRunLoopModeInferiorExecuting;
346 }
347}
348
349//----------------------------------------------------------------------
350// Watch for signals:
351// SIGINT: so we can halt our inferior. (disabled for now)
352// SIGPIPE: in case our child process dies
353//----------------------------------------------------------------------
354int g_sigint_received = 0;
355int g_sigpipe_received = 0;
356void
357signal_handler(int signo)
358{
359 DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s (%s)", __FUNCTION__, SysSignal::Name(signo));
360
361 switch (signo)
362 {
363 case SIGINT:
364 g_sigint_received++;
365 if (g_pid != INVALID_NUB_PROCESS)
366 {
367 // Only send a SIGINT once...
368 if (g_sigint_received == 1)
369 {
370 switch (DNBProcessGetState (g_pid))
371 {
372 case eStateRunning:
373 case eStateStepping:
374 DNBProcessSignal (g_pid, SIGSTOP);
375 return;
376 }
377 }
378 }
379 exit (SIGINT);
380 break;
381
382 case SIGPIPE:
383 g_sigpipe_received = 1;
384 break;
385 }
386}
387
388// Return the new run loop mode based off of the current process state
389RNBRunLoopMode
Greg Clayton6779606a2011-01-22 23:43:18 +0000390HandleProcessStateChange (RNBRemote *remote, bool initialize)
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000391{
392 RNBContext& ctx = remote->Context();
393 nub_process_t pid = ctx.ProcessID();
394
395 if (pid == INVALID_NUB_PROCESS)
396 {
397 DNBLogThreadedIf (LOG_RNB_MINIMAL, "#### %s error: pid invalid, exiting...", __FUNCTION__);
398 return eRNBRunLoopModeExit;
399 }
400 nub_state_t pid_state = DNBProcessGetState (pid);
401
402 DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s (&remote, initialize=%i) pid_state = %s", __FUNCTION__, (int)initialize, DNBStateAsString (pid_state));
403
404 switch (pid_state)
405 {
406 case eStateInvalid:
407 case eStateUnloaded:
408 // Something bad happened
409 return eRNBRunLoopModeExit;
410 break;
411
412 case eStateAttaching:
413 case eStateLaunching:
414 return eRNBRunLoopModeInferiorExecuting;
415
416 case eStateSuspended:
417 case eStateCrashed:
418 case eStateStopped:
419 // If we stop due to a signal, so clear the fact that we got a SIGINT
420 // so we can stop ourselves again (but only while our inferior
421 // process is running..)
422 g_sigint_received = 0;
423 if (initialize == false)
424 {
425 // Compare the last stop count to our current notion of a stop count
426 // to make sure we don't notify more than once for a given stop.
427 nub_size_t prev_pid_stop_count = ctx.GetProcessStopCount();
428 bool pid_stop_count_changed = ctx.SetProcessStopCount(DNBProcessGetStopCount(pid));
429 if (pid_stop_count_changed)
430 {
431 remote->FlushSTDIO();
432
433 if (ctx.GetProcessStopCount() == 1)
434 {
435 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);
436 }
437 else
438 {
439
440 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);
441 remote->NotifyThatProcessStopped ();
442 }
443 }
444 else
445 {
446 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);
447 }
448 }
449 return eRNBRunLoopModeInferiorExecuting;
450
451 case eStateStepping:
452 case eStateRunning:
453 return eRNBRunLoopModeInferiorExecuting;
454
455 case eStateExited:
456 remote->HandlePacket_last_signal(NULL);
457 return eRNBRunLoopModeExit;
458
459 }
460
461 // Catch all...
462 return eRNBRunLoopModeExit;
463}
464// This function handles the case where our inferior program is stopped and
465// we are waiting for gdb remote protocol packets. When a packet occurs that
466// makes the inferior run, we need to leave this function with a new state
467// as the return code.
468RNBRunLoopMode
Greg Clayton6779606a2011-01-22 23:43:18 +0000469RNBRunLoopInferiorExecuting (RNBRemote *remote)
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000470{
471 DNBLogThreadedIf (LOG_RNB_MINIMAL, "#### %s", __FUNCTION__);
472 RNBContext& ctx = remote->Context();
473
474 // Init our mode and set 'is_running' based on the current process state
475 RNBRunLoopMode mode = HandleProcessStateChange (remote, true);
476
477 while (ctx.ProcessID() != INVALID_NUB_PROCESS)
478 {
479
480 std::string set_events_str;
481 uint32_t event_mask = ctx.NormalEventBits();
482
483 if (!ctx.ProcessStateRunning())
484 {
485 // Clear the stdio bits if we are not running so we don't send any async packets
486 event_mask &= ~RNBContext::event_proc_stdio_available;
487 }
488
489 // We want to make sure we consume all process state changes and have
490 // whomever is notifying us to wait for us to reset the event bit before
491 // continuing.
492 //ctx.Events().SetResetAckMask (RNBContext::event_proc_state_changed);
493
494 DNBLogThreadedIf (LOG_RNB_EVENTS, "%s ctx.Events().WaitForSetEvents(0x%08x) ...",__FUNCTION__, event_mask);
495 nub_event_t set_events = ctx.Events().WaitForSetEvents(event_mask);
496 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));
497
498 if (set_events)
499 {
500 if ((set_events & RNBContext::event_proc_thread_exiting) ||
501 (set_events & RNBContext::event_proc_stdio_available))
502 {
503 remote->FlushSTDIO();
504 }
505
506 if (set_events & RNBContext::event_read_packet_available)
507 {
508 // handleReceivedPacket will take care of resetting the
509 // event_read_packet_available events when there are no more...
510 set_events ^= RNBContext::event_read_packet_available;
511
512 if (ctx.ProcessStateRunning())
513 {
514 if (remote->HandleAsyncPacket() == rnb_not_connected)
515 {
516 // TODO: connect again? Exit?
517 }
518 }
519 else
520 {
521 if (remote->HandleReceivedPacket() == rnb_not_connected)
522 {
523 // TODO: connect again? Exit?
524 }
525 }
526 }
527
528 if (set_events & RNBContext::event_proc_state_changed)
529 {
530 mode = HandleProcessStateChange (remote, false);
531 ctx.Events().ResetEvents(RNBContext::event_proc_state_changed);
532 set_events ^= RNBContext::event_proc_state_changed;
533 }
534
535 if (set_events & RNBContext::event_proc_thread_exiting)
536 {
537 mode = eRNBRunLoopModeExit;
538 }
539
540 if (set_events & RNBContext::event_read_thread_exiting)
541 {
542 // Out remote packet receiving thread exited, exit for now.
543 if (ctx.HasValidProcessID())
544 {
545 // TODO: We should add code that will leave the current process
546 // in its current state and listen for another connection...
547 if (ctx.ProcessStateRunning())
548 {
549 DNBProcessKill (ctx.ProcessID());
550 }
551 }
552 mode = eRNBRunLoopModeExit;
553 }
554 }
555
556 // Reset all event bits that weren't reset for now...
557 if (set_events != 0)
558 ctx.Events().ResetEvents(set_events);
559
560 if (mode != eRNBRunLoopModeInferiorExecuting)
561 break;
562 }
563
564 return mode;
565}
566
567
568//----------------------------------------------------------------------
569// Convenience function to set up the remote listening port
570// Returns 1 for success 0 for failure.
571//----------------------------------------------------------------------
572
573static int
Greg Clayton6779606a2011-01-22 23:43:18 +0000574StartListening (RNBRemote *remote, int listen_port)
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000575{
Greg Clayton6779606a2011-01-22 23:43:18 +0000576 if (!remote->Comm().IsConnected())
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000577 {
578 RNBLogSTDOUT ("Listening to port %i...\n", listen_port);
Greg Clayton6779606a2011-01-22 23:43:18 +0000579 if (remote->Comm().Listen(listen_port) != rnb_success)
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000580 {
581 RNBLogSTDERR ("Failed to get connection from a remote gdb process.\n");
582 return 0;
583 }
584 else
585 {
Greg Clayton6779606a2011-01-22 23:43:18 +0000586 remote->StartReadRemoteDataThread();
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000587 }
588 }
589 return 1;
590}
591
592//----------------------------------------------------------------------
593// ASL Logging callback that can be registered with DNBLogSetLogCallback
594//----------------------------------------------------------------------
595void
596ASLLogCallback(void *baton, uint32_t flags, const char *format, va_list args)
597{
598 if (format == NULL)
599 return;
600 static aslmsg g_aslmsg = NULL;
601 if (g_aslmsg == NULL)
602 {
603 g_aslmsg = ::asl_new (ASL_TYPE_MSG);
604 char asl_key_sender[PATH_MAX];
605 snprintf(asl_key_sender, sizeof(asl_key_sender), "com.apple.%s-%g", DEBUGSERVER_PROGRAM_NAME, DEBUGSERVER_VERSION_NUM);
606 ::asl_set (g_aslmsg, ASL_KEY_SENDER, asl_key_sender);
607 }
608
609 int asl_level;
610 if (flags & DNBLOG_FLAG_FATAL) asl_level = ASL_LEVEL_CRIT;
611 else if (flags & DNBLOG_FLAG_ERROR) asl_level = ASL_LEVEL_ERR;
612 else if (flags & DNBLOG_FLAG_WARNING) asl_level = ASL_LEVEL_WARNING;
613 else if (flags & DNBLOG_FLAG_VERBOSE) asl_level = ASL_LEVEL_WARNING; //ASL_LEVEL_INFO;
614 else asl_level = ASL_LEVEL_WARNING; //ASL_LEVEL_DEBUG;
615
616 ::asl_vlog (NULL, g_aslmsg, asl_level, format, args);
617}
618
619//----------------------------------------------------------------------
620// FILE based Logging callback that can be registered with
621// DNBLogSetLogCallback
622//----------------------------------------------------------------------
623void
624FileLogCallback(void *baton, uint32_t flags, const char *format, va_list args)
625{
626 if (baton == NULL || format == NULL)
627 return;
628
629 ::vfprintf ((FILE *)baton, format, args);
630 ::fprintf ((FILE *)baton, "\n");
631}
632
633
634void
635show_usage_and_exit (int exit_code)
636{
637 RNBLogSTDERR ("Usage:\n %s host:port [program-name program-arg1 program-arg2 ...]\n", DEBUGSERVER_PROGRAM_NAME);
638 RNBLogSTDERR (" %s /path/file [program-name program-arg1 program-arg2 ...]\n", DEBUGSERVER_PROGRAM_NAME);
639 RNBLogSTDERR (" %s host:port --attach=<pid>\n", DEBUGSERVER_PROGRAM_NAME);
640 RNBLogSTDERR (" %s /path/file --attach=<pid>\n", DEBUGSERVER_PROGRAM_NAME);
641 RNBLogSTDERR (" %s host:port --attach=<process_name>\n", DEBUGSERVER_PROGRAM_NAME);
642 RNBLogSTDERR (" %s /path/file --attach=<process_name>\n", DEBUGSERVER_PROGRAM_NAME);
643 exit (exit_code);
644}
645
646
647//----------------------------------------------------------------------
648// option descriptors for getopt_long()
649//----------------------------------------------------------------------
650static struct option g_long_options[] =
651{
652 { "attach", required_argument, NULL, 'a' },
Greg Clayton3af9ea52010-11-18 05:57:03 +0000653 { "arch", required_argument, NULL, 'A' },
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000654 { "debug", no_argument, NULL, 'g' },
655 { "verbose", no_argument, NULL, 'v' },
656 { "lockdown", no_argument, &g_lockdown_opt, 1 }, // short option "-k"
657 { "applist", no_argument, &g_applist_opt, 1 }, // short option "-t"
658 { "log-file", required_argument, NULL, 'l' },
659 { "log-flags", required_argument, NULL, 'f' },
660 { "launch", required_argument, NULL, 'x' }, // Valid values are "auto", "posix-spawn", "fork-exec", "springboard" (arm only)
661 { "waitfor", required_argument, NULL, 'w' }, // Wait for a process whose name starts with ARG
662 { "waitfor-interval", required_argument, NULL, 'i' }, // Time in usecs to wait between sampling the pid list when waiting for a process by name
663 { "waitfor-duration", required_argument, NULL, 'd' }, // The time in seconds to wait for a process to show up by name
664 { "native-regs", no_argument, NULL, 'r' }, // Specify to use the native registers instead of the gdb defaults for the architecture.
Greg Claytonbd82a5d2011-01-23 05:56:20 +0000665 { "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)
666 { "stdin-path", required_argument, NULL, 'I' }, // Set the STDIN path to be used when launching applications (only if debugserver launches the process)
Johnny Chena3435452011-01-25 16:56:01 +0000667 { "stdout-path", required_argument, NULL, 'O' }, // Set the STDOUT path to be used when launching applications (only if debugserver launches the process)
668 { "stderr-path", required_argument, NULL, 'E' }, // Set the STDERR path to be used when launching applications (only if debugserver launches the process)
Greg Claytonbd82a5d2011-01-23 05:56:20 +0000669 { "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)
670 { "setsid", no_argument, NULL, 'S' }, // call setsid() to make debugserver run in its own session
Greg Claytonf681b942010-08-31 18:35:14 +0000671 { "disable-aslr", no_argument, NULL, 'D' }, // Use _POSIX_SPAWN_DISABLE_ASLR to avoid shared library randomization
Greg Claytonbd82a5d2011-01-23 05:56:20 +0000672 { "working-dir", required_argument, NULL, 'W' }, // The working directory that the inferior process should have (only if debugserver launches the process)
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000673 { NULL, 0, NULL, 0 }
674};
675
676
677//----------------------------------------------------------------------
678// main
679//----------------------------------------------------------------------
680int
681main (int argc, char *argv[])
682{
683 g_isatty = ::isatty (STDIN_FILENO);
684
685 // ::printf ("uid=%u euid=%u gid=%u egid=%u\n",
686 // getuid(),
687 // geteuid(),
688 // getgid(),
689 // getegid());
690
691
692 // signal (SIGINT, signal_handler);
693 signal (SIGPIPE, signal_handler);
Greg Clayton3382c2c2010-07-30 23:14:42 +0000694 signal (SIGHUP, signal_handler);
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000695
Greg Clayton71337622011-02-24 22:24:29 +0000696 g_remoteSP.reset (new RNBRemote ());
697
698
699 RNBRemote *remote = g_remoteSP.get();
700 if (remote == NULL)
701 {
702 RNBLogSTDERR ("error: failed to create a remote connection class\n");
703 return -1;
704 }
705
706 RNBContext& ctx = remote->Context();
707
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000708 int i;
709 int attach_pid = INVALID_NUB_PROCESS;
710
711 FILE* log_file = NULL;
712 uint32_t log_flags = 0;
713 // Parse our options
714 int ch;
715 int long_option_index = 0;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000716 int debug = 0;
717 std::string compile_options;
718 std::string waitfor_pid_name; // Wait for a process that starts with this name
719 std::string attach_pid_name;
Greg Clayton3af9ea52010-11-18 05:57:03 +0000720 std::string arch_name;
Greg Claytonbd82a5d2011-01-23 05:56:20 +0000721 std::string working_dir; // The new working directory to use for the inferior
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000722 useconds_t waitfor_interval = 1000; // Time in usecs between process lists polls when waiting for a process by name, default 1 msec.
723 useconds_t waitfor_duration = 0; // Time in seconds to wait for a process by name, 0 means wait forever.
Caroline Ticef8da8632010-12-03 18:46:09 +0000724 bool no_stdio = false;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000725
726#if !defined (DNBLOG_ENABLED)
727 compile_options += "(no-logging) ";
728#endif
729
730 RNBRunLoopMode start_mode = eRNBRunLoopModeExit;
731
Caroline Ticef8da8632010-12-03 18:46:09 +0000732 while ((ch = getopt_long(argc, argv, "a:A:d:gi:vktl:f:w:x:rs:n", g_long_options, &long_option_index)) != -1)
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000733 {
734 DNBLogDebug("option: ch == %c (0x%2.2x) --%s%c%s\n",
735 ch, (uint8_t)ch,
736 g_long_options[long_option_index].name,
737 g_long_options[long_option_index].has_arg ? '=' : ' ',
738 optarg ? optarg : "");
739 switch (ch)
740 {
741 case 0: // Any optional that auto set themselves will return 0
742 break;
743
Greg Clayton3af9ea52010-11-18 05:57:03 +0000744 case 'A':
745 if (optarg && optarg[0])
746 arch_name.assign(optarg);
747 break;
748
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000749 case 'a':
750 if (optarg && optarg[0])
751 {
752 if (isdigit(optarg[0]))
753 {
754 char *end = NULL;
755 attach_pid = strtoul(optarg, &end, 0);
756 if (end == NULL || *end != '\0')
757 {
758 RNBLogSTDERR ("error: invalid pid option '%s'\n", optarg);
759 exit (4);
760 }
761 }
762 else
763 {
764 attach_pid_name = optarg;
765 }
766 start_mode = eRNBRunLoopModeInferiorAttaching;
767 }
768 break;
769
770 // --waitfor=NAME
771 case 'w':
772 if (optarg && optarg[0])
773 {
774 waitfor_pid_name = optarg;
775 start_mode = eRNBRunLoopModeInferiorAttaching;
776 }
777 break;
778
779 // --waitfor-interval=USEC
780 case 'i':
781 if (optarg && optarg[0])
782 {
783 char *end = NULL;
784 waitfor_interval = strtoul(optarg, &end, 0);
785 if (end == NULL || *end != '\0')
786 {
787 RNBLogSTDERR ("error: invalid waitfor-interval option value '%s'.\n", optarg);
788 exit (6);
789 }
790 }
791 break;
792
793 // --waitfor-duration=SEC
794 case 'd':
795 if (optarg && optarg[0])
796 {
797 char *end = NULL;
798 waitfor_duration = strtoul(optarg, &end, 0);
799 if (end == NULL || *end != '\0')
800 {
801 RNBLogSTDERR ("error: invalid waitfor-duration option value '%s'.\n", optarg);
802 exit (7);
803 }
804 }
805 break;
806
Greg Claytonbd82a5d2011-01-23 05:56:20 +0000807 case 'W':
Greg Clayton6779606a2011-01-22 23:43:18 +0000808 if (optarg && optarg[0])
Greg Claytonbd82a5d2011-01-23 05:56:20 +0000809 working_dir.assign(optarg);
Greg Clayton6779606a2011-01-22 23:43:18 +0000810 break;
811
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000812 case 'x':
813 if (optarg && optarg[0])
814 {
815 if (strcasecmp(optarg, "auto") == 0)
816 g_launch_flavor = eLaunchFlavorDefault;
817 else if (strcasestr(optarg, "posix") == optarg)
818 g_launch_flavor = eLaunchFlavorPosixSpawn;
819 else if (strcasestr(optarg, "fork") == optarg)
820 g_launch_flavor = eLaunchFlavorForkExec;
821#if defined (__arm__)
822 else if (strcasestr(optarg, "spring") == optarg)
823 g_launch_flavor = eLaunchFlavorSpringBoard;
824#endif
825 else
826 {
827 RNBLogSTDERR ("error: invalid TYPE for the --launch=TYPE (-x TYPE) option: '%s'\n", optarg);
828 RNBLogSTDERR ("Valid values TYPE are:\n");
829 RNBLogSTDERR (" auto Auto-detect the best launch method to use.\n");
830 RNBLogSTDERR (" posix Launch the executable using posix_spawn.\n");
831 RNBLogSTDERR (" fork Launch the executable using fork and exec.\n");
832#if defined (__arm__)
833 RNBLogSTDERR (" spring Launch the executable through Springboard.\n");
834#endif
835 exit (5);
836 }
837 }
838 break;
839
840 case 'l': // Set Log File
841 if (optarg && optarg[0])
842 {
843 if (strcasecmp(optarg, "stdout") == 0)
844 log_file = stdout;
845 else if (strcasecmp(optarg, "stderr") == 0)
846 log_file = stderr;
847 else
Jim Inghamc530be62011-01-24 03:46:59 +0000848 {
Greg Claytonbd82a5d2011-01-23 05:56:20 +0000849 log_file = fopen(optarg, "w");
Jim Inghamc530be62011-01-24 03:46:59 +0000850 if (log_file != NULL)
851 setlinebuf(log_file);
852 }
853
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000854 if (log_file == NULL)
855 {
856 const char *errno_str = strerror(errno);
857 RNBLogSTDERR ("Failed to open log file '%s' for writing: errno = %i (%s)", optarg, errno, errno_str ? errno_str : "unknown error");
858 }
859 }
860 break;
861
862 case 'f': // Log Flags
863 if (optarg && optarg[0])
864 log_flags = strtoul(optarg, NULL, 0);
865 break;
866
867 case 'g':
868 debug = 1;
869 DNBLogSetDebug(1);
870 break;
871
872 case 't':
873 g_applist_opt = 1;
874 break;
875
876 case 'k':
877 g_lockdown_opt = 1;
878 break;
879
880 case 'r':
Greg Clayton71337622011-02-24 22:24:29 +0000881 remote->SetUseNativeRegisters (true);
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000882 break;
883
884 case 'v':
885 DNBLogSetVerbose(1);
886 break;
887
888 case 's':
Greg Clayton71337622011-02-24 22:24:29 +0000889 ctx.GetSTDIN().assign(optarg);
890 ctx.GetSTDOUT().assign(optarg);
891 ctx.GetSTDERR().assign(optarg);
Greg Clayton6779606a2011-01-22 23:43:18 +0000892 break;
893
894 case 'I':
Greg Clayton71337622011-02-24 22:24:29 +0000895 ctx.GetSTDIN().assign(optarg);
Greg Clayton6779606a2011-01-22 23:43:18 +0000896 break;
897
898 case 'O':
Greg Clayton71337622011-02-24 22:24:29 +0000899 ctx.GetSTDOUT().assign(optarg);
Greg Clayton6779606a2011-01-22 23:43:18 +0000900 break;
901
902 case 'E':
Greg Clayton71337622011-02-24 22:24:29 +0000903 ctx.GetSTDERR().assign(optarg);
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000904 break;
905
Caroline Ticef8da8632010-12-03 18:46:09 +0000906 case 'n':
907 no_stdio = true;
908 break;
909
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000910 case 'S':
911 // Put debugserver into a new session. Terminals group processes
912 // into sessions and when a special terminal key sequences
913 // (like control+c) are typed they can cause signals to go out to
914 // all processes in a session. Using this --setsid (-S) option
915 // will cause debugserver to run in its own sessions and be free
916 // from such issues.
917 //
918 // This is useful when debugserver is spawned from a command
919 // line application that uses debugserver to do the debugging,
920 // yet that application doesn't want debugserver receiving the
921 // signals sent to the session (i.e. dying when anyone hits ^C).
922 setsid();
923 break;
Greg Claytonf681b942010-08-31 18:35:14 +0000924 case 'D':
925 g_disable_aslr = 1;
926 break;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000927 }
928 }
Greg Clayton3af9ea52010-11-18 05:57:03 +0000929
930 if (arch_name.empty())
931 {
Greg Clayton71337622011-02-24 22:24:29 +0000932#if defined (__arm__)
Greg Clayton3af9ea52010-11-18 05:57:03 +0000933 arch_name.assign ("arm");
934#endif
935 }
Greg Clayton3c144382010-12-01 22:45:40 +0000936 else
937 {
938 DNBSetArchitecture (arch_name.c_str());
939 }
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000940
Greg Clayton71337622011-02-24 22:24:29 +0000941// if (arch_name.empty())
942// {
943// fprintf(stderr, "error: no architecture was specified\n");
944// exit (8);
945// }
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000946 // Skip any options we consumed with getopt_long
947 argc -= optind;
948 argv += optind;
949
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000950
Greg Claytonbd82a5d2011-01-23 05:56:20 +0000951 if (!working_dir.empty())
Greg Clayton6779606a2011-01-22 23:43:18 +0000952 {
Greg Claytonbd82a5d2011-01-23 05:56:20 +0000953 if (remote->Context().SetWorkingDirectory (working_dir.c_str()) == false)
Greg Clayton6779606a2011-01-22 23:43:18 +0000954 {
Greg Claytonbd82a5d2011-01-23 05:56:20 +0000955 RNBLogSTDERR ("error: working directory doesn't exist '%s'.\n", working_dir.c_str());
Greg Clayton6779606a2011-01-22 23:43:18 +0000956 exit (8);
957 }
958 }
959
960 remote->Initialize();
Greg Clayton3af9ea52010-11-18 05:57:03 +0000961
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000962 // It is ok for us to set NULL as the logfile (this will disable any logging)
963
964 if (log_file != NULL)
965 {
966 DNBLogSetLogCallback(FileLogCallback, log_file);
967 // If our log file was set, yet we have no log flags, log everything!
968 if (log_flags == 0)
969 log_flags = LOG_ALL | LOG_RNB_ALL;
970
971 DNBLogSetLogMask (log_flags);
972 }
973 else
974 {
975 // Enable DNB logging
976 DNBLogSetLogCallback(ASLLogCallback, NULL);
977 DNBLogSetLogMask (log_flags);
978
979 }
980
981 if (DNBLogEnabled())
982 {
983 for (i=0; i<argc; i++)
984 DNBLogDebug("argv[%i] = %s", i, argv[i]);
985 }
986
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000987 // as long as we're dropping remotenub in as a replacement for gdbserver,
988 // explicitly note that this is not gdbserver.
989
990 RNBLogSTDOUT ("%s-%g %sfor %s.\n",
991 DEBUGSERVER_PROGRAM_NAME,
992 DEBUGSERVER_VERSION_NUM,
993 compile_options.c_str(),
994 RNB_ARCH);
995
996 int listen_port = INT32_MAX;
997 char str[PATH_MAX];
998
999 if (g_lockdown_opt == 0 && g_applist_opt == 0)
1000 {
1001 // Make sure we at least have port
1002 if (argc < 1)
1003 {
1004 show_usage_and_exit (1);
1005 }
1006 // accept 'localhost:' prefix on port number
1007
1008 int items_scanned = ::sscanf (argv[0], "%[^:]:%i", str, &listen_port);
1009 if (items_scanned == 2)
1010 {
1011 DNBLogDebug("host = '%s' port = %i", str, listen_port);
1012 }
1013 else if (argv[0][0] == '/')
1014 {
1015 listen_port = INT32_MAX;
1016 strncpy(str, argv[0], sizeof(str));
1017 }
1018 else
1019 {
1020 show_usage_and_exit (2);
1021 }
1022
1023 // We just used the 'host:port' or the '/path/file' arg...
1024 argc--;
1025 argv++;
1026
1027 }
1028
1029 // If we know we're waiting to attach, we don't need any of this other info.
1030 if (start_mode != eRNBRunLoopModeInferiorAttaching)
1031 {
1032 if (argc == 0 || g_lockdown_opt)
1033 {
1034 if (g_lockdown_opt != 0)
1035 {
1036 // Work around for SIGPIPE crashes due to posix_spawn issue.
1037 // We have to close STDOUT and STDERR, else the first time we
1038 // try and do any, we get SIGPIPE and die as posix_spawn is
1039 // doing bad things with our file descriptors at the moment.
1040 int null = open("/dev/null", O_RDWR);
1041 dup2(null, STDOUT_FILENO);
1042 dup2(null, STDERR_FILENO);
1043 }
1044 else if (g_applist_opt != 0)
1045 {
1046 // List all applications we are able to see
1047 std::string applist_plist;
1048 int err = ListApplications(applist_plist, false, false);
1049 if (err == 0)
1050 {
1051 fputs (applist_plist.c_str(), stdout);
1052 }
1053 else
1054 {
1055 RNBLogSTDERR ("error: ListApplications returned error %i\n", err);
1056 }
1057 // Exit with appropriate error if we were asked to list the applications
1058 // with no other args were given (and we weren't trying to do this over
1059 // lockdown)
1060 return err;
1061 }
1062
1063 DNBLogDebug("Get args from remote protocol...");
1064 start_mode = eRNBRunLoopModeGetStartModeFromRemoteProtocol;
1065 }
1066 else
1067 {
1068 start_mode = eRNBRunLoopModeInferiorLaunching;
1069 // Fill in the argv array in the context from the rest of our args.
1070 // Skip the name of this executable and the port number
1071 for (int i = 0; i < argc; i++)
1072 {
1073 DNBLogDebug("inferior_argv[%i] = '%s'", i, argv[i]);
1074 ctx.PushArgument (argv[i]);
1075 }
1076 }
1077 }
1078
1079 if (start_mode == eRNBRunLoopModeExit)
1080 return -1;
1081
1082 RNBRunLoopMode mode = start_mode;
1083 char err_str[1024] = {'\0'};
1084
1085 while (mode != eRNBRunLoopModeExit)
1086 {
1087 switch (mode)
1088 {
1089 case eRNBRunLoopModeGetStartModeFromRemoteProtocol:
1090#if defined (__arm__)
1091 if (g_lockdown_opt)
1092 {
Greg Clayton6779606a2011-01-22 23:43:18 +00001093 if (!remote->Comm().IsConnected())
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001094 {
Greg Clayton6779606a2011-01-22 23:43:18 +00001095 if (remote->Comm().ConnectToService () != rnb_success)
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001096 {
1097 RNBLogSTDERR ("Failed to get connection from a remote gdb process.\n");
1098 mode = eRNBRunLoopModeExit;
1099 }
1100 else if (g_applist_opt != 0)
1101 {
1102 // List all applications we are able to see
1103 std::string applist_plist;
1104 if (ListApplications(applist_plist, false, false) == 0)
1105 {
1106 DNBLogDebug("Task list: %s", applist_plist.c_str());
1107
Greg Clayton6779606a2011-01-22 23:43:18 +00001108 remote->Comm().Write(applist_plist.c_str(), applist_plist.size());
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001109 // Issue a read that will never yield any data until the other side
1110 // closes the socket so this process doesn't just exit and cause the
1111 // socket to close prematurely on the other end and cause data loss.
1112 std::string buf;
Greg Clayton6779606a2011-01-22 23:43:18 +00001113 remote->Comm().Read(buf);
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001114 }
Greg Clayton6779606a2011-01-22 23:43:18 +00001115 remote->Comm().Disconnect(false);
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001116 mode = eRNBRunLoopModeExit;
1117 break;
1118 }
1119 else
1120 {
1121 // Start watching for remote packets
Greg Clayton6779606a2011-01-22 23:43:18 +00001122 remote->StartReadRemoteDataThread();
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001123 }
1124 }
1125 }
1126 else
1127#endif
1128 if (listen_port != INT32_MAX)
1129 {
Greg Clayton6779606a2011-01-22 23:43:18 +00001130 if (!StartListening (remote, listen_port))
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001131 mode = eRNBRunLoopModeExit;
1132 }
1133 else if (str[0] == '/')
1134 {
Greg Clayton6779606a2011-01-22 23:43:18 +00001135 if (remote->Comm().OpenFile (str))
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001136 mode = eRNBRunLoopModeExit;
1137 }
1138 if (mode != eRNBRunLoopModeExit)
1139 {
1140 RNBLogSTDOUT ("Got a connection, waiting for process information for launching or attaching.\n");
1141
Greg Clayton6779606a2011-01-22 23:43:18 +00001142 mode = RNBRunLoopGetStartModeFromRemote (remote);
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001143 }
1144 break;
1145
1146 case eRNBRunLoopModeInferiorAttaching:
1147 if (!waitfor_pid_name.empty())
1148 {
1149 // Set our end wait time if we are using a waitfor-duration
1150 // option that may have been specified
1151 struct timespec attach_timeout_abstime, *timeout_ptr = NULL;
1152 if (waitfor_duration != 0)
1153 {
1154 DNBTimer::OffsetTimeOfDay(&attach_timeout_abstime, waitfor_duration, 0);
1155 timeout_ptr = &attach_timeout_abstime;
1156 }
1157 nub_launch_flavor_t launch_flavor = g_launch_flavor;
1158 if (launch_flavor == eLaunchFlavorDefault)
1159 {
1160 // Our default launch method is posix spawn
1161 launch_flavor = eLaunchFlavorPosixSpawn;
1162
1163#if defined (__arm__)
1164 // Check if we have an app bundle, if so launch using SpringBoard.
1165 if (waitfor_pid_name.find (".app") != std::string::npos)
1166 {
1167 launch_flavor = eLaunchFlavorSpringBoard;
1168 }
1169#endif
1170 }
1171
1172 ctx.SetLaunchFlavor(launch_flavor);
1173
1174 nub_process_t pid = DNBProcessAttachWait (waitfor_pid_name.c_str(), launch_flavor, timeout_ptr, waitfor_interval, err_str, sizeof(err_str));
1175 g_pid = pid;
1176
1177 if (pid == INVALID_NUB_PROCESS)
1178 {
1179 ctx.LaunchStatus().SetError(-1, DNBError::Generic);
1180 if (err_str[0])
1181 ctx.LaunchStatus().SetErrorString(err_str);
1182 RNBLogSTDERR ("error: failed to attach to process named: \"%s\" %s", waitfor_pid_name.c_str(), err_str);
1183 mode = eRNBRunLoopModeExit;
1184 }
1185 else
1186 {
1187 ctx.SetProcessID(pid);
1188 mode = eRNBRunLoopModeInferiorExecuting;
1189 }
1190 }
1191 else if (attach_pid != INVALID_NUB_PROCESS)
1192 {
1193
1194 RNBLogSTDOUT ("Attaching to process %i...\n", attach_pid);
1195 nub_process_t attached_pid;
Greg Clayton6779606a2011-01-22 23:43:18 +00001196 mode = RNBRunLoopLaunchAttaching (remote, attach_pid, attached_pid);
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001197 if (mode != eRNBRunLoopModeInferiorExecuting)
1198 {
1199 const char *error_str = remote->Context().LaunchStatus().AsString();
1200 RNBLogSTDERR ("error: failed to attach process %i: %s\n", attach_pid, error_str ? error_str : "unknown error.");
1201 mode = eRNBRunLoopModeExit;
1202 }
1203 }
1204 else if (!attach_pid_name.empty ())
1205 {
1206 struct timespec attach_timeout_abstime, *timeout_ptr = NULL;
1207 if (waitfor_duration != 0)
1208 {
1209 DNBTimer::OffsetTimeOfDay(&attach_timeout_abstime, waitfor_duration, 0);
1210 timeout_ptr = &attach_timeout_abstime;
1211 }
1212
1213 nub_process_t pid = DNBProcessAttachByName (attach_pid_name.c_str(), timeout_ptr, err_str, sizeof(err_str));
1214 g_pid = pid;
1215 if (pid == INVALID_NUB_PROCESS)
1216 {
1217 ctx.LaunchStatus().SetError(-1, DNBError::Generic);
1218 if (err_str[0])
1219 ctx.LaunchStatus().SetErrorString(err_str);
1220 RNBLogSTDERR ("error: failed to attach to process named: \"%s\" %s", waitfor_pid_name.c_str(), err_str);
1221 mode = eRNBRunLoopModeExit;
1222 }
1223 else
1224 {
1225 ctx.SetProcessID(pid);
1226 mode = eRNBRunLoopModeInferiorExecuting;
1227 }
1228
1229 }
1230 else
1231 {
1232 RNBLogSTDERR ("error: asked to attach with empty name and invalid PID.");
1233 mode = eRNBRunLoopModeExit;
1234 }
1235
1236 if (mode != eRNBRunLoopModeExit)
1237 {
1238 if (listen_port != INT32_MAX)
1239 {
Greg Clayton6779606a2011-01-22 23:43:18 +00001240 if (!StartListening (remote, listen_port))
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001241 mode = eRNBRunLoopModeExit;
1242 }
1243 else if (str[0] == '/')
1244 {
Greg Clayton6779606a2011-01-22 23:43:18 +00001245 if (remote->Comm().OpenFile (str))
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001246 mode = eRNBRunLoopModeExit;
1247 }
1248 if (mode != eRNBRunLoopModeExit)
1249 RNBLogSTDOUT ("Got a connection, waiting for debugger instructions for process %d.\n", attach_pid);
1250 }
1251 break;
1252
1253 case eRNBRunLoopModeInferiorLaunching:
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001254 {
Greg Clayton6779606a2011-01-22 23:43:18 +00001255 mode = RNBRunLoopLaunchInferior (remote,
Greg Clayton71337622011-02-24 22:24:29 +00001256 ctx.GetSTDINPath(),
1257 ctx.GetSTDOUTPath(),
1258 ctx.GetSTDERRPath(),
Greg Clayton6779606a2011-01-22 23:43:18 +00001259 no_stdio);
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001260
Greg Clayton6779606a2011-01-22 23:43:18 +00001261 if (mode == eRNBRunLoopModeInferiorExecuting)
1262 {
1263 if (listen_port != INT32_MAX)
1264 {
1265 if (!StartListening (remote, listen_port))
1266 mode = eRNBRunLoopModeExit;
1267 }
1268 else if (str[0] == '/')
1269 {
1270 if (remote->Comm().OpenFile (str))
1271 mode = eRNBRunLoopModeExit;
1272 }
1273
1274 if (mode != eRNBRunLoopModeExit)
1275 RNBLogSTDOUT ("Got a connection, waiting for debugger instructions.\n");
1276 }
1277 else
1278 {
1279 const char *error_str = remote->Context().LaunchStatus().AsString();
1280 RNBLogSTDERR ("error: failed to launch process %s: %s\n", argv[0], error_str ? error_str : "unknown error.");
1281 }
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001282 }
1283 break;
1284
1285 case eRNBRunLoopModeInferiorExecuting:
Greg Clayton6779606a2011-01-22 23:43:18 +00001286 mode = RNBRunLoopInferiorExecuting(remote);
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001287 break;
1288
1289 default:
1290 mode = eRNBRunLoopModeExit;
1291 case eRNBRunLoopModeExit:
1292 break;
1293 }
1294 }
1295
Greg Clayton6779606a2011-01-22 23:43:18 +00001296 remote->StopReadRemoteDataThread ();
1297 remote->Context().SetProcessID(INVALID_NUB_PROCESS);
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001298
1299 return 0;
1300}