blob: 8fab13f2dd7368bc1aa7d150253edc01099c1cf5 [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';
207 nub_process_t pid = DNBProcessLaunch (resolved_path,
208 &inferior_argv[0],
209 &inferior_envp[0],
Greg Clayton6779606a2011-01-22 23:43:18 +0000210 ctx.GetWorkingDirectory(),
211 stdin_path,
212 stdout_path,
213 stderr_path,
Caroline Ticef8da8632010-12-03 18:46:09 +0000214 no_stdio,
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000215 launch_flavor,
Greg Claytonf681b942010-08-31 18:35:14 +0000216 g_disable_aslr,
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000217 launch_err_str,
218 sizeof(launch_err_str));
219
220 g_pid = pid;
221
222 if (pid == INVALID_NUB_PROCESS && strlen(launch_err_str) > 0)
223 {
Greg Clayton3382c2c2010-07-30 23:14:42 +0000224 DNBLogThreaded ("%s DNBProcessLaunch() returned error: '%s'", __FUNCTION__, launch_err_str);
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000225 ctx.LaunchStatus().SetError(-1, DNBError::Generic);
226 ctx.LaunchStatus().SetErrorString(launch_err_str);
227 }
228 else
Greg Clayton71337622011-02-24 22:24:29 +0000229 {
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000230 ctx.LaunchStatus().Clear();
Greg Clayton71337622011-02-24 22:24:29 +0000231 }
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000232
233 if (remote->Comm().IsConnected())
234 {
235 // It we are connected already, the next thing gdb will do is ask
236 // whether the launch succeeded, and if not, whether there is an
237 // error code. So we need to fetch one packet from gdb before we wait
238 // on the stop from the target.
239
240 uint32_t event_mask = RNBContext::event_read_packet_available;
241 nub_event_t set_events = ctx.Events().WaitForSetEvents(event_mask);
242
243 if (set_events & RNBContext::event_read_packet_available)
244 {
245 rnb_err_t err = rnb_err;
246 RNBRemote::PacketEnum type;
247
248 err = remote->HandleReceivedPacket (&type);
249
250 if (err != rnb_success)
251 {
252 DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s Error getting packet.", __FUNCTION__);
253 return eRNBRunLoopModeExit;
254 }
255 if (type != RNBRemote::query_launch_success)
256 {
257 DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s Didn't get the expected qLaunchSuccess packet.", __FUNCTION__);
258 }
259 }
260 }
261
262 while (pid != INVALID_NUB_PROCESS)
263 {
264 // Wait for process to start up and hit entry point
265 DNBLogThreadedIf (LOG_RNB_EVENTS, "%s DNBProcessWaitForEvent (%4.4x, eEventProcessRunningStateChanged | eEventProcessStoppedStateChanged, true, INFINITE)...", __FUNCTION__, pid);
266 nub_event_t set_events = DNBProcessWaitForEvents (pid, eEventProcessRunningStateChanged | eEventProcessStoppedStateChanged, true, NULL);
267 DNBLogThreadedIf (LOG_RNB_EVENTS, "%s DNBProcessWaitForEvent (%4.4x, eEventProcessRunningStateChanged | eEventProcessStoppedStateChanged, true, INFINITE) => 0x%8.8x", __FUNCTION__, pid, set_events);
268
269 if (set_events == 0)
270 {
271 pid = INVALID_NUB_PROCESS;
272 g_pid = pid;
273 }
274 else
275 {
276 if (set_events & (eEventProcessRunningStateChanged | eEventProcessStoppedStateChanged))
277 {
278 nub_state_t pid_state = DNBProcessGetState (pid);
279 DNBLogThreadedIf (LOG_RNB_EVENTS, "%s process %4.4x state changed (eEventProcessStateChanged): %s", __FUNCTION__, pid, DNBStateAsString(pid_state));
280
281 switch (pid_state)
282 {
283 default:
284 case eStateInvalid:
285 case eStateUnloaded:
286 case eStateAttaching:
287 case eStateLaunching:
288 case eStateSuspended:
289 break; // Ignore
290
291 case eStateRunning:
292 case eStateStepping:
293 // Still waiting to stop at entry point...
294 break;
295
296 case eStateStopped:
297 case eStateCrashed:
298 ctx.SetProcessID(pid);
299 return eRNBRunLoopModeInferiorExecuting;
300
301 case eStateDetached:
302 case eStateExited:
303 pid = INVALID_NUB_PROCESS;
304 g_pid = pid;
305 return eRNBRunLoopModeExit;
306 }
307 }
308
309 DNBProcessResetEvents(pid, set_events);
310 }
311 }
312
313 return eRNBRunLoopModeExit;
314}
315
316
317//----------------------------------------------------------------------
318// This run loop mode will wait for the process to launch and hit its
319// entry point. It will currently ignore all events except for the
320// process state changed event, where it watches for the process stopped
321// or crash process state.
322//----------------------------------------------------------------------
323RNBRunLoopMode
Greg Clayton6779606a2011-01-22 23:43:18 +0000324RNBRunLoopLaunchAttaching (RNBRemote *remote, nub_process_t attach_pid, nub_process_t& pid)
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000325{
326 RNBContext& ctx = remote->Context();
327
328 DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s Attaching to pid %i...", __FUNCTION__, attach_pid);
329 char err_str[1024];
330 pid = DNBProcessAttach (attach_pid, NULL, err_str, sizeof(err_str));
331 g_pid = pid;
332
333 if (pid == INVALID_NUB_PROCESS)
334 {
335 ctx.LaunchStatus().SetError(-1, DNBError::Generic);
336 if (err_str[0])
337 ctx.LaunchStatus().SetErrorString(err_str);
338 return eRNBRunLoopModeExit;
339 }
340 else
341 {
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000342 ctx.SetProcessID(pid);
343 return eRNBRunLoopModeInferiorExecuting;
344 }
345}
346
347//----------------------------------------------------------------------
348// Watch for signals:
349// SIGINT: so we can halt our inferior. (disabled for now)
350// SIGPIPE: in case our child process dies
351//----------------------------------------------------------------------
352int g_sigint_received = 0;
353int g_sigpipe_received = 0;
354void
355signal_handler(int signo)
356{
357 DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s (%s)", __FUNCTION__, SysSignal::Name(signo));
358
359 switch (signo)
360 {
361 case SIGINT:
362 g_sigint_received++;
363 if (g_pid != INVALID_NUB_PROCESS)
364 {
365 // Only send a SIGINT once...
366 if (g_sigint_received == 1)
367 {
368 switch (DNBProcessGetState (g_pid))
369 {
370 case eStateRunning:
371 case eStateStepping:
372 DNBProcessSignal (g_pid, SIGSTOP);
373 return;
374 }
375 }
376 }
377 exit (SIGINT);
378 break;
379
380 case SIGPIPE:
381 g_sigpipe_received = 1;
382 break;
383 }
384}
385
386// Return the new run loop mode based off of the current process state
387RNBRunLoopMode
Greg Clayton6779606a2011-01-22 23:43:18 +0000388HandleProcessStateChange (RNBRemote *remote, bool initialize)
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000389{
390 RNBContext& ctx = remote->Context();
391 nub_process_t pid = ctx.ProcessID();
392
393 if (pid == INVALID_NUB_PROCESS)
394 {
395 DNBLogThreadedIf (LOG_RNB_MINIMAL, "#### %s error: pid invalid, exiting...", __FUNCTION__);
396 return eRNBRunLoopModeExit;
397 }
398 nub_state_t pid_state = DNBProcessGetState (pid);
399
400 DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s (&remote, initialize=%i) pid_state = %s", __FUNCTION__, (int)initialize, DNBStateAsString (pid_state));
401
402 switch (pid_state)
403 {
404 case eStateInvalid:
405 case eStateUnloaded:
406 // Something bad happened
407 return eRNBRunLoopModeExit;
408 break;
409
410 case eStateAttaching:
411 case eStateLaunching:
412 return eRNBRunLoopModeInferiorExecuting;
413
414 case eStateSuspended:
415 case eStateCrashed:
416 case eStateStopped:
417 // If we stop due to a signal, so clear the fact that we got a SIGINT
418 // so we can stop ourselves again (but only while our inferior
419 // process is running..)
420 g_sigint_received = 0;
421 if (initialize == false)
422 {
423 // Compare the last stop count to our current notion of a stop count
424 // to make sure we don't notify more than once for a given stop.
425 nub_size_t prev_pid_stop_count = ctx.GetProcessStopCount();
426 bool pid_stop_count_changed = ctx.SetProcessStopCount(DNBProcessGetStopCount(pid));
427 if (pid_stop_count_changed)
428 {
429 remote->FlushSTDIO();
430
431 if (ctx.GetProcessStopCount() == 1)
432 {
433 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);
434 }
435 else
436 {
437
438 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);
439 remote->NotifyThatProcessStopped ();
440 }
441 }
442 else
443 {
444 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);
445 }
446 }
447 return eRNBRunLoopModeInferiorExecuting;
448
449 case eStateStepping:
450 case eStateRunning:
451 return eRNBRunLoopModeInferiorExecuting;
452
453 case eStateExited:
454 remote->HandlePacket_last_signal(NULL);
455 return eRNBRunLoopModeExit;
456
457 }
458
459 // Catch all...
460 return eRNBRunLoopModeExit;
461}
462// This function handles the case where our inferior program is stopped and
463// we are waiting for gdb remote protocol packets. When a packet occurs that
464// makes the inferior run, we need to leave this function with a new state
465// as the return code.
466RNBRunLoopMode
Greg Clayton6779606a2011-01-22 23:43:18 +0000467RNBRunLoopInferiorExecuting (RNBRemote *remote)
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000468{
469 DNBLogThreadedIf (LOG_RNB_MINIMAL, "#### %s", __FUNCTION__);
470 RNBContext& ctx = remote->Context();
471
472 // Init our mode and set 'is_running' based on the current process state
473 RNBRunLoopMode mode = HandleProcessStateChange (remote, true);
474
475 while (ctx.ProcessID() != INVALID_NUB_PROCESS)
476 {
477
478 std::string set_events_str;
479 uint32_t event_mask = ctx.NormalEventBits();
480
481 if (!ctx.ProcessStateRunning())
482 {
483 // Clear the stdio bits if we are not running so we don't send any async packets
484 event_mask &= ~RNBContext::event_proc_stdio_available;
485 }
486
487 // We want to make sure we consume all process state changes and have
488 // whomever is notifying us to wait for us to reset the event bit before
489 // continuing.
490 //ctx.Events().SetResetAckMask (RNBContext::event_proc_state_changed);
491
492 DNBLogThreadedIf (LOG_RNB_EVENTS, "%s ctx.Events().WaitForSetEvents(0x%08x) ...",__FUNCTION__, event_mask);
493 nub_event_t set_events = ctx.Events().WaitForSetEvents(event_mask);
494 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));
495
496 if (set_events)
497 {
498 if ((set_events & RNBContext::event_proc_thread_exiting) ||
499 (set_events & RNBContext::event_proc_stdio_available))
500 {
501 remote->FlushSTDIO();
502 }
503
504 if (set_events & RNBContext::event_read_packet_available)
505 {
506 // handleReceivedPacket will take care of resetting the
507 // event_read_packet_available events when there are no more...
508 set_events ^= RNBContext::event_read_packet_available;
509
510 if (ctx.ProcessStateRunning())
511 {
512 if (remote->HandleAsyncPacket() == rnb_not_connected)
513 {
514 // TODO: connect again? Exit?
515 }
516 }
517 else
518 {
519 if (remote->HandleReceivedPacket() == rnb_not_connected)
520 {
521 // TODO: connect again? Exit?
522 }
523 }
524 }
525
526 if (set_events & RNBContext::event_proc_state_changed)
527 {
528 mode = HandleProcessStateChange (remote, false);
529 ctx.Events().ResetEvents(RNBContext::event_proc_state_changed);
530 set_events ^= RNBContext::event_proc_state_changed;
531 }
532
533 if (set_events & RNBContext::event_proc_thread_exiting)
534 {
535 mode = eRNBRunLoopModeExit;
536 }
537
538 if (set_events & RNBContext::event_read_thread_exiting)
539 {
540 // Out remote packet receiving thread exited, exit for now.
541 if (ctx.HasValidProcessID())
542 {
543 // TODO: We should add code that will leave the current process
544 // in its current state and listen for another connection...
545 if (ctx.ProcessStateRunning())
546 {
547 DNBProcessKill (ctx.ProcessID());
548 }
549 }
550 mode = eRNBRunLoopModeExit;
551 }
552 }
553
554 // Reset all event bits that weren't reset for now...
555 if (set_events != 0)
556 ctx.Events().ResetEvents(set_events);
557
558 if (mode != eRNBRunLoopModeInferiorExecuting)
559 break;
560 }
561
562 return mode;
563}
564
565
566//----------------------------------------------------------------------
567// Convenience function to set up the remote listening port
568// Returns 1 for success 0 for failure.
569//----------------------------------------------------------------------
570
571static int
Greg Clayton6779606a2011-01-22 23:43:18 +0000572StartListening (RNBRemote *remote, int listen_port)
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000573{
Greg Clayton6779606a2011-01-22 23:43:18 +0000574 if (!remote->Comm().IsConnected())
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000575 {
576 RNBLogSTDOUT ("Listening to port %i...\n", listen_port);
Greg Clayton6779606a2011-01-22 23:43:18 +0000577 if (remote->Comm().Listen(listen_port) != rnb_success)
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000578 {
579 RNBLogSTDERR ("Failed to get connection from a remote gdb process.\n");
580 return 0;
581 }
582 else
583 {
Greg Clayton6779606a2011-01-22 23:43:18 +0000584 remote->StartReadRemoteDataThread();
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000585 }
586 }
587 return 1;
588}
589
590//----------------------------------------------------------------------
591// ASL Logging callback that can be registered with DNBLogSetLogCallback
592//----------------------------------------------------------------------
593void
594ASLLogCallback(void *baton, uint32_t flags, const char *format, va_list args)
595{
596 if (format == NULL)
597 return;
598 static aslmsg g_aslmsg = NULL;
599 if (g_aslmsg == NULL)
600 {
601 g_aslmsg = ::asl_new (ASL_TYPE_MSG);
602 char asl_key_sender[PATH_MAX];
603 snprintf(asl_key_sender, sizeof(asl_key_sender), "com.apple.%s-%g", DEBUGSERVER_PROGRAM_NAME, DEBUGSERVER_VERSION_NUM);
604 ::asl_set (g_aslmsg, ASL_KEY_SENDER, asl_key_sender);
605 }
606
607 int asl_level;
608 if (flags & DNBLOG_FLAG_FATAL) asl_level = ASL_LEVEL_CRIT;
609 else if (flags & DNBLOG_FLAG_ERROR) asl_level = ASL_LEVEL_ERR;
610 else if (flags & DNBLOG_FLAG_WARNING) asl_level = ASL_LEVEL_WARNING;
611 else if (flags & DNBLOG_FLAG_VERBOSE) asl_level = ASL_LEVEL_WARNING; //ASL_LEVEL_INFO;
612 else asl_level = ASL_LEVEL_WARNING; //ASL_LEVEL_DEBUG;
613
614 ::asl_vlog (NULL, g_aslmsg, asl_level, format, args);
615}
616
617//----------------------------------------------------------------------
618// FILE based Logging callback that can be registered with
619// DNBLogSetLogCallback
620//----------------------------------------------------------------------
621void
622FileLogCallback(void *baton, uint32_t flags, const char *format, va_list args)
623{
624 if (baton == NULL || format == NULL)
625 return;
626
627 ::vfprintf ((FILE *)baton, format, args);
628 ::fprintf ((FILE *)baton, "\n");
629}
630
631
632void
633show_usage_and_exit (int exit_code)
634{
635 RNBLogSTDERR ("Usage:\n %s host:port [program-name program-arg1 program-arg2 ...]\n", DEBUGSERVER_PROGRAM_NAME);
636 RNBLogSTDERR (" %s /path/file [program-name program-arg1 program-arg2 ...]\n", DEBUGSERVER_PROGRAM_NAME);
637 RNBLogSTDERR (" %s host:port --attach=<pid>\n", DEBUGSERVER_PROGRAM_NAME);
638 RNBLogSTDERR (" %s /path/file --attach=<pid>\n", DEBUGSERVER_PROGRAM_NAME);
639 RNBLogSTDERR (" %s host:port --attach=<process_name>\n", DEBUGSERVER_PROGRAM_NAME);
640 RNBLogSTDERR (" %s /path/file --attach=<process_name>\n", DEBUGSERVER_PROGRAM_NAME);
641 exit (exit_code);
642}
643
644
645//----------------------------------------------------------------------
646// option descriptors for getopt_long()
647//----------------------------------------------------------------------
648static struct option g_long_options[] =
649{
650 { "attach", required_argument, NULL, 'a' },
Greg Clayton3af9ea52010-11-18 05:57:03 +0000651 { "arch", required_argument, NULL, 'A' },
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000652 { "debug", no_argument, NULL, 'g' },
653 { "verbose", no_argument, NULL, 'v' },
654 { "lockdown", no_argument, &g_lockdown_opt, 1 }, // short option "-k"
655 { "applist", no_argument, &g_applist_opt, 1 }, // short option "-t"
656 { "log-file", required_argument, NULL, 'l' },
657 { "log-flags", required_argument, NULL, 'f' },
658 { "launch", required_argument, NULL, 'x' }, // Valid values are "auto", "posix-spawn", "fork-exec", "springboard" (arm only)
659 { "waitfor", required_argument, NULL, 'w' }, // Wait for a process whose name starts with ARG
660 { "waitfor-interval", required_argument, NULL, 'i' }, // Time in usecs to wait between sampling the pid list when waiting for a process by name
661 { "waitfor-duration", required_argument, NULL, 'd' }, // The time in seconds to wait for a process to show up by name
662 { "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 +0000663 { "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)
664 { "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 +0000665 { "stdout-path", required_argument, NULL, 'O' }, // Set the STDOUT path to be used when launching applications (only if debugserver launches the process)
666 { "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 +0000667 { "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)
668 { "setsid", no_argument, NULL, 'S' }, // call setsid() to make debugserver run in its own session
Greg Claytonf681b942010-08-31 18:35:14 +0000669 { "disable-aslr", no_argument, NULL, 'D' }, // Use _POSIX_SPAWN_DISABLE_ASLR to avoid shared library randomization
Greg Claytonbd82a5d2011-01-23 05:56:20 +0000670 { "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 +0000671 { NULL, 0, NULL, 0 }
672};
673
674
675//----------------------------------------------------------------------
676// main
677//----------------------------------------------------------------------
678int
679main (int argc, char *argv[])
680{
681 g_isatty = ::isatty (STDIN_FILENO);
682
683 // ::printf ("uid=%u euid=%u gid=%u egid=%u\n",
684 // getuid(),
685 // geteuid(),
686 // getgid(),
687 // getegid());
688
689
690 // signal (SIGINT, signal_handler);
691 signal (SIGPIPE, signal_handler);
Greg Clayton3382c2c2010-07-30 23:14:42 +0000692 signal (SIGHUP, signal_handler);
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000693
Greg Clayton71337622011-02-24 22:24:29 +0000694 g_remoteSP.reset (new RNBRemote ());
695
696
697 RNBRemote *remote = g_remoteSP.get();
698 if (remote == NULL)
699 {
700 RNBLogSTDERR ("error: failed to create a remote connection class\n");
701 return -1;
702 }
703
704 RNBContext& ctx = remote->Context();
705
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000706 int i;
707 int attach_pid = INVALID_NUB_PROCESS;
708
709 FILE* log_file = NULL;
710 uint32_t log_flags = 0;
711 // Parse our options
712 int ch;
713 int long_option_index = 0;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000714 int debug = 0;
715 std::string compile_options;
716 std::string waitfor_pid_name; // Wait for a process that starts with this name
717 std::string attach_pid_name;
Greg Clayton3af9ea52010-11-18 05:57:03 +0000718 std::string arch_name;
Greg Claytonbd82a5d2011-01-23 05:56:20 +0000719 std::string working_dir; // The new working directory to use for the inferior
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000720 useconds_t waitfor_interval = 1000; // Time in usecs between process lists polls when waiting for a process by name, default 1 msec.
721 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 +0000722 bool no_stdio = false;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000723
724#if !defined (DNBLOG_ENABLED)
725 compile_options += "(no-logging) ";
726#endif
727
728 RNBRunLoopMode start_mode = eRNBRunLoopModeExit;
729
Caroline Ticef8da8632010-12-03 18:46:09 +0000730 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 +0000731 {
732 DNBLogDebug("option: ch == %c (0x%2.2x) --%s%c%s\n",
733 ch, (uint8_t)ch,
734 g_long_options[long_option_index].name,
735 g_long_options[long_option_index].has_arg ? '=' : ' ',
736 optarg ? optarg : "");
737 switch (ch)
738 {
739 case 0: // Any optional that auto set themselves will return 0
740 break;
741
Greg Clayton3af9ea52010-11-18 05:57:03 +0000742 case 'A':
743 if (optarg && optarg[0])
744 arch_name.assign(optarg);
745 break;
746
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000747 case 'a':
748 if (optarg && optarg[0])
749 {
750 if (isdigit(optarg[0]))
751 {
752 char *end = NULL;
753 attach_pid = strtoul(optarg, &end, 0);
754 if (end == NULL || *end != '\0')
755 {
756 RNBLogSTDERR ("error: invalid pid option '%s'\n", optarg);
757 exit (4);
758 }
759 }
760 else
761 {
762 attach_pid_name = optarg;
763 }
764 start_mode = eRNBRunLoopModeInferiorAttaching;
765 }
766 break;
767
768 // --waitfor=NAME
769 case 'w':
770 if (optarg && optarg[0])
771 {
772 waitfor_pid_name = optarg;
773 start_mode = eRNBRunLoopModeInferiorAttaching;
774 }
775 break;
776
777 // --waitfor-interval=USEC
778 case 'i':
779 if (optarg && optarg[0])
780 {
781 char *end = NULL;
782 waitfor_interval = strtoul(optarg, &end, 0);
783 if (end == NULL || *end != '\0')
784 {
785 RNBLogSTDERR ("error: invalid waitfor-interval option value '%s'.\n", optarg);
786 exit (6);
787 }
788 }
789 break;
790
791 // --waitfor-duration=SEC
792 case 'd':
793 if (optarg && optarg[0])
794 {
795 char *end = NULL;
796 waitfor_duration = strtoul(optarg, &end, 0);
797 if (end == NULL || *end != '\0')
798 {
799 RNBLogSTDERR ("error: invalid waitfor-duration option value '%s'.\n", optarg);
800 exit (7);
801 }
802 }
803 break;
804
Greg Claytonbd82a5d2011-01-23 05:56:20 +0000805 case 'W':
Greg Clayton6779606a2011-01-22 23:43:18 +0000806 if (optarg && optarg[0])
Greg Claytonbd82a5d2011-01-23 05:56:20 +0000807 working_dir.assign(optarg);
Greg Clayton6779606a2011-01-22 23:43:18 +0000808 break;
809
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000810 case 'x':
811 if (optarg && optarg[0])
812 {
813 if (strcasecmp(optarg, "auto") == 0)
814 g_launch_flavor = eLaunchFlavorDefault;
815 else if (strcasestr(optarg, "posix") == optarg)
816 g_launch_flavor = eLaunchFlavorPosixSpawn;
817 else if (strcasestr(optarg, "fork") == optarg)
818 g_launch_flavor = eLaunchFlavorForkExec;
819#if defined (__arm__)
820 else if (strcasestr(optarg, "spring") == optarg)
821 g_launch_flavor = eLaunchFlavorSpringBoard;
822#endif
823 else
824 {
825 RNBLogSTDERR ("error: invalid TYPE for the --launch=TYPE (-x TYPE) option: '%s'\n", optarg);
826 RNBLogSTDERR ("Valid values TYPE are:\n");
827 RNBLogSTDERR (" auto Auto-detect the best launch method to use.\n");
828 RNBLogSTDERR (" posix Launch the executable using posix_spawn.\n");
829 RNBLogSTDERR (" fork Launch the executable using fork and exec.\n");
830#if defined (__arm__)
831 RNBLogSTDERR (" spring Launch the executable through Springboard.\n");
832#endif
833 exit (5);
834 }
835 }
836 break;
837
838 case 'l': // Set Log File
839 if (optarg && optarg[0])
840 {
841 if (strcasecmp(optarg, "stdout") == 0)
842 log_file = stdout;
843 else if (strcasecmp(optarg, "stderr") == 0)
844 log_file = stderr;
845 else
Jim Inghamc530be62011-01-24 03:46:59 +0000846 {
Greg Claytonbd82a5d2011-01-23 05:56:20 +0000847 log_file = fopen(optarg, "w");
Jim Inghamc530be62011-01-24 03:46:59 +0000848 if (log_file != NULL)
849 setlinebuf(log_file);
850 }
851
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000852 if (log_file == NULL)
853 {
854 const char *errno_str = strerror(errno);
855 RNBLogSTDERR ("Failed to open log file '%s' for writing: errno = %i (%s)", optarg, errno, errno_str ? errno_str : "unknown error");
856 }
857 }
858 break;
859
860 case 'f': // Log Flags
861 if (optarg && optarg[0])
862 log_flags = strtoul(optarg, NULL, 0);
863 break;
864
865 case 'g':
866 debug = 1;
867 DNBLogSetDebug(1);
868 break;
869
870 case 't':
871 g_applist_opt = 1;
872 break;
873
874 case 'k':
875 g_lockdown_opt = 1;
876 break;
877
878 case 'r':
Greg Clayton71337622011-02-24 22:24:29 +0000879 remote->SetUseNativeRegisters (true);
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000880 break;
881
882 case 'v':
883 DNBLogSetVerbose(1);
884 break;
885
886 case 's':
Greg Clayton71337622011-02-24 22:24:29 +0000887 ctx.GetSTDIN().assign(optarg);
888 ctx.GetSTDOUT().assign(optarg);
889 ctx.GetSTDERR().assign(optarg);
Greg Clayton6779606a2011-01-22 23:43:18 +0000890 break;
891
892 case 'I':
Greg Clayton71337622011-02-24 22:24:29 +0000893 ctx.GetSTDIN().assign(optarg);
Greg Clayton6779606a2011-01-22 23:43:18 +0000894 break;
895
896 case 'O':
Greg Clayton71337622011-02-24 22:24:29 +0000897 ctx.GetSTDOUT().assign(optarg);
Greg Clayton6779606a2011-01-22 23:43:18 +0000898 break;
899
900 case 'E':
Greg Clayton71337622011-02-24 22:24:29 +0000901 ctx.GetSTDERR().assign(optarg);
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000902 break;
903
Caroline Ticef8da8632010-12-03 18:46:09 +0000904 case 'n':
905 no_stdio = true;
906 break;
907
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000908 case 'S':
909 // Put debugserver into a new session. Terminals group processes
910 // into sessions and when a special terminal key sequences
911 // (like control+c) are typed they can cause signals to go out to
912 // all processes in a session. Using this --setsid (-S) option
913 // will cause debugserver to run in its own sessions and be free
914 // from such issues.
915 //
916 // This is useful when debugserver is spawned from a command
917 // line application that uses debugserver to do the debugging,
918 // yet that application doesn't want debugserver receiving the
919 // signals sent to the session (i.e. dying when anyone hits ^C).
920 setsid();
921 break;
Greg Claytonf681b942010-08-31 18:35:14 +0000922 case 'D':
923 g_disable_aslr = 1;
924 break;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000925 }
926 }
Greg Clayton3af9ea52010-11-18 05:57:03 +0000927
928 if (arch_name.empty())
929 {
Greg Clayton71337622011-02-24 22:24:29 +0000930#if defined (__arm__)
Greg Clayton3af9ea52010-11-18 05:57:03 +0000931 arch_name.assign ("arm");
932#endif
933 }
Greg Clayton3c144382010-12-01 22:45:40 +0000934 else
935 {
936 DNBSetArchitecture (arch_name.c_str());
937 }
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000938
Greg Clayton71337622011-02-24 22:24:29 +0000939// if (arch_name.empty())
940// {
941// fprintf(stderr, "error: no architecture was specified\n");
942// exit (8);
943// }
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000944 // Skip any options we consumed with getopt_long
945 argc -= optind;
946 argv += optind;
947
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000948
Greg Claytonbd82a5d2011-01-23 05:56:20 +0000949 if (!working_dir.empty())
Greg Clayton6779606a2011-01-22 23:43:18 +0000950 {
Greg Claytonbd82a5d2011-01-23 05:56:20 +0000951 if (remote->Context().SetWorkingDirectory (working_dir.c_str()) == false)
Greg Clayton6779606a2011-01-22 23:43:18 +0000952 {
Greg Claytonbd82a5d2011-01-23 05:56:20 +0000953 RNBLogSTDERR ("error: working directory doesn't exist '%s'.\n", working_dir.c_str());
Greg Clayton6779606a2011-01-22 23:43:18 +0000954 exit (8);
955 }
956 }
957
958 remote->Initialize();
Greg Clayton3af9ea52010-11-18 05:57:03 +0000959
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000960 // It is ok for us to set NULL as the logfile (this will disable any logging)
961
962 if (log_file != NULL)
963 {
964 DNBLogSetLogCallback(FileLogCallback, log_file);
965 // If our log file was set, yet we have no log flags, log everything!
966 if (log_flags == 0)
967 log_flags = LOG_ALL | LOG_RNB_ALL;
968
969 DNBLogSetLogMask (log_flags);
970 }
971 else
972 {
973 // Enable DNB logging
974 DNBLogSetLogCallback(ASLLogCallback, NULL);
975 DNBLogSetLogMask (log_flags);
976
977 }
978
979 if (DNBLogEnabled())
980 {
981 for (i=0; i<argc; i++)
982 DNBLogDebug("argv[%i] = %s", i, argv[i]);
983 }
984
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000985 // as long as we're dropping remotenub in as a replacement for gdbserver,
986 // explicitly note that this is not gdbserver.
987
988 RNBLogSTDOUT ("%s-%g %sfor %s.\n",
989 DEBUGSERVER_PROGRAM_NAME,
990 DEBUGSERVER_VERSION_NUM,
991 compile_options.c_str(),
992 RNB_ARCH);
993
994 int listen_port = INT32_MAX;
995 char str[PATH_MAX];
996
997 if (g_lockdown_opt == 0 && g_applist_opt == 0)
998 {
999 // Make sure we at least have port
1000 if (argc < 1)
1001 {
1002 show_usage_and_exit (1);
1003 }
1004 // accept 'localhost:' prefix on port number
1005
1006 int items_scanned = ::sscanf (argv[0], "%[^:]:%i", str, &listen_port);
1007 if (items_scanned == 2)
1008 {
1009 DNBLogDebug("host = '%s' port = %i", str, listen_port);
1010 }
1011 else if (argv[0][0] == '/')
1012 {
1013 listen_port = INT32_MAX;
1014 strncpy(str, argv[0], sizeof(str));
1015 }
1016 else
1017 {
1018 show_usage_and_exit (2);
1019 }
1020
1021 // We just used the 'host:port' or the '/path/file' arg...
1022 argc--;
1023 argv++;
1024
1025 }
1026
1027 // If we know we're waiting to attach, we don't need any of this other info.
1028 if (start_mode != eRNBRunLoopModeInferiorAttaching)
1029 {
1030 if (argc == 0 || g_lockdown_opt)
1031 {
1032 if (g_lockdown_opt != 0)
1033 {
1034 // Work around for SIGPIPE crashes due to posix_spawn issue.
1035 // We have to close STDOUT and STDERR, else the first time we
1036 // try and do any, we get SIGPIPE and die as posix_spawn is
1037 // doing bad things with our file descriptors at the moment.
1038 int null = open("/dev/null", O_RDWR);
1039 dup2(null, STDOUT_FILENO);
1040 dup2(null, STDERR_FILENO);
1041 }
1042 else if (g_applist_opt != 0)
1043 {
1044 // List all applications we are able to see
1045 std::string applist_plist;
1046 int err = ListApplications(applist_plist, false, false);
1047 if (err == 0)
1048 {
1049 fputs (applist_plist.c_str(), stdout);
1050 }
1051 else
1052 {
1053 RNBLogSTDERR ("error: ListApplications returned error %i\n", err);
1054 }
1055 // Exit with appropriate error if we were asked to list the applications
1056 // with no other args were given (and we weren't trying to do this over
1057 // lockdown)
1058 return err;
1059 }
1060
1061 DNBLogDebug("Get args from remote protocol...");
1062 start_mode = eRNBRunLoopModeGetStartModeFromRemoteProtocol;
1063 }
1064 else
1065 {
1066 start_mode = eRNBRunLoopModeInferiorLaunching;
1067 // Fill in the argv array in the context from the rest of our args.
1068 // Skip the name of this executable and the port number
1069 for (int i = 0; i < argc; i++)
1070 {
1071 DNBLogDebug("inferior_argv[%i] = '%s'", i, argv[i]);
1072 ctx.PushArgument (argv[i]);
1073 }
1074 }
1075 }
1076
1077 if (start_mode == eRNBRunLoopModeExit)
1078 return -1;
1079
1080 RNBRunLoopMode mode = start_mode;
1081 char err_str[1024] = {'\0'};
1082
1083 while (mode != eRNBRunLoopModeExit)
1084 {
1085 switch (mode)
1086 {
1087 case eRNBRunLoopModeGetStartModeFromRemoteProtocol:
1088#if defined (__arm__)
1089 if (g_lockdown_opt)
1090 {
Greg Clayton6779606a2011-01-22 23:43:18 +00001091 if (!remote->Comm().IsConnected())
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001092 {
Greg Clayton6779606a2011-01-22 23:43:18 +00001093 if (remote->Comm().ConnectToService () != rnb_success)
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001094 {
1095 RNBLogSTDERR ("Failed to get connection from a remote gdb process.\n");
1096 mode = eRNBRunLoopModeExit;
1097 }
1098 else if (g_applist_opt != 0)
1099 {
1100 // List all applications we are able to see
1101 std::string applist_plist;
1102 if (ListApplications(applist_plist, false, false) == 0)
1103 {
1104 DNBLogDebug("Task list: %s", applist_plist.c_str());
1105
Greg Clayton6779606a2011-01-22 23:43:18 +00001106 remote->Comm().Write(applist_plist.c_str(), applist_plist.size());
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001107 // Issue a read that will never yield any data until the other side
1108 // closes the socket so this process doesn't just exit and cause the
1109 // socket to close prematurely on the other end and cause data loss.
1110 std::string buf;
Greg Clayton6779606a2011-01-22 23:43:18 +00001111 remote->Comm().Read(buf);
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001112 }
Greg Clayton6779606a2011-01-22 23:43:18 +00001113 remote->Comm().Disconnect(false);
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001114 mode = eRNBRunLoopModeExit;
1115 break;
1116 }
1117 else
1118 {
1119 // Start watching for remote packets
Greg Clayton6779606a2011-01-22 23:43:18 +00001120 remote->StartReadRemoteDataThread();
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001121 }
1122 }
1123 }
1124 else
1125#endif
1126 if (listen_port != INT32_MAX)
1127 {
Greg Clayton6779606a2011-01-22 23:43:18 +00001128 if (!StartListening (remote, listen_port))
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001129 mode = eRNBRunLoopModeExit;
1130 }
1131 else if (str[0] == '/')
1132 {
Greg Clayton6779606a2011-01-22 23:43:18 +00001133 if (remote->Comm().OpenFile (str))
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001134 mode = eRNBRunLoopModeExit;
1135 }
1136 if (mode != eRNBRunLoopModeExit)
1137 {
1138 RNBLogSTDOUT ("Got a connection, waiting for process information for launching or attaching.\n");
1139
Greg Clayton6779606a2011-01-22 23:43:18 +00001140 mode = RNBRunLoopGetStartModeFromRemote (remote);
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001141 }
1142 break;
1143
1144 case eRNBRunLoopModeInferiorAttaching:
1145 if (!waitfor_pid_name.empty())
1146 {
1147 // Set our end wait time if we are using a waitfor-duration
1148 // option that may have been specified
1149 struct timespec attach_timeout_abstime, *timeout_ptr = NULL;
1150 if (waitfor_duration != 0)
1151 {
1152 DNBTimer::OffsetTimeOfDay(&attach_timeout_abstime, waitfor_duration, 0);
1153 timeout_ptr = &attach_timeout_abstime;
1154 }
1155 nub_launch_flavor_t launch_flavor = g_launch_flavor;
1156 if (launch_flavor == eLaunchFlavorDefault)
1157 {
1158 // Our default launch method is posix spawn
1159 launch_flavor = eLaunchFlavorPosixSpawn;
1160
1161#if defined (__arm__)
1162 // Check if we have an app bundle, if so launch using SpringBoard.
1163 if (waitfor_pid_name.find (".app") != std::string::npos)
1164 {
1165 launch_flavor = eLaunchFlavorSpringBoard;
1166 }
1167#endif
1168 }
1169
1170 ctx.SetLaunchFlavor(launch_flavor);
1171
1172 nub_process_t pid = DNBProcessAttachWait (waitfor_pid_name.c_str(), launch_flavor, timeout_ptr, waitfor_interval, err_str, sizeof(err_str));
1173 g_pid = pid;
1174
1175 if (pid == INVALID_NUB_PROCESS)
1176 {
1177 ctx.LaunchStatus().SetError(-1, DNBError::Generic);
1178 if (err_str[0])
1179 ctx.LaunchStatus().SetErrorString(err_str);
1180 RNBLogSTDERR ("error: failed to attach to process named: \"%s\" %s", waitfor_pid_name.c_str(), err_str);
1181 mode = eRNBRunLoopModeExit;
1182 }
1183 else
1184 {
1185 ctx.SetProcessID(pid);
1186 mode = eRNBRunLoopModeInferiorExecuting;
1187 }
1188 }
1189 else if (attach_pid != INVALID_NUB_PROCESS)
1190 {
1191
1192 RNBLogSTDOUT ("Attaching to process %i...\n", attach_pid);
1193 nub_process_t attached_pid;
Greg Clayton6779606a2011-01-22 23:43:18 +00001194 mode = RNBRunLoopLaunchAttaching (remote, attach_pid, attached_pid);
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001195 if (mode != eRNBRunLoopModeInferiorExecuting)
1196 {
1197 const char *error_str = remote->Context().LaunchStatus().AsString();
1198 RNBLogSTDERR ("error: failed to attach process %i: %s\n", attach_pid, error_str ? error_str : "unknown error.");
1199 mode = eRNBRunLoopModeExit;
1200 }
1201 }
1202 else if (!attach_pid_name.empty ())
1203 {
1204 struct timespec attach_timeout_abstime, *timeout_ptr = NULL;
1205 if (waitfor_duration != 0)
1206 {
1207 DNBTimer::OffsetTimeOfDay(&attach_timeout_abstime, waitfor_duration, 0);
1208 timeout_ptr = &attach_timeout_abstime;
1209 }
1210
1211 nub_process_t pid = DNBProcessAttachByName (attach_pid_name.c_str(), timeout_ptr, err_str, sizeof(err_str));
1212 g_pid = pid;
1213 if (pid == INVALID_NUB_PROCESS)
1214 {
1215 ctx.LaunchStatus().SetError(-1, DNBError::Generic);
1216 if (err_str[0])
1217 ctx.LaunchStatus().SetErrorString(err_str);
1218 RNBLogSTDERR ("error: failed to attach to process named: \"%s\" %s", waitfor_pid_name.c_str(), err_str);
1219 mode = eRNBRunLoopModeExit;
1220 }
1221 else
1222 {
1223 ctx.SetProcessID(pid);
1224 mode = eRNBRunLoopModeInferiorExecuting;
1225 }
1226
1227 }
1228 else
1229 {
1230 RNBLogSTDERR ("error: asked to attach with empty name and invalid PID.");
1231 mode = eRNBRunLoopModeExit;
1232 }
1233
1234 if (mode != eRNBRunLoopModeExit)
1235 {
1236 if (listen_port != INT32_MAX)
1237 {
Greg Clayton6779606a2011-01-22 23:43:18 +00001238 if (!StartListening (remote, listen_port))
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001239 mode = eRNBRunLoopModeExit;
1240 }
1241 else if (str[0] == '/')
1242 {
Greg Clayton6779606a2011-01-22 23:43:18 +00001243 if (remote->Comm().OpenFile (str))
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001244 mode = eRNBRunLoopModeExit;
1245 }
1246 if (mode != eRNBRunLoopModeExit)
1247 RNBLogSTDOUT ("Got a connection, waiting for debugger instructions for process %d.\n", attach_pid);
1248 }
1249 break;
1250
1251 case eRNBRunLoopModeInferiorLaunching:
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001252 {
Greg Clayton6779606a2011-01-22 23:43:18 +00001253 mode = RNBRunLoopLaunchInferior (remote,
Greg Clayton71337622011-02-24 22:24:29 +00001254 ctx.GetSTDINPath(),
1255 ctx.GetSTDOUTPath(),
1256 ctx.GetSTDERRPath(),
Greg Clayton6779606a2011-01-22 23:43:18 +00001257 no_stdio);
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001258
Greg Clayton6779606a2011-01-22 23:43:18 +00001259 if (mode == eRNBRunLoopModeInferiorExecuting)
1260 {
1261 if (listen_port != INT32_MAX)
1262 {
1263 if (!StartListening (remote, listen_port))
1264 mode = eRNBRunLoopModeExit;
1265 }
1266 else if (str[0] == '/')
1267 {
1268 if (remote->Comm().OpenFile (str))
1269 mode = eRNBRunLoopModeExit;
1270 }
1271
1272 if (mode != eRNBRunLoopModeExit)
1273 RNBLogSTDOUT ("Got a connection, waiting for debugger instructions.\n");
1274 }
1275 else
1276 {
1277 const char *error_str = remote->Context().LaunchStatus().AsString();
1278 RNBLogSTDERR ("error: failed to launch process %s: %s\n", argv[0], error_str ? error_str : "unknown error.");
1279 }
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001280 }
1281 break;
1282
1283 case eRNBRunLoopModeInferiorExecuting:
Greg Clayton6779606a2011-01-22 23:43:18 +00001284 mode = RNBRunLoopInferiorExecuting(remote);
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001285 break;
1286
1287 default:
1288 mode = eRNBRunLoopModeExit;
1289 case eRNBRunLoopModeExit:
1290 break;
1291 }
1292 }
1293
Greg Clayton6779606a2011-01-22 23:43:18 +00001294 remote->StopReadRemoteDataThread ();
1295 remote->Context().SetProcessID(INVALID_NUB_PROCESS);
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001296
1297 return 0;
1298}