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