• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1//===-- Host.mm -------------------------------------------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "lldb/Host/Host.h"
10#include "PosixSpawnResponsible.h"
11
12#include <AvailabilityMacros.h>
13#include <TargetConditionals.h>
14
15#if TARGET_OS_OSX
16#define __XPC_PRIVATE_H__
17#include <xpc/xpc.h>
18
19#define LaunchUsingXPCRightName "com.apple.lldb.RootDebuggingXPCService"
20
21// These XPC messaging keys are used for communication between Host.mm and the
22// XPC service.
23#define LauncherXPCServiceAuthKey "auth-key"
24#define LauncherXPCServiceArgPrefxKey "arg"
25#define LauncherXPCServiceEnvPrefxKey "env"
26#define LauncherXPCServiceCPUTypeKey "cpuType"
27#define LauncherXPCServicePosixspawnFlagsKey "posixspawnFlags"
28#define LauncherXPCServiceStdInPathKeyKey "stdInPath"
29#define LauncherXPCServiceStdOutPathKeyKey "stdOutPath"
30#define LauncherXPCServiceStdErrPathKeyKey "stdErrPath"
31#define LauncherXPCServiceChildPIDKey "childPID"
32#define LauncherXPCServiceErrorTypeKey "errorType"
33#define LauncherXPCServiceCodeTypeKey "errorCode"
34
35#endif
36
37#include "llvm/Support/Host.h"
38
39#include <asl.h>
40#include <crt_externs.h>
41#include <grp.h>
42#include <libproc.h>
43#include <pwd.h>
44#include <spawn.h>
45#include <stdio.h>
46#include <stdlib.h>
47#include <sys/proc.h>
48#include <sys/stat.h>
49#include <sys/sysctl.h>
50#include <sys/types.h>
51#include <unistd.h>
52
53#include "lldb/Host/ConnectionFileDescriptor.h"
54#include "lldb/Host/FileSystem.h"
55#include "lldb/Host/HostInfo.h"
56#include "lldb/Host/ProcessLaunchInfo.h"
57#include "lldb/Host/ThreadLauncher.h"
58#include "lldb/Utility/ArchSpec.h"
59#include "lldb/Utility/DataBufferHeap.h"
60#include "lldb/Utility/DataExtractor.h"
61#include "lldb/Utility/Endian.h"
62#include "lldb/Utility/FileSpec.h"
63#include "lldb/Utility/Log.h"
64#include "lldb/Utility/NameMatches.h"
65#include "lldb/Utility/ProcessInfo.h"
66#include "lldb/Utility/StreamString.h"
67#include "lldb/Utility/StructuredData.h"
68#include "lldb/lldb-defines.h"
69
70#include "llvm/ADT/ScopeExit.h"
71#include "llvm/Support/Errno.h"
72#include "llvm/Support/FileSystem.h"
73
74#include "../cfcpp/CFCBundle.h"
75#include "../cfcpp/CFCMutableArray.h"
76#include "../cfcpp/CFCMutableDictionary.h"
77#include "../cfcpp/CFCReleaser.h"
78#include "../cfcpp/CFCString.h"
79
80#include <objc/objc-auto.h>
81
82#include <CoreFoundation/CoreFoundation.h>
83#include <Foundation/Foundation.h>
84
85#ifndef _POSIX_SPAWN_DISABLE_ASLR
86#define _POSIX_SPAWN_DISABLE_ASLR 0x0100
87#endif
88
89extern "C" {
90int __pthread_chdir(const char *path);
91int __pthread_fchdir(int fildes);
92}
93
94using namespace lldb;
95using namespace lldb_private;
96
97bool Host::GetBundleDirectory(const FileSpec &file,
98                              FileSpec &bundle_directory) {
99#if defined(__APPLE__)
100  if (FileSystem::Instance().IsDirectory(file)) {
101    char path[PATH_MAX];
102    if (file.GetPath(path, sizeof(path))) {
103      CFCBundle bundle(path);
104      if (bundle.GetPath(path, sizeof(path))) {
105        bundle_directory.SetFile(path, FileSpec::Style::native);
106        return true;
107      }
108    }
109  }
110#endif
111  bundle_directory.Clear();
112  return false;
113}
114
115bool Host::ResolveExecutableInBundle(FileSpec &file) {
116#if defined(__APPLE__)
117  if (FileSystem::Instance().IsDirectory(file)) {
118    char path[PATH_MAX];
119    if (file.GetPath(path, sizeof(path))) {
120      CFCBundle bundle(path);
121      CFCReleaser<CFURLRef> url(bundle.CopyExecutableURL());
122      if (url.get()) {
123        if (::CFURLGetFileSystemRepresentation(url.get(), YES, (UInt8 *)path,
124                                               sizeof(path))) {
125          file.SetFile(path, FileSpec::Style::native);
126          return true;
127        }
128      }
129    }
130  }
131#endif
132  return false;
133}
134
135#if TARGET_OS_OSX
136
137static void *AcceptPIDFromInferior(void *arg) {
138  const char *connect_url = (const char *)arg;
139  ConnectionFileDescriptor file_conn;
140  Status error;
141  if (file_conn.Connect(connect_url, &error) == eConnectionStatusSuccess) {
142    char pid_str[256];
143    ::memset(pid_str, 0, sizeof(pid_str));
144    ConnectionStatus status;
145    const size_t pid_str_len = file_conn.Read(
146        pid_str, sizeof(pid_str), std::chrono::seconds(0), status, NULL);
147    if (pid_str_len > 0) {
148      int pid = atoi(pid_str);
149      return (void *)(intptr_t)pid;
150    }
151  }
152  return NULL;
153}
154
155const char *applscript_in_new_tty = "tell application \"Terminal\"\n"
156                                    "   activate\n"
157                                    "	do script \"/bin/bash -c '%s';exit\"\n"
158                                    "end tell\n";
159
160const char *applscript_in_existing_tty = "\
161set the_shell_script to \"/bin/bash -c '%s';exit\"\n\
162tell application \"Terminal\"\n\
163	repeat with the_window in (get windows)\n\
164		repeat with the_tab in tabs of the_window\n\
165			set the_tty to tty in the_tab\n\
166			if the_tty contains \"%s\" then\n\
167				if the_tab is not busy then\n\
168					set selected of the_tab to true\n\
169					set frontmost of the_window to true\n\
170					do script the_shell_script in the_tab\n\
171					return\n\
172				end if\n\
173			end if\n\
174		end repeat\n\
175	end repeat\n\
176	do script the_shell_script\n\
177end tell\n";
178
179static Status
180LaunchInNewTerminalWithAppleScript(const char *exe_path,
181                                   ProcessLaunchInfo &launch_info) {
182  Status error;
183  char unix_socket_name[PATH_MAX] = "/tmp/XXXXXX";
184  if (::mktemp(unix_socket_name) == NULL) {
185    error.SetErrorString("failed to make temporary path for a unix socket");
186    return error;
187  }
188
189  StreamString command;
190  FileSpec darwin_debug_file_spec = HostInfo::GetSupportExeDir();
191  if (!darwin_debug_file_spec) {
192    error.SetErrorString("can't locate the 'darwin-debug' executable");
193    return error;
194  }
195
196  darwin_debug_file_spec.GetFilename().SetCString("darwin-debug");
197
198  if (!FileSystem::Instance().Exists(darwin_debug_file_spec)) {
199    error.SetErrorStringWithFormat(
200        "the 'darwin-debug' executable doesn't exists at '%s'",
201        darwin_debug_file_spec.GetPath().c_str());
202    return error;
203  }
204
205  char launcher_path[PATH_MAX];
206  darwin_debug_file_spec.GetPath(launcher_path, sizeof(launcher_path));
207
208  const ArchSpec &arch_spec = launch_info.GetArchitecture();
209  // Only set the architecture if it is valid and if it isn't Haswell (x86_64h).
210  if (arch_spec.IsValid() &&
211      arch_spec.GetCore() != ArchSpec::eCore_x86_64_x86_64h)
212    command.Printf("arch -arch %s ", arch_spec.GetArchitectureName());
213
214  command.Printf("'%s' --unix-socket=%s", launcher_path, unix_socket_name);
215
216  if (arch_spec.IsValid())
217    command.Printf(" --arch=%s", arch_spec.GetArchitectureName());
218
219  FileSpec working_dir{launch_info.GetWorkingDirectory()};
220  if (working_dir)
221    command.Printf(" --working-dir '%s'", working_dir.GetCString());
222  else {
223    char cwd[PATH_MAX];
224    if (getcwd(cwd, PATH_MAX))
225      command.Printf(" --working-dir '%s'", cwd);
226  }
227
228  if (launch_info.GetFlags().Test(eLaunchFlagDisableASLR))
229    command.PutCString(" --disable-aslr");
230
231  // We are launching on this host in a terminal. So compare the environment on
232  // the host to what is supplied in the launch_info. Any items that aren't in
233  // the host environment need to be sent to darwin-debug. If we send all
234  // environment entries, we might blow the max command line length, so we only
235  // send user modified entries.
236  Environment host_env = Host::GetEnvironment();
237
238  for (const auto &KV : launch_info.GetEnvironment()) {
239    auto host_entry = host_env.find(KV.first());
240    if (host_entry == host_env.end() || host_entry->second != KV.second)
241      command.Format(" --env='{0}'", Environment::compose(KV));
242  }
243
244  command.PutCString(" -- ");
245
246  const char **argv = launch_info.GetArguments().GetConstArgumentVector();
247  if (argv) {
248    for (size_t i = 0; argv[i] != NULL; ++i) {
249      if (i == 0)
250        command.Printf(" '%s'", exe_path);
251      else
252        command.Printf(" '%s'", argv[i]);
253    }
254  } else {
255    command.Printf(" '%s'", exe_path);
256  }
257  command.PutCString(" ; echo Process exited with status $?");
258  if (launch_info.GetFlags().Test(lldb::eLaunchFlagCloseTTYOnExit))
259    command.PutCString(" ; exit");
260
261  StreamString applescript_source;
262
263  applescript_source.Printf(applscript_in_new_tty,
264                            command.GetString().str().c_str());
265  NSAppleScript *applescript = [[NSAppleScript alloc]
266      initWithSource:[NSString stringWithCString:applescript_source.GetString()
267                                                     .str()
268                                                     .c_str()
269                                        encoding:NSUTF8StringEncoding]];
270
271  lldb::pid_t pid = LLDB_INVALID_PROCESS_ID;
272
273  Status lldb_error;
274  // Sleep and wait a bit for debugserver to start to listen...
275  ConnectionFileDescriptor file_conn;
276  char connect_url[128];
277  ::snprintf(connect_url, sizeof(connect_url), "unix-accept://%s",
278             unix_socket_name);
279
280  // Spawn a new thread to accept incoming connection on the connect_url
281  // so we can grab the pid from the inferior. We have to do this because we
282  // are sending an AppleScript that will launch a process in Terminal.app,
283  // in a shell and the shell will fork/exec a couple of times before we get
284  // to the process that we wanted to launch. So when our process actually
285  // gets launched, we will handshake with it and get the process ID for it.
286  llvm::Expected<HostThread> accept_thread = ThreadLauncher::LaunchThread(
287      unix_socket_name, AcceptPIDFromInferior, connect_url);
288
289  if (!accept_thread)
290    return Status(accept_thread.takeError());
291
292  [applescript executeAndReturnError:nil];
293
294  thread_result_t accept_thread_result = NULL;
295  lldb_error = accept_thread->Join(&accept_thread_result);
296  if (lldb_error.Success() && accept_thread_result) {
297    pid = (intptr_t)accept_thread_result;
298  }
299
300  llvm::sys::fs::remove(unix_socket_name);
301  [applescript release];
302  if (pid != LLDB_INVALID_PROCESS_ID)
303    launch_info.SetProcessID(pid);
304  return error;
305}
306
307#endif // TARGET_OS_OSX
308
309bool Host::OpenFileInExternalEditor(const FileSpec &file_spec,
310                                    uint32_t line_no) {
311#if !TARGET_OS_OSX
312  return false;
313#else // !TARGET_OS_OSX
314  // We attach this to an 'odoc' event to specify a particular selection
315  typedef struct {
316    int16_t reserved0; // must be zero
317    int16_t fLineNumber;
318    int32_t fSelStart;
319    int32_t fSelEnd;
320    uint32_t reserved1; // must be zero
321    uint32_t reserved2; // must be zero
322  } BabelAESelInfo;
323
324  Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_HOST));
325  char file_path[PATH_MAX];
326  file_spec.GetPath(file_path, PATH_MAX);
327  CFCString file_cfstr(file_path, kCFStringEncodingUTF8);
328  CFCReleaser<CFURLRef> file_URL(::CFURLCreateWithFileSystemPath(
329      NULL, file_cfstr.get(), kCFURLPOSIXPathStyle, false));
330
331  LLDB_LOGF(log,
332            "Sending source file: \"%s\" and line: %d to external editor.\n",
333            file_path, line_no);
334
335  long error;
336  BabelAESelInfo file_and_line_info = {
337      0,                      // reserved0
338      (int16_t)(line_no - 1), // fLineNumber (zero based line number)
339      1,                      // fSelStart
340      1024,                   // fSelEnd
341      0,                      // reserved1
342      0                       // reserved2
343  };
344
345  AEKeyDesc file_and_line_desc;
346
347  error = ::AECreateDesc(typeUTF8Text, &file_and_line_info,
348                         sizeof(file_and_line_info),
349                         &(file_and_line_desc.descContent));
350
351  if (error != noErr) {
352    LLDB_LOGF(log, "Error creating AEDesc: %ld.\n", error);
353    return false;
354  }
355
356  file_and_line_desc.descKey = keyAEPosition;
357
358  static std::string g_app_name;
359  static FSRef g_app_fsref;
360
361  LSApplicationParameters app_params;
362  ::memset(&app_params, 0, sizeof(app_params));
363  app_params.flags =
364      kLSLaunchDefaults | kLSLaunchDontAddToRecents | kLSLaunchDontSwitch;
365
366  char *external_editor = ::getenv("LLDB_EXTERNAL_EDITOR");
367
368  if (external_editor) {
369    LLDB_LOGF(log, "Looking for external editor \"%s\".\n", external_editor);
370
371    if (g_app_name.empty() ||
372        strcmp(g_app_name.c_str(), external_editor) != 0) {
373      CFCString editor_name(external_editor, kCFStringEncodingUTF8);
374      error = ::LSFindApplicationForInfo(kLSUnknownCreator, NULL,
375                                         editor_name.get(), &g_app_fsref, NULL);
376
377      // If we found the app, then store away the name so we don't have to
378      // re-look it up.
379      if (error != noErr) {
380        LLDB_LOGF(log,
381                  "Could not find External Editor application, error: %ld.\n",
382                  error);
383        return false;
384      }
385    }
386    app_params.application = &g_app_fsref;
387  }
388
389  ProcessSerialNumber psn;
390  CFCReleaser<CFArrayRef> file_array(
391      CFArrayCreate(NULL, (const void **)file_URL.ptr_address(false), 1, NULL));
392  error = ::LSOpenURLsWithRole(file_array.get(), kLSRolesAll,
393                               &file_and_line_desc, &app_params, &psn, 1);
394
395  AEDisposeDesc(&(file_and_line_desc.descContent));
396
397  if (error != noErr) {
398    LLDB_LOGF(log, "LSOpenURLsWithRole failed, error: %ld.\n", error);
399
400    return false;
401  }
402
403  return true;
404#endif // TARGET_OS_OSX
405}
406
407Environment Host::GetEnvironment() { return Environment(*_NSGetEnviron()); }
408
409static bool GetMacOSXProcessCPUType(ProcessInstanceInfo &process_info) {
410  if (process_info.ProcessIDIsValid()) {
411    // Make a new mib to stay thread safe
412    int mib[CTL_MAXNAME] = {
413        0,
414    };
415    size_t mib_len = CTL_MAXNAME;
416    if (::sysctlnametomib("sysctl.proc_cputype", mib, &mib_len))
417      return false;
418
419    mib[mib_len] = process_info.GetProcessID();
420    mib_len++;
421
422    cpu_type_t cpu, sub = 0;
423    size_t len = sizeof(cpu);
424    if (::sysctl(mib, mib_len, &cpu, &len, 0, 0) == 0) {
425      switch (cpu) {
426      case CPU_TYPE_I386:
427        sub = CPU_SUBTYPE_I386_ALL;
428        break;
429      case CPU_TYPE_X86_64:
430        sub = CPU_SUBTYPE_X86_64_ALL;
431        break;
432
433#if defined(CPU_TYPE_ARM64) && defined(CPU_SUBTYPE_ARM64_ALL)
434      case CPU_TYPE_ARM64:
435        sub = CPU_SUBTYPE_ARM64_ALL;
436        break;
437#endif
438
439#if defined(CPU_TYPE_ARM64_32) && defined(CPU_SUBTYPE_ARM64_32_ALL)
440      case CPU_TYPE_ARM64_32:
441        sub = CPU_SUBTYPE_ARM64_32_ALL;
442        break;
443#endif
444
445      case CPU_TYPE_ARM: {
446        // Note that we fetched the cpu type from the PROCESS but we can't get a
447        // cpusubtype of the
448        // process -- we can only get the host's cpu subtype.
449        uint32_t cpusubtype = 0;
450        len = sizeof(cpusubtype);
451        if (::sysctlbyname("hw.cpusubtype", &cpusubtype, &len, NULL, 0) == 0)
452          sub = cpusubtype;
453
454        bool host_cpu_is_64bit;
455        uint32_t is64bit_capable;
456        size_t is64bit_capable_len = sizeof(is64bit_capable);
457        host_cpu_is_64bit =
458            sysctlbyname("hw.cpu64bit_capable", &is64bit_capable,
459                         &is64bit_capable_len, NULL, 0) == 0;
460
461        // if the host is an armv8 device, its cpusubtype will be in
462        // CPU_SUBTYPE_ARM64 numbering
463        // and we need to rewrite it to a reasonable CPU_SUBTYPE_ARM value
464        // instead.
465
466        if (host_cpu_is_64bit) {
467          sub = CPU_SUBTYPE_ARM_V7;
468        }
469      } break;
470
471      default:
472        break;
473      }
474      process_info.GetArchitecture().SetArchitecture(eArchTypeMachO, cpu, sub);
475      return true;
476    }
477  }
478  process_info.GetArchitecture().Clear();
479  return false;
480}
481
482static bool GetMacOSXProcessArgs(const ProcessInstanceInfoMatch *match_info_ptr,
483                                 ProcessInstanceInfo &process_info) {
484  if (process_info.ProcessIDIsValid()) {
485    int proc_args_mib[3] = {CTL_KERN, KERN_PROCARGS2,
486                            (int)process_info.GetProcessID()};
487
488    size_t arg_data_size = 0;
489    if (::sysctl(proc_args_mib, 3, nullptr, &arg_data_size, NULL, 0) ||
490        arg_data_size == 0)
491      arg_data_size = 8192;
492
493    // Add a few bytes to the calculated length, I know we need to add at least
494    // one byte
495    // to this number otherwise we get junk back, so add 128 just in case...
496    DataBufferHeap arg_data(arg_data_size + 128, 0);
497    arg_data_size = arg_data.GetByteSize();
498    if (::sysctl(proc_args_mib, 3, arg_data.GetBytes(), &arg_data_size, NULL,
499                 0) == 0) {
500      DataExtractor data(arg_data.GetBytes(), arg_data_size,
501                         endian::InlHostByteOrder(), sizeof(void *));
502      lldb::offset_t offset = 0;
503      uint32_t argc = data.GetU32(&offset);
504      llvm::Triple &triple = process_info.GetArchitecture().GetTriple();
505      const llvm::Triple::ArchType triple_arch = triple.getArch();
506      const bool check_for_ios_simulator =
507          (triple_arch == llvm::Triple::x86 ||
508           triple_arch == llvm::Triple::x86_64);
509      const char *cstr = data.GetCStr(&offset);
510      if (cstr) {
511        process_info.GetExecutableFile().SetFile(cstr, FileSpec::Style::native);
512
513        if (match_info_ptr == NULL ||
514            NameMatches(
515                process_info.GetExecutableFile().GetFilename().GetCString(),
516                match_info_ptr->GetNameMatchType(),
517                match_info_ptr->GetProcessInfo().GetName())) {
518          // Skip NULLs
519          while (true) {
520            const uint8_t *p = data.PeekData(offset, 1);
521            if ((p == NULL) || (*p != '\0'))
522              break;
523            ++offset;
524          }
525          // Now extract all arguments
526          Args &proc_args = process_info.GetArguments();
527          for (int i = 0; i < static_cast<int>(argc); ++i) {
528            cstr = data.GetCStr(&offset);
529            if (cstr)
530              proc_args.AppendArgument(llvm::StringRef(cstr));
531          }
532
533          Environment &proc_env = process_info.GetEnvironment();
534          while ((cstr = data.GetCStr(&offset))) {
535            if (cstr[0] == '\0')
536              break;
537
538            if (check_for_ios_simulator) {
539              if (strncmp(cstr, "SIMULATOR_UDID=", strlen("SIMULATOR_UDID=")) ==
540                  0)
541                process_info.GetArchitecture().GetTriple().setOS(
542                    llvm::Triple::IOS);
543              else
544                process_info.GetArchitecture().GetTriple().setOS(
545                    llvm::Triple::MacOSX);
546            }
547
548            proc_env.insert(cstr);
549          }
550          return true;
551        }
552      }
553    }
554  }
555  return false;
556}
557
558static bool GetMacOSXProcessUserAndGroup(ProcessInstanceInfo &process_info) {
559  if (process_info.ProcessIDIsValid()) {
560    int mib[4];
561    mib[0] = CTL_KERN;
562    mib[1] = KERN_PROC;
563    mib[2] = KERN_PROC_PID;
564    mib[3] = process_info.GetProcessID();
565    struct kinfo_proc proc_kinfo;
566    size_t proc_kinfo_size = sizeof(struct kinfo_proc);
567
568    if (::sysctl(mib, 4, &proc_kinfo, &proc_kinfo_size, NULL, 0) == 0) {
569      if (proc_kinfo_size > 0) {
570        process_info.SetParentProcessID(proc_kinfo.kp_eproc.e_ppid);
571        process_info.SetUserID(proc_kinfo.kp_eproc.e_pcred.p_ruid);
572        process_info.SetGroupID(proc_kinfo.kp_eproc.e_pcred.p_rgid);
573        process_info.SetEffectiveUserID(proc_kinfo.kp_eproc.e_ucred.cr_uid);
574        if (proc_kinfo.kp_eproc.e_ucred.cr_ngroups > 0)
575          process_info.SetEffectiveGroupID(
576              proc_kinfo.kp_eproc.e_ucred.cr_groups[0]);
577        else
578          process_info.SetEffectiveGroupID(UINT32_MAX);
579        return true;
580      }
581    }
582  }
583  process_info.SetParentProcessID(LLDB_INVALID_PROCESS_ID);
584  process_info.SetUserID(UINT32_MAX);
585  process_info.SetGroupID(UINT32_MAX);
586  process_info.SetEffectiveUserID(UINT32_MAX);
587  process_info.SetEffectiveGroupID(UINT32_MAX);
588  return false;
589}
590
591uint32_t Host::FindProcessesImpl(const ProcessInstanceInfoMatch &match_info,
592                                 ProcessInstanceInfoList &process_infos) {
593  std::vector<struct kinfo_proc> kinfos;
594
595  int mib[3] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL};
596
597  size_t pid_data_size = 0;
598  if (::sysctl(mib, 3, nullptr, &pid_data_size, nullptr, 0) != 0)
599    return 0;
600
601  // Add a few extra in case a few more show up
602  const size_t estimated_pid_count =
603      (pid_data_size / sizeof(struct kinfo_proc)) + 10;
604
605  kinfos.resize(estimated_pid_count);
606  pid_data_size = kinfos.size() * sizeof(struct kinfo_proc);
607
608  if (::sysctl(mib, 3, &kinfos[0], &pid_data_size, nullptr, 0) != 0)
609    return 0;
610
611  const size_t actual_pid_count = (pid_data_size / sizeof(struct kinfo_proc));
612
613  bool all_users = match_info.GetMatchAllUsers();
614  const lldb::pid_t our_pid = getpid();
615  const uid_t our_uid = getuid();
616  for (size_t i = 0; i < actual_pid_count; i++) {
617    const struct kinfo_proc &kinfo = kinfos[i];
618
619    bool kinfo_user_matches = false;
620    if (all_users)
621      kinfo_user_matches = true;
622    else
623      kinfo_user_matches = kinfo.kp_eproc.e_pcred.p_ruid == our_uid;
624
625    // Special case, if lldb is being run as root we can attach to anything.
626    if (our_uid == 0)
627      kinfo_user_matches = true;
628
629    if (!kinfo_user_matches || // Make sure the user is acceptable
630        static_cast<lldb::pid_t>(kinfo.kp_proc.p_pid) ==
631            our_pid ||                   // Skip this process
632        kinfo.kp_proc.p_pid == 0 ||      // Skip kernel (kernel pid is zero)
633        kinfo.kp_proc.p_stat == SZOMB || // Zombies are bad, they like brains...
634        kinfo.kp_proc.p_flag & P_TRACED ||   // Being debugged?
635        kinfo.kp_proc.p_flag & P_WEXIT)
636      continue;
637
638    ProcessInstanceInfo process_info;
639    process_info.SetProcessID(kinfo.kp_proc.p_pid);
640    process_info.SetParentProcessID(kinfo.kp_eproc.e_ppid);
641    process_info.SetUserID(kinfo.kp_eproc.e_pcred.p_ruid);
642    process_info.SetGroupID(kinfo.kp_eproc.e_pcred.p_rgid);
643    process_info.SetEffectiveUserID(kinfo.kp_eproc.e_ucred.cr_uid);
644    if (kinfo.kp_eproc.e_ucred.cr_ngroups > 0)
645      process_info.SetEffectiveGroupID(kinfo.kp_eproc.e_ucred.cr_groups[0]);
646    else
647      process_info.SetEffectiveGroupID(UINT32_MAX);
648
649    // Make sure our info matches before we go fetch the name and cpu type
650    if (!match_info.UserIDsMatch(process_info) ||
651        !match_info.ProcessIDsMatch(process_info))
652      continue;
653
654    // Get CPU type first so we can know to look for iOS simulator is we have
655    // x86 or x86_64
656    if (GetMacOSXProcessCPUType(process_info)) {
657      if (GetMacOSXProcessArgs(&match_info, process_info)) {
658        if (match_info.Matches(process_info))
659          process_infos.push_back(process_info);
660      }
661    }
662  }
663  return process_infos.size();
664}
665
666bool Host::GetProcessInfo(lldb::pid_t pid, ProcessInstanceInfo &process_info) {
667  process_info.SetProcessID(pid);
668  bool success = false;
669
670  // Get CPU type first so we can know to look for iOS simulator is we have x86
671  // or x86_64
672  if (GetMacOSXProcessCPUType(process_info))
673    success = true;
674
675  if (GetMacOSXProcessArgs(NULL, process_info))
676    success = true;
677
678  if (GetMacOSXProcessUserAndGroup(process_info))
679    success = true;
680
681  if (success)
682    return true;
683
684  process_info.Clear();
685  return false;
686}
687
688#if TARGET_OS_OSX
689static void PackageXPCArguments(xpc_object_t message, const char *prefix,
690                                const Args &args) {
691  size_t count = args.GetArgumentCount();
692  char buf[50]; // long enough for 'argXXX'
693  memset(buf, 0, 50);
694  sprintf(buf, "%sCount", prefix);
695  xpc_dictionary_set_int64(message, buf, count);
696  for (size_t i = 0; i < count; i++) {
697    memset(buf, 0, 50);
698    sprintf(buf, "%s%zi", prefix, i);
699    xpc_dictionary_set_string(message, buf, args.GetArgumentAtIndex(i));
700  }
701}
702
703static void PackageXPCEnvironment(xpc_object_t message, llvm::StringRef prefix,
704                                  const Environment &env) {
705  xpc_dictionary_set_int64(message, (prefix + "Count").str().c_str(),
706                           env.size());
707  size_t i = 0;
708  for (const auto &KV : env) {
709    xpc_dictionary_set_string(message, (prefix + llvm::Twine(i)).str().c_str(),
710                              Environment::compose(KV).c_str());
711  }
712}
713
714/*
715 A valid authorizationRef means that
716    - there is the LaunchUsingXPCRightName rights in the /etc/authorization
717    - we have successfully copied the rights to be send over the XPC wire
718 Once obtained, it will be valid for as long as the process lives.
719 */
720static AuthorizationRef authorizationRef = NULL;
721static Status getXPCAuthorization(ProcessLaunchInfo &launch_info) {
722  Status error;
723  Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST |
724                                                  LIBLLDB_LOG_PROCESS));
725
726  if ((launch_info.GetUserID() == 0) && !authorizationRef) {
727    OSStatus createStatus =
728        AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment,
729                            kAuthorizationFlagDefaults, &authorizationRef);
730    if (createStatus != errAuthorizationSuccess) {
731      error.SetError(1, eErrorTypeGeneric);
732      error.SetErrorString("Can't create authorizationRef.");
733      LLDB_LOG(log, "error: {0}", error);
734      return error;
735    }
736
737    OSStatus rightsStatus =
738        AuthorizationRightGet(LaunchUsingXPCRightName, NULL);
739    if (rightsStatus != errAuthorizationSuccess) {
740      // No rights in the security database, Create it with the right prompt.
741      CFStringRef prompt =
742          CFSTR("Xcode is trying to take control of a root process.");
743      CFStringRef keys[] = {CFSTR("en")};
744      CFTypeRef values[] = {prompt};
745      CFDictionaryRef promptDict = CFDictionaryCreate(
746          kCFAllocatorDefault, (const void **)keys, (const void **)values, 1,
747          &kCFCopyStringDictionaryKeyCallBacks,
748          &kCFTypeDictionaryValueCallBacks);
749
750      CFStringRef keys1[] = {CFSTR("class"), CFSTR("group"), CFSTR("comment"),
751                             CFSTR("default-prompt"), CFSTR("shared")};
752      CFTypeRef values1[] = {CFSTR("user"), CFSTR("admin"),
753                             CFSTR(LaunchUsingXPCRightName), promptDict,
754                             kCFBooleanFalse};
755      CFDictionaryRef dict = CFDictionaryCreate(
756          kCFAllocatorDefault, (const void **)keys1, (const void **)values1, 5,
757          &kCFCopyStringDictionaryKeyCallBacks,
758          &kCFTypeDictionaryValueCallBacks);
759      rightsStatus = AuthorizationRightSet(
760          authorizationRef, LaunchUsingXPCRightName, dict, NULL, NULL, NULL);
761      CFRelease(promptDict);
762      CFRelease(dict);
763    }
764
765    OSStatus copyRightStatus = errAuthorizationDenied;
766    if (rightsStatus == errAuthorizationSuccess) {
767      AuthorizationItem item1 = {LaunchUsingXPCRightName, 0, NULL, 0};
768      AuthorizationItem items[] = {item1};
769      AuthorizationRights requestedRights = {1, items};
770      AuthorizationFlags authorizationFlags =
771          kAuthorizationFlagInteractionAllowed | kAuthorizationFlagExtendRights;
772      copyRightStatus = AuthorizationCopyRights(
773          authorizationRef, &requestedRights, kAuthorizationEmptyEnvironment,
774          authorizationFlags, NULL);
775    }
776
777    if (copyRightStatus != errAuthorizationSuccess) {
778      // Eventually when the commandline supports running as root and the user
779      // is not
780      // logged in in the current audit session, we will need the trick in gdb
781      // where
782      // we ask the user to type in the root passwd in the terminal.
783      error.SetError(2, eErrorTypeGeneric);
784      error.SetErrorStringWithFormat(
785          "Launching as root needs root authorization.");
786      LLDB_LOG(log, "error: {0}", error);
787
788      if (authorizationRef) {
789        AuthorizationFree(authorizationRef, kAuthorizationFlagDefaults);
790        authorizationRef = NULL;
791      }
792    }
793  }
794
795  return error;
796}
797#endif
798
799static short GetPosixspawnFlags(const ProcessLaunchInfo &launch_info) {
800  short flags = POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK;
801
802  if (launch_info.GetFlags().Test(eLaunchFlagExec))
803    flags |= POSIX_SPAWN_SETEXEC; // Darwin specific posix_spawn flag
804
805  if (launch_info.GetFlags().Test(eLaunchFlagDebug))
806    flags |= POSIX_SPAWN_START_SUSPENDED; // Darwin specific posix_spawn flag
807
808  if (launch_info.GetFlags().Test(eLaunchFlagDisableASLR))
809    flags |= _POSIX_SPAWN_DISABLE_ASLR; // Darwin specific posix_spawn flag
810
811  if (launch_info.GetLaunchInSeparateProcessGroup())
812    flags |= POSIX_SPAWN_SETPGROUP;
813
814#ifdef POSIX_SPAWN_CLOEXEC_DEFAULT
815#if defined(__x86_64__) || defined(__i386__)
816  static LazyBool g_use_close_on_exec_flag = eLazyBoolCalculate;
817  if (g_use_close_on_exec_flag == eLazyBoolCalculate) {
818    g_use_close_on_exec_flag = eLazyBoolNo;
819
820    llvm::VersionTuple version = HostInfo::GetOSVersion();
821    if (version > llvm::VersionTuple(10, 7)) {
822      // Kernel panic if we use the POSIX_SPAWN_CLOEXEC_DEFAULT on 10.7 or
823      // earlier
824      g_use_close_on_exec_flag = eLazyBoolYes;
825    }
826  }
827#else
828  static LazyBool g_use_close_on_exec_flag = eLazyBoolYes;
829#endif // defined(__x86_64__) || defined(__i386__)
830  // Close all files exception those with file actions if this is supported.
831  if (g_use_close_on_exec_flag == eLazyBoolYes)
832    flags |= POSIX_SPAWN_CLOEXEC_DEFAULT;
833#endif // ifdef POSIX_SPAWN_CLOEXEC_DEFAULT
834  return flags;
835}
836
837static Status LaunchProcessXPC(const char *exe_path,
838                               ProcessLaunchInfo &launch_info,
839                               lldb::pid_t &pid) {
840#if TARGET_OS_OSX
841  Status error = getXPCAuthorization(launch_info);
842  if (error.Fail())
843    return error;
844
845  Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST |
846                                                  LIBLLDB_LOG_PROCESS));
847
848  uid_t requested_uid = launch_info.GetUserID();
849  const char *xpc_service = nil;
850  bool send_auth = false;
851  AuthorizationExternalForm extForm;
852  if (requested_uid == 0) {
853    if (AuthorizationMakeExternalForm(authorizationRef, &extForm) ==
854        errAuthorizationSuccess) {
855      send_auth = true;
856    } else {
857      error.SetError(3, eErrorTypeGeneric);
858      error.SetErrorStringWithFormat("Launching root via XPC needs to "
859                                     "externalize authorization reference.");
860      LLDB_LOG(log, "error: {0}", error);
861      return error;
862    }
863    xpc_service = LaunchUsingXPCRightName;
864  } else {
865    error.SetError(4, eErrorTypeGeneric);
866    error.SetErrorStringWithFormat(
867        "Launching via XPC is only currently available for root.");
868    LLDB_LOG(log, "error: {0}", error);
869    return error;
870  }
871
872  xpc_connection_t conn = xpc_connection_create(xpc_service, NULL);
873
874  xpc_connection_set_event_handler(conn, ^(xpc_object_t event) {
875    xpc_type_t type = xpc_get_type(event);
876
877    if (type == XPC_TYPE_ERROR) {
878      if (event == XPC_ERROR_CONNECTION_INTERRUPTED) {
879        // The service has either canceled itself, crashed, or been terminated.
880        // The XPC connection is still valid and sending a message to it will
881        // re-launch the service.
882        // If the service is state-full, this is the time to initialize the new
883        // service.
884        return;
885      } else if (event == XPC_ERROR_CONNECTION_INVALID) {
886        // The service is invalid. Either the service name supplied to
887        // xpc_connection_create() is incorrect
888        // or we (this process) have canceled the service; we can do any cleanup
889        // of application state at this point.
890        // printf("Service disconnected");
891        return;
892      } else {
893        // printf("Unexpected error from service: %s",
894        // xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION));
895      }
896
897    } else {
898      // printf("Received unexpected event in handler");
899    }
900  });
901
902  xpc_connection_set_finalizer_f(conn, xpc_finalizer_t(xpc_release));
903  xpc_connection_resume(conn);
904  xpc_object_t message = xpc_dictionary_create(nil, nil, 0);
905
906  if (send_auth) {
907    xpc_dictionary_set_data(message, LauncherXPCServiceAuthKey, extForm.bytes,
908                            sizeof(AuthorizationExternalForm));
909  }
910
911  PackageXPCArguments(message, LauncherXPCServiceArgPrefxKey,
912                      launch_info.GetArguments());
913  PackageXPCEnvironment(message, LauncherXPCServiceEnvPrefxKey,
914                        launch_info.GetEnvironment());
915
916  // Posix spawn stuff.
917  xpc_dictionary_set_int64(message, LauncherXPCServiceCPUTypeKey,
918                           launch_info.GetArchitecture().GetMachOCPUType());
919  xpc_dictionary_set_int64(message, LauncherXPCServicePosixspawnFlagsKey,
920                           GetPosixspawnFlags(launch_info));
921  const FileAction *file_action = launch_info.GetFileActionForFD(STDIN_FILENO);
922  if (file_action && !file_action->GetPath().empty()) {
923    xpc_dictionary_set_string(message, LauncherXPCServiceStdInPathKeyKey,
924                              file_action->GetPath().str().c_str());
925  }
926  file_action = launch_info.GetFileActionForFD(STDOUT_FILENO);
927  if (file_action && !file_action->GetPath().empty()) {
928    xpc_dictionary_set_string(message, LauncherXPCServiceStdOutPathKeyKey,
929                              file_action->GetPath().str().c_str());
930  }
931  file_action = launch_info.GetFileActionForFD(STDERR_FILENO);
932  if (file_action && !file_action->GetPath().empty()) {
933    xpc_dictionary_set_string(message, LauncherXPCServiceStdErrPathKeyKey,
934                              file_action->GetPath().str().c_str());
935  }
936
937  xpc_object_t reply =
938      xpc_connection_send_message_with_reply_sync(conn, message);
939  xpc_type_t returnType = xpc_get_type(reply);
940  if (returnType == XPC_TYPE_DICTIONARY) {
941    pid = xpc_dictionary_get_int64(reply, LauncherXPCServiceChildPIDKey);
942    if (pid == 0) {
943      int errorType =
944          xpc_dictionary_get_int64(reply, LauncherXPCServiceErrorTypeKey);
945      int errorCode =
946          xpc_dictionary_get_int64(reply, LauncherXPCServiceCodeTypeKey);
947
948      error.SetError(errorCode, eErrorTypeGeneric);
949      error.SetErrorStringWithFormat(
950          "Problems with launching via XPC. Error type : %i, code : %i",
951          errorType, errorCode);
952      LLDB_LOG(log, "error: {0}", error);
953
954      if (authorizationRef) {
955        AuthorizationFree(authorizationRef, kAuthorizationFlagDefaults);
956        authorizationRef = NULL;
957      }
958    }
959  } else if (returnType == XPC_TYPE_ERROR) {
960    error.SetError(5, eErrorTypeGeneric);
961    error.SetErrorStringWithFormat(
962        "Problems with launching via XPC. XPC error : %s",
963        xpc_dictionary_get_string(reply, XPC_ERROR_KEY_DESCRIPTION));
964    LLDB_LOG(log, "error: {0}", error);
965  }
966
967  return error;
968#else
969  Status error;
970  return error;
971#endif
972}
973
974static bool AddPosixSpawnFileAction(void *_file_actions, const FileAction *info,
975                                    Log *log, Status &error) {
976  if (info == NULL)
977    return false;
978
979  posix_spawn_file_actions_t *file_actions =
980      static_cast<posix_spawn_file_actions_t *>(_file_actions);
981
982  switch (info->GetAction()) {
983  case FileAction::eFileActionNone:
984    error.Clear();
985    break;
986
987  case FileAction::eFileActionClose:
988    if (info->GetFD() == -1)
989      error.SetErrorString(
990          "invalid fd for posix_spawn_file_actions_addclose(...)");
991    else {
992      error.SetError(
993          ::posix_spawn_file_actions_addclose(file_actions, info->GetFD()),
994          eErrorTypePOSIX);
995      if (error.Fail())
996        LLDB_LOG(log,
997                 "error: {0}, posix_spawn_file_actions_addclose "
998                 "(action={1}, fd={2})",
999                 error, file_actions, info->GetFD());
1000    }
1001    break;
1002
1003  case FileAction::eFileActionDuplicate:
1004    if (info->GetFD() == -1)
1005      error.SetErrorString(
1006          "invalid fd for posix_spawn_file_actions_adddup2(...)");
1007    else if (info->GetActionArgument() == -1)
1008      error.SetErrorString(
1009          "invalid duplicate fd for posix_spawn_file_actions_adddup2(...)");
1010    else {
1011      error.SetError(
1012          ::posix_spawn_file_actions_adddup2(file_actions, info->GetFD(),
1013                                             info->GetActionArgument()),
1014          eErrorTypePOSIX);
1015      if (error.Fail())
1016        LLDB_LOG(log,
1017                 "error: {0}, posix_spawn_file_actions_adddup2 "
1018                 "(action={1}, fd={2}, dup_fd={3})",
1019                 error, file_actions, info->GetFD(), info->GetActionArgument());
1020    }
1021    break;
1022
1023  case FileAction::eFileActionOpen:
1024    if (info->GetFD() == -1)
1025      error.SetErrorString(
1026          "invalid fd in posix_spawn_file_actions_addopen(...)");
1027    else {
1028      int oflag = info->GetActionArgument();
1029
1030      mode_t mode = 0;
1031
1032      if (oflag & O_CREAT)
1033        mode = 0640;
1034
1035      error.SetError(::posix_spawn_file_actions_addopen(
1036                         file_actions, info->GetFD(),
1037                         info->GetPath().str().c_str(), oflag, mode),
1038                     eErrorTypePOSIX);
1039      if (error.Fail())
1040        LLDB_LOG(log,
1041                 "error: {0}, posix_spawn_file_actions_addopen (action={1}, "
1042                 "fd={2}, path='{3}', oflag={4}, mode={5})",
1043                 error, file_actions, info->GetFD(), info->GetPath(), oflag,
1044                 mode);
1045    }
1046    break;
1047  }
1048  return error.Success();
1049}
1050
1051static Status LaunchProcessPosixSpawn(const char *exe_path,
1052                                      const ProcessLaunchInfo &launch_info,
1053                                      lldb::pid_t &pid) {
1054  Status error;
1055  Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST |
1056                                                  LIBLLDB_LOG_PROCESS));
1057
1058  posix_spawnattr_t attr;
1059  error.SetError(::posix_spawnattr_init(&attr), eErrorTypePOSIX);
1060
1061  if (error.Fail()) {
1062    LLDB_LOG(log, "error: {0}, ::posix_spawnattr_init ( &attr )", error);
1063    return error;
1064  }
1065
1066  // Make sure we clean up the posix spawn attributes before exiting this scope.
1067  auto cleanup_attr =
1068      llvm::make_scope_exit([&]() { posix_spawnattr_destroy(&attr); });
1069
1070  sigset_t no_signals;
1071  sigset_t all_signals;
1072  sigemptyset(&no_signals);
1073  sigfillset(&all_signals);
1074  ::posix_spawnattr_setsigmask(&attr, &no_signals);
1075  ::posix_spawnattr_setsigdefault(&attr, &all_signals);
1076
1077  short flags = GetPosixspawnFlags(launch_info);
1078
1079  error.SetError(::posix_spawnattr_setflags(&attr, flags), eErrorTypePOSIX);
1080  if (error.Fail()) {
1081    LLDB_LOG(log,
1082             "error: {0}, ::posix_spawnattr_setflags ( &attr, flags={1:x} )",
1083             error, flags);
1084    return error;
1085  }
1086
1087  bool is_graphical = true;
1088
1089#if TARGET_OS_OSX
1090  SecuritySessionId session_id;
1091  SessionAttributeBits session_attributes;
1092  OSStatus status =
1093      SessionGetInfo(callerSecuritySession, &session_id, &session_attributes);
1094  if (status == errSessionSuccess)
1095    is_graphical = session_attributes & sessionHasGraphicAccess;
1096#endif
1097
1098  //  When lldb is ran through a graphical session, make the debuggee process
1099  //  responsible for its own TCC permissions instead of inheriting them from
1100  //  its parent.
1101  if (is_graphical && launch_info.GetFlags().Test(eLaunchFlagDebug) &&
1102      !launch_info.GetFlags().Test(eLaunchFlagInheritTCCFromParent)) {
1103    error.SetError(setup_posix_spawn_responsible_flag(&attr), eErrorTypePOSIX);
1104    if (error.Fail()) {
1105      LLDB_LOG(log, "error: {0}, setup_posix_spawn_responsible_flag(&attr)",
1106               error);
1107      return error;
1108    }
1109  }
1110
1111  const char *tmp_argv[2];
1112  char *const *argv = const_cast<char *const *>(
1113      launch_info.GetArguments().GetConstArgumentVector());
1114  Environment::Envp envp = launch_info.GetEnvironment().getEnvp();
1115  if (argv == NULL) {
1116    // posix_spawn gets very unhappy if it doesn't have at least the program
1117    // name in argv[0]. One of the side affects I have noticed is the
1118    // environment
1119    // variables don't make it into the child process if "argv == NULL"!!!
1120    tmp_argv[0] = exe_path;
1121    tmp_argv[1] = NULL;
1122    argv = const_cast<char *const *>(tmp_argv);
1123  }
1124
1125  FileSpec working_dir{launch_info.GetWorkingDirectory()};
1126  if (working_dir) {
1127    // Set the working directory on this thread only
1128    if (__pthread_chdir(working_dir.GetCString()) < 0) {
1129      if (errno == ENOENT) {
1130        error.SetErrorStringWithFormat("No such file or directory: %s",
1131                                       working_dir.GetCString());
1132      } else if (errno == ENOTDIR) {
1133        error.SetErrorStringWithFormat("Path doesn't name a directory: %s",
1134                                       working_dir.GetCString());
1135      } else {
1136        error.SetErrorStringWithFormat("An unknown error occurred when "
1137                                       "changing directory for process "
1138                                       "execution.");
1139      }
1140      return error;
1141    }
1142  }
1143
1144  ::pid_t result_pid = LLDB_INVALID_PROCESS_ID;
1145  const size_t num_file_actions = launch_info.GetNumFileActions();
1146  if (num_file_actions > 0) {
1147    posix_spawn_file_actions_t file_actions;
1148    error.SetError(::posix_spawn_file_actions_init(&file_actions),
1149                   eErrorTypePOSIX);
1150    if (error.Fail()) {
1151      LLDB_LOG(log,
1152               "error: {0}, ::posix_spawn_file_actions_init ( &file_actions )",
1153               error);
1154      return error;
1155    }
1156
1157    // Make sure we clean up the posix file actions before exiting this scope.
1158    auto cleanup_fileact = llvm::make_scope_exit(
1159        [&]() { posix_spawn_file_actions_destroy(&file_actions); });
1160
1161    for (size_t i = 0; i < num_file_actions; ++i) {
1162      const FileAction *launch_file_action =
1163          launch_info.GetFileActionAtIndex(i);
1164      if (launch_file_action) {
1165        if (!AddPosixSpawnFileAction(&file_actions, launch_file_action, log,
1166                                     error))
1167          return error;
1168      }
1169    }
1170
1171    error.SetError(
1172        ::posix_spawnp(&result_pid, exe_path, &file_actions, &attr, argv, envp),
1173        eErrorTypePOSIX);
1174
1175    if (error.Fail()) {
1176      LLDB_LOG(log,
1177               "error: {0}, ::posix_spawnp(pid => {1}, path = '{2}', "
1178               "file_actions = {3}, "
1179               "attr = {4}, argv = {5}, envp = {6} )",
1180               error, result_pid, exe_path, &file_actions, &attr, argv,
1181               envp.get());
1182      if (log) {
1183        for (int ii = 0; argv[ii]; ++ii)
1184          LLDB_LOG(log, "argv[{0}] = '{1}'", ii, argv[ii]);
1185      }
1186    }
1187
1188  } else {
1189    error.SetError(
1190        ::posix_spawnp(&result_pid, exe_path, NULL, &attr, argv, envp),
1191        eErrorTypePOSIX);
1192
1193    if (error.Fail()) {
1194      LLDB_LOG(log,
1195               "error: {0}, ::posix_spawnp ( pid => {1}, path = '{2}', "
1196               "file_actions = NULL, attr = {3}, argv = {4}, envp = {5} )",
1197               error, result_pid, exe_path, &attr, argv, envp.get());
1198      if (log) {
1199        for (int ii = 0; argv[ii]; ++ii)
1200          LLDB_LOG(log, "argv[{0}] = '{1}'", ii, argv[ii]);
1201      }
1202    }
1203  }
1204  pid = result_pid;
1205
1206  if (working_dir) {
1207    // No more thread specific current working directory
1208    __pthread_fchdir(-1);
1209  }
1210
1211  return error;
1212}
1213
1214static bool ShouldLaunchUsingXPC(ProcessLaunchInfo &launch_info) {
1215  bool result = false;
1216
1217#if TARGET_OS_OSX
1218  bool launchingAsRoot = launch_info.GetUserID() == 0;
1219  bool currentUserIsRoot = HostInfo::GetEffectiveUserID() == 0;
1220
1221  if (launchingAsRoot && !currentUserIsRoot) {
1222    // If current user is already root, we don't need XPC's help.
1223    result = true;
1224  }
1225#endif
1226
1227  return result;
1228}
1229
1230Status Host::LaunchProcess(ProcessLaunchInfo &launch_info) {
1231  Status error;
1232
1233  FileSystem &fs = FileSystem::Instance();
1234  FileSpec exe_spec(launch_info.GetExecutableFile());
1235
1236  if (!fs.Exists(exe_spec))
1237    FileSystem::Instance().Resolve(exe_spec);
1238
1239  if (!fs.Exists(exe_spec))
1240    FileSystem::Instance().ResolveExecutableLocation(exe_spec);
1241
1242  if (!fs.Exists(exe_spec)) {
1243    error.SetErrorStringWithFormatv("executable doesn't exist: '{0}'",
1244                                    exe_spec);
1245    return error;
1246  }
1247
1248  if (launch_info.GetFlags().Test(eLaunchFlagLaunchInTTY)) {
1249#if TARGET_OS_OSX
1250    return LaunchInNewTerminalWithAppleScript(exe_spec.GetPath().c_str(),
1251                                              launch_info);
1252#else
1253    error.SetErrorString("launching a process in a new terminal is not "
1254                         "supported on iOS devices");
1255    return error;
1256#endif
1257  }
1258
1259  lldb::pid_t pid = LLDB_INVALID_PROCESS_ID;
1260
1261  // From now on we'll deal with the external (devirtualized) path.
1262  auto exe_path = fs.GetExternalPath(exe_spec);
1263  if (!exe_path)
1264    return Status(exe_path.getError());
1265
1266  if (ShouldLaunchUsingXPC(launch_info))
1267    error = LaunchProcessXPC(exe_path->c_str(), launch_info, pid);
1268  else
1269    error = LaunchProcessPosixSpawn(exe_path->c_str(), launch_info, pid);
1270
1271  if (pid != LLDB_INVALID_PROCESS_ID) {
1272    // If all went well, then set the process ID into the launch info
1273    launch_info.SetProcessID(pid);
1274
1275    // Make sure we reap any processes we spawn or we will have zombies.
1276    bool monitoring = launch_info.MonitorProcess();
1277    UNUSED_IF_ASSERT_DISABLED(monitoring);
1278    assert(monitoring);
1279  } else {
1280    // Invalid process ID, something didn't go well
1281    if (error.Success())
1282      error.SetErrorString("process launch failed for unknown reasons");
1283  }
1284  return error;
1285}
1286
1287Status Host::ShellExpandArguments(ProcessLaunchInfo &launch_info) {
1288  Status error;
1289  if (launch_info.GetFlags().Test(eLaunchFlagShellExpandArguments)) {
1290    FileSpec expand_tool_spec = HostInfo::GetSupportExeDir();
1291    if (!expand_tool_spec) {
1292      error.SetErrorString(
1293          "could not get support executable directory for lldb-argdumper tool");
1294      return error;
1295    }
1296    expand_tool_spec.AppendPathComponent("lldb-argdumper");
1297    if (!FileSystem::Instance().Exists(expand_tool_spec)) {
1298      error.SetErrorStringWithFormat(
1299          "could not find the lldb-argdumper tool: %s",
1300          expand_tool_spec.GetPath().c_str());
1301      return error;
1302    }
1303
1304    StreamString expand_tool_spec_stream;
1305    expand_tool_spec_stream.Printf("\"%s\"",
1306                                   expand_tool_spec.GetPath().c_str());
1307
1308    Args expand_command(expand_tool_spec_stream.GetData());
1309    expand_command.AppendArguments(launch_info.GetArguments());
1310
1311    int status;
1312    std::string output;
1313    FileSpec cwd(launch_info.GetWorkingDirectory());
1314    if (!FileSystem::Instance().Exists(cwd)) {
1315      char *wd = getcwd(nullptr, 0);
1316      if (wd == nullptr) {
1317        error.SetErrorStringWithFormat(
1318            "cwd does not exist; cannot launch with shell argument expansion");
1319        return error;
1320      } else {
1321        FileSpec working_dir(wd);
1322        free(wd);
1323        launch_info.SetWorkingDirectory(working_dir);
1324      }
1325    }
1326    bool run_in_shell = true;
1327    bool hide_stderr = true;
1328    Status e =
1329        RunShellCommand(expand_command, cwd, &status, nullptr, &output,
1330                        std::chrono::seconds(10), run_in_shell, hide_stderr);
1331
1332    if (e.Fail())
1333      return e;
1334
1335    if (status != 0) {
1336      error.SetErrorStringWithFormat("lldb-argdumper exited with error %d",
1337                                     status);
1338      return error;
1339    }
1340
1341    auto data_sp = StructuredData::ParseJSON(output);
1342    if (!data_sp) {
1343      error.SetErrorString("invalid JSON");
1344      return error;
1345    }
1346
1347    auto dict_sp = data_sp->GetAsDictionary();
1348    if (!data_sp) {
1349      error.SetErrorString("invalid JSON");
1350      return error;
1351    }
1352
1353    auto args_sp = dict_sp->GetObjectForDotSeparatedPath("arguments");
1354    if (!args_sp) {
1355      error.SetErrorString("invalid JSON");
1356      return error;
1357    }
1358
1359    auto args_array_sp = args_sp->GetAsArray();
1360    if (!args_array_sp) {
1361      error.SetErrorString("invalid JSON");
1362      return error;
1363    }
1364
1365    launch_info.GetArguments().Clear();
1366
1367    for (size_t i = 0; i < args_array_sp->GetSize(); i++) {
1368      auto item_sp = args_array_sp->GetItemAtIndex(i);
1369      if (!item_sp)
1370        continue;
1371      auto str_sp = item_sp->GetAsString();
1372      if (!str_sp)
1373        continue;
1374
1375      launch_info.GetArguments().AppendArgument(str_sp->GetValue());
1376    }
1377  }
1378
1379  return error;
1380}
1381
1382llvm::Expected<HostThread> Host::StartMonitoringChildProcess(
1383    const Host::MonitorChildProcessCallback &callback, lldb::pid_t pid,
1384    bool monitor_signals) {
1385  unsigned long mask = DISPATCH_PROC_EXIT;
1386  if (monitor_signals)
1387    mask |= DISPATCH_PROC_SIGNAL;
1388
1389  Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_HOST |
1390                                                  LIBLLDB_LOG_PROCESS));
1391
1392  dispatch_source_t source = ::dispatch_source_create(
1393      DISPATCH_SOURCE_TYPE_PROC, pid, mask,
1394      ::dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
1395
1396  LLDB_LOGF(log,
1397            "Host::StartMonitoringChildProcess "
1398            "(callback, pid=%i, monitor_signals=%i) "
1399            "source = %p\n",
1400            static_cast<int>(pid), monitor_signals,
1401            static_cast<void *>(source));
1402
1403  if (source) {
1404    Host::MonitorChildProcessCallback callback_copy = callback;
1405    ::dispatch_source_set_cancel_handler(source, ^{
1406      dispatch_release(source);
1407    });
1408    ::dispatch_source_set_event_handler(source, ^{
1409
1410      int status = 0;
1411      int wait_pid = 0;
1412      bool cancel = false;
1413      bool exited = false;
1414      wait_pid = llvm::sys::RetryAfterSignal(-1, ::waitpid, pid, &status, 0);
1415      if (wait_pid >= 0) {
1416        int signal = 0;
1417        int exit_status = 0;
1418        const char *status_cstr = NULL;
1419        if (WIFSTOPPED(status)) {
1420          signal = WSTOPSIG(status);
1421          status_cstr = "STOPPED";
1422        } else if (WIFEXITED(status)) {
1423          exit_status = WEXITSTATUS(status);
1424          status_cstr = "EXITED";
1425          exited = true;
1426        } else if (WIFSIGNALED(status)) {
1427          signal = WTERMSIG(status);
1428          status_cstr = "SIGNALED";
1429          exited = true;
1430          exit_status = -1;
1431        } else {
1432          status_cstr = "???";
1433        }
1434
1435        LLDB_LOGF(log,
1436                  "::waitpid (pid = %llu, &status, 0) => pid = %i, status "
1437                  "= 0x%8.8x (%s), signal = %i, exit_status = %i",
1438                  pid, wait_pid, status, status_cstr, signal, exit_status);
1439
1440        if (callback_copy)
1441          cancel = callback_copy(pid, exited, signal, exit_status);
1442
1443        if (exited || cancel) {
1444          ::dispatch_source_cancel(source);
1445        }
1446      }
1447    });
1448
1449    ::dispatch_resume(source);
1450  }
1451  return HostThread();
1452}
1453
1454//----------------------------------------------------------------------
1455// Log to both stderr and to ASL Logging when running on MacOSX.
1456//----------------------------------------------------------------------
1457void Host::SystemLog(SystemLogType type, const char *format, va_list args) {
1458  if (format && format[0]) {
1459    static aslmsg g_aslmsg = NULL;
1460    if (g_aslmsg == NULL) {
1461      g_aslmsg = ::asl_new(ASL_TYPE_MSG);
1462      char asl_key_sender[PATH_MAX];
1463      snprintf(asl_key_sender, sizeof(asl_key_sender),
1464               "com.apple.LLDB.framework");
1465      ::asl_set(g_aslmsg, ASL_KEY_SENDER, asl_key_sender);
1466    }
1467
1468    // Copy the va_list so we can log this message twice
1469    va_list copy_args;
1470    va_copy(copy_args, args);
1471    // Log to stderr
1472    ::vfprintf(stderr, format, copy_args);
1473    va_end(copy_args);
1474
1475    int asl_level;
1476    switch (type) {
1477    case eSystemLogError:
1478      asl_level = ASL_LEVEL_ERR;
1479      break;
1480
1481    case eSystemLogWarning:
1482      asl_level = ASL_LEVEL_WARNING;
1483      break;
1484    }
1485
1486    // Log to ASL
1487    ::asl_vlog(NULL, g_aslmsg, asl_level, format, args);
1488  }
1489}
1490