1 //===-- debugserver.cpp -----------------------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include <sys/socket.h>
11 #include <sys/types.h>
12 #include <errno.h>
13 #include <getopt.h>
14 #include <netinet/in.h>
15 #include <sys/select.h>
16 #include <sys/sysctl.h>
17 #include <string>
18 #include <vector>
19 #include <asl.h>
20 
21 #include "CFString.h"
22 #include "DNB.h"
23 #include "DNBLog.h"
24 #include "DNBTimer.h"
25 #include "PseudoTerminal.h"
26 #include "RNBContext.h"
27 #include "RNBServices.h"
28 #include "RNBSocket.h"
29 #include "RNBRemote.h"
30 #include "SysSignal.h"
31 
32 // Global PID in case we get a signal and need to stop the process...
33 nub_process_t g_pid = INVALID_NUB_PROCESS;
34 
35 //----------------------------------------------------------------------
36 // Run loop modes which determine which run loop function will be called
37 //----------------------------------------------------------------------
38 typedef enum
39 {
40     eRNBRunLoopModeInvalid = 0,
41     eRNBRunLoopModeGetStartModeFromRemoteProtocol,
42     eRNBRunLoopModeInferiorAttaching,
43     eRNBRunLoopModeInferiorLaunching,
44     eRNBRunLoopModeInferiorExecuting,
45     eRNBRunLoopModeExit
46 } RNBRunLoopMode;
47 
48 
49 //----------------------------------------------------------------------
50 // Global Variables
51 //----------------------------------------------------------------------
52 RNBRemoteSP g_remoteSP;
53 static int g_lockdown_opt  = 0;
54 static int g_applist_opt = 0;
55 static nub_launch_flavor_t g_launch_flavor = eLaunchFlavorDefault;
56 
57 int 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 //----------------------------------------------------------------------
65 RNBRunLoopMode RNBRunLoopGetStartModeFromRemote (RNBRemoteSP &remote);
66 RNBRunLoopMode 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 //----------------------------------------------------------------------
75 RNBRunLoopMode
76 RNBRunLoopGetStartModeFromRemote (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 //----------------------------------------------------------------------
152 RNBRunLoopMode
153 RNBRunLoopLaunchInferior (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 //----------------------------------------------------------------------
316 RNBRunLoopMode
317 RNBRunLoopLaunchAttaching (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 //----------------------------------------------------------------------
346 int g_sigint_received = 0;
347 int g_sigpipe_received = 0;
348 void
349 signal_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
381 RNBRunLoopMode
382 HandleProcessStateChange (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.
460 RNBRunLoopMode
461 RNBRunLoopInferiorExecuting (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 
565 static int
566 StartListening (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 //----------------------------------------------------------------------
587 void
588 ASLLogCallback(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 //----------------------------------------------------------------------
615 void
616 FileLogCallback(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 
626 void
627 show_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 //----------------------------------------------------------------------
642 static 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 //----------------------------------------------------------------------
665 int
666 main (int argc, char *argv[])
667 {
668     g_isatty = ::isatty (STDIN_FILENO);
669 
670     //  ::printf ("uid=%u euid=%u gid=%u egid=%u\n",
671     //            getuid(),
672     //            geteuid(),
673     //            getgid(),
674     //            getegid());
675 
676 
677     //    signal (SIGINT, signal_handler);
678     signal (SIGPIPE, signal_handler);
679 
680     int i;
681     int attach_pid = INVALID_NUB_PROCESS;
682 
683     FILE* log_file = NULL;
684     uint32_t log_flags = 0;
685     // Parse our options
686     int ch;
687     int long_option_index = 0;
688     int use_native_registers = 0;
689     int debug = 0;
690     std::string compile_options;
691     std::string waitfor_pid_name;           // Wait for a process that starts with this name
692     std::string attach_pid_name;
693     std::string stdio_path;
694     useconds_t waitfor_interval = 1000;     // Time in usecs between process lists polls when waiting for a process by name, default 1 msec.
695     useconds_t waitfor_duration = 0;        // Time in seconds to wait for a process by name, 0 means wait forever.
696 
697 #if !defined (DNBLOG_ENABLED)
698     compile_options += "(no-logging) ";
699 #endif
700 
701     RNBRunLoopMode start_mode = eRNBRunLoopModeExit;
702 
703     while ((ch = getopt_long(argc, argv, "a:d:gi:vktl:f:w:x:r", g_long_options, &long_option_index)) != -1)
704     {
705         DNBLogDebug("option: ch == %c (0x%2.2x) --%s%c%s\n",
706                     ch, (uint8_t)ch,
707                     g_long_options[long_option_index].name,
708                     g_long_options[long_option_index].has_arg ? '=' : ' ',
709                     optarg ? optarg : "");
710         switch (ch)
711         {
712             case 0:   // Any optional that auto set themselves will return 0
713                 break;
714 
715             case 'a':
716                 if (optarg && optarg[0])
717                 {
718                     if (isdigit(optarg[0]))
719                     {
720                         char *end = NULL;
721                         attach_pid = strtoul(optarg, &end, 0);
722                         if (end == NULL || *end != '\0')
723                         {
724                             RNBLogSTDERR ("error: invalid pid option '%s'\n", optarg);
725                             exit (4);
726                         }
727                     }
728                     else
729                     {
730                         attach_pid_name = optarg;
731                     }
732                     start_mode = eRNBRunLoopModeInferiorAttaching;
733                 }
734                 break;
735 
736                 // --waitfor=NAME
737             case 'w':
738                 if (optarg && optarg[0])
739                 {
740                     waitfor_pid_name = optarg;
741                     start_mode = eRNBRunLoopModeInferiorAttaching;
742                 }
743                 break;
744 
745                 // --waitfor-interval=USEC
746             case 'i':
747                 if (optarg && optarg[0])
748                 {
749                     char *end = NULL;
750                     waitfor_interval = strtoul(optarg, &end, 0);
751                     if (end == NULL || *end != '\0')
752                     {
753                         RNBLogSTDERR ("error: invalid waitfor-interval option value '%s'.\n", optarg);
754                         exit (6);
755                     }
756                 }
757                 break;
758 
759                 // --waitfor-duration=SEC
760             case 'd':
761                 if (optarg && optarg[0])
762                 {
763                     char *end = NULL;
764                     waitfor_duration = strtoul(optarg, &end, 0);
765                     if (end == NULL || *end != '\0')
766                     {
767                         RNBLogSTDERR ("error: invalid waitfor-duration option value '%s'.\n", optarg);
768                         exit (7);
769                     }
770                 }
771                 break;
772 
773             case 'x':
774                 if (optarg && optarg[0])
775                 {
776                     if (strcasecmp(optarg, "auto") == 0)
777                         g_launch_flavor = eLaunchFlavorDefault;
778                     else if (strcasestr(optarg, "posix") == optarg)
779                         g_launch_flavor = eLaunchFlavorPosixSpawn;
780                     else if (strcasestr(optarg, "fork") == optarg)
781                         g_launch_flavor = eLaunchFlavorForkExec;
782 #if defined (__arm__)
783                     else if (strcasestr(optarg, "spring") == optarg)
784                         g_launch_flavor = eLaunchFlavorSpringBoard;
785 #endif
786                     else
787                     {
788                         RNBLogSTDERR ("error: invalid TYPE for the --launch=TYPE (-x TYPE) option: '%s'\n", optarg);
789                         RNBLogSTDERR ("Valid values TYPE are:\n");
790                         RNBLogSTDERR ("  auto    Auto-detect the best launch method to use.\n");
791                         RNBLogSTDERR ("  posix   Launch the executable using posix_spawn.\n");
792                         RNBLogSTDERR ("  fork    Launch the executable using fork and exec.\n");
793 #if defined (__arm__)
794                         RNBLogSTDERR ("  spring  Launch the executable through Springboard.\n");
795 #endif
796                         exit (5);
797                     }
798                 }
799                 break;
800 
801             case 'l': // Set Log File
802                 if (optarg && optarg[0])
803                 {
804                     if (strcasecmp(optarg, "stdout") == 0)
805                         log_file = stdout;
806                     else if (strcasecmp(optarg, "stderr") == 0)
807                         log_file = stderr;
808                     else
809                         log_file = fopen(optarg, "w+");
810 
811                     if (log_file == NULL)
812                     {
813                         const char *errno_str = strerror(errno);
814                         RNBLogSTDERR ("Failed to open log file '%s' for writing: errno = %i (%s)", optarg, errno, errno_str ? errno_str : "unknown error");
815                     }
816                 }
817                 break;
818 
819             case 'f': // Log Flags
820                 if (optarg && optarg[0])
821                     log_flags = strtoul(optarg, NULL, 0);
822                 break;
823 
824             case 'g':
825                 debug = 1;
826                 DNBLogSetDebug(1);
827                 break;
828 
829             case 't':
830                 g_applist_opt = 1;
831                 break;
832 
833             case 'k':
834                 g_lockdown_opt = 1;
835                 break;
836 
837             case 'r':
838                 use_native_registers = 1;
839                 break;
840 
841             case 'v':
842                 DNBLogSetVerbose(1);
843                 break;
844 
845             case 's':
846                 stdio_path = optarg;
847                 break;
848 
849             case 'S':
850                 // Put debugserver into a new session. Terminals group processes
851                 // into sessions and when a special terminal key sequences
852                 // (like control+c) are typed they can cause signals to go out to
853                 // all processes in a session. Using this --setsid (-S) option
854                 // will cause debugserver to run in its own sessions and be free
855                 // from such issues.
856                 //
857                 // This is useful when debugserver is spawned from a command
858                 // line application that uses debugserver to do the debugging,
859                 // yet that application doesn't want debugserver receiving the
860                 // signals sent to the session (i.e. dying when anyone hits ^C).
861                 setsid();
862                 break;
863         }
864     }
865 
866     // Skip any options we consumed with getopt_long
867     argc -= optind;
868     argv += optind;
869 
870     g_remoteSP.reset (new RNBRemote (use_native_registers));
871 
872     RNBRemote *remote = g_remoteSP.get();
873     if (remote == NULL)
874     {
875         RNBLogSTDERR ("error: failed to create a remote connection class\n");
876         return -1;
877     }
878 
879     RNBContext& ctx = remote->Context();
880 
881 
882     // It is ok for us to set NULL as the logfile (this will disable any logging)
883 
884     if (log_file != NULL)
885     {
886         DNBLogSetLogCallback(FileLogCallback, log_file);
887         // If our log file was set, yet we have no log flags, log everything!
888         if (log_flags == 0)
889             log_flags = LOG_ALL | LOG_RNB_ALL;
890 
891         DNBLogSetLogMask (log_flags);
892     }
893     else
894     {
895         // Enable DNB logging
896         DNBLogSetLogCallback(ASLLogCallback, NULL);
897         DNBLogSetLogMask (log_flags);
898 
899     }
900 
901     if (DNBLogEnabled())
902     {
903         for (i=0; i<argc; i++)
904             DNBLogDebug("argv[%i] = %s", i, argv[i]);
905     }
906 
907     // Now that we have read in the options and enabled logging, initialize
908     // the rest of RNBRemote
909     RNBRemote::InitializeRegisters (use_native_registers);
910 
911 
912     // as long as we're dropping remotenub in as a replacement for gdbserver,
913     // explicitly note that this is not gdbserver.
914 
915     RNBLogSTDOUT ("%s-%g %sfor %s.\n",
916                   DEBUGSERVER_PROGRAM_NAME,
917                   DEBUGSERVER_VERSION_NUM,
918                   compile_options.c_str(),
919                   RNB_ARCH);
920 
921     int listen_port = INT32_MAX;
922     char str[PATH_MAX];
923 
924     if (g_lockdown_opt == 0 && g_applist_opt == 0)
925     {
926         // Make sure we at least have port
927         if (argc < 1)
928         {
929             show_usage_and_exit (1);
930         }
931         // accept 'localhost:' prefix on port number
932 
933         int items_scanned = ::sscanf (argv[0], "%[^:]:%i", str, &listen_port);
934         if (items_scanned == 2)
935         {
936             DNBLogDebug("host = '%s'  port = %i", str, listen_port);
937         }
938         else if (argv[0][0] == '/')
939         {
940             listen_port = INT32_MAX;
941             strncpy(str, argv[0], sizeof(str));
942         }
943         else
944         {
945             show_usage_and_exit (2);
946         }
947 
948         // We just used the 'host:port' or the '/path/file' arg...
949         argc--;
950         argv++;
951 
952     }
953 
954     //  If we know we're waiting to attach, we don't need any of this other info.
955     if (start_mode != eRNBRunLoopModeInferiorAttaching)
956     {
957         if (argc == 0 || g_lockdown_opt)
958         {
959             if (g_lockdown_opt != 0)
960             {
961                 // Work around for SIGPIPE crashes due to posix_spawn issue.
962                 // We have to close STDOUT and STDERR, else the first time we
963                 // try and do any, we get SIGPIPE and die as posix_spawn is
964                 // doing bad things with our file descriptors at the moment.
965                 int null = open("/dev/null", O_RDWR);
966                 dup2(null, STDOUT_FILENO);
967                 dup2(null, STDERR_FILENO);
968             }
969             else if (g_applist_opt != 0)
970             {
971                 // List all applications we are able to see
972                 std::string applist_plist;
973                 int err = ListApplications(applist_plist, false, false);
974                 if (err == 0)
975                 {
976                     fputs (applist_plist.c_str(), stdout);
977                 }
978                 else
979                 {
980                     RNBLogSTDERR ("error: ListApplications returned error %i\n", err);
981                 }
982                 // Exit with appropriate error if we were asked to list the applications
983                 // with no other args were given (and we weren't trying to do this over
984                 // lockdown)
985                 return err;
986             }
987 
988             DNBLogDebug("Get args from remote protocol...");
989             start_mode = eRNBRunLoopModeGetStartModeFromRemoteProtocol;
990         }
991         else
992         {
993             start_mode = eRNBRunLoopModeInferiorLaunching;
994             // Fill in the argv array in the context from the rest of our args.
995             // Skip the name of this executable and the port number
996             for (int i = 0; i < argc; i++)
997             {
998                 DNBLogDebug("inferior_argv[%i] = '%s'", i, argv[i]);
999                 ctx.PushArgument (argv[i]);
1000             }
1001         }
1002     }
1003 
1004     if (start_mode == eRNBRunLoopModeExit)
1005         return -1;
1006 
1007     RNBRunLoopMode mode = start_mode;
1008     char err_str[1024] = {'\0'};
1009 
1010     while (mode != eRNBRunLoopModeExit)
1011     {
1012         switch (mode)
1013         {
1014             case eRNBRunLoopModeGetStartModeFromRemoteProtocol:
1015 #if defined (__arm__)
1016                 if (g_lockdown_opt)
1017                 {
1018                     if (!g_remoteSP->Comm().IsConnected())
1019                     {
1020                         if (g_remoteSP->Comm().ConnectToService () != rnb_success)
1021                         {
1022                             RNBLogSTDERR ("Failed to get connection from a remote gdb process.\n");
1023                             mode = eRNBRunLoopModeExit;
1024                         }
1025                         else if (g_applist_opt != 0)
1026                         {
1027                             // List all applications we are able to see
1028                             std::string applist_plist;
1029                             if (ListApplications(applist_plist, false, false) == 0)
1030                             {
1031                                 DNBLogDebug("Task list: %s", applist_plist.c_str());
1032 
1033                                 g_remoteSP->Comm().Write(applist_plist.c_str(), applist_plist.size());
1034                                 // Issue a read that will never yield any data until the other side
1035                                 // closes the socket so this process doesn't just exit and cause the
1036                                 // socket to close prematurely on the other end and cause data loss.
1037                                 std::string buf;
1038                                 g_remoteSP->Comm().Read(buf);
1039                             }
1040                             g_remoteSP->Comm().Disconnect(false);
1041                             mode = eRNBRunLoopModeExit;
1042                             break;
1043                         }
1044                         else
1045                         {
1046                             // Start watching for remote packets
1047                             g_remoteSP->StartReadRemoteDataThread();
1048                         }
1049                     }
1050                 }
1051                 else
1052 #endif
1053                     if (listen_port != INT32_MAX)
1054                     {
1055                         if (!StartListening (g_remoteSP, listen_port))
1056                             mode = eRNBRunLoopModeExit;
1057                     }
1058                     else if (str[0] == '/')
1059                     {
1060                         if (g_remoteSP->Comm().OpenFile (str))
1061                             mode = eRNBRunLoopModeExit;
1062                     }
1063                 if (mode != eRNBRunLoopModeExit)
1064                 {
1065                     RNBLogSTDOUT ("Got a connection, waiting for process information for launching or attaching.\n");
1066 
1067                     mode = RNBRunLoopGetStartModeFromRemote (g_remoteSP);
1068                 }
1069                 break;
1070 
1071             case eRNBRunLoopModeInferiorAttaching:
1072                 if (!waitfor_pid_name.empty())
1073                 {
1074                     // Set our end wait time if we are using a waitfor-duration
1075                     // option that may have been specified
1076                     struct timespec attach_timeout_abstime, *timeout_ptr = NULL;
1077                     if (waitfor_duration != 0)
1078                     {
1079                         DNBTimer::OffsetTimeOfDay(&attach_timeout_abstime, waitfor_duration, 0);
1080                         timeout_ptr = &attach_timeout_abstime;
1081                     }
1082                     nub_launch_flavor_t launch_flavor = g_launch_flavor;
1083                     if (launch_flavor == eLaunchFlavorDefault)
1084                     {
1085                         // Our default launch method is posix spawn
1086                         launch_flavor = eLaunchFlavorPosixSpawn;
1087 
1088 #if defined (__arm__)
1089                         // Check if we have an app bundle, if so launch using SpringBoard.
1090                         if (waitfor_pid_name.find (".app") != std::string::npos)
1091                         {
1092                             launch_flavor = eLaunchFlavorSpringBoard;
1093                         }
1094 #endif
1095                     }
1096 
1097                     ctx.SetLaunchFlavor(launch_flavor);
1098 
1099                     nub_process_t pid = DNBProcessAttachWait (waitfor_pid_name.c_str(), launch_flavor, timeout_ptr, waitfor_interval, err_str, sizeof(err_str));
1100                     g_pid = pid;
1101 
1102                     if (pid == INVALID_NUB_PROCESS)
1103                     {
1104                         ctx.LaunchStatus().SetError(-1, DNBError::Generic);
1105                         if (err_str[0])
1106                             ctx.LaunchStatus().SetErrorString(err_str);
1107                         RNBLogSTDERR ("error: failed to attach to process named: \"%s\" %s", waitfor_pid_name.c_str(), err_str);
1108                         mode = eRNBRunLoopModeExit;
1109                     }
1110                     else
1111                     {
1112                         ctx.SetProcessID(pid);
1113                         mode = eRNBRunLoopModeInferiorExecuting;
1114                     }
1115                 }
1116                 else if (attach_pid != INVALID_NUB_PROCESS)
1117                 {
1118 
1119                     RNBLogSTDOUT ("Attaching to process %i...\n", attach_pid);
1120                     nub_process_t attached_pid;
1121                     mode = RNBRunLoopLaunchAttaching (g_remoteSP, attach_pid, attached_pid);
1122                     if (mode != eRNBRunLoopModeInferiorExecuting)
1123                     {
1124                         const char *error_str = remote->Context().LaunchStatus().AsString();
1125                         RNBLogSTDERR ("error: failed to attach process %i: %s\n", attach_pid, error_str ? error_str : "unknown error.");
1126                         mode = eRNBRunLoopModeExit;
1127                     }
1128                 }
1129                 else if (!attach_pid_name.empty ())
1130                 {
1131                     struct timespec attach_timeout_abstime, *timeout_ptr = NULL;
1132                     if (waitfor_duration != 0)
1133                     {
1134                         DNBTimer::OffsetTimeOfDay(&attach_timeout_abstime, waitfor_duration, 0);
1135                         timeout_ptr = &attach_timeout_abstime;
1136                     }
1137 
1138                     nub_process_t pid = DNBProcessAttachByName (attach_pid_name.c_str(), timeout_ptr, err_str, sizeof(err_str));
1139                     g_pid = pid;
1140                     if (pid == INVALID_NUB_PROCESS)
1141                     {
1142                         ctx.LaunchStatus().SetError(-1, DNBError::Generic);
1143                         if (err_str[0])
1144                             ctx.LaunchStatus().SetErrorString(err_str);
1145                         RNBLogSTDERR ("error: failed to attach to process named: \"%s\" %s", waitfor_pid_name.c_str(), err_str);
1146                         mode = eRNBRunLoopModeExit;
1147                     }
1148                     else
1149                     {
1150                         ctx.SetProcessID(pid);
1151                         mode = eRNBRunLoopModeInferiorExecuting;
1152                     }
1153 
1154                 }
1155                 else
1156                 {
1157                     RNBLogSTDERR ("error: asked to attach with empty name and invalid PID.");
1158                     mode = eRNBRunLoopModeExit;
1159                 }
1160 
1161                 if (mode != eRNBRunLoopModeExit)
1162                 {
1163                     if (listen_port != INT32_MAX)
1164                     {
1165                         if (!StartListening (g_remoteSP, listen_port))
1166                             mode = eRNBRunLoopModeExit;
1167                     }
1168                     else if (str[0] == '/')
1169                     {
1170                         if (g_remoteSP->Comm().OpenFile (str))
1171                             mode = eRNBRunLoopModeExit;
1172                     }
1173                     if (mode != eRNBRunLoopModeExit)
1174                         RNBLogSTDOUT ("Got a connection, waiting for debugger instructions for process %d.\n", attach_pid);
1175                 }
1176                 break;
1177 
1178             case eRNBRunLoopModeInferiorLaunching:
1179                 mode = RNBRunLoopLaunchInferior (g_remoteSP, stdio_path.empty() ? NULL : stdio_path.c_str());
1180 
1181                 if (mode == eRNBRunLoopModeInferiorExecuting)
1182                 {
1183                     if (listen_port != INT32_MAX)
1184                     {
1185                         if (!StartListening (g_remoteSP, listen_port))
1186                             mode = eRNBRunLoopModeExit;
1187                     }
1188                     else if (str[0] == '/')
1189                     {
1190                         if (g_remoteSP->Comm().OpenFile (str))
1191                             mode = eRNBRunLoopModeExit;
1192                     }
1193 
1194                     if (mode != eRNBRunLoopModeExit)
1195                         RNBLogSTDOUT ("Got a connection, waiting for debugger instructions.\n");
1196                 }
1197                 else
1198                 {
1199                     const char *error_str = remote->Context().LaunchStatus().AsString();
1200                     RNBLogSTDERR ("error: failed to launch process %s: %s\n", argv[0], error_str ? error_str : "unknown error.");
1201                 }
1202                 break;
1203 
1204             case eRNBRunLoopModeInferiorExecuting:
1205                 mode = RNBRunLoopInferiorExecuting(g_remoteSP);
1206                 break;
1207 
1208             default:
1209                 mode = eRNBRunLoopModeExit;
1210             case eRNBRunLoopModeExit:
1211                 break;
1212         }
1213     }
1214 
1215     g_remoteSP->StopReadRemoteDataThread ();
1216     g_remoteSP->Context().SetProcessID(INVALID_NUB_PROCESS);
1217 
1218     return 0;
1219 }
1220