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