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 #include <arpa/inet.h>
21 #include <netdb.h>
22 #include <netinet/in.h>
23 #include <netinet/tcp.h>
24 #include <sys/un.h>
25 #include <sys/types.h>
26 #include <crt_externs.h> // for _NSGetEnviron()
27 
28 #if defined (__APPLE__)
29 #include <sched.h>
30 extern "C" int proc_set_wakemon_params(pid_t, int, int); // <libproc_internal.h> SPI
31 #endif
32 
33 #include "CFString.h"
34 #include "DNB.h"
35 #include "DNBLog.h"
36 #include "DNBTimer.h"
37 #include "OsLogger.h"
38 #include "PseudoTerminal.h"
39 #include "RNBContext.h"
40 #include "RNBServices.h"
41 #include "RNBSocket.h"
42 #include "RNBRemote.h"
43 #include "SysSignal.h"
44 
45 // Global PID in case we get a signal and need to stop the process...
46 nub_process_t g_pid = INVALID_NUB_PROCESS;
47 
48 //----------------------------------------------------------------------
49 // Run loop modes which determine which run loop function will be called
50 //----------------------------------------------------------------------
51 typedef enum
52 {
53     eRNBRunLoopModeInvalid = 0,
54     eRNBRunLoopModeGetStartModeFromRemoteProtocol,
55     eRNBRunLoopModeInferiorAttaching,
56     eRNBRunLoopModeInferiorLaunching,
57     eRNBRunLoopModeInferiorExecuting,
58     eRNBRunLoopModePlatformMode,
59     eRNBRunLoopModeExit
60 } RNBRunLoopMode;
61 
62 
63 //----------------------------------------------------------------------
64 // Global Variables
65 //----------------------------------------------------------------------
66 RNBRemoteSP g_remoteSP;
67 static int g_lockdown_opt  = 0;
68 static int g_applist_opt = 0;
69 static nub_launch_flavor_t g_launch_flavor = eLaunchFlavorDefault;
70 int g_disable_aslr = 0;
71 
72 int g_isatty = 0;
73 bool g_detach_on_error = true;
74 
75 #define RNBLogSTDOUT(fmt, ...) do { if (g_isatty) { fprintf(stdout, fmt, ## __VA_ARGS__); } else { _DNBLog(0, fmt, ## __VA_ARGS__); } } while (0)
76 #define RNBLogSTDERR(fmt, ...) do { if (g_isatty) { fprintf(stderr, fmt, ## __VA_ARGS__); } else { _DNBLog(0, fmt, ## __VA_ARGS__); } } while (0)
77 
78 //----------------------------------------------------------------------
79 // Get our program path and arguments from the remote connection.
80 // We will need to start up the remote connection without a PID, get the
81 // arguments, wait for the new process to finish launching and hit its
82 // entry point,  and then return the run loop mode that should come next.
83 //----------------------------------------------------------------------
84 RNBRunLoopMode
85 RNBRunLoopGetStartModeFromRemote (RNBRemote* remote)
86 {
87     std::string packet;
88 
89     if (remote)
90     {
91         RNBContext& ctx = remote->Context();
92         uint32_t event_mask = RNBContext::event_read_packet_available |
93                               RNBContext::event_read_thread_exiting;
94 
95         // Spin waiting to get the A packet.
96         while (1)
97         {
98             DNBLogThreadedIf (LOG_RNB_MAX, "%s ctx.Events().WaitForSetEvents( 0x%08x ) ...",__FUNCTION__, event_mask);
99             nub_event_t set_events = ctx.Events().WaitForSetEvents(event_mask);
100             DNBLogThreadedIf (LOG_RNB_MAX, "%s ctx.Events().WaitForSetEvents( 0x%08x ) => 0x%08x", __FUNCTION__, event_mask, set_events);
101 
102             if (set_events & RNBContext::event_read_thread_exiting)
103             {
104                 RNBLogSTDERR ("error: packet read thread exited.\n");
105                 return eRNBRunLoopModeExit;
106             }
107 
108             if (set_events & RNBContext::event_read_packet_available)
109             {
110                 rnb_err_t err = rnb_err;
111                 RNBRemote::PacketEnum type;
112 
113                 err = remote->HandleReceivedPacket (&type);
114 
115                 // check if we tried to attach to a process
116                 if (type == RNBRemote::vattach || type == RNBRemote::vattachwait || type == RNBRemote::vattachorwait)
117                 {
118                     if (err == rnb_success)
119                     {
120                         RNBLogSTDOUT ("Attach succeeded, ready to debug.\n");
121                         return eRNBRunLoopModeInferiorExecuting;
122                     }
123                     else
124                     {
125                         RNBLogSTDERR ("error: attach failed.\n");
126                         return eRNBRunLoopModeExit;
127                     }
128                 }
129 
130                 if (err == rnb_success)
131                 {
132                     // If we got our arguments we are ready to launch using the arguments
133                     // and any environment variables we received.
134                     if (type == RNBRemote::set_argv)
135                     {
136                         return eRNBRunLoopModeInferiorLaunching;
137                     }
138                 }
139                 else if (err == rnb_not_connected)
140                 {
141                     RNBLogSTDERR ("error: connection lost.\n");
142                     return eRNBRunLoopModeExit;
143                 }
144                 else
145                 {
146                     // a catch all for any other gdb remote packets that failed
147                     DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s Error getting packet.",__FUNCTION__);
148                     continue;
149                 }
150 
151                 DNBLogThreadedIf (LOG_RNB_MINIMAL, "#### %s", __FUNCTION__);
152             }
153             else
154             {
155                 DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s Connection closed before getting \"A\" packet.", __FUNCTION__);
156                 return eRNBRunLoopModeExit;
157             }
158         }
159     }
160     return eRNBRunLoopModeExit;
161 }
162 
163 
164 //----------------------------------------------------------------------
165 // This run loop mode will wait for the process to launch and hit its
166 // entry point. It will currently ignore all events except for the
167 // process state changed event, where it watches for the process stopped
168 // or crash process state.
169 //----------------------------------------------------------------------
170 RNBRunLoopMode
171 RNBRunLoopLaunchInferior (RNBRemote *remote, const char *stdin_path, const char *stdout_path, const char *stderr_path, bool no_stdio)
172 {
173     RNBContext& ctx = remote->Context();
174 
175     // The Process stuff takes a c array, the RNBContext has a vector...
176     // So make up a c array.
177 
178     DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s Launching '%s'...", __FUNCTION__, ctx.ArgumentAtIndex(0));
179 
180     size_t inferior_argc = ctx.ArgumentCount();
181     // Initialize inferior_argv with inferior_argc + 1 NULLs
182     std::vector<const char *> inferior_argv(inferior_argc + 1, NULL);
183 
184     size_t i;
185     for (i = 0; i < inferior_argc; i++)
186         inferior_argv[i] = ctx.ArgumentAtIndex(i);
187 
188     // Pass the environment array the same way:
189 
190     size_t inferior_envc = ctx.EnvironmentCount();
191     // Initialize inferior_argv with inferior_argc + 1 NULLs
192     std::vector<const char *> inferior_envp(inferior_envc + 1, NULL);
193 
194     for (i = 0; i < inferior_envc; i++)
195         inferior_envp[i] = ctx.EnvironmentAtIndex(i);
196 
197     // Our launch type hasn't been set to anything concrete, so we need to
198     // figure our how we are going to launch automatically.
199 
200     nub_launch_flavor_t launch_flavor = g_launch_flavor;
201     if (launch_flavor == eLaunchFlavorDefault)
202     {
203         // Our default launch method is posix spawn
204         launch_flavor = eLaunchFlavorPosixSpawn;
205 
206 #if defined WITH_FBS
207         // Check if we have an app bundle, if so launch using BackBoard Services.
208         if (strstr(inferior_argv[0], ".app"))
209         {
210             launch_flavor = eLaunchFlavorFBS;
211         }
212 #elif defined WITH_BKS
213         // Check if we have an app bundle, if so launch using BackBoard Services.
214         if (strstr(inferior_argv[0], ".app"))
215         {
216             launch_flavor = eLaunchFlavorBKS;
217         }
218 #elif defined WITH_SPRINGBOARD
219         // Check if we have an app bundle, if so launch using SpringBoard.
220         if (strstr(inferior_argv[0], ".app"))
221         {
222             launch_flavor = eLaunchFlavorSpringBoard;
223         }
224 #endif
225     }
226 
227     ctx.SetLaunchFlavor(launch_flavor);
228     char resolved_path[PATH_MAX];
229 
230     // If we fail to resolve the path to our executable, then just use what we
231     // were given and hope for the best
232     if ( !DNBResolveExecutablePath (inferior_argv[0], resolved_path, sizeof(resolved_path)) )
233         ::strncpy(resolved_path, inferior_argv[0], sizeof(resolved_path));
234 
235     char launch_err_str[PATH_MAX];
236     launch_err_str[0] = '\0';
237     const char * cwd = (ctx.GetWorkingDirPath() != NULL ? ctx.GetWorkingDirPath()
238                                                         : ctx.GetWorkingDirectory());
239     const char *process_event = ctx.GetProcessEvent();
240     nub_process_t pid = DNBProcessLaunch (resolved_path,
241                                           &inferior_argv[0],
242                                           &inferior_envp[0],
243                                           cwd,
244                                           stdin_path,
245                                           stdout_path,
246                                           stderr_path,
247                                           no_stdio,
248                                           launch_flavor,
249                                           g_disable_aslr,
250                                           process_event,
251                                           launch_err_str,
252                                           sizeof(launch_err_str));
253 
254     g_pid = pid;
255 
256     if (pid == INVALID_NUB_PROCESS && strlen (launch_err_str) > 0)
257     {
258         DNBLogThreaded ("%s DNBProcessLaunch() returned error: '%s'", __FUNCTION__, launch_err_str);
259         ctx.LaunchStatus().SetError(-1, DNBError::Generic);
260         ctx.LaunchStatus().SetErrorString(launch_err_str);
261     }
262     else if (pid == INVALID_NUB_PROCESS)
263     {
264         DNBLogThreaded ("%s DNBProcessLaunch() failed to launch process, unknown failure", __FUNCTION__);
265         ctx.LaunchStatus().SetError(-1, DNBError::Generic);
266         ctx.LaunchStatus().SetErrorString("<unknown failure>");
267     }
268     else
269     {
270         ctx.LaunchStatus().Clear();
271     }
272 
273     if (remote->Comm().IsConnected())
274     {
275         // It we are connected already, the next thing gdb will do is ask
276         // whether the launch succeeded, and if not, whether there is an
277         // error code.  So we need to fetch one packet from gdb before we wait
278         // on the stop from the target.
279 
280         uint32_t event_mask = RNBContext::event_read_packet_available;
281         nub_event_t set_events = ctx.Events().WaitForSetEvents(event_mask);
282 
283         if (set_events & RNBContext::event_read_packet_available)
284         {
285             rnb_err_t err = rnb_err;
286             RNBRemote::PacketEnum type;
287 
288             err = remote->HandleReceivedPacket (&type);
289 
290             if (err != rnb_success)
291             {
292                 DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s Error getting packet.", __FUNCTION__);
293                 return eRNBRunLoopModeExit;
294             }
295             if (type != RNBRemote::query_launch_success)
296             {
297                 DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s Didn't get the expected qLaunchSuccess packet.", __FUNCTION__);
298             }
299         }
300     }
301 
302     while (pid != INVALID_NUB_PROCESS)
303     {
304         // Wait for process to start up and hit entry point
305         DNBLogThreadedIf (LOG_RNB_EVENTS, "%s DNBProcessWaitForEvent (%4.4x, eEventProcessRunningStateChanged | eEventProcessStoppedStateChanged, true, INFINITE)...", __FUNCTION__, pid);
306         nub_event_t set_events = DNBProcessWaitForEvents (pid, eEventProcessRunningStateChanged | eEventProcessStoppedStateChanged, true, NULL);
307         DNBLogThreadedIf (LOG_RNB_EVENTS, "%s DNBProcessWaitForEvent (%4.4x, eEventProcessRunningStateChanged | eEventProcessStoppedStateChanged, true, INFINITE) => 0x%8.8x", __FUNCTION__, pid, set_events);
308 
309         if (set_events == 0)
310         {
311             pid = INVALID_NUB_PROCESS;
312             g_pid = pid;
313         }
314         else
315         {
316             if (set_events & (eEventProcessRunningStateChanged | eEventProcessStoppedStateChanged))
317             {
318                 nub_state_t pid_state = DNBProcessGetState (pid);
319                 DNBLogThreadedIf (LOG_RNB_EVENTS, "%s process %4.4x state changed (eEventProcessStateChanged): %s", __FUNCTION__, pid, DNBStateAsString(pid_state));
320 
321                 switch (pid_state)
322                 {
323                     case eStateInvalid:
324                     case eStateUnloaded:
325                     case eStateAttaching:
326                     case eStateLaunching:
327                     case eStateSuspended:
328                         break;  // Ignore
329 
330                     case eStateRunning:
331                     case eStateStepping:
332                         // Still waiting to stop at entry point...
333                         break;
334 
335                     case eStateStopped:
336                     case eStateCrashed:
337                         ctx.SetProcessID(pid);
338                         return eRNBRunLoopModeInferiorExecuting;
339 
340                     case eStateDetached:
341                     case eStateExited:
342                         pid = INVALID_NUB_PROCESS;
343                         g_pid = pid;
344                         return eRNBRunLoopModeExit;
345                 }
346             }
347 
348             DNBProcessResetEvents(pid, set_events);
349         }
350     }
351 
352     return eRNBRunLoopModeExit;
353 }
354 
355 
356 //----------------------------------------------------------------------
357 // This run loop mode will wait for the process to launch and hit its
358 // entry point. It will currently ignore all events except for the
359 // process state changed event, where it watches for the process stopped
360 // or crash process state.
361 //----------------------------------------------------------------------
362 RNBRunLoopMode
363 RNBRunLoopLaunchAttaching (RNBRemote *remote, nub_process_t attach_pid, nub_process_t& pid)
364 {
365     RNBContext& ctx = remote->Context();
366 
367     DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s Attaching to pid %i...", __FUNCTION__, attach_pid);
368     char err_str[1024];
369     pid = DNBProcessAttach (attach_pid, NULL, err_str, sizeof(err_str));
370     g_pid = pid;
371 
372     if (pid == INVALID_NUB_PROCESS)
373     {
374         ctx.LaunchStatus().SetError(-1, DNBError::Generic);
375         if (err_str[0])
376             ctx.LaunchStatus().SetErrorString(err_str);
377         return eRNBRunLoopModeExit;
378     }
379     else
380     {
381         ctx.SetProcessID(pid);
382         return eRNBRunLoopModeInferiorExecuting;
383     }
384 }
385 
386 //----------------------------------------------------------------------
387 // Watch for signals:
388 // SIGINT: so we can halt our inferior. (disabled for now)
389 // SIGPIPE: in case our child process dies
390 //----------------------------------------------------------------------
391 int g_sigint_received = 0;
392 int g_sigpipe_received = 0;
393 void
394 signal_handler(int signo)
395 {
396     DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s (%s)", __FUNCTION__, SysSignal::Name(signo));
397 
398     switch (signo)
399     {
400         case SIGINT:
401             g_sigint_received++;
402             if (g_pid != INVALID_NUB_PROCESS)
403             {
404                 // Only send a SIGINT once...
405                 if (g_sigint_received == 1)
406                 {
407                     switch (DNBProcessGetState (g_pid))
408                     {
409                         case eStateRunning:
410                         case eStateStepping:
411                             DNBProcessSignal (g_pid, SIGSTOP);
412                             return;
413                         default:
414                             break;
415                     }
416                 }
417             }
418             exit (SIGINT);
419             break;
420 
421         case SIGPIPE:
422             g_sigpipe_received = 1;
423             break;
424     }
425 }
426 
427 // Return the new run loop mode based off of the current process state
428 RNBRunLoopMode
429 HandleProcessStateChange (RNBRemote *remote, bool initialize)
430 {
431     RNBContext& ctx = remote->Context();
432     nub_process_t pid = ctx.ProcessID();
433 
434     if (pid == INVALID_NUB_PROCESS)
435     {
436         DNBLogThreadedIf (LOG_RNB_MINIMAL, "#### %s error: pid invalid, exiting...", __FUNCTION__);
437         return eRNBRunLoopModeExit;
438     }
439     nub_state_t pid_state = DNBProcessGetState (pid);
440 
441     DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s (&remote, initialize=%i)  pid_state = %s", __FUNCTION__, (int)initialize, DNBStateAsString (pid_state));
442 
443     switch (pid_state)
444     {
445         case eStateInvalid:
446         case eStateUnloaded:
447             // Something bad happened
448             return eRNBRunLoopModeExit;
449             break;
450 
451         case eStateAttaching:
452         case eStateLaunching:
453             return eRNBRunLoopModeInferiorExecuting;
454 
455         case eStateSuspended:
456         case eStateCrashed:
457         case eStateStopped:
458             // If we stop due to a signal, so clear the fact that we got a SIGINT
459             // so we can stop ourselves again (but only while our inferior
460             // process is running..)
461             g_sigint_received = 0;
462             if (initialize == false)
463             {
464                 // Compare the last stop count to our current notion of a stop count
465                 // to make sure we don't notify more than once for a given stop.
466                 nub_size_t prev_pid_stop_count = ctx.GetProcessStopCount();
467                 bool pid_stop_count_changed = ctx.SetProcessStopCount(DNBProcessGetStopCount(pid));
468                 if (pid_stop_count_changed)
469                 {
470                     remote->FlushSTDIO();
471 
472                     if (ctx.GetProcessStopCount() == 1)
473                     {
474                         DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s (&remote, initialize=%i)  pid_state = %s pid_stop_count %llu (old %llu)) Notify??? no, first stop...", __FUNCTION__, (int)initialize, DNBStateAsString (pid_state), (uint64_t)ctx.GetProcessStopCount(), (uint64_t)prev_pid_stop_count);
475                     }
476                     else
477                     {
478 
479                         DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s (&remote, initialize=%i)  pid_state = %s pid_stop_count %llu (old %llu)) Notify??? YES!!!", __FUNCTION__, (int)initialize, DNBStateAsString (pid_state), (uint64_t)ctx.GetProcessStopCount(), (uint64_t)prev_pid_stop_count);
480                         remote->NotifyThatProcessStopped ();
481                     }
482                 }
483                 else
484                 {
485                     DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s (&remote, initialize=%i)  pid_state = %s pid_stop_count %llu (old %llu)) Notify??? skipping...", __FUNCTION__, (int)initialize, DNBStateAsString (pid_state), (uint64_t)ctx.GetProcessStopCount(), (uint64_t)prev_pid_stop_count);
486                 }
487             }
488             return eRNBRunLoopModeInferiorExecuting;
489 
490         case eStateStepping:
491         case eStateRunning:
492             return eRNBRunLoopModeInferiorExecuting;
493 
494         case eStateExited:
495             remote->HandlePacket_last_signal(NULL);
496         case eStateDetached:
497             return eRNBRunLoopModeExit;
498 
499     }
500 
501     // Catch all...
502     return eRNBRunLoopModeExit;
503 }
504 // This function handles the case where our inferior program is stopped and
505 // we are waiting for gdb remote protocol packets. When a packet occurs that
506 // makes the inferior run, we need to leave this function with a new state
507 // as the return code.
508 RNBRunLoopMode
509 RNBRunLoopInferiorExecuting (RNBRemote *remote)
510 {
511     DNBLogThreadedIf (LOG_RNB_MINIMAL, "#### %s", __FUNCTION__);
512     RNBContext& ctx = remote->Context();
513 
514     // Init our mode and set 'is_running' based on the current process state
515     RNBRunLoopMode mode = HandleProcessStateChange (remote, true);
516 
517     while (ctx.ProcessID() != INVALID_NUB_PROCESS)
518     {
519 
520         std::string set_events_str;
521         uint32_t event_mask = ctx.NormalEventBits();
522 
523         if (!ctx.ProcessStateRunning())
524         {
525             // Clear some bits if we are not running so we don't send any async packets
526             event_mask &= ~RNBContext::event_proc_stdio_available;
527             event_mask &= ~RNBContext::event_proc_profile_data;
528             // When we enable async structured data packets over another logical channel,
529             // this can be relaxed.
530             event_mask &= ~RNBContext::event_darwin_log_data_available;
531         }
532 
533         // We want to make sure we consume all process state changes and have
534         // whomever is notifying us to wait for us to reset the event bit before
535         // continuing.
536         //ctx.Events().SetResetAckMask (RNBContext::event_proc_state_changed);
537 
538         DNBLogThreadedIf (LOG_RNB_EVENTS, "%s ctx.Events().WaitForSetEvents(0x%08x) ...",__FUNCTION__, event_mask);
539         nub_event_t set_events = ctx.Events().WaitForSetEvents(event_mask);
540         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));
541 
542         if (set_events)
543         {
544             if ((set_events & RNBContext::event_proc_thread_exiting) ||
545                 (set_events & RNBContext::event_proc_stdio_available))
546             {
547                 remote->FlushSTDIO();
548             }
549 
550             if (set_events & RNBContext::event_proc_profile_data)
551             {
552                 remote->SendAsyncProfileData();
553             }
554 
555             if (set_events & RNBContext::event_darwin_log_data_available)
556             {
557                 remote->SendAsyncDarwinLogData();
558             }
559 
560             if (set_events & RNBContext::event_read_packet_available)
561             {
562                 // handleReceivedPacket will take care of resetting the
563                 // event_read_packet_available events when there are no more...
564                 set_events ^= RNBContext::event_read_packet_available;
565 
566                 if (ctx.ProcessStateRunning())
567                 {
568                     if (remote->HandleAsyncPacket() == rnb_not_connected)
569                     {
570                         // TODO: connect again? Exit?
571                     }
572                 }
573                 else
574                 {
575                     if (remote->HandleReceivedPacket() == rnb_not_connected)
576                     {
577                         // TODO: connect again? Exit?
578                     }
579                 }
580             }
581 
582             if (set_events & RNBContext::event_proc_state_changed)
583             {
584                 mode = HandleProcessStateChange (remote, false);
585                 ctx.Events().ResetEvents(RNBContext::event_proc_state_changed);
586                 set_events ^= RNBContext::event_proc_state_changed;
587             }
588 
589             if (set_events & RNBContext::event_proc_thread_exiting)
590             {
591                 mode = eRNBRunLoopModeExit;
592             }
593 
594             if (set_events & RNBContext::event_read_thread_exiting)
595             {
596                 // Out remote packet receiving thread exited, exit for now.
597                 if (ctx.HasValidProcessID())
598                 {
599                     // TODO: We should add code that will leave the current process
600                     // in its current state and listen for another connection...
601                     if (ctx.ProcessStateRunning())
602                     {
603                         if (ctx.GetDetachOnError())
604                         {
605                             DNBLog ("debugserver's event read thread is exiting, detaching from the inferior process.");
606                             DNBProcessDetach (ctx.ProcessID());
607                         }
608                         else
609                         {
610                             DNBLog ("debugserver's event read thread is exiting, killing the inferior process.");
611                             DNBProcessKill (ctx.ProcessID());
612                         }
613                     }
614                     else
615                     {
616                         if (ctx.GetDetachOnError())
617                         {
618                             DNBLog ("debugserver's event read thread is exiting, detaching from the inferior process.");
619                             DNBProcessDetach (ctx.ProcessID());
620                         }
621                     }
622                 }
623                 mode = eRNBRunLoopModeExit;
624             }
625         }
626 
627         // Reset all event bits that weren't reset for now...
628         if (set_events != 0)
629             ctx.Events().ResetEvents(set_events);
630 
631         if (mode != eRNBRunLoopModeInferiorExecuting)
632             break;
633     }
634 
635     return mode;
636 }
637 
638 
639 RNBRunLoopMode
640 RNBRunLoopPlatform (RNBRemote *remote)
641 {
642     RNBRunLoopMode mode = eRNBRunLoopModePlatformMode;
643     RNBContext& ctx = remote->Context();
644 
645     while (mode == eRNBRunLoopModePlatformMode)
646     {
647         std::string set_events_str;
648         const uint32_t event_mask = RNBContext::event_read_packet_available |
649                                     RNBContext::event_read_thread_exiting;
650 
651         DNBLogThreadedIf (LOG_RNB_EVENTS, "%s ctx.Events().WaitForSetEvents(0x%08x) ...",__FUNCTION__, event_mask);
652         nub_event_t set_events = ctx.Events().WaitForSetEvents(event_mask);
653         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));
654 
655         if (set_events)
656         {
657             if (set_events & RNBContext::event_read_packet_available)
658             {
659                 if (remote->HandleReceivedPacket() == rnb_not_connected)
660                     mode = eRNBRunLoopModeExit;
661             }
662 
663             if (set_events & RNBContext::event_read_thread_exiting)
664             {
665                 mode = eRNBRunLoopModeExit;
666             }
667             ctx.Events().ResetEvents(set_events);
668         }
669     }
670     return eRNBRunLoopModeExit;
671 }
672 
673 //----------------------------------------------------------------------
674 // Convenience function to set up the remote listening port
675 // Returns 1 for success 0 for failure.
676 //----------------------------------------------------------------------
677 
678 static void
679 PortWasBoundCallbackUnixSocket (const void *baton, in_port_t port)
680 {
681     //::printf ("PortWasBoundCallbackUnixSocket (baton = %p, port = %u)\n", baton, port);
682 
683     const char *unix_socket_name = (const char *)baton;
684 
685     if (unix_socket_name && unix_socket_name[0])
686     {
687         // We were given a unix socket name to use to communicate the port
688         // that we ended up binding to back to our parent process
689         struct sockaddr_un saddr_un;
690         int s = ::socket (AF_UNIX, SOCK_STREAM, 0);
691         if (s < 0)
692         {
693             perror("error: socket (AF_UNIX, SOCK_STREAM, 0)");
694             exit(1);
695         }
696 
697         saddr_un.sun_family = AF_UNIX;
698         ::strncpy(saddr_un.sun_path, unix_socket_name, sizeof(saddr_un.sun_path) - 1);
699         saddr_un.sun_path[sizeof(saddr_un.sun_path) - 1] = '\0';
700         saddr_un.sun_len = SUN_LEN (&saddr_un);
701 
702         if (::connect (s, (struct sockaddr *)&saddr_un, static_cast<socklen_t>(SUN_LEN (&saddr_un))) < 0)
703         {
704             perror("error: connect (socket, &saddr_un, saddr_un_len)");
705             exit(1);
706         }
707 
708         //::printf ("connect () sucess!!\n");
709 
710 
711         // We were able to connect to the socket, now write our PID so whomever
712         // launched us will know this process's ID
713         RNBLogSTDOUT ("Listening to port %i...\n", port);
714 
715         char pid_str[64];
716         const int pid_str_len = ::snprintf (pid_str, sizeof(pid_str), "%u", port);
717         const ssize_t bytes_sent = ::send (s, pid_str, pid_str_len, 0);
718 
719         if (pid_str_len != bytes_sent)
720         {
721             perror("error: send (s, pid_str, pid_str_len, 0)");
722             exit (1);
723         }
724 
725         //::printf ("send () sucess!!\n");
726 
727         // We are done with the socket
728         close (s);
729     }
730 }
731 
732 static void
733 PortWasBoundCallbackNamedPipe (const void *baton, uint16_t port)
734 {
735     const char *named_pipe = (const char *)baton;
736     if (named_pipe && named_pipe[0])
737     {
738         int fd = ::open(named_pipe, O_WRONLY);
739         if (fd > -1)
740         {
741             char port_str[64];
742             const ssize_t port_str_len = ::snprintf (port_str, sizeof(port_str), "%u", port);
743             // Write the port number as a C string with the NULL terminator
744             ::write (fd, port_str, port_str_len + 1);
745             close (fd);
746         }
747     }
748 }
749 
750 static int
751 ConnectRemote (RNBRemote *remote,
752                const char *host,
753                int port,
754                bool reverse_connect,
755                const char *named_pipe_path,
756                const char *unix_socket_name)
757 {
758     if (!remote->Comm().IsConnected())
759     {
760         if (reverse_connect)
761         {
762             if (port == 0)
763             {
764                 DNBLogThreaded("error: invalid port supplied for reverse connection: %i.\n", port);
765                 return 0;
766             }
767             if (remote->Comm().Connect(host, port) != rnb_success)
768             {
769                 DNBLogThreaded("Failed to reverse connect to %s:%i.\n", host, port);
770                 return 0;
771             }
772         }
773         else
774         {
775             if (port != 0)
776                 RNBLogSTDOUT ("Listening to port %i for a connection from %s...\n", port, host ? host : "127.0.0.1");
777             if (unix_socket_name && unix_socket_name[0])
778             {
779                 if (remote->Comm().Listen(host, port, PortWasBoundCallbackUnixSocket, unix_socket_name) != rnb_success)
780                 {
781                     RNBLogSTDERR ("Failed to get connection from a remote gdb process.\n");
782                     return 0;
783                 }
784             }
785             else
786             {
787                 if (remote->Comm().Listen(host, port, PortWasBoundCallbackNamedPipe, named_pipe_path) != rnb_success)
788                 {
789                     RNBLogSTDERR ("Failed to get connection from a remote gdb process.\n");
790                     return 0;
791                 }
792             }
793         }
794         remote->StartReadRemoteDataThread();
795     }
796     return 1;
797 }
798 
799 //----------------------------------------------------------------------
800 // ASL Logging callback that can be registered with DNBLogSetLogCallback
801 //----------------------------------------------------------------------
802 void
803 ASLLogCallback(void *baton, uint32_t flags, const char *format, va_list args)
804 {
805     if (format == NULL)
806         return;
807     static aslmsg g_aslmsg = NULL;
808     if (g_aslmsg == NULL)
809     {
810         g_aslmsg = ::asl_new (ASL_TYPE_MSG);
811         char asl_key_sender[PATH_MAX];
812         snprintf(asl_key_sender, sizeof(asl_key_sender), "com.apple.%s-%s", DEBUGSERVER_PROGRAM_NAME, DEBUGSERVER_VERSION_STR);
813         ::asl_set (g_aslmsg, ASL_KEY_SENDER, asl_key_sender);
814     }
815 
816     int asl_level;
817     if (flags & DNBLOG_FLAG_FATAL)        asl_level = ASL_LEVEL_CRIT;
818     else if (flags & DNBLOG_FLAG_ERROR)   asl_level = ASL_LEVEL_ERR;
819     else if (flags & DNBLOG_FLAG_WARNING) asl_level = ASL_LEVEL_WARNING;
820     else if (flags & DNBLOG_FLAG_VERBOSE) asl_level = ASL_LEVEL_WARNING; //ASL_LEVEL_INFO;
821     else                                  asl_level = ASL_LEVEL_WARNING; //ASL_LEVEL_DEBUG;
822 
823     ::asl_vlog (NULL, g_aslmsg, asl_level, format, args);
824 }
825 
826 //----------------------------------------------------------------------
827 // FILE based Logging callback that can be registered with
828 // DNBLogSetLogCallback
829 //----------------------------------------------------------------------
830 void
831 FileLogCallback(void *baton, uint32_t flags, const char *format, va_list args)
832 {
833     if (baton == NULL || format == NULL)
834         return;
835 
836     ::vfprintf((FILE *)baton, format, args);
837     ::fprintf((FILE *)baton, "\n");
838     ::fflush((FILE *)baton);
839 }
840 
841 
842 void
843 show_usage_and_exit (int exit_code)
844 {
845     RNBLogSTDERR ("Usage:\n  %s host:port [program-name program-arg1 program-arg2 ...]\n", DEBUGSERVER_PROGRAM_NAME);
846     RNBLogSTDERR ("  %s /path/file [program-name program-arg1 program-arg2 ...]\n", DEBUGSERVER_PROGRAM_NAME);
847     RNBLogSTDERR ("  %s host:port --attach=<pid>\n", DEBUGSERVER_PROGRAM_NAME);
848     RNBLogSTDERR ("  %s /path/file --attach=<pid>\n", DEBUGSERVER_PROGRAM_NAME);
849     RNBLogSTDERR ("  %s host:port --attach=<process_name>\n", DEBUGSERVER_PROGRAM_NAME);
850     RNBLogSTDERR ("  %s /path/file --attach=<process_name>\n", DEBUGSERVER_PROGRAM_NAME);
851     exit (exit_code);
852 }
853 
854 
855 //----------------------------------------------------------------------
856 // option descriptors for getopt_long_only()
857 //----------------------------------------------------------------------
858 static struct option g_long_options[] =
859 {
860     { "attach",             required_argument,  NULL,               'a' },
861     { "arch",               required_argument,  NULL,               'A' },
862     { "debug",              no_argument,        NULL,               'g' },
863     { "kill-on-error",      no_argument,        NULL,               'K' },
864     { "verbose",            no_argument,        NULL,               'v' },
865     { "lockdown",           no_argument,        &g_lockdown_opt,    1   },  // short option "-k"
866     { "applist",            no_argument,        &g_applist_opt,     1   },  // short option "-t"
867     { "log-file",           required_argument,  NULL,               'l' },
868     { "log-flags",          required_argument,  NULL,               'f' },
869     { "launch",             required_argument,  NULL,               'x' },  // Valid values are "auto", "posix-spawn", "fork-exec", "springboard" (arm only)
870     { "waitfor",            required_argument,  NULL,               'w' },  // Wait for a process whose name starts with ARG
871     { "waitfor-interval",   required_argument,  NULL,               'i' },  // Time in usecs to wait between sampling the pid list when waiting for a process by name
872     { "waitfor-duration",   required_argument,  NULL,               'd' },  // The time in seconds to wait for a process to show up by name
873     { "native-regs",        no_argument,        NULL,               'r' },  // Specify to use the native registers instead of the gdb defaults for the architecture.
874     { "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)
875     { "stdin-path",         required_argument,  NULL,               'I' },  // Set the STDIN path to be used when launching applications (only if debugserver launches the process)
876     { "stdout-path",        required_argument,  NULL,               'O' },  // Set the STDOUT path to be used when launching applications (only if debugserver launches the process)
877     { "stderr-path",        required_argument,  NULL,               'E' },  // Set the STDERR path to be used when launching applications (only if debugserver launches the process)
878     { "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)
879     { "setsid",             no_argument,        NULL,               'S' },  // call setsid() to make debugserver run in its own session
880     { "disable-aslr",       no_argument,        NULL,               'D' },  // Use _POSIX_SPAWN_DISABLE_ASLR to avoid shared library randomization
881     { "working-dir",        required_argument,  NULL,               'W' },  // The working directory that the inferior process should have (only if debugserver launches the process)
882     { "platform",           required_argument,  NULL,               'p' },  // Put this executable into a remote platform mode
883     { "unix-socket",        required_argument,  NULL,               'u' },  // If we need to handshake with our parent process, an option will be passed down that specifies a unix socket name to use
884     { "fd",                 required_argument,  NULL,            'FDSC' },  // A file descriptor was passed to this process when spawned that is already open and ready for communication
885     { "named-pipe",         required_argument,  NULL,               'P' },
886     { "reverse-connect",    no_argument,        NULL,               'R' },
887     { "env",                required_argument,  NULL,               'e' },  // When debugserver launches the process, set a single environment entry as specified by the option value ("./debugserver -e FOO=1 -e BAR=2 localhost:1234 -- /bin/ls")
888     { "forward-env",        no_argument,        NULL,               'F' },  // When debugserver launches the process, forward debugserver's current environment variables to the child process ("./debugserver -F localhost:1234 -- /bin/ls"
889     { NULL,                 0,                  NULL,               0   }
890 };
891 
892 
893 //----------------------------------------------------------------------
894 // main
895 //----------------------------------------------------------------------
896 int
897 main (int argc, char *argv[])
898 {
899     // If debugserver is launched with DYLD_INSERT_LIBRARIES, unset it so we
900     // don't spawn child processes with this enabled.
901     unsetenv("DYLD_INSERT_LIBRARIES");
902 
903     const char *argv_sub_zero = argv[0]; // save a copy of argv[0] for error reporting post-launch
904 
905 #if defined (__APPLE__)
906     pthread_setname_np ("main thread");
907 #if defined (__arm__) || defined (__arm64__) || defined (__aarch64__)
908     struct sched_param thread_param;
909     int thread_sched_policy;
910     if (pthread_getschedparam(pthread_self(), &thread_sched_policy, &thread_param) == 0)
911     {
912         thread_param.sched_priority = 47;
913         pthread_setschedparam(pthread_self(), thread_sched_policy, &thread_param);
914     }
915 
916     ::proc_set_wakemon_params (getpid(), 500, 0); // Allow up to 500 wakeups/sec to avoid EXC_RESOURCE for normal use.
917 #endif
918 #endif
919 
920     g_isatty = ::isatty (STDIN_FILENO);
921 
922     //  ::printf ("uid=%u euid=%u gid=%u egid=%u\n",
923     //            getuid(),
924     //            geteuid(),
925     //            getgid(),
926     //            getegid());
927 
928 
929     //    signal (SIGINT, signal_handler);
930     signal (SIGPIPE, signal_handler);
931     signal (SIGHUP, signal_handler);
932 
933     // We're always sitting in waitpid or kevent waiting on our target process' death,
934     // we don't need no stinking SIGCHLD's...
935 
936     sigset_t sigset;
937     sigemptyset(&sigset);
938     sigaddset(&sigset, SIGCHLD);
939     sigprocmask(SIG_BLOCK, &sigset, NULL);
940 
941     g_remoteSP.reset (new RNBRemote ());
942 
943 
944     RNBRemote *remote = g_remoteSP.get();
945     if (remote == NULL)
946     {
947         RNBLogSTDERR ("error: failed to create a remote connection class\n");
948         return -1;
949     }
950 
951     RNBContext& ctx = remote->Context();
952 
953     int i;
954     int attach_pid = INVALID_NUB_PROCESS;
955 
956     FILE* log_file = NULL;
957     uint32_t log_flags = 0;
958     // Parse our options
959     int ch;
960     int long_option_index = 0;
961     int debug = 0;
962     int communication_fd = -1;
963     std::string compile_options;
964     std::string waitfor_pid_name;           // Wait for a process that starts with this name
965     std::string attach_pid_name;
966     std::string arch_name;
967     std::string working_dir;                // The new working directory to use for the inferior
968     std::string unix_socket_name;           // If we need to handshake with our parent process, an option will be passed down that specifies a unix socket name to use
969     std::string named_pipe_path;            // If we need to handshake with our parent process, an option will be passed down that specifies a named pipe to use
970     useconds_t waitfor_interval = 1000;     // Time in usecs between process lists polls when waiting for a process by name, default 1 msec.
971     useconds_t waitfor_duration = 0;        // Time in seconds to wait for a process by name, 0 means wait forever.
972     bool no_stdio = false;
973     bool reverse_connect = false;           // Set to true by an option to indicate we should reverse connect to the host:port supplied as the first debugserver argument
974 
975 #if !defined (DNBLOG_ENABLED)
976     compile_options += "(no-logging) ";
977 #endif
978 
979     RNBRunLoopMode start_mode = eRNBRunLoopModeExit;
980 
981     char short_options[512];
982     uint32_t short_options_idx = 0;
983 
984      // Handle the two case that don't have short options in g_long_options
985     short_options[short_options_idx++] = 'k';
986     short_options[short_options_idx++] = 't';
987 
988     for (i=0; g_long_options[i].name != NULL; ++i)
989     {
990         if (isalpha(g_long_options[i].val))
991         {
992             short_options[short_options_idx++] = g_long_options[i].val;
993             switch (g_long_options[i].has_arg)
994             {
995                 default:
996                 case no_argument:
997                     break;
998 
999                 case optional_argument:
1000                     short_options[short_options_idx++] = ':';
1001                     // Fall through to required_argument case below...
1002                 case required_argument:
1003                     short_options[short_options_idx++] = ':';
1004                     break;
1005             }
1006         }
1007     }
1008     // NULL terminate the short option string.
1009     short_options[short_options_idx++] = '\0';
1010 
1011 #if __GLIBC__
1012     optind = 0;
1013 #else
1014     optreset = 1;
1015     optind = 1;
1016 #endif
1017 
1018     while ((ch = getopt_long_only(argc, argv, short_options, g_long_options, &long_option_index)) != -1)
1019     {
1020         DNBLogDebug("option: ch == %c (0x%2.2x) --%s%c%s\n",
1021                     ch, (uint8_t)ch,
1022                     g_long_options[long_option_index].name,
1023                     g_long_options[long_option_index].has_arg ? '=' : ' ',
1024                     optarg ? optarg : "");
1025         switch (ch)
1026         {
1027             case 0:   // Any optional that auto set themselves will return 0
1028                 break;
1029 
1030             case 'A':
1031                 if (optarg && optarg[0])
1032                     arch_name.assign(optarg);
1033                 break;
1034 
1035             case 'a':
1036                 if (optarg && optarg[0])
1037                 {
1038                     if (isdigit(optarg[0]))
1039                     {
1040                         char *end = NULL;
1041                         attach_pid = static_cast<int>(strtoul(optarg, &end, 0));
1042                         if (end == NULL || *end != '\0')
1043                         {
1044                             RNBLogSTDERR ("error: invalid pid option '%s'\n", optarg);
1045                             exit (4);
1046                         }
1047                     }
1048                     else
1049                     {
1050                         attach_pid_name = optarg;
1051                     }
1052                     start_mode = eRNBRunLoopModeInferiorAttaching;
1053                 }
1054                 break;
1055 
1056                 // --waitfor=NAME
1057             case 'w':
1058                 if (optarg && optarg[0])
1059                 {
1060                     waitfor_pid_name = optarg;
1061                     start_mode = eRNBRunLoopModeInferiorAttaching;
1062                 }
1063                 break;
1064 
1065                 // --waitfor-interval=USEC
1066             case 'i':
1067                 if (optarg && optarg[0])
1068                 {
1069                     char *end = NULL;
1070                     waitfor_interval = static_cast<useconds_t>(strtoul(optarg, &end, 0));
1071                     if (end == NULL || *end != '\0')
1072                     {
1073                         RNBLogSTDERR ("error: invalid waitfor-interval option value '%s'.\n", optarg);
1074                         exit (6);
1075                     }
1076                 }
1077                 break;
1078 
1079                 // --waitfor-duration=SEC
1080             case 'd':
1081                 if (optarg && optarg[0])
1082                 {
1083                     char *end = NULL;
1084                     waitfor_duration = static_cast<useconds_t>(strtoul(optarg, &end, 0));
1085                     if (end == NULL || *end != '\0')
1086                     {
1087                         RNBLogSTDERR ("error: invalid waitfor-duration option value '%s'.\n", optarg);
1088                         exit (7);
1089                     }
1090                 }
1091                 break;
1092 
1093             case 'K':
1094                 g_detach_on_error = false;
1095                 break;
1096             case 'W':
1097                 if (optarg && optarg[0])
1098                     working_dir.assign(optarg);
1099                 break;
1100 
1101             case 'x':
1102                 if (optarg && optarg[0])
1103                 {
1104                     if (strcasecmp(optarg, "auto") == 0)
1105                         g_launch_flavor = eLaunchFlavorDefault;
1106                     else if (strcasestr(optarg, "posix") == optarg)
1107                         g_launch_flavor = eLaunchFlavorPosixSpawn;
1108                     else if (strcasestr(optarg, "fork") == optarg)
1109                         g_launch_flavor = eLaunchFlavorForkExec;
1110 #ifdef WITH_SPRINGBOARD
1111                     else if (strcasestr(optarg, "spring") == optarg)
1112                         g_launch_flavor = eLaunchFlavorSpringBoard;
1113 #endif
1114 #ifdef WITH_BKS
1115                     else if (strcasestr(optarg, "backboard") == optarg)
1116                         g_launch_flavor = eLaunchFlavorBKS;
1117 #endif
1118 #ifdef WITH_FBS
1119                     else if (strcasestr(optarg, "frontboard") == optarg)
1120                         g_launch_flavor = eLaunchFlavorFBS;
1121 #endif
1122 
1123                     else
1124                     {
1125                         RNBLogSTDERR ("error: invalid TYPE for the --launch=TYPE (-x TYPE) option: '%s'\n", optarg);
1126                         RNBLogSTDERR ("Valid values TYPE are:\n");
1127                         RNBLogSTDERR ("  auto       Auto-detect the best launch method to use.\n");
1128                         RNBLogSTDERR ("  posix      Launch the executable using posix_spawn.\n");
1129                         RNBLogSTDERR ("  fork       Launch the executable using fork and exec.\n");
1130 #ifdef WITH_SPRINGBOARD
1131                         RNBLogSTDERR ("  spring     Launch the executable through Springboard.\n");
1132 #endif
1133 #ifdef WITH_BKS
1134                         RNBLogSTDERR ("  backboard  Launch the executable through BackBoard Services.\n");
1135 #endif
1136 #ifdef WITH_FBS
1137                         RNBLogSTDERR ("  frontboard  Launch the executable through FrontBoard Services.\n");
1138 #endif
1139                         exit (5);
1140                     }
1141                 }
1142                 break;
1143 
1144             case 'l': // Set Log File
1145                 if (optarg && optarg[0])
1146                 {
1147                     if (strcasecmp(optarg, "stdout") == 0)
1148                         log_file = stdout;
1149                     else if (strcasecmp(optarg, "stderr") == 0)
1150                         log_file = stderr;
1151                     else
1152                     {
1153                         log_file = fopen(optarg, "w");
1154                         if (log_file != NULL)
1155                             setlinebuf(log_file);
1156                     }
1157 
1158                     if (log_file == NULL)
1159                     {
1160                         const char *errno_str = strerror(errno);
1161                         RNBLogSTDERR ("Failed to open log file '%s' for writing: errno = %i (%s)", optarg, errno, errno_str ? errno_str : "unknown error");
1162                     }
1163                 }
1164                 break;
1165 
1166             case 'f': // Log Flags
1167                 if (optarg && optarg[0])
1168                     log_flags = static_cast<uint32_t>(strtoul(optarg, NULL, 0));
1169                 break;
1170 
1171             case 'g':
1172                 debug = 1;
1173                 DNBLogSetDebug(debug);
1174                 break;
1175 
1176             case 't':
1177                 g_applist_opt = 1;
1178                 break;
1179 
1180             case 'k':
1181                 g_lockdown_opt = 1;
1182                 break;
1183 
1184             case 'r':
1185                 // Do nothing, native regs is the default these days
1186                 break;
1187 
1188             case 'R':
1189                 reverse_connect = true;
1190                 break;
1191             case 'v':
1192                 DNBLogSetVerbose(1);
1193                 break;
1194 
1195             case 's':
1196                 ctx.GetSTDIN().assign(optarg);
1197                 ctx.GetSTDOUT().assign(optarg);
1198                 ctx.GetSTDERR().assign(optarg);
1199                 break;
1200 
1201             case 'I':
1202                 ctx.GetSTDIN().assign(optarg);
1203                 break;
1204 
1205             case 'O':
1206                 ctx.GetSTDOUT().assign(optarg);
1207                 break;
1208 
1209             case 'E':
1210                 ctx.GetSTDERR().assign(optarg);
1211                 break;
1212 
1213             case 'n':
1214                 no_stdio = true;
1215                 break;
1216 
1217             case 'S':
1218                 // Put debugserver into a new session. Terminals group processes
1219                 // into sessions and when a special terminal key sequences
1220                 // (like control+c) are typed they can cause signals to go out to
1221                 // all processes in a session. Using this --setsid (-S) option
1222                 // will cause debugserver to run in its own sessions and be free
1223                 // from such issues.
1224                 //
1225                 // This is useful when debugserver is spawned from a command
1226                 // line application that uses debugserver to do the debugging,
1227                 // yet that application doesn't want debugserver receiving the
1228                 // signals sent to the session (i.e. dying when anyone hits ^C).
1229                 setsid();
1230                 break;
1231             case 'D':
1232                 g_disable_aslr = 1;
1233                 break;
1234 
1235             case 'p':
1236                 start_mode = eRNBRunLoopModePlatformMode;
1237                 break;
1238 
1239             case 'u':
1240                 unix_socket_name.assign (optarg);
1241                 break;
1242 
1243             case 'P':
1244                 named_pipe_path.assign (optarg);
1245                 break;
1246 
1247             case 'e':
1248                 // Pass a single specified environment variable down to the process that gets launched
1249                 remote->Context().PushEnvironment(optarg);
1250                 break;
1251 
1252             case 'F':
1253                 // Pass the current environment down to the process that gets launched
1254                 {
1255                     char **host_env = *_NSGetEnviron();
1256                     char *env_entry;
1257                     size_t i;
1258                     for (i=0; (env_entry = host_env[i]) != NULL; ++i)
1259                         remote->Context().PushEnvironment(env_entry);
1260                 }
1261                 break;
1262 
1263             case 'FDSC':
1264                 // File descriptor passed to this process during fork/exec and is already
1265                 // open and ready for communication.
1266                 communication_fd = atoi(optarg);
1267                 break;
1268         }
1269     }
1270 
1271     if (arch_name.empty())
1272     {
1273 #if defined (__arm__)
1274         arch_name.assign ("arm");
1275 #endif
1276     }
1277     else
1278     {
1279         DNBSetArchitecture (arch_name.c_str());
1280     }
1281 
1282 //    if (arch_name.empty())
1283 //    {
1284 //        fprintf(stderr, "error: no architecture was specified\n");
1285 //        exit (8);
1286 //    }
1287     // Skip any options we consumed with getopt_long_only
1288     argc -= optind;
1289     argv += optind;
1290 
1291 
1292     if (!working_dir.empty())
1293     {
1294         if (remote->Context().SetWorkingDirectory (working_dir.c_str()) == false)
1295         {
1296             RNBLogSTDERR ("error: working directory doesn't exist '%s'.\n", working_dir.c_str());
1297             exit (8);
1298         }
1299     }
1300 
1301     remote->Context().SetDetachOnError(g_detach_on_error);
1302 
1303     remote->Initialize();
1304 
1305     // It is ok for us to set NULL as the logfile (this will disable any logging)
1306 
1307     if (log_file != NULL)
1308     {
1309         DNBLogSetLogCallback(FileLogCallback, log_file);
1310         // If our log file was set, yet we have no log flags, log everything!
1311         if (log_flags == 0)
1312             log_flags = LOG_ALL | LOG_RNB_ALL;
1313 
1314         DNBLogSetLogMask (log_flags);
1315     }
1316     else
1317     {
1318         // Enable DNB logging
1319 
1320         // if os_log() support is available, log through that.
1321         auto log_callback = OsLogger::GetLogFunction();
1322         if (log_callback)
1323         {
1324             DNBLogSetLogCallback(log_callback, nullptr);
1325             DNBLog("debugserver will use os_log for internal logging.");
1326         }
1327         else
1328         {
1329             // Fall back to ASL support.
1330             DNBLogSetLogCallback(ASLLogCallback, NULL);
1331             DNBLog("debugserver will use ASL for internal logging.");
1332         }
1333         DNBLogSetLogMask (log_flags);
1334 
1335     }
1336 
1337     if (DNBLogEnabled())
1338     {
1339         for (i=0; i<argc; i++)
1340             DNBLogDebug("argv[%i] = %s", i, argv[i]);
1341     }
1342 
1343     // as long as we're dropping remotenub in as a replacement for gdbserver,
1344     // explicitly note that this is not gdbserver.
1345 
1346     RNBLogSTDOUT ("%s-%s %sfor %s.\n",
1347                   DEBUGSERVER_PROGRAM_NAME,
1348                   DEBUGSERVER_VERSION_STR,
1349                   compile_options.c_str(),
1350                   RNB_ARCH);
1351 
1352     std::string host;
1353     int port = INT32_MAX;
1354     char str[PATH_MAX];
1355     str[0] = '\0';
1356 
1357     if (g_lockdown_opt == 0 && g_applist_opt == 0 && communication_fd == -1)
1358     {
1359         // Make sure we at least have port
1360         if (argc < 1)
1361         {
1362             show_usage_and_exit (1);
1363         }
1364         // accept 'localhost:' prefix on port number
1365 
1366         int items_scanned = ::sscanf (argv[0], "%[^:]:%i", str, &port);
1367         if (items_scanned == 2)
1368         {
1369             host = str;
1370             DNBLogDebug("host = '%s'  port = %i", host.c_str(), port);
1371         }
1372         else
1373         {
1374             // No hostname means "localhost"
1375             int items_scanned = ::sscanf (argv[0], "%i", &port);
1376             if (items_scanned == 1)
1377             {
1378                 host = "127.0.0.1";
1379                 DNBLogDebug("host = '%s'  port = %i", host.c_str(), port);
1380             }
1381             else if (argv[0][0] == '/')
1382             {
1383                 port = INT32_MAX;
1384                 strncpy(str, argv[0], sizeof(str));
1385             }
1386             else
1387             {
1388                 show_usage_and_exit (2);
1389             }
1390         }
1391 
1392         // We just used the 'host:port' or the '/path/file' arg...
1393         argc--;
1394         argv++;
1395 
1396     }
1397 
1398     //  If we know we're waiting to attach, we don't need any of this other info.
1399     if (start_mode != eRNBRunLoopModeInferiorAttaching &&
1400         start_mode != eRNBRunLoopModePlatformMode)
1401     {
1402         if (argc == 0 || g_lockdown_opt)
1403         {
1404             if (g_lockdown_opt != 0)
1405             {
1406                 // Work around for SIGPIPE crashes due to posix_spawn issue.
1407                 // We have to close STDOUT and STDERR, else the first time we
1408                 // try and do any, we get SIGPIPE and die as posix_spawn is
1409                 // doing bad things with our file descriptors at the moment.
1410                 int null = open("/dev/null", O_RDWR);
1411                 dup2(null, STDOUT_FILENO);
1412                 dup2(null, STDERR_FILENO);
1413             }
1414             else if (g_applist_opt != 0)
1415             {
1416                 // List all applications we are able to see
1417                 std::string applist_plist;
1418                 int err = ListApplications(applist_plist, false, false);
1419                 if (err == 0)
1420                 {
1421                     fputs (applist_plist.c_str(), stdout);
1422                 }
1423                 else
1424                 {
1425                     RNBLogSTDERR ("error: ListApplications returned error %i\n", err);
1426                 }
1427                 // Exit with appropriate error if we were asked to list the applications
1428                 // with no other args were given (and we weren't trying to do this over
1429                 // lockdown)
1430                 return err;
1431             }
1432 
1433             DNBLogDebug("Get args from remote protocol...");
1434             start_mode = eRNBRunLoopModeGetStartModeFromRemoteProtocol;
1435         }
1436         else
1437         {
1438             start_mode = eRNBRunLoopModeInferiorLaunching;
1439             // Fill in the argv array in the context from the rest of our args.
1440             // Skip the name of this executable and the port number
1441             for (int i = 0; i < argc; i++)
1442             {
1443                 DNBLogDebug("inferior_argv[%i] = '%s'", i, argv[i]);
1444                 ctx.PushArgument (argv[i]);
1445             }
1446         }
1447     }
1448 
1449     if (start_mode == eRNBRunLoopModeExit)
1450         return -1;
1451 
1452     RNBRunLoopMode mode = start_mode;
1453     char err_str[1024] = {'\0'};
1454 
1455     while (mode != eRNBRunLoopModeExit)
1456     {
1457         switch (mode)
1458         {
1459             case eRNBRunLoopModeGetStartModeFromRemoteProtocol:
1460 #ifdef WITH_LOCKDOWN
1461                 if (g_lockdown_opt)
1462                 {
1463                     if (!remote->Comm().IsConnected())
1464                     {
1465                         if (remote->Comm().ConnectToService () != rnb_success)
1466                         {
1467                             RNBLogSTDERR ("Failed to get connection from a remote gdb process.\n");
1468                             mode = eRNBRunLoopModeExit;
1469                         }
1470                         else if (g_applist_opt != 0)
1471                         {
1472                             // List all applications we are able to see
1473                             std::string applist_plist;
1474                             if (ListApplications(applist_plist, false, false) == 0)
1475                             {
1476                                 DNBLogDebug("Task list: %s", applist_plist.c_str());
1477 
1478                                 remote->Comm().Write(applist_plist.c_str(), applist_plist.size());
1479                                 // Issue a read that will never yield any data until the other side
1480                                 // closes the socket so this process doesn't just exit and cause the
1481                                 // socket to close prematurely on the other end and cause data loss.
1482                                 std::string buf;
1483                                 remote->Comm().Read(buf);
1484                             }
1485                             remote->Comm().Disconnect(false);
1486                             mode = eRNBRunLoopModeExit;
1487                             break;
1488                         }
1489                         else
1490                         {
1491                             // Start watching for remote packets
1492                             remote->StartReadRemoteDataThread();
1493                         }
1494                     }
1495                 }
1496                 else
1497 #endif
1498                 if (port != INT32_MAX)
1499                 {
1500                     if (!ConnectRemote (remote, host.c_str(), port, reverse_connect, named_pipe_path.c_str(), unix_socket_name.c_str()))
1501                         mode = eRNBRunLoopModeExit;
1502                 }
1503                 else if (str[0] == '/')
1504                 {
1505                     if (remote->Comm().OpenFile (str))
1506                         mode = eRNBRunLoopModeExit;
1507                 }
1508                 else if (communication_fd >= 0)
1509                 {
1510                     // We were passed a file descriptor to use during fork/exec that is already open
1511                     // in our process, so lets just use it!
1512                     if (remote->Comm().useFD(communication_fd))
1513                         mode = eRNBRunLoopModeExit;
1514                     else
1515                         remote->StartReadRemoteDataThread();
1516                 }
1517 
1518                 if (mode != eRNBRunLoopModeExit)
1519                 {
1520                     RNBLogSTDOUT ("Got a connection, waiting for process information for launching or attaching.\n");
1521 
1522                     mode = RNBRunLoopGetStartModeFromRemote (remote);
1523                 }
1524                 break;
1525 
1526             case eRNBRunLoopModeInferiorAttaching:
1527                 if (!waitfor_pid_name.empty())
1528                 {
1529                     // Set our end wait time if we are using a waitfor-duration
1530                     // option that may have been specified
1531                     struct timespec attach_timeout_abstime, *timeout_ptr = NULL;
1532                     if (waitfor_duration != 0)
1533                     {
1534                         DNBTimer::OffsetTimeOfDay(&attach_timeout_abstime, waitfor_duration, 0);
1535                         timeout_ptr = &attach_timeout_abstime;
1536                     }
1537                     nub_launch_flavor_t launch_flavor = g_launch_flavor;
1538                     if (launch_flavor == eLaunchFlavorDefault)
1539                     {
1540                         // Our default launch method is posix spawn
1541                         launch_flavor = eLaunchFlavorPosixSpawn;
1542 
1543 #if defined WITH_FBS
1544                         // Check if we have an app bundle, if so launch using SpringBoard.
1545                         if (waitfor_pid_name.find (".app") != std::string::npos)
1546                         {
1547                             launch_flavor = eLaunchFlavorFBS;
1548                         }
1549 #elif defined WITH_BKS
1550                         // Check if we have an app bundle, if so launch using SpringBoard.
1551                         if (waitfor_pid_name.find (".app") != std::string::npos)
1552                         {
1553                             launch_flavor = eLaunchFlavorBKS;
1554                         }
1555 #elif defined WITH_SPRINGBOARD
1556                         // Check if we have an app bundle, if so launch using SpringBoard.
1557                         if (waitfor_pid_name.find (".app") != std::string::npos)
1558                         {
1559                             launch_flavor = eLaunchFlavorSpringBoard;
1560                         }
1561 #endif
1562                     }
1563 
1564                     ctx.SetLaunchFlavor(launch_flavor);
1565                     bool ignore_existing = false;
1566                     RNBLogSTDOUT ("Waiting to attach to process %s...\n", waitfor_pid_name.c_str());
1567                     nub_process_t pid = DNBProcessAttachWait (waitfor_pid_name.c_str(), launch_flavor, ignore_existing, timeout_ptr, waitfor_interval, err_str, sizeof(err_str));
1568                     g_pid = pid;
1569 
1570                     if (pid == INVALID_NUB_PROCESS)
1571                     {
1572                         ctx.LaunchStatus().SetError(-1, DNBError::Generic);
1573                         if (err_str[0])
1574                             ctx.LaunchStatus().SetErrorString(err_str);
1575                         RNBLogSTDERR ("error: failed to attach to process named: \"%s\" %s\n", waitfor_pid_name.c_str(), err_str);
1576                         mode = eRNBRunLoopModeExit;
1577                     }
1578                     else
1579                     {
1580                         ctx.SetProcessID(pid);
1581                         mode = eRNBRunLoopModeInferiorExecuting;
1582                     }
1583                 }
1584                 else if (attach_pid != INVALID_NUB_PROCESS)
1585                 {
1586 
1587                     RNBLogSTDOUT ("Attaching to process %i...\n", attach_pid);
1588                     nub_process_t attached_pid;
1589                     mode = RNBRunLoopLaunchAttaching (remote, attach_pid, attached_pid);
1590                     if (mode != eRNBRunLoopModeInferiorExecuting)
1591                     {
1592                         const char *error_str = remote->Context().LaunchStatus().AsString();
1593                         RNBLogSTDERR ("error: failed to attach process %i: %s\n", attach_pid, error_str ? error_str : "unknown error.");
1594                         mode = eRNBRunLoopModeExit;
1595                     }
1596                 }
1597                 else if (!attach_pid_name.empty ())
1598                 {
1599                     struct timespec attach_timeout_abstime, *timeout_ptr = NULL;
1600                     if (waitfor_duration != 0)
1601                     {
1602                         DNBTimer::OffsetTimeOfDay(&attach_timeout_abstime, waitfor_duration, 0);
1603                         timeout_ptr = &attach_timeout_abstime;
1604                     }
1605 
1606                     RNBLogSTDOUT ("Attaching to process %s...\n", attach_pid_name.c_str());
1607                     nub_process_t pid = DNBProcessAttachByName (attach_pid_name.c_str(), timeout_ptr, err_str, sizeof(err_str));
1608                     g_pid = pid;
1609                     if (pid == INVALID_NUB_PROCESS)
1610                     {
1611                         ctx.LaunchStatus().SetError(-1, DNBError::Generic);
1612                         if (err_str[0])
1613                             ctx.LaunchStatus().SetErrorString(err_str);
1614                         RNBLogSTDERR ("error: failed to attach to process named: \"%s\" %s\n", waitfor_pid_name.c_str(), err_str);
1615                         mode = eRNBRunLoopModeExit;
1616                     }
1617                     else
1618                     {
1619                         ctx.SetProcessID(pid);
1620                         mode = eRNBRunLoopModeInferiorExecuting;
1621                     }
1622 
1623                 }
1624                 else
1625                 {
1626                     RNBLogSTDERR ("error: asked to attach with empty name and invalid PID.\n");
1627                     mode = eRNBRunLoopModeExit;
1628                 }
1629 
1630                 if (mode != eRNBRunLoopModeExit)
1631                 {
1632                     if (port != INT32_MAX)
1633                     {
1634                         if (!ConnectRemote (remote, host.c_str(), port, reverse_connect, named_pipe_path.c_str(), unix_socket_name.c_str()))
1635                             mode = eRNBRunLoopModeExit;
1636                     }
1637                     else if (str[0] == '/')
1638                     {
1639                         if (remote->Comm().OpenFile (str))
1640                             mode = eRNBRunLoopModeExit;
1641                     }
1642                     else if (communication_fd >= 0)
1643                     {
1644                         // We were passed a file descriptor to use during fork/exec that is already open
1645                         // in our process, so lets just use it!
1646                         if (remote->Comm().useFD(communication_fd))
1647                             mode = eRNBRunLoopModeExit;
1648                         else
1649                             remote->StartReadRemoteDataThread();
1650                     }
1651 
1652                     if (mode != eRNBRunLoopModeExit)
1653                         RNBLogSTDOUT ("Waiting for debugger instructions for process %d.\n", attach_pid);
1654                 }
1655                 break;
1656 
1657             case eRNBRunLoopModeInferiorLaunching:
1658                 {
1659                     mode = RNBRunLoopLaunchInferior (remote,
1660                                                      ctx.GetSTDINPath(),
1661                                                      ctx.GetSTDOUTPath(),
1662                                                      ctx.GetSTDERRPath(),
1663                                                      no_stdio);
1664 
1665                     if (mode == eRNBRunLoopModeInferiorExecuting)
1666                     {
1667                         if (port != INT32_MAX)
1668                         {
1669                             if (!ConnectRemote (remote, host.c_str(), port, reverse_connect, named_pipe_path.c_str(), unix_socket_name.c_str()))
1670                                 mode = eRNBRunLoopModeExit;
1671                         }
1672                         else if (str[0] == '/')
1673                         {
1674                             if (remote->Comm().OpenFile (str))
1675                                 mode = eRNBRunLoopModeExit;
1676                         }
1677                         else if (communication_fd >= 0)
1678                         {
1679                             // We were passed a file descriptor to use during fork/exec that is already open
1680                             // in our process, so lets just use it!
1681                             if (remote->Comm().useFD(communication_fd))
1682                                 mode = eRNBRunLoopModeExit;
1683                             else
1684                                 remote->StartReadRemoteDataThread();
1685                         }
1686 
1687                         if (mode != eRNBRunLoopModeExit)
1688                         {
1689                             const char *proc_name = "<unknown>";
1690                             if (ctx.ArgumentCount() > 0)
1691                                 proc_name = ctx.ArgumentAtIndex(0);
1692                             RNBLogSTDOUT ("Got a connection, launched process %s (pid = %d).\n", proc_name, ctx.ProcessID());
1693                         }
1694                     }
1695                     else
1696                     {
1697                         const char *error_str = remote->Context().LaunchStatus().AsString();
1698                         RNBLogSTDERR ("error: failed to launch process %s: %s\n", argv_sub_zero, error_str ? error_str : "unknown error.");
1699                     }
1700                 }
1701                 break;
1702 
1703             case eRNBRunLoopModeInferiorExecuting:
1704                 mode = RNBRunLoopInferiorExecuting(remote);
1705                 break;
1706 
1707             case eRNBRunLoopModePlatformMode:
1708                 if (port != INT32_MAX)
1709                 {
1710                     if (!ConnectRemote (remote, host.c_str(), port, reverse_connect, named_pipe_path.c_str(), unix_socket_name.c_str()))
1711                         mode = eRNBRunLoopModeExit;
1712                 }
1713                 else if (str[0] == '/')
1714                 {
1715                     if (remote->Comm().OpenFile (str))
1716                         mode = eRNBRunLoopModeExit;
1717                 }
1718                 else if (communication_fd >= 0)
1719                 {
1720                     // We were passed a file descriptor to use during fork/exec that is already open
1721                     // in our process, so lets just use it!
1722                     if (remote->Comm().useFD(communication_fd))
1723                         mode = eRNBRunLoopModeExit;
1724                     else
1725                         remote->StartReadRemoteDataThread();
1726                 }
1727 
1728                 if (mode != eRNBRunLoopModeExit)
1729                     mode = RNBRunLoopPlatform (remote);
1730                 break;
1731 
1732             default:
1733                 mode = eRNBRunLoopModeExit;
1734             case eRNBRunLoopModeExit:
1735                 break;
1736         }
1737     }
1738 
1739     remote->StopReadRemoteDataThread ();
1740     remote->Context().SetProcessID(INVALID_NUB_PROCESS);
1741     RNBLogSTDOUT ("Exiting.\n");
1742 
1743     return 0;
1744 }
1745