xref: /llvm-project/lldb/source/Host/macosx/objcxx/Host.mm (revision c7605bfd4eaf1b0fe46fa91bd0e3f7aa17585d89)
12c77eefeSRaphael Isemann//===-- Host.mm -------------------------------------------------*- C++ -*-===//
22c77eefeSRaphael Isemann//
32946cd70SChandler Carruth// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth// See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
62c77eefeSRaphael Isemann//
72c77eefeSRaphael Isemann//===----------------------------------------------------------------------===//
82c77eefeSRaphael Isemann
92c77eefeSRaphael Isemann#include "lldb/Host/Host.h"
10041c7b84SJonas Devlieghere#include "PosixSpawnResponsible.h"
112c77eefeSRaphael Isemann
122c77eefeSRaphael Isemann#include <AvailabilityMacros.h>
13f203100eSVedant Kumar#include <TargetConditionals.h>
142c77eefeSRaphael Isemann
15f203100eSVedant Kumar#if TARGET_OS_OSX
162c77eefeSRaphael Isemann#define __XPC_PRIVATE_H__
172c77eefeSRaphael Isemann#include <xpc/xpc.h>
182c77eefeSRaphael Isemann
192c77eefeSRaphael Isemann#define LaunchUsingXPCRightName "com.apple.lldb.RootDebuggingXPCService"
202c77eefeSRaphael Isemann
212c77eefeSRaphael Isemann// These XPC messaging keys are used for communication between Host.mm and the
222c77eefeSRaphael Isemann// XPC service.
232c77eefeSRaphael Isemann#define LauncherXPCServiceAuthKey "auth-key"
242c77eefeSRaphael Isemann#define LauncherXPCServiceArgPrefxKey "arg"
252c77eefeSRaphael Isemann#define LauncherXPCServiceEnvPrefxKey "env"
262c77eefeSRaphael Isemann#define LauncherXPCServiceCPUTypeKey "cpuType"
272c77eefeSRaphael Isemann#define LauncherXPCServicePosixspawnFlagsKey "posixspawnFlags"
282c77eefeSRaphael Isemann#define LauncherXPCServiceStdInPathKeyKey "stdInPath"
292c77eefeSRaphael Isemann#define LauncherXPCServiceStdOutPathKeyKey "stdOutPath"
302c77eefeSRaphael Isemann#define LauncherXPCServiceStdErrPathKeyKey "stdErrPath"
312c77eefeSRaphael Isemann#define LauncherXPCServiceChildPIDKey "childPID"
322c77eefeSRaphael Isemann#define LauncherXPCServiceErrorTypeKey "errorType"
332c77eefeSRaphael Isemann#define LauncherXPCServiceCodeTypeKey "errorCode"
342c77eefeSRaphael Isemann
35b945b62cSJonas Devlieghere#include <bsm/audit.h>
36b945b62cSJonas Devlieghere#include <bsm/audit_session.h>
372c77eefeSRaphael Isemann#endif
382c77eefeSRaphael Isemann
39d768bf99SArchibald Elliott#include "llvm/TargetParser/Host.h"
402c77eefeSRaphael Isemann
412c77eefeSRaphael Isemann#include <asl.h>
422c77eefeSRaphael Isemann#include <crt_externs.h>
4376e47d48SRaphael Isemann#include <cstdio>
4476e47d48SRaphael Isemann#include <cstdlib>
4598e50a7dSJonas Devlieghere#include <dlfcn.h>
462c77eefeSRaphael Isemann#include <grp.h>
472c77eefeSRaphael Isemann#include <libproc.h>
482c77eefeSRaphael Isemann#include <pwd.h>
492c77eefeSRaphael Isemann#include <spawn.h>
502c77eefeSRaphael Isemann#include <sys/proc.h>
512c77eefeSRaphael Isemann#include <sys/stat.h>
522c77eefeSRaphael Isemann#include <sys/sysctl.h>
532c77eefeSRaphael Isemann#include <sys/types.h>
542c77eefeSRaphael Isemann#include <unistd.h>
552c77eefeSRaphael Isemann
562c77eefeSRaphael Isemann#include "lldb/Host/ConnectionFileDescriptor.h"
572c22c800SJonas Devlieghere#include "lldb/Host/FileSystem.h"
582c77eefeSRaphael Isemann#include "lldb/Host/HostInfo.h"
59eef758e9SPavel Labath#include "lldb/Host/ProcessLaunchInfo.h"
602c77eefeSRaphael Isemann#include "lldb/Host/ThreadLauncher.h"
612c77eefeSRaphael Isemann#include "lldb/Utility/ArchSpec.h"
62c34698a8SPavel Labath#include "lldb/Utility/LLDBLog.h"
632c77eefeSRaphael Isemann#include "lldb/Utility/DataBufferHeap.h"
642c77eefeSRaphael Isemann#include "lldb/Utility/DataExtractor.h"
652c77eefeSRaphael Isemann#include "lldb/Utility/Endian.h"
662c77eefeSRaphael Isemann#include "lldb/Utility/FileSpec.h"
672c77eefeSRaphael Isemann#include "lldb/Utility/Log.h"
682c77eefeSRaphael Isemann#include "lldb/Utility/NameMatches.h"
69805e7106SZachary Turner#include "lldb/Utility/ProcessInfo.h"
702c77eefeSRaphael Isemann#include "lldb/Utility/StreamString.h"
712c77eefeSRaphael Isemann#include "lldb/Utility/StructuredData.h"
722c77eefeSRaphael Isemann#include "lldb/lldb-defines.h"
732c77eefeSRaphael Isemann
74e0ea8d87SJonas Devlieghere#include "llvm/ADT/ScopeExit.h"
752c77eefeSRaphael Isemann#include "llvm/Support/Errno.h"
76e0ea8d87SJonas Devlieghere#include "llvm/Support/FileSystem.h"
772c77eefeSRaphael Isemann
7807570f55SJason Molenda#include "../cfcpp/CFCBundle.h"
7907570f55SJason Molenda#include "../cfcpp/CFCMutableArray.h"
8007570f55SJason Molenda#include "../cfcpp/CFCMutableDictionary.h"
8107570f55SJason Molenda#include "../cfcpp/CFCReleaser.h"
8207570f55SJason Molenda#include "../cfcpp/CFCString.h"
832c77eefeSRaphael Isemann
842c77eefeSRaphael Isemann#include <objc/objc-auto.h>
851e5d5261SJonas Devlieghere#include <os/log.h>
862c77eefeSRaphael Isemann
872c77eefeSRaphael Isemann#include <CoreFoundation/CoreFoundation.h>
882c77eefeSRaphael Isemann#include <Foundation/Foundation.h>
892c77eefeSRaphael Isemann
902c77eefeSRaphael Isemann#ifndef _POSIX_SPAWN_DISABLE_ASLR
912c77eefeSRaphael Isemann#define _POSIX_SPAWN_DISABLE_ASLR 0x0100
922c77eefeSRaphael Isemann#endif
932c77eefeSRaphael Isemann
942c77eefeSRaphael Isemannextern "C" {
952c77eefeSRaphael Isemannint __pthread_chdir(const char *path);
962c77eefeSRaphael Isemannint __pthread_fchdir(int fildes);
972c77eefeSRaphael Isemann}
982c77eefeSRaphael Isemann
992c77eefeSRaphael Isemannusing namespace lldb;
1002c77eefeSRaphael Isemannusing namespace lldb_private;
1012c77eefeSRaphael Isemann
1021e5d5261SJonas Devliegherestatic os_log_t g_os_log;
1031e5d5261SJonas Devliegherestatic std::once_flag g_os_log_once;
1041e5d5261SJonas Devlieghere
105528f5ba7SJonas Devliegherevoid Host::SystemLog(Severity severity, llvm::StringRef message) {
1061e5d5261SJonas Devlieghere  if (__builtin_available(macos 10.12, iOS 10, tvOS 10, watchOS 3, *)) {
1071e5d5261SJonas Devlieghere    std::call_once(g_os_log_once, []() {
1081e5d5261SJonas Devlieghere      g_os_log = os_log_create("com.apple.dt.lldb", "lldb");
1091e5d5261SJonas Devlieghere    });
110528f5ba7SJonas Devlieghere    switch (severity) {
111528f5ba7SJonas Devlieghere    case lldb::eSeverityInfo:
112528f5ba7SJonas Devlieghere    case lldb::eSeverityWarning:
1131e5d5261SJonas Devlieghere      os_log(g_os_log, "%{public}s", message.str().c_str());
114a7e9e3ebSJonas Devlieghere      break;
115528f5ba7SJonas Devlieghere    case lldb::eSeverityError:
116a7e9e3ebSJonas Devlieghere      os_log_error(g_os_log, "%{public}s", message.str().c_str());
117a7e9e3ebSJonas Devlieghere      break;
118a7e9e3ebSJonas Devlieghere    }
1191e5d5261SJonas Devlieghere  } else {
1201e5d5261SJonas Devlieghere    llvm::errs() << message;
1211e5d5261SJonas Devlieghere  }
1221e5d5261SJonas Devlieghere}
1231e5d5261SJonas Devlieghere
1242c77eefeSRaphael Isemannbool Host::GetBundleDirectory(const FileSpec &file,
1252c77eefeSRaphael Isemann                              FileSpec &bundle_directory) {
1262c77eefeSRaphael Isemann#if defined(__APPLE__)
1273a58d898SJonas Devlieghere  if (FileSystem::Instance().IsDirectory(file)) {
1282c77eefeSRaphael Isemann    char path[PATH_MAX];
1292c77eefeSRaphael Isemann    if (file.GetPath(path, sizeof(path))) {
1302c77eefeSRaphael Isemann      CFCBundle bundle(path);
1312c77eefeSRaphael Isemann      if (bundle.GetPath(path, sizeof(path))) {
1328f3be7a3SJonas Devlieghere        bundle_directory.SetFile(path, FileSpec::Style::native);
1332c77eefeSRaphael Isemann        return true;
1342c77eefeSRaphael Isemann      }
1352c77eefeSRaphael Isemann    }
1362c77eefeSRaphael Isemann  }
1372c77eefeSRaphael Isemann#endif
1382c77eefeSRaphael Isemann  bundle_directory.Clear();
1392c77eefeSRaphael Isemann  return false;
1402c77eefeSRaphael Isemann}
1412c77eefeSRaphael Isemann
1422c77eefeSRaphael Isemannbool Host::ResolveExecutableInBundle(FileSpec &file) {
1432c77eefeSRaphael Isemann#if defined(__APPLE__)
1443a58d898SJonas Devlieghere  if (FileSystem::Instance().IsDirectory(file)) {
1452c77eefeSRaphael Isemann    char path[PATH_MAX];
1462c77eefeSRaphael Isemann    if (file.GetPath(path, sizeof(path))) {
1472c77eefeSRaphael Isemann      CFCBundle bundle(path);
1482c77eefeSRaphael Isemann      CFCReleaser<CFURLRef> url(bundle.CopyExecutableURL());
1492c77eefeSRaphael Isemann      if (url.get()) {
1502c77eefeSRaphael Isemann        if (::CFURLGetFileSystemRepresentation(url.get(), YES, (UInt8 *)path,
1512c77eefeSRaphael Isemann                                               sizeof(path))) {
1528f3be7a3SJonas Devlieghere          file.SetFile(path, FileSpec::Style::native);
1532c77eefeSRaphael Isemann          return true;
1542c77eefeSRaphael Isemann        }
1552c77eefeSRaphael Isemann      }
1562c77eefeSRaphael Isemann    }
1572c77eefeSRaphael Isemann  }
1582c77eefeSRaphael Isemann#endif
1592c77eefeSRaphael Isemann  return false;
1602c77eefeSRaphael Isemann}
1612c77eefeSRaphael Isemann
162f203100eSVedant Kumar#if TARGET_OS_OSX
163f203100eSVedant Kumar
164d0810779SPavel Labathstatic void *AcceptPIDFromInferior(const char *connect_url) {
1652c77eefeSRaphael Isemann  ConnectionFileDescriptor file_conn;
1662c77eefeSRaphael Isemann  Status error;
1672c77eefeSRaphael Isemann  if (file_conn.Connect(connect_url, &error) == eConnectionStatusSuccess) {
1682c77eefeSRaphael Isemann    char pid_str[256];
1692c77eefeSRaphael Isemann    ::memset(pid_str, 0, sizeof(pid_str));
1702c77eefeSRaphael Isemann    ConnectionStatus status;
1712c77eefeSRaphael Isemann    const size_t pid_str_len = file_conn.Read(
1722c77eefeSRaphael Isemann        pid_str, sizeof(pid_str), std::chrono::seconds(0), status, NULL);
1732c77eefeSRaphael Isemann    if (pid_str_len > 0) {
1742c77eefeSRaphael Isemann      int pid = atoi(pid_str);
1752c77eefeSRaphael Isemann      return (void *)(intptr_t)pid;
1762c77eefeSRaphael Isemann    }
1772c77eefeSRaphael Isemann  }
1782c77eefeSRaphael Isemann  return NULL;
1792c77eefeSRaphael Isemann}
1802c77eefeSRaphael Isemann
1812c77eefeSRaphael Isemannconst char *applscript_in_new_tty = "tell application \"Terminal\"\n"
1822c77eefeSRaphael Isemann                                    "   activate\n"
1832c77eefeSRaphael Isemann                                    "	do script \"/bin/bash -c '%s';exit\"\n"
1842c77eefeSRaphael Isemann                                    "end tell\n";
1852c77eefeSRaphael Isemann
1862c77eefeSRaphael Isemannconst char *applscript_in_existing_tty = "\
1872c77eefeSRaphael Isemannset the_shell_script to \"/bin/bash -c '%s';exit\"\n\
1882c77eefeSRaphael Isemanntell application \"Terminal\"\n\
1892c77eefeSRaphael Isemann	repeat with the_window in (get windows)\n\
1902c77eefeSRaphael Isemann		repeat with the_tab in tabs of the_window\n\
1912c77eefeSRaphael Isemann			set the_tty to tty in the_tab\n\
1922c77eefeSRaphael Isemann			if the_tty contains \"%s\" then\n\
1932c77eefeSRaphael Isemann				if the_tab is not busy then\n\
1942c77eefeSRaphael Isemann					set selected of the_tab to true\n\
1952c77eefeSRaphael Isemann					set frontmost of the_window to true\n\
1962c77eefeSRaphael Isemann					do script the_shell_script in the_tab\n\
1972c77eefeSRaphael Isemann					return\n\
1982c77eefeSRaphael Isemann				end if\n\
1992c77eefeSRaphael Isemann			end if\n\
2002c77eefeSRaphael Isemann		end repeat\n\
2012c77eefeSRaphael Isemann	end repeat\n\
2022c77eefeSRaphael Isemann	do script the_shell_script\n\
2032c77eefeSRaphael Isemannend tell\n";
2042c77eefeSRaphael Isemann
2052c77eefeSRaphael Isemannstatic Status
2062c77eefeSRaphael IsemannLaunchInNewTerminalWithAppleScript(const char *exe_path,
2072c77eefeSRaphael Isemann                                   ProcessLaunchInfo &launch_info) {
2082c77eefeSRaphael Isemann  Status error;
2092c77eefeSRaphael Isemann  char unix_socket_name[PATH_MAX] = "/tmp/XXXXXX";
2102c77eefeSRaphael Isemann  if (::mktemp(unix_socket_name) == NULL) {
2110642cd76SAdrian Prantl    error = Status::FromErrorString(
2120642cd76SAdrian Prantl        "failed to make temporary path for a unix socket");
2132c77eefeSRaphael Isemann    return error;
2142c77eefeSRaphael Isemann  }
2152c77eefeSRaphael Isemann
2162c77eefeSRaphael Isemann  StreamString command;
21760f028ffSPavel Labath  FileSpec darwin_debug_file_spec = HostInfo::GetSupportExeDir();
21860f028ffSPavel Labath  if (!darwin_debug_file_spec) {
2190642cd76SAdrian Prantl    error =
2200642cd76SAdrian Prantl        Status::FromErrorString("can't locate the 'darwin-debug' executable");
2212c77eefeSRaphael Isemann    return error;
2222c77eefeSRaphael Isemann  }
2232c77eefeSRaphael Isemann
224529a3d87SGreg Clayton  darwin_debug_file_spec.SetFilename("darwin-debug");
2252c77eefeSRaphael Isemann
226dbd7fabaSJonas Devlieghere  if (!FileSystem::Instance().Exists(darwin_debug_file_spec)) {
2270642cd76SAdrian Prantl    error = Status::FromErrorStringWithFormat(
2282c77eefeSRaphael Isemann        "the 'darwin-debug' executable doesn't exists at '%s'",
2292c77eefeSRaphael Isemann        darwin_debug_file_spec.GetPath().c_str());
2302c77eefeSRaphael Isemann    return error;
2312c77eefeSRaphael Isemann  }
2322c77eefeSRaphael Isemann
2332c77eefeSRaphael Isemann  char launcher_path[PATH_MAX];
2342c77eefeSRaphael Isemann  darwin_debug_file_spec.GetPath(launcher_path, sizeof(launcher_path));
2352c77eefeSRaphael Isemann
2362c77eefeSRaphael Isemann  const ArchSpec &arch_spec = launch_info.GetArchitecture();
2372c77eefeSRaphael Isemann  // Only set the architecture if it is valid and if it isn't Haswell (x86_64h).
2382c77eefeSRaphael Isemann  if (arch_spec.IsValid() &&
2392c77eefeSRaphael Isemann      arch_spec.GetCore() != ArchSpec::eCore_x86_64_x86_64h)
2402c77eefeSRaphael Isemann    command.Printf("arch -arch %s ", arch_spec.GetArchitectureName());
2412c77eefeSRaphael Isemann
2425205c177SJonas Devlieghere  command.Printf(R"(\"%s\" --unix-socket=%s)", launcher_path, unix_socket_name);
2432c77eefeSRaphael Isemann
2442c77eefeSRaphael Isemann  if (arch_spec.IsValid())
2452c77eefeSRaphael Isemann    command.Printf(" --arch=%s", arch_spec.GetArchitectureName());
2462c77eefeSRaphael Isemann
2472c77eefeSRaphael Isemann  FileSpec working_dir{launch_info.GetWorkingDirectory()};
2482c77eefeSRaphael Isemann  if (working_dir)
249529a3d87SGreg Clayton    command.Printf(R"( --working-dir \"%s\")", working_dir.GetPath().c_str());
2502c77eefeSRaphael Isemann  else {
2512c77eefeSRaphael Isemann    char cwd[PATH_MAX];
2522c77eefeSRaphael Isemann    if (getcwd(cwd, PATH_MAX))
2535205c177SJonas Devlieghere      command.Printf(R"( --working-dir \"%s\")", cwd);
2542c77eefeSRaphael Isemann  }
2552c77eefeSRaphael Isemann
2562c77eefeSRaphael Isemann  if (launch_info.GetFlags().Test(eLaunchFlagDisableASLR))
2572c77eefeSRaphael Isemann    command.PutCString(" --disable-aslr");
2582c77eefeSRaphael Isemann
2592c77eefeSRaphael Isemann  // We are launching on this host in a terminal. So compare the environment on
2602c77eefeSRaphael Isemann  // the host to what is supplied in the launch_info. Any items that aren't in
2612c77eefeSRaphael Isemann  // the host environment need to be sent to darwin-debug. If we send all
2622c77eefeSRaphael Isemann  // environment entries, we might blow the max command line length, so we only
2632c77eefeSRaphael Isemann  // send user modified entries.
2642c77eefeSRaphael Isemann  Environment host_env = Host::GetEnvironment();
2652c77eefeSRaphael Isemann
2662c77eefeSRaphael Isemann  for (const auto &KV : launch_info.GetEnvironment()) {
2672c77eefeSRaphael Isemann    auto host_entry = host_env.find(KV.first());
2682c77eefeSRaphael Isemann    if (host_entry == host_env.end() || host_entry->second != KV.second)
2695205c177SJonas Devlieghere      command.Format(R"( --env=\"{0}\")", Environment::compose(KV));
2702c77eefeSRaphael Isemann  }
2712c77eefeSRaphael Isemann
2722c77eefeSRaphael Isemann  command.PutCString(" -- ");
2732c77eefeSRaphael Isemann
2742c77eefeSRaphael Isemann  const char **argv = launch_info.GetArguments().GetConstArgumentVector();
2752c77eefeSRaphael Isemann  if (argv) {
2762c77eefeSRaphael Isemann    for (size_t i = 0; argv[i] != NULL; ++i) {
2772c77eefeSRaphael Isemann      if (i == 0)
2785205c177SJonas Devlieghere        command.Printf(R"( \"%s\")", exe_path);
2792c77eefeSRaphael Isemann      else
2805205c177SJonas Devlieghere        command.Printf(R"( \"%s\")", argv[i]);
2812c77eefeSRaphael Isemann    }
2822c77eefeSRaphael Isemann  } else {
2835205c177SJonas Devlieghere    command.Printf(R"( \"%s\")", exe_path);
2842c77eefeSRaphael Isemann  }
2852c77eefeSRaphael Isemann  command.PutCString(" ; echo Process exited with status $?");
2862c77eefeSRaphael Isemann  if (launch_info.GetFlags().Test(lldb::eLaunchFlagCloseTTYOnExit))
2872c77eefeSRaphael Isemann    command.PutCString(" ; exit");
2882c77eefeSRaphael Isemann
2892c77eefeSRaphael Isemann  StreamString applescript_source;
2902c77eefeSRaphael Isemann
2912c77eefeSRaphael Isemann  applescript_source.Printf(applscript_in_new_tty,
2922c77eefeSRaphael Isemann                            command.GetString().str().c_str());
2935205c177SJonas Devlieghere
2942c77eefeSRaphael Isemann  NSAppleScript *applescript = [[NSAppleScript alloc]
2952c77eefeSRaphael Isemann      initWithSource:[NSString stringWithCString:applescript_source.GetString()
2962c77eefeSRaphael Isemann                                                     .str()
2972c77eefeSRaphael Isemann                                                     .c_str()
2982c77eefeSRaphael Isemann                                        encoding:NSUTF8StringEncoding]];
2992c77eefeSRaphael Isemann
3002c77eefeSRaphael Isemann  lldb::pid_t pid = LLDB_INVALID_PROCESS_ID;
3012c77eefeSRaphael Isemann
3022c77eefeSRaphael Isemann  Status lldb_error;
3032c77eefeSRaphael Isemann  // Sleep and wait a bit for debugserver to start to listen...
3042c77eefeSRaphael Isemann  ConnectionFileDescriptor file_conn;
3052c77eefeSRaphael Isemann  char connect_url[128];
3062c77eefeSRaphael Isemann  ::snprintf(connect_url, sizeof(connect_url), "unix-accept://%s",
3072c77eefeSRaphael Isemann             unix_socket_name);
3082c77eefeSRaphael Isemann
3092c77eefeSRaphael Isemann  // Spawn a new thread to accept incoming connection on the connect_url
3102c77eefeSRaphael Isemann  // so we can grab the pid from the inferior. We have to do this because we
3112c77eefeSRaphael Isemann  // are sending an AppleScript that will launch a process in Terminal.app,
3122c77eefeSRaphael Isemann  // in a shell and the shell will fork/exec a couple of times before we get
3132c77eefeSRaphael Isemann  // to the process that we wanted to launch. So when our process actually
3142c77eefeSRaphael Isemann  // gets launched, we will handshake with it and get the process ID for it.
315f39c2e18SJonas Devlieghere  llvm::Expected<HostThread> accept_thread = ThreadLauncher::LaunchThread(
316d0810779SPavel Labath      unix_socket_name, [&] { return AcceptPIDFromInferior(connect_url); });
317f39c2e18SJonas Devlieghere
318f39c2e18SJonas Devlieghere  if (!accept_thread)
319a0dd90ebSAdrian Prantl    return Status::FromError(accept_thread.takeError());
3202c77eefeSRaphael Isemann
3212c77eefeSRaphael Isemann  [applescript executeAndReturnError:nil];
3222c77eefeSRaphael Isemann
3232c77eefeSRaphael Isemann  thread_result_t accept_thread_result = NULL;
324f39c2e18SJonas Devlieghere  lldb_error = accept_thread->Join(&accept_thread_result);
3252c77eefeSRaphael Isemann  if (lldb_error.Success() && accept_thread_result) {
3262c77eefeSRaphael Isemann    pid = (intptr_t)accept_thread_result;
3272c77eefeSRaphael Isemann  }
3282c77eefeSRaphael Isemann
3292c77eefeSRaphael Isemann  llvm::sys::fs::remove(unix_socket_name);
3302c77eefeSRaphael Isemann  [applescript release];
3312c77eefeSRaphael Isemann  if (pid != LLDB_INVALID_PROCESS_ID)
3322c77eefeSRaphael Isemann    launch_info.SetProcessID(pid);
3332c77eefeSRaphael Isemann  return error;
3342c77eefeSRaphael Isemann}
3352c77eefeSRaphael Isemann
336f203100eSVedant Kumar#endif // TARGET_OS_OSX
3372c77eefeSRaphael Isemann
338b12b35adSJonas Devliegherellvm::Error Host::OpenFileInExternalEditor(llvm::StringRef editor,
339b12b35adSJonas Devlieghere                                           const FileSpec &file_spec,
3402c77eefeSRaphael Isemann                                           uint32_t line_no) {
341f203100eSVedant Kumar#if !TARGET_OS_OSX
34213dbc16bSJonas Devlieghere  return llvm::errorCodeToError(
34313dbc16bSJonas Devlieghere      std::error_code(ENOTSUP, std::system_category()));
344f203100eSVedant Kumar#else // !TARGET_OS_OSX
34513dbc16bSJonas Devlieghere  Log *log = GetLog(LLDBLog::Host);
34613dbc16bSJonas Devlieghere
34713dbc16bSJonas Devlieghere  const std::string file_path = file_spec.GetPath();
34813dbc16bSJonas Devlieghere
34913dbc16bSJonas Devlieghere  LLDB_LOG(log, "Sending {0}:{1} to external editor",
35013dbc16bSJonas Devlieghere           file_path.empty() ? "<invalid>" : file_path, line_no);
35113dbc16bSJonas Devlieghere
35213dbc16bSJonas Devlieghere  if (file_path.empty())
35313dbc16bSJonas Devlieghere    return llvm::createStringError(llvm::inconvertibleErrorCode(),
35413dbc16bSJonas Devlieghere                                   "no file specified");
35513dbc16bSJonas Devlieghere
35613dbc16bSJonas Devlieghere  CFCString file_cfstr(file_path.c_str(), kCFStringEncodingUTF8);
35713dbc16bSJonas Devlieghere  CFCReleaser<CFURLRef> file_URL = ::CFURLCreateWithFileSystemPath(
35813dbc16bSJonas Devlieghere      /*allocator=*/NULL,
35913dbc16bSJonas Devlieghere      /*filePath*/ file_cfstr.get(),
36013dbc16bSJonas Devlieghere      /*pathStyle=*/kCFURLPOSIXPathStyle,
36113dbc16bSJonas Devlieghere      /*isDirectory=*/false);
36213dbc16bSJonas Devlieghere
36313dbc16bSJonas Devlieghere  if (!file_URL.get())
36413dbc16bSJonas Devlieghere    return llvm::createStringError(
36513dbc16bSJonas Devlieghere        llvm::inconvertibleErrorCode(),
36613dbc16bSJonas Devlieghere        llvm::formatv("could not create CFURL from path \"{0}\"", file_path));
36713dbc16bSJonas Devlieghere
36813dbc16bSJonas Devlieghere  // Create a new Apple Event descriptor.
3692c77eefeSRaphael Isemann  typedef struct {
3702c77eefeSRaphael Isemann    int16_t reserved0; // must be zero
3712c77eefeSRaphael Isemann    int16_t fLineNumber;
3722c77eefeSRaphael Isemann    int32_t fSelStart;
3732c77eefeSRaphael Isemann    int32_t fSelEnd;
3742c77eefeSRaphael Isemann    uint32_t reserved1; // must be zero
3752c77eefeSRaphael Isemann    uint32_t reserved2; // must be zero
3762c77eefeSRaphael Isemann  } BabelAESelInfo;
3772c77eefeSRaphael Isemann
37813dbc16bSJonas Devlieghere  // We attach this to an 'odoc' event to specify a particular selection.
3792c77eefeSRaphael Isemann  BabelAESelInfo file_and_line_info = {
3802c77eefeSRaphael Isemann      0,                      // reserved0
3812c77eefeSRaphael Isemann      (int16_t)(line_no - 1), // fLineNumber (zero based line number)
3822c77eefeSRaphael Isemann      1,                      // fSelStart
3832c77eefeSRaphael Isemann      1024,                   // fSelEnd
3842c77eefeSRaphael Isemann      0,                      // reserved1
3852c77eefeSRaphael Isemann      0                       // reserved2
3862c77eefeSRaphael Isemann  };
3872c77eefeSRaphael Isemann
3882c77eefeSRaphael Isemann  AEKeyDesc file_and_line_desc;
3892c77eefeSRaphael Isemann  file_and_line_desc.descKey = keyAEPosition;
39013dbc16bSJonas Devlieghere  long error = ::AECreateDesc(/*typeCode=*/typeUTF8Text,
39113dbc16bSJonas Devlieghere                              /*dataPtr=*/&file_and_line_info,
39213dbc16bSJonas Devlieghere                              /*dataSize=*/sizeof(file_and_line_info),
39313dbc16bSJonas Devlieghere                              /*result=*/&(file_and_line_desc.descContent));
3942c77eefeSRaphael Isemann
39513dbc16bSJonas Devlieghere  if (error != noErr)
39613dbc16bSJonas Devlieghere    return llvm::createStringError(
39713dbc16bSJonas Devlieghere        llvm::inconvertibleErrorCode(),
39813dbc16bSJonas Devlieghere        llvm::formatv("creating Apple Event descriptor failed: error {0}",
39913dbc16bSJonas Devlieghere                      error));
4002c77eefeSRaphael Isemann
40113dbc16bSJonas Devlieghere  // Deallocate the descriptor on exit.
40213dbc16bSJonas Devlieghere  auto on_exit = llvm::make_scope_exit(
40313dbc16bSJonas Devlieghere      [&]() { AEDisposeDesc(&(file_and_line_desc.descContent)); });
40413dbc16bSJonas Devlieghere
405b12b35adSJonas Devlieghere  if (editor.empty()) {
406b12b35adSJonas Devlieghere    if (const char *lldb_external_editor = ::getenv("LLDB_EXTERNAL_EDITOR"))
407b12b35adSJonas Devlieghere      editor = lldb_external_editor;
408b12b35adSJonas Devlieghere  }
40913dbc16bSJonas Devlieghere
410b12b35adSJonas Devlieghere  std::optional<FSRef> app_fsref;
411b12b35adSJonas Devlieghere  if (!editor.empty()) {
412b12b35adSJonas Devlieghere    LLDB_LOG(log, "Looking for external editor: {0}", editor);
413b12b35adSJonas Devlieghere
414b12b35adSJonas Devlieghere    app_fsref.emplace();
415b12b35adSJonas Devlieghere    CFCString editor_name(editor.data(), kCFStringEncodingUTF8);
41613dbc16bSJonas Devlieghere    long app_error = ::LSFindApplicationForInfo(
41713dbc16bSJonas Devlieghere        /*inCreator=*/kLSUnknownCreator, /*inBundleID=*/NULL,
418b12b35adSJonas Devlieghere        /*inName=*/editor_name.get(), /*outAppRef=*/&(*app_fsref),
41913dbc16bSJonas Devlieghere        /*outAppURL=*/NULL);
420b12b35adSJonas Devlieghere    if (app_error != noErr)
421b12b35adSJonas Devlieghere      return llvm::createStringError(
422b12b35adSJonas Devlieghere          llvm::inconvertibleErrorCode(),
42313dbc16bSJonas Devlieghere          llvm::formatv("could not find external editor \"{0}\": "
42413dbc16bSJonas Devlieghere                        "LSFindApplicationForInfo returned error {1}",
425b12b35adSJonas Devlieghere                        editor, app_error));
42613dbc16bSJonas Devlieghere  }
42713dbc16bSJonas Devlieghere
42813dbc16bSJonas Devlieghere  // Build app launch parameters.
4292c77eefeSRaphael Isemann  LSApplicationParameters app_params;
4302c77eefeSRaphael Isemann  ::memset(&app_params, 0, sizeof(app_params));
4312c77eefeSRaphael Isemann  app_params.flags =
4322c77eefeSRaphael Isemann      kLSLaunchDefaults | kLSLaunchDontAddToRecents | kLSLaunchDontSwitch;
433b12b35adSJonas Devlieghere  if (app_fsref)
434b12b35adSJonas Devlieghere    app_params.application = &(*app_fsref);
4352c77eefeSRaphael Isemann
4362c77eefeSRaphael Isemann  ProcessSerialNumber psn;
43713dbc16bSJonas Devlieghere  std::array<CFURLRef, 1> file_array = {file_URL.get()};
43813dbc16bSJonas Devlieghere  CFCReleaser<CFArrayRef> cf_array(
43913dbc16bSJonas Devlieghere      CFArrayCreate(/*allocator=*/NULL, /*values=*/(const void **)&file_array,
44013dbc16bSJonas Devlieghere                    /*numValues*/ 1, /*callBacks=*/NULL));
44113dbc16bSJonas Devlieghere  error = ::LSOpenURLsWithRole(
44213dbc16bSJonas Devlieghere      /*inURLs=*/cf_array.get(), /*inRole=*/kLSRolesEditor,
44313dbc16bSJonas Devlieghere      /*inAEParam=*/&file_and_line_desc,
44413dbc16bSJonas Devlieghere      /*inAppParams=*/&app_params, /*outPSNs=*/&psn, /*inMaxPSNCount=*/1);
4452c77eefeSRaphael Isemann
44613dbc16bSJonas Devlieghere  if (error != noErr)
44713dbc16bSJonas Devlieghere    return llvm::createStringError(
44813dbc16bSJonas Devlieghere        llvm::inconvertibleErrorCode(),
44913dbc16bSJonas Devlieghere        llvm::formatv("LSOpenURLsWithRole failed: error {0}", error));
4502c77eefeSRaphael Isemann
45113dbc16bSJonas Devlieghere  return llvm::Error::success();
452f203100eSVedant Kumar#endif // TARGET_OS_OSX
4532c77eefeSRaphael Isemann}
4542c77eefeSRaphael Isemann
455b945b62cSJonas Devliegherebool Host::IsInteractiveGraphicSession() {
456b945b62cSJonas Devlieghere#if !TARGET_OS_OSX
457b945b62cSJonas Devlieghere  return false;
458b945b62cSJonas Devlieghere#else
459b945b62cSJonas Devlieghere  auditinfo_addr_t info;
460b945b62cSJonas Devlieghere  getaudit_addr(&info, sizeof(info));
461b945b62cSJonas Devlieghere  return info.ai_flags & AU_SESSION_FLAG_HAS_GRAPHIC_ACCESS;
462b945b62cSJonas Devlieghere#endif
463b945b62cSJonas Devlieghere}
464b945b62cSJonas Devlieghere
4652c77eefeSRaphael IsemannEnvironment Host::GetEnvironment() { return Environment(*_NSGetEnviron()); }
4662c77eefeSRaphael Isemann
4672c77eefeSRaphael Isemannstatic bool GetMacOSXProcessCPUType(ProcessInstanceInfo &process_info) {
4682c77eefeSRaphael Isemann  if (process_info.ProcessIDIsValid()) {
4692c77eefeSRaphael Isemann    // Make a new mib to stay thread safe
4702c77eefeSRaphael Isemann    int mib[CTL_MAXNAME] = {
4712c77eefeSRaphael Isemann        0,
4722c77eefeSRaphael Isemann    };
4732c77eefeSRaphael Isemann    size_t mib_len = CTL_MAXNAME;
4742c77eefeSRaphael Isemann    if (::sysctlnametomib("sysctl.proc_cputype", mib, &mib_len))
4752c77eefeSRaphael Isemann      return false;
4762c77eefeSRaphael Isemann
4772c77eefeSRaphael Isemann    mib[mib_len] = process_info.GetProcessID();
4782c77eefeSRaphael Isemann    mib_len++;
4792c77eefeSRaphael Isemann
4802c77eefeSRaphael Isemann    cpu_type_t cpu, sub = 0;
4812c77eefeSRaphael Isemann    size_t len = sizeof(cpu);
4822c77eefeSRaphael Isemann    if (::sysctl(mib, mib_len, &cpu, &len, 0, 0) == 0) {
4832c77eefeSRaphael Isemann      switch (cpu) {
4842c77eefeSRaphael Isemann      case CPU_TYPE_I386:
4852c77eefeSRaphael Isemann        sub = CPU_SUBTYPE_I386_ALL;
4862c77eefeSRaphael Isemann        break;
4872c77eefeSRaphael Isemann      case CPU_TYPE_X86_64:
4882c77eefeSRaphael Isemann        sub = CPU_SUBTYPE_X86_64_ALL;
4892c77eefeSRaphael Isemann        break;
4902c77eefeSRaphael Isemann
4912c77eefeSRaphael Isemann#if defined(CPU_TYPE_ARM64) && defined(CPU_SUBTYPE_ARM64_ALL)
4922c77eefeSRaphael Isemann      case CPU_TYPE_ARM64:
4932c77eefeSRaphael Isemann        sub = CPU_SUBTYPE_ARM64_ALL;
4942c77eefeSRaphael Isemann        break;
4952c77eefeSRaphael Isemann#endif
4962c77eefeSRaphael Isemann
4977dd7a360SJason Molenda#if defined(CPU_TYPE_ARM64_32) && defined(CPU_SUBTYPE_ARM64_32_ALL)
4987dd7a360SJason Molenda      case CPU_TYPE_ARM64_32:
4997dd7a360SJason Molenda        sub = CPU_SUBTYPE_ARM64_32_ALL;
5007dd7a360SJason Molenda        break;
5017dd7a360SJason Molenda#endif
5027dd7a360SJason Molenda
5032c77eefeSRaphael Isemann      case CPU_TYPE_ARM: {
5042c77eefeSRaphael Isemann        // Note that we fetched the cpu type from the PROCESS but we can't get a
5052c77eefeSRaphael Isemann        // cpusubtype of the
5062c77eefeSRaphael Isemann        // process -- we can only get the host's cpu subtype.
5072c77eefeSRaphael Isemann        uint32_t cpusubtype = 0;
5082c77eefeSRaphael Isemann        len = sizeof(cpusubtype);
5092c77eefeSRaphael Isemann        if (::sysctlbyname("hw.cpusubtype", &cpusubtype, &len, NULL, 0) == 0)
5102c77eefeSRaphael Isemann          sub = cpusubtype;
5112c77eefeSRaphael Isemann
5122c77eefeSRaphael Isemann        bool host_cpu_is_64bit;
5132c77eefeSRaphael Isemann        uint32_t is64bit_capable;
5142c77eefeSRaphael Isemann        size_t is64bit_capable_len = sizeof(is64bit_capable);
515a6682a41SJonas Devlieghere        host_cpu_is_64bit =
516a6682a41SJonas Devlieghere            sysctlbyname("hw.cpu64bit_capable", &is64bit_capable,
517a6682a41SJonas Devlieghere                         &is64bit_capable_len, NULL, 0) == 0;
5182c77eefeSRaphael Isemann
5192c77eefeSRaphael Isemann        // if the host is an armv8 device, its cpusubtype will be in
5202c77eefeSRaphael Isemann        // CPU_SUBTYPE_ARM64 numbering
5212c77eefeSRaphael Isemann        // and we need to rewrite it to a reasonable CPU_SUBTYPE_ARM value
5222c77eefeSRaphael Isemann        // instead.
5232c77eefeSRaphael Isemann
5242c77eefeSRaphael Isemann        if (host_cpu_is_64bit) {
5252c77eefeSRaphael Isemann          sub = CPU_SUBTYPE_ARM_V7;
5262c77eefeSRaphael Isemann        }
5272c77eefeSRaphael Isemann      } break;
5282c77eefeSRaphael Isemann
5292c77eefeSRaphael Isemann      default:
5302c77eefeSRaphael Isemann        break;
5312c77eefeSRaphael Isemann      }
5322c77eefeSRaphael Isemann      process_info.GetArchitecture().SetArchitecture(eArchTypeMachO, cpu, sub);
5332c77eefeSRaphael Isemann      return true;
5342c77eefeSRaphael Isemann    }
5352c77eefeSRaphael Isemann  }
5362c77eefeSRaphael Isemann  process_info.GetArchitecture().Clear();
5372c77eefeSRaphael Isemann  return false;
5382c77eefeSRaphael Isemann}
5392c77eefeSRaphael Isemann
5402c77eefeSRaphael Isemannstatic bool GetMacOSXProcessArgs(const ProcessInstanceInfoMatch *match_info_ptr,
5412c77eefeSRaphael Isemann                                 ProcessInstanceInfo &process_info) {
5422c77eefeSRaphael Isemann  if (process_info.ProcessIDIsValid()) {
5432c77eefeSRaphael Isemann    int proc_args_mib[3] = {CTL_KERN, KERN_PROCARGS2,
5442c77eefeSRaphael Isemann                            (int)process_info.GetProcessID()};
5452c77eefeSRaphael Isemann
5462c77eefeSRaphael Isemann    size_t arg_data_size = 0;
5472c77eefeSRaphael Isemann    if (::sysctl(proc_args_mib, 3, nullptr, &arg_data_size, NULL, 0) ||
5482c77eefeSRaphael Isemann        arg_data_size == 0)
5492c77eefeSRaphael Isemann      arg_data_size = 8192;
5502c77eefeSRaphael Isemann
5512c77eefeSRaphael Isemann    // Add a few bytes to the calculated length, I know we need to add at least
5522c77eefeSRaphael Isemann    // one byte
5532c77eefeSRaphael Isemann    // to this number otherwise we get junk back, so add 128 just in case...
5542c77eefeSRaphael Isemann    DataBufferHeap arg_data(arg_data_size + 128, 0);
5552c77eefeSRaphael Isemann    arg_data_size = arg_data.GetByteSize();
5562c77eefeSRaphael Isemann    if (::sysctl(proc_args_mib, 3, arg_data.GetBytes(), &arg_data_size, NULL,
5572c77eefeSRaphael Isemann                 0) == 0) {
5582c77eefeSRaphael Isemann      DataExtractor data(arg_data.GetBytes(), arg_data_size,
5592c77eefeSRaphael Isemann                         endian::InlHostByteOrder(), sizeof(void *));
5602c77eefeSRaphael Isemann      lldb::offset_t offset = 0;
5612c77eefeSRaphael Isemann      uint32_t argc = data.GetU32(&offset);
5622c77eefeSRaphael Isemann      llvm::Triple &triple = process_info.GetArchitecture().GetTriple();
5632c77eefeSRaphael Isemann      const llvm::Triple::ArchType triple_arch = triple.getArch();
5642c77eefeSRaphael Isemann      const bool check_for_ios_simulator =
5652c77eefeSRaphael Isemann          (triple_arch == llvm::Triple::x86 ||
5662c77eefeSRaphael Isemann           triple_arch == llvm::Triple::x86_64);
5672c77eefeSRaphael Isemann      const char *cstr = data.GetCStr(&offset);
5682c77eefeSRaphael Isemann      if (cstr) {
5698f3be7a3SJonas Devlieghere        process_info.GetExecutableFile().SetFile(cstr, FileSpec::Style::native);
5702c77eefeSRaphael Isemann
5712c77eefeSRaphael Isemann        if (match_info_ptr == NULL ||
5722c77eefeSRaphael Isemann            NameMatches(
5732c77eefeSRaphael Isemann                process_info.GetExecutableFile().GetFilename().GetCString(),
5742c77eefeSRaphael Isemann                match_info_ptr->GetNameMatchType(),
5752c77eefeSRaphael Isemann                match_info_ptr->GetProcessInfo().GetName())) {
5762c77eefeSRaphael Isemann          // Skip NULLs
57709ad8c8fSJonas Devlieghere          while (true) {
5782c77eefeSRaphael Isemann            const uint8_t *p = data.PeekData(offset, 1);
5792c77eefeSRaphael Isemann            if ((p == NULL) || (*p != '\0'))
5802c77eefeSRaphael Isemann              break;
5812c77eefeSRaphael Isemann            ++offset;
5822c77eefeSRaphael Isemann          }
5832c77eefeSRaphael Isemann          // Now extract all arguments
5842c77eefeSRaphael Isemann          Args &proc_args = process_info.GetArguments();
5852c77eefeSRaphael Isemann          for (int i = 0; i < static_cast<int>(argc); ++i) {
5862c77eefeSRaphael Isemann            cstr = data.GetCStr(&offset);
5872c77eefeSRaphael Isemann            if (cstr)
5882c77eefeSRaphael Isemann              proc_args.AppendArgument(llvm::StringRef(cstr));
5892c77eefeSRaphael Isemann          }
5902c77eefeSRaphael Isemann
5912c77eefeSRaphael Isemann          Environment &proc_env = process_info.GetEnvironment();
5922c77eefeSRaphael Isemann          while ((cstr = data.GetCStr(&offset))) {
5932c77eefeSRaphael Isemann            if (cstr[0] == '\0')
5942c77eefeSRaphael Isemann              break;
5952c77eefeSRaphael Isemann
5962c77eefeSRaphael Isemann            if (check_for_ios_simulator) {
5972c77eefeSRaphael Isemann              if (strncmp(cstr, "SIMULATOR_UDID=", strlen("SIMULATOR_UDID=")) ==
5982c77eefeSRaphael Isemann                  0)
5992c77eefeSRaphael Isemann                process_info.GetArchitecture().GetTriple().setOS(
6002c77eefeSRaphael Isemann                    llvm::Triple::IOS);
6012c77eefeSRaphael Isemann              else
6022c77eefeSRaphael Isemann                process_info.GetArchitecture().GetTriple().setOS(
6032c77eefeSRaphael Isemann                    llvm::Triple::MacOSX);
6042c77eefeSRaphael Isemann            }
6052c77eefeSRaphael Isemann
6062c77eefeSRaphael Isemann            proc_env.insert(cstr);
6072c77eefeSRaphael Isemann          }
6082c77eefeSRaphael Isemann          return true;
6092c77eefeSRaphael Isemann        }
6102c77eefeSRaphael Isemann      }
6112c77eefeSRaphael Isemann    }
6122c77eefeSRaphael Isemann  }
6132c77eefeSRaphael Isemann  return false;
6142c77eefeSRaphael Isemann}
6152c77eefeSRaphael Isemann
6162c77eefeSRaphael Isemannstatic bool GetMacOSXProcessUserAndGroup(ProcessInstanceInfo &process_info) {
6172c77eefeSRaphael Isemann  if (process_info.ProcessIDIsValid()) {
6182c77eefeSRaphael Isemann    int mib[4];
6192c77eefeSRaphael Isemann    mib[0] = CTL_KERN;
6202c77eefeSRaphael Isemann    mib[1] = KERN_PROC;
6212c77eefeSRaphael Isemann    mib[2] = KERN_PROC_PID;
6222c77eefeSRaphael Isemann    mib[3] = process_info.GetProcessID();
6232c77eefeSRaphael Isemann    struct kinfo_proc proc_kinfo;
6242c77eefeSRaphael Isemann    size_t proc_kinfo_size = sizeof(struct kinfo_proc);
6252c77eefeSRaphael Isemann
6262c77eefeSRaphael Isemann    if (::sysctl(mib, 4, &proc_kinfo, &proc_kinfo_size, NULL, 0) == 0) {
6272c77eefeSRaphael Isemann      if (proc_kinfo_size > 0) {
6282c77eefeSRaphael Isemann        process_info.SetParentProcessID(proc_kinfo.kp_eproc.e_ppid);
6292c77eefeSRaphael Isemann        process_info.SetUserID(proc_kinfo.kp_eproc.e_pcred.p_ruid);
6302c77eefeSRaphael Isemann        process_info.SetGroupID(proc_kinfo.kp_eproc.e_pcred.p_rgid);
6312c77eefeSRaphael Isemann        process_info.SetEffectiveUserID(proc_kinfo.kp_eproc.e_ucred.cr_uid);
6322c77eefeSRaphael Isemann        if (proc_kinfo.kp_eproc.e_ucred.cr_ngroups > 0)
6332c77eefeSRaphael Isemann          process_info.SetEffectiveGroupID(
6342c77eefeSRaphael Isemann              proc_kinfo.kp_eproc.e_ucred.cr_groups[0]);
6352c77eefeSRaphael Isemann        else
6362c77eefeSRaphael Isemann          process_info.SetEffectiveGroupID(UINT32_MAX);
6372c77eefeSRaphael Isemann        return true;
6382c77eefeSRaphael Isemann      }
6392c77eefeSRaphael Isemann    }
6402c77eefeSRaphael Isemann  }
6412c77eefeSRaphael Isemann  process_info.SetParentProcessID(LLDB_INVALID_PROCESS_ID);
6422c77eefeSRaphael Isemann  process_info.SetUserID(UINT32_MAX);
6432c77eefeSRaphael Isemann  process_info.SetGroupID(UINT32_MAX);
6442c77eefeSRaphael Isemann  process_info.SetEffectiveUserID(UINT32_MAX);
6452c77eefeSRaphael Isemann  process_info.SetEffectiveGroupID(UINT32_MAX);
6462c77eefeSRaphael Isemann  return false;
6472c77eefeSRaphael Isemann}
6482c77eefeSRaphael Isemann
6492451cbf0SJonas Devlieghereuint32_t Host::FindProcessesImpl(const ProcessInstanceInfoMatch &match_info,
6502c77eefeSRaphael Isemann                                 ProcessInstanceInfoList &process_infos) {
6512c77eefeSRaphael Isemann  std::vector<struct kinfo_proc> kinfos;
6522c77eefeSRaphael Isemann
6532c77eefeSRaphael Isemann  int mib[3] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL};
6542c77eefeSRaphael Isemann
6552c77eefeSRaphael Isemann  size_t pid_data_size = 0;
65634fb64d6SJonas Devlieghere  if (::sysctl(mib, 3, nullptr, &pid_data_size, nullptr, 0) != 0)
6572c77eefeSRaphael Isemann    return 0;
6582c77eefeSRaphael Isemann
6592c77eefeSRaphael Isemann  // Add a few extra in case a few more show up
6602c77eefeSRaphael Isemann  const size_t estimated_pid_count =
6612c77eefeSRaphael Isemann      (pid_data_size / sizeof(struct kinfo_proc)) + 10;
6622c77eefeSRaphael Isemann
6632c77eefeSRaphael Isemann  kinfos.resize(estimated_pid_count);
6642c77eefeSRaphael Isemann  pid_data_size = kinfos.size() * sizeof(struct kinfo_proc);
6652c77eefeSRaphael Isemann
66634fb64d6SJonas Devlieghere  if (::sysctl(mib, 3, &kinfos[0], &pid_data_size, nullptr, 0) != 0)
6672c77eefeSRaphael Isemann    return 0;
6682c77eefeSRaphael Isemann
6692c77eefeSRaphael Isemann  const size_t actual_pid_count = (pid_data_size / sizeof(struct kinfo_proc));
6702c77eefeSRaphael Isemann
6712c77eefeSRaphael Isemann  bool all_users = match_info.GetMatchAllUsers();
6722c77eefeSRaphael Isemann  const lldb::pid_t our_pid = getpid();
6732c77eefeSRaphael Isemann  const uid_t our_uid = getuid();
6742c77eefeSRaphael Isemann  for (size_t i = 0; i < actual_pid_count; i++) {
6752c77eefeSRaphael Isemann    const struct kinfo_proc &kinfo = kinfos[i];
6762c77eefeSRaphael Isemann
6772c77eefeSRaphael Isemann    bool kinfo_user_matches = false;
6782c77eefeSRaphael Isemann    if (all_users)
6792c77eefeSRaphael Isemann      kinfo_user_matches = true;
6802c77eefeSRaphael Isemann    else
6812c77eefeSRaphael Isemann      kinfo_user_matches = kinfo.kp_eproc.e_pcred.p_ruid == our_uid;
6822c77eefeSRaphael Isemann
6832c77eefeSRaphael Isemann    // Special case, if lldb is being run as root we can attach to anything.
6842c77eefeSRaphael Isemann    if (our_uid == 0)
6852c77eefeSRaphael Isemann      kinfo_user_matches = true;
6862c77eefeSRaphael Isemann
687a6682a41SJonas Devlieghere    if (!kinfo_user_matches || // Make sure the user is acceptable
6882c77eefeSRaphael Isemann        static_cast<lldb::pid_t>(kinfo.kp_proc.p_pid) ==
6892c77eefeSRaphael Isemann            our_pid ||                   // Skip this process
6902c77eefeSRaphael Isemann        kinfo.kp_proc.p_pid == 0 ||      // Skip kernel (kernel pid is zero)
6912c77eefeSRaphael Isemann        kinfo.kp_proc.p_stat == SZOMB || // Zombies are bad, they like brains...
6922c77eefeSRaphael Isemann        kinfo.kp_proc.p_flag & P_TRACED ||   // Being debugged?
693b4fdddf9SDavide Italiano        kinfo.kp_proc.p_flag & P_WEXIT)
6942c77eefeSRaphael Isemann      continue;
6952c77eefeSRaphael Isemann
6962c77eefeSRaphael Isemann    ProcessInstanceInfo process_info;
6972c77eefeSRaphael Isemann    process_info.SetProcessID(kinfo.kp_proc.p_pid);
6982c77eefeSRaphael Isemann    process_info.SetParentProcessID(kinfo.kp_eproc.e_ppid);
6992c77eefeSRaphael Isemann    process_info.SetUserID(kinfo.kp_eproc.e_pcred.p_ruid);
7002c77eefeSRaphael Isemann    process_info.SetGroupID(kinfo.kp_eproc.e_pcred.p_rgid);
7012c77eefeSRaphael Isemann    process_info.SetEffectiveUserID(kinfo.kp_eproc.e_ucred.cr_uid);
7022c77eefeSRaphael Isemann    if (kinfo.kp_eproc.e_ucred.cr_ngroups > 0)
7032c77eefeSRaphael Isemann      process_info.SetEffectiveGroupID(kinfo.kp_eproc.e_ucred.cr_groups[0]);
7042c77eefeSRaphael Isemann    else
7052c77eefeSRaphael Isemann      process_info.SetEffectiveGroupID(UINT32_MAX);
7062c77eefeSRaphael Isemann
7072c77eefeSRaphael Isemann    // Make sure our info matches before we go fetch the name and cpu type
708a8b18baaSPavel Labath    if (!match_info.UserIDsMatch(process_info) ||
709a8b18baaSPavel Labath        !match_info.ProcessIDsMatch(process_info))
710a8b18baaSPavel Labath      continue;
711a8b18baaSPavel Labath
7122c77eefeSRaphael Isemann    // Get CPU type first so we can know to look for iOS simulator is we have
7132c77eefeSRaphael Isemann    // x86 or x86_64
7142c77eefeSRaphael Isemann    if (GetMacOSXProcessCPUType(process_info)) {
7152c77eefeSRaphael Isemann      if (GetMacOSXProcessArgs(&match_info, process_info)) {
7162c77eefeSRaphael Isemann        if (match_info.Matches(process_info))
717638b06cfSJonas Devlieghere          process_infos.push_back(process_info);
7182c77eefeSRaphael Isemann      }
7192c77eefeSRaphael Isemann    }
7202c77eefeSRaphael Isemann  }
721638b06cfSJonas Devlieghere  return process_infos.size();
7222c77eefeSRaphael Isemann}
7232c77eefeSRaphael Isemann
7242c77eefeSRaphael Isemannbool Host::GetProcessInfo(lldb::pid_t pid, ProcessInstanceInfo &process_info) {
7252c77eefeSRaphael Isemann  process_info.SetProcessID(pid);
7262c77eefeSRaphael Isemann  bool success = false;
7272c77eefeSRaphael Isemann
7282c77eefeSRaphael Isemann  // Get CPU type first so we can know to look for iOS simulator is we have x86
7292c77eefeSRaphael Isemann  // or x86_64
7302c77eefeSRaphael Isemann  if (GetMacOSXProcessCPUType(process_info))
7312c77eefeSRaphael Isemann    success = true;
7322c77eefeSRaphael Isemann
7332c77eefeSRaphael Isemann  if (GetMacOSXProcessArgs(NULL, process_info))
7342c77eefeSRaphael Isemann    success = true;
7352c77eefeSRaphael Isemann
7362c77eefeSRaphael Isemann  if (GetMacOSXProcessUserAndGroup(process_info))
7372c77eefeSRaphael Isemann    success = true;
7382c77eefeSRaphael Isemann
7392c77eefeSRaphael Isemann  if (success)
7402c77eefeSRaphael Isemann    return true;
7412c77eefeSRaphael Isemann
7422c77eefeSRaphael Isemann  process_info.Clear();
7432c77eefeSRaphael Isemann  return false;
7442c77eefeSRaphael Isemann}
7452c77eefeSRaphael Isemann
746f203100eSVedant Kumar#if TARGET_OS_OSX
7472c77eefeSRaphael Isemannstatic void PackageXPCArguments(xpc_object_t message, const char *prefix,
7482c77eefeSRaphael Isemann                                const Args &args) {
7492c77eefeSRaphael Isemann  size_t count = args.GetArgumentCount();
7502c77eefeSRaphael Isemann  char buf[50]; // long enough for 'argXXX'
751d03d98b7SDave Lee  memset(buf, 0, sizeof(buf));
752d03d98b7SDave Lee  snprintf(buf, sizeof(buf), "%sCount", prefix);
7532c77eefeSRaphael Isemann  xpc_dictionary_set_int64(message, buf, count);
7542c77eefeSRaphael Isemann  for (size_t i = 0; i < count; i++) {
755d03d98b7SDave Lee    memset(buf, 0, sizeof(buf));
756d03d98b7SDave Lee    snprintf(buf, sizeof(buf), "%s%zi", prefix, i);
7572c77eefeSRaphael Isemann    xpc_dictionary_set_string(message, buf, args.GetArgumentAtIndex(i));
7582c77eefeSRaphael Isemann  }
7592c77eefeSRaphael Isemann}
7602c77eefeSRaphael Isemann
7612c77eefeSRaphael Isemannstatic void PackageXPCEnvironment(xpc_object_t message, llvm::StringRef prefix,
7622c77eefeSRaphael Isemann                                  const Environment &env) {
7632c77eefeSRaphael Isemann  xpc_dictionary_set_int64(message, (prefix + "Count").str().c_str(),
7642c77eefeSRaphael Isemann                           env.size());
7652c77eefeSRaphael Isemann  size_t i = 0;
7662c77eefeSRaphael Isemann  for (const auto &KV : env) {
7672c77eefeSRaphael Isemann    xpc_dictionary_set_string(message, (prefix + llvm::Twine(i)).str().c_str(),
7682c77eefeSRaphael Isemann                              Environment::compose(KV).c_str());
7692c77eefeSRaphael Isemann  }
7702c77eefeSRaphael Isemann}
7712c77eefeSRaphael Isemann
7722c77eefeSRaphael Isemann/*
7732c77eefeSRaphael Isemann A valid authorizationRef means that
7742c77eefeSRaphael Isemann    - there is the LaunchUsingXPCRightName rights in the /etc/authorization
7752c77eefeSRaphael Isemann    - we have successfully copied the rights to be send over the XPC wire
7762c77eefeSRaphael Isemann Once obtained, it will be valid for as long as the process lives.
7772c77eefeSRaphael Isemann */
7782c77eefeSRaphael Isemannstatic AuthorizationRef authorizationRef = NULL;
7792c77eefeSRaphael Isemannstatic Status getXPCAuthorization(ProcessLaunchInfo &launch_info) {
7802c77eefeSRaphael Isemann  Status error;
781a007a6d8SPavel Labath  Log *log(GetLog(LLDBLog::Host | LLDBLog::Process));
7822c77eefeSRaphael Isemann
7832c77eefeSRaphael Isemann  if ((launch_info.GetUserID() == 0) && !authorizationRef) {
7842c77eefeSRaphael Isemann    OSStatus createStatus =
7852c77eefeSRaphael Isemann        AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment,
7862c77eefeSRaphael Isemann                            kAuthorizationFlagDefaults, &authorizationRef);
7872c77eefeSRaphael Isemann    if (createStatus != errAuthorizationSuccess) {
7880642cd76SAdrian Prantl      error = Status(1, eErrorTypeGeneric);
7890642cd76SAdrian Prantl      error = Status::FromErrorString("Can't create authorizationRef.");
7902c77eefeSRaphael Isemann      LLDB_LOG(log, "error: {0}", error);
7912c77eefeSRaphael Isemann      return error;
7922c77eefeSRaphael Isemann    }
7932c77eefeSRaphael Isemann
7942c77eefeSRaphael Isemann    OSStatus rightsStatus =
7952c77eefeSRaphael Isemann        AuthorizationRightGet(LaunchUsingXPCRightName, NULL);
7962c77eefeSRaphael Isemann    if (rightsStatus != errAuthorizationSuccess) {
7972c77eefeSRaphael Isemann      // No rights in the security database, Create it with the right prompt.
7982c77eefeSRaphael Isemann      CFStringRef prompt =
7992c77eefeSRaphael Isemann          CFSTR("Xcode is trying to take control of a root process.");
8002c77eefeSRaphael Isemann      CFStringRef keys[] = {CFSTR("en")};
8012c77eefeSRaphael Isemann      CFTypeRef values[] = {prompt};
8022c77eefeSRaphael Isemann      CFDictionaryRef promptDict = CFDictionaryCreate(
8032c77eefeSRaphael Isemann          kCFAllocatorDefault, (const void **)keys, (const void **)values, 1,
8042c77eefeSRaphael Isemann          &kCFCopyStringDictionaryKeyCallBacks,
8052c77eefeSRaphael Isemann          &kCFTypeDictionaryValueCallBacks);
8062c77eefeSRaphael Isemann
8072c77eefeSRaphael Isemann      CFStringRef keys1[] = {CFSTR("class"), CFSTR("group"), CFSTR("comment"),
8082c77eefeSRaphael Isemann                             CFSTR("default-prompt"), CFSTR("shared")};
8092c77eefeSRaphael Isemann      CFTypeRef values1[] = {CFSTR("user"), CFSTR("admin"),
8102c77eefeSRaphael Isemann                             CFSTR(LaunchUsingXPCRightName), promptDict,
8112c77eefeSRaphael Isemann                             kCFBooleanFalse};
8122c77eefeSRaphael Isemann      CFDictionaryRef dict = CFDictionaryCreate(
8132c77eefeSRaphael Isemann          kCFAllocatorDefault, (const void **)keys1, (const void **)values1, 5,
8142c77eefeSRaphael Isemann          &kCFCopyStringDictionaryKeyCallBacks,
8152c77eefeSRaphael Isemann          &kCFTypeDictionaryValueCallBacks);
8162c77eefeSRaphael Isemann      rightsStatus = AuthorizationRightSet(
8172c77eefeSRaphael Isemann          authorizationRef, LaunchUsingXPCRightName, dict, NULL, NULL, NULL);
8182c77eefeSRaphael Isemann      CFRelease(promptDict);
8192c77eefeSRaphael Isemann      CFRelease(dict);
8202c77eefeSRaphael Isemann    }
8212c77eefeSRaphael Isemann
8222c77eefeSRaphael Isemann    OSStatus copyRightStatus = errAuthorizationDenied;
8232c77eefeSRaphael Isemann    if (rightsStatus == errAuthorizationSuccess) {
8242c77eefeSRaphael Isemann      AuthorizationItem item1 = {LaunchUsingXPCRightName, 0, NULL, 0};
8252c77eefeSRaphael Isemann      AuthorizationItem items[] = {item1};
8262c77eefeSRaphael Isemann      AuthorizationRights requestedRights = {1, items};
8272c77eefeSRaphael Isemann      AuthorizationFlags authorizationFlags =
8282c77eefeSRaphael Isemann          kAuthorizationFlagInteractionAllowed | kAuthorizationFlagExtendRights;
8292c77eefeSRaphael Isemann      copyRightStatus = AuthorizationCopyRights(
8302c77eefeSRaphael Isemann          authorizationRef, &requestedRights, kAuthorizationEmptyEnvironment,
8312c77eefeSRaphael Isemann          authorizationFlags, NULL);
8322c77eefeSRaphael Isemann    }
8332c77eefeSRaphael Isemann
8342c77eefeSRaphael Isemann    if (copyRightStatus != errAuthorizationSuccess) {
8352c77eefeSRaphael Isemann      // Eventually when the commandline supports running as root and the user
8362c77eefeSRaphael Isemann      // is not
837678e3ee1SFangrui Song      // logged in to the current audit session, we will need the trick in gdb
8382c77eefeSRaphael Isemann      // where
8392c77eefeSRaphael Isemann      // we ask the user to type in the root passwd in the terminal.
8400642cd76SAdrian Prantl      error = Status(2, eErrorTypeGeneric);
8410642cd76SAdrian Prantl      error = Status::FromErrorStringWithFormat(
8422c77eefeSRaphael Isemann          "Launching as root needs root authorization.");
8432c77eefeSRaphael Isemann      LLDB_LOG(log, "error: {0}", error);
8442c77eefeSRaphael Isemann
8452c77eefeSRaphael Isemann      if (authorizationRef) {
8462c77eefeSRaphael Isemann        AuthorizationFree(authorizationRef, kAuthorizationFlagDefaults);
8472c77eefeSRaphael Isemann        authorizationRef = NULL;
8482c77eefeSRaphael Isemann      }
8492c77eefeSRaphael Isemann    }
8502c77eefeSRaphael Isemann  }
8512c77eefeSRaphael Isemann
8522c77eefeSRaphael Isemann  return error;
8532c77eefeSRaphael Isemann}
8542c77eefeSRaphael Isemann#endif
8552c77eefeSRaphael Isemann
8562c77eefeSRaphael Isemannstatic short GetPosixspawnFlags(const ProcessLaunchInfo &launch_info) {
8572c77eefeSRaphael Isemann  short flags = POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK;
8582c77eefeSRaphael Isemann
8592c77eefeSRaphael Isemann  if (launch_info.GetFlags().Test(eLaunchFlagExec))
8602c77eefeSRaphael Isemann    flags |= POSIX_SPAWN_SETEXEC; // Darwin specific posix_spawn flag
8612c77eefeSRaphael Isemann
8622c77eefeSRaphael Isemann  if (launch_info.GetFlags().Test(eLaunchFlagDebug))
8632c77eefeSRaphael Isemann    flags |= POSIX_SPAWN_START_SUSPENDED; // Darwin specific posix_spawn flag
8642c77eefeSRaphael Isemann
8652c77eefeSRaphael Isemann  if (launch_info.GetFlags().Test(eLaunchFlagDisableASLR))
8662c77eefeSRaphael Isemann    flags |= _POSIX_SPAWN_DISABLE_ASLR; // Darwin specific posix_spawn flag
8672c77eefeSRaphael Isemann
8682c77eefeSRaphael Isemann  if (launch_info.GetLaunchInSeparateProcessGroup())
8692c77eefeSRaphael Isemann    flags |= POSIX_SPAWN_SETPGROUP;
8702c77eefeSRaphael Isemann
8712c77eefeSRaphael Isemann#ifdef POSIX_SPAWN_CLOEXEC_DEFAULT
8722c77eefeSRaphael Isemann#if defined(__x86_64__) || defined(__i386__)
8732c77eefeSRaphael Isemann  static LazyBool g_use_close_on_exec_flag = eLazyBoolCalculate;
8742c77eefeSRaphael Isemann  if (g_use_close_on_exec_flag == eLazyBoolCalculate) {
8752c77eefeSRaphael Isemann    g_use_close_on_exec_flag = eLazyBoolNo;
8762c77eefeSRaphael Isemann
8776682979cSPavel Labath    llvm::VersionTuple version = HostInfo::GetOSVersion();
8786682979cSPavel Labath    if (version > llvm::VersionTuple(10, 7)) {
8792c77eefeSRaphael Isemann      // Kernel panic if we use the POSIX_SPAWN_CLOEXEC_DEFAULT on 10.7 or
8802c77eefeSRaphael Isemann      // earlier
8812c77eefeSRaphael Isemann      g_use_close_on_exec_flag = eLazyBoolYes;
8822c77eefeSRaphael Isemann    }
8832c77eefeSRaphael Isemann  }
8842c77eefeSRaphael Isemann#else
8852c77eefeSRaphael Isemann  static LazyBool g_use_close_on_exec_flag = eLazyBoolYes;
8862c77eefeSRaphael Isemann#endif // defined(__x86_64__) || defined(__i386__)
8872c77eefeSRaphael Isemann  // Close all files exception those with file actions if this is supported.
8882c77eefeSRaphael Isemann  if (g_use_close_on_exec_flag == eLazyBoolYes)
8892c77eefeSRaphael Isemann    flags |= POSIX_SPAWN_CLOEXEC_DEFAULT;
8902c77eefeSRaphael Isemann#endif // ifdef POSIX_SPAWN_CLOEXEC_DEFAULT
8912c77eefeSRaphael Isemann  return flags;
8922c77eefeSRaphael Isemann}
8932c77eefeSRaphael Isemann
894*c7605bfdSJonas Devliegherestatic void finalize_xpc(void *xpc_object) {
895*c7605bfdSJonas Devlieghere  xpc_release((xpc_object_t)xpc_object);
896*c7605bfdSJonas Devlieghere}
897*c7605bfdSJonas Devlieghere
8982c77eefeSRaphael Isemannstatic Status LaunchProcessXPC(const char *exe_path,
8992c77eefeSRaphael Isemann                               ProcessLaunchInfo &launch_info,
9002c77eefeSRaphael Isemann                               lldb::pid_t &pid) {
901f203100eSVedant Kumar#if TARGET_OS_OSX
9022c77eefeSRaphael Isemann  Status error = getXPCAuthorization(launch_info);
9032c77eefeSRaphael Isemann  if (error.Fail())
9042c77eefeSRaphael Isemann    return error;
9052c77eefeSRaphael Isemann
906a007a6d8SPavel Labath  Log *log(GetLog(LLDBLog::Host | LLDBLog::Process));
9072c77eefeSRaphael Isemann
9082c77eefeSRaphael Isemann  uid_t requested_uid = launch_info.GetUserID();
9092c77eefeSRaphael Isemann  const char *xpc_service = nil;
9102c77eefeSRaphael Isemann  bool send_auth = false;
9112c77eefeSRaphael Isemann  AuthorizationExternalForm extForm;
9122c77eefeSRaphael Isemann  if (requested_uid == 0) {
9132c77eefeSRaphael Isemann    if (AuthorizationMakeExternalForm(authorizationRef, &extForm) ==
9142c77eefeSRaphael Isemann        errAuthorizationSuccess) {
9152c77eefeSRaphael Isemann      send_auth = true;
9162c77eefeSRaphael Isemann    } else {
9170642cd76SAdrian Prantl      error = Status(3, eErrorTypeGeneric);
9180642cd76SAdrian Prantl      error = Status::FromErrorStringWithFormat(
9190642cd76SAdrian Prantl          "Launching root via XPC needs to "
9202c77eefeSRaphael Isemann          "externalize authorization reference.");
9212c77eefeSRaphael Isemann      LLDB_LOG(log, "error: {0}", error);
9222c77eefeSRaphael Isemann      return error;
9232c77eefeSRaphael Isemann    }
9242c77eefeSRaphael Isemann    xpc_service = LaunchUsingXPCRightName;
9252c77eefeSRaphael Isemann  } else {
9260642cd76SAdrian Prantl    error = Status(4, eErrorTypeGeneric);
9270642cd76SAdrian Prantl    error = Status::FromErrorStringWithFormat(
9282c77eefeSRaphael Isemann        "Launching via XPC is only currently available for root.");
9292c77eefeSRaphael Isemann    LLDB_LOG(log, "error: {0}", error);
9302c77eefeSRaphael Isemann    return error;
9312c77eefeSRaphael Isemann  }
9322c77eefeSRaphael Isemann
9332c77eefeSRaphael Isemann  xpc_connection_t conn = xpc_connection_create(xpc_service, NULL);
9342c77eefeSRaphael Isemann
9352c77eefeSRaphael Isemann  xpc_connection_set_event_handler(conn, ^(xpc_object_t event) {
9362c77eefeSRaphael Isemann    xpc_type_t type = xpc_get_type(event);
9372c77eefeSRaphael Isemann
9382c77eefeSRaphael Isemann    if (type == XPC_TYPE_ERROR) {
9392c77eefeSRaphael Isemann      if (event == XPC_ERROR_CONNECTION_INTERRUPTED) {
9402c77eefeSRaphael Isemann        // The service has either canceled itself, crashed, or been terminated.
9412c77eefeSRaphael Isemann        // The XPC connection is still valid and sending a message to it will
9422c77eefeSRaphael Isemann        // re-launch the service.
9432c77eefeSRaphael Isemann        // If the service is state-full, this is the time to initialize the new
9442c77eefeSRaphael Isemann        // service.
9452c77eefeSRaphael Isemann        return;
9462c77eefeSRaphael Isemann      } else if (event == XPC_ERROR_CONNECTION_INVALID) {
9472c77eefeSRaphael Isemann        // The service is invalid. Either the service name supplied to
9482c77eefeSRaphael Isemann        // xpc_connection_create() is incorrect
9492c77eefeSRaphael Isemann        // or we (this process) have canceled the service; we can do any cleanup
9502c77eefeSRaphael Isemann        // of application state at this point.
9512c77eefeSRaphael Isemann        // printf("Service disconnected");
9522c77eefeSRaphael Isemann        return;
9532c77eefeSRaphael Isemann      } else {
9542c77eefeSRaphael Isemann        // printf("Unexpected error from service: %s",
9552c77eefeSRaphael Isemann        // xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION));
9562c77eefeSRaphael Isemann      }
9572c77eefeSRaphael Isemann
9582c77eefeSRaphael Isemann    } else {
9592c77eefeSRaphael Isemann      // printf("Received unexpected event in handler");
9602c77eefeSRaphael Isemann    }
9612c77eefeSRaphael Isemann  });
9622c77eefeSRaphael Isemann
963*c7605bfdSJonas Devlieghere  xpc_connection_set_finalizer_f(conn, finalize_xpc);
9642c77eefeSRaphael Isemann  xpc_connection_resume(conn);
9652c77eefeSRaphael Isemann  xpc_object_t message = xpc_dictionary_create(nil, nil, 0);
9662c77eefeSRaphael Isemann
9672c77eefeSRaphael Isemann  if (send_auth) {
9682c77eefeSRaphael Isemann    xpc_dictionary_set_data(message, LauncherXPCServiceAuthKey, extForm.bytes,
9692c77eefeSRaphael Isemann                            sizeof(AuthorizationExternalForm));
9702c77eefeSRaphael Isemann  }
9712c77eefeSRaphael Isemann
9722c77eefeSRaphael Isemann  PackageXPCArguments(message, LauncherXPCServiceArgPrefxKey,
9732c77eefeSRaphael Isemann                      launch_info.GetArguments());
9742c77eefeSRaphael Isemann  PackageXPCEnvironment(message, LauncherXPCServiceEnvPrefxKey,
9752c77eefeSRaphael Isemann                        launch_info.GetEnvironment());
9762c77eefeSRaphael Isemann
9772c77eefeSRaphael Isemann  // Posix spawn stuff.
9782c77eefeSRaphael Isemann  xpc_dictionary_set_int64(message, LauncherXPCServiceCPUTypeKey,
9792c77eefeSRaphael Isemann                           launch_info.GetArchitecture().GetMachOCPUType());
9802c77eefeSRaphael Isemann  xpc_dictionary_set_int64(message, LauncherXPCServicePosixspawnFlagsKey,
9812c77eefeSRaphael Isemann                           GetPosixspawnFlags(launch_info));
9822c77eefeSRaphael Isemann  const FileAction *file_action = launch_info.GetFileActionForFD(STDIN_FILENO);
9832c77eefeSRaphael Isemann  if (file_action && !file_action->GetPath().empty()) {
9842c77eefeSRaphael Isemann    xpc_dictionary_set_string(message, LauncherXPCServiceStdInPathKeyKey,
9852c77eefeSRaphael Isemann                              file_action->GetPath().str().c_str());
9862c77eefeSRaphael Isemann  }
9872c77eefeSRaphael Isemann  file_action = launch_info.GetFileActionForFD(STDOUT_FILENO);
9882c77eefeSRaphael Isemann  if (file_action && !file_action->GetPath().empty()) {
9892c77eefeSRaphael Isemann    xpc_dictionary_set_string(message, LauncherXPCServiceStdOutPathKeyKey,
9902c77eefeSRaphael Isemann                              file_action->GetPath().str().c_str());
9912c77eefeSRaphael Isemann  }
9922c77eefeSRaphael Isemann  file_action = launch_info.GetFileActionForFD(STDERR_FILENO);
9932c77eefeSRaphael Isemann  if (file_action && !file_action->GetPath().empty()) {
9942c77eefeSRaphael Isemann    xpc_dictionary_set_string(message, LauncherXPCServiceStdErrPathKeyKey,
9952c77eefeSRaphael Isemann                              file_action->GetPath().str().c_str());
9962c77eefeSRaphael Isemann  }
9972c77eefeSRaphael Isemann
9982c77eefeSRaphael Isemann  xpc_object_t reply =
9992c77eefeSRaphael Isemann      xpc_connection_send_message_with_reply_sync(conn, message);
10002c77eefeSRaphael Isemann  xpc_type_t returnType = xpc_get_type(reply);
10012c77eefeSRaphael Isemann  if (returnType == XPC_TYPE_DICTIONARY) {
10022c77eefeSRaphael Isemann    pid = xpc_dictionary_get_int64(reply, LauncherXPCServiceChildPIDKey);
10032c77eefeSRaphael Isemann    if (pid == 0) {
10042c77eefeSRaphael Isemann      int errorType =
10052c77eefeSRaphael Isemann          xpc_dictionary_get_int64(reply, LauncherXPCServiceErrorTypeKey);
10062c77eefeSRaphael Isemann      int errorCode =
10072c77eefeSRaphael Isemann          xpc_dictionary_get_int64(reply, LauncherXPCServiceCodeTypeKey);
10082c77eefeSRaphael Isemann
10090642cd76SAdrian Prantl      error = Status(errorCode, eErrorTypeGeneric);
10100642cd76SAdrian Prantl      error = Status::FromErrorStringWithFormat(
10112c77eefeSRaphael Isemann          "Problems with launching via XPC. Error type : %i, code : %i",
10122c77eefeSRaphael Isemann          errorType, errorCode);
10132c77eefeSRaphael Isemann      LLDB_LOG(log, "error: {0}", error);
10142c77eefeSRaphael Isemann
10152c77eefeSRaphael Isemann      if (authorizationRef) {
10162c77eefeSRaphael Isemann        AuthorizationFree(authorizationRef, kAuthorizationFlagDefaults);
10172c77eefeSRaphael Isemann        authorizationRef = NULL;
10182c77eefeSRaphael Isemann      }
10192c77eefeSRaphael Isemann    }
10202c77eefeSRaphael Isemann  } else if (returnType == XPC_TYPE_ERROR) {
10210642cd76SAdrian Prantl    error = Status(5, eErrorTypeGeneric);
10220642cd76SAdrian Prantl    error = Status::FromErrorStringWithFormat(
10232c77eefeSRaphael Isemann        "Problems with launching via XPC. XPC error : %s",
10242c77eefeSRaphael Isemann        xpc_dictionary_get_string(reply, XPC_ERROR_KEY_DESCRIPTION));
10252c77eefeSRaphael Isemann    LLDB_LOG(log, "error: {0}", error);
10262c77eefeSRaphael Isemann  }
10272c77eefeSRaphael Isemann
10282c77eefeSRaphael Isemann  return error;
10292c77eefeSRaphael Isemann#else
10302c77eefeSRaphael Isemann  Status error;
10312c77eefeSRaphael Isemann  return error;
10322c77eefeSRaphael Isemann#endif
10332c77eefeSRaphael Isemann}
10342c77eefeSRaphael Isemann
10352c77eefeSRaphael Isemannstatic bool AddPosixSpawnFileAction(void *_file_actions, const FileAction *info,
10362c77eefeSRaphael Isemann                                    Log *log, Status &error) {
10372c77eefeSRaphael Isemann  if (info == NULL)
10382c77eefeSRaphael Isemann    return false;
10392c77eefeSRaphael Isemann
10402c77eefeSRaphael Isemann  posix_spawn_file_actions_t *file_actions =
104165fdb342SRaphael Isemann      static_cast<posix_spawn_file_actions_t *>(_file_actions);
10422c77eefeSRaphael Isemann
10432c77eefeSRaphael Isemann  switch (info->GetAction()) {
10442c77eefeSRaphael Isemann  case FileAction::eFileActionNone:
10452c77eefeSRaphael Isemann    error.Clear();
10462c77eefeSRaphael Isemann    break;
10472c77eefeSRaphael Isemann
10482c77eefeSRaphael Isemann  case FileAction::eFileActionClose:
10492c77eefeSRaphael Isemann    if (info->GetFD() == -1)
10500642cd76SAdrian Prantl      error = Status::FromErrorString(
10512c77eefeSRaphael Isemann          "invalid fd for posix_spawn_file_actions_addclose(...)");
10522c77eefeSRaphael Isemann    else {
10530642cd76SAdrian Prantl      error = Status(
10542c77eefeSRaphael Isemann          ::posix_spawn_file_actions_addclose(file_actions, info->GetFD()),
10552c77eefeSRaphael Isemann          eErrorTypePOSIX);
10562c77eefeSRaphael Isemann      if (error.Fail())
10572c77eefeSRaphael Isemann        LLDB_LOG(log,
10582c77eefeSRaphael Isemann                 "error: {0}, posix_spawn_file_actions_addclose "
10592c77eefeSRaphael Isemann                 "(action={1}, fd={2})",
10602c77eefeSRaphael Isemann                 error, file_actions, info->GetFD());
10612c77eefeSRaphael Isemann    }
10622c77eefeSRaphael Isemann    break;
10632c77eefeSRaphael Isemann
10642c77eefeSRaphael Isemann  case FileAction::eFileActionDuplicate:
10652c77eefeSRaphael Isemann    if (info->GetFD() == -1)
10660642cd76SAdrian Prantl      error = Status::FromErrorString(
10672c77eefeSRaphael Isemann          "invalid fd for posix_spawn_file_actions_adddup2(...)");
10682c77eefeSRaphael Isemann    else if (info->GetActionArgument() == -1)
10690642cd76SAdrian Prantl      error = Status::FromErrorString(
10702c77eefeSRaphael Isemann          "invalid duplicate fd for posix_spawn_file_actions_adddup2(...)");
10712c77eefeSRaphael Isemann    else {
10720642cd76SAdrian Prantl      error =
10730642cd76SAdrian Prantl          Status(::posix_spawn_file_actions_adddup2(file_actions, info->GetFD(),
10742c77eefeSRaphael Isemann                                                    info->GetActionArgument()),
10752c77eefeSRaphael Isemann                 eErrorTypePOSIX);
10762c77eefeSRaphael Isemann      if (error.Fail())
10772c77eefeSRaphael Isemann        LLDB_LOG(log,
10782c77eefeSRaphael Isemann                 "error: {0}, posix_spawn_file_actions_adddup2 "
10792c77eefeSRaphael Isemann                 "(action={1}, fd={2}, dup_fd={3})",
10802c77eefeSRaphael Isemann                 error, file_actions, info->GetFD(), info->GetActionArgument());
10812c77eefeSRaphael Isemann    }
10822c77eefeSRaphael Isemann    break;
10832c77eefeSRaphael Isemann
10842c77eefeSRaphael Isemann  case FileAction::eFileActionOpen:
10852c77eefeSRaphael Isemann    if (info->GetFD() == -1)
10860642cd76SAdrian Prantl      error = Status::FromErrorString(
10872c77eefeSRaphael Isemann          "invalid fd in posix_spawn_file_actions_addopen(...)");
10882c77eefeSRaphael Isemann    else {
10892c77eefeSRaphael Isemann      int oflag = info->GetActionArgument();
10902c77eefeSRaphael Isemann
10912c77eefeSRaphael Isemann      mode_t mode = 0;
10922c77eefeSRaphael Isemann
10932c77eefeSRaphael Isemann      if (oflag & O_CREAT)
10942c77eefeSRaphael Isemann        mode = 0640;
10952c77eefeSRaphael Isemann
10960642cd76SAdrian Prantl      error = Status(::posix_spawn_file_actions_addopen(
10972c77eefeSRaphael Isemann                         file_actions, info->GetFD(),
10982c77eefeSRaphael Isemann                         info->GetPath().str().c_str(), oflag, mode),
10992c77eefeSRaphael Isemann                     eErrorTypePOSIX);
11002c77eefeSRaphael Isemann      if (error.Fail())
11012c77eefeSRaphael Isemann        LLDB_LOG(log,
11022c77eefeSRaphael Isemann                 "error: {0}, posix_spawn_file_actions_addopen (action={1}, "
11032c77eefeSRaphael Isemann                 "fd={2}, path='{3}', oflag={4}, mode={5})",
11042c77eefeSRaphael Isemann                 error, file_actions, info->GetFD(), info->GetPath(), oflag,
11052c77eefeSRaphael Isemann                 mode);
11062c77eefeSRaphael Isemann    }
11072c77eefeSRaphael Isemann    break;
11082c77eefeSRaphael Isemann  }
11092c77eefeSRaphael Isemann  return error.Success();
11102c77eefeSRaphael Isemann}
11112c77eefeSRaphael Isemann
11122c77eefeSRaphael Isemannstatic Status LaunchProcessPosixSpawn(const char *exe_path,
11132c77eefeSRaphael Isemann                                      const ProcessLaunchInfo &launch_info,
11142c77eefeSRaphael Isemann                                      lldb::pid_t &pid) {
11152c77eefeSRaphael Isemann  Status error;
1116a007a6d8SPavel Labath  Log *log(GetLog(LLDBLog::Host | LLDBLog::Process));
11172c77eefeSRaphael Isemann
11182c77eefeSRaphael Isemann  posix_spawnattr_t attr;
11190642cd76SAdrian Prantl  error = Status(::posix_spawnattr_init(&attr), eErrorTypePOSIX);
11202c77eefeSRaphael Isemann
11212c77eefeSRaphael Isemann  if (error.Fail()) {
11222c77eefeSRaphael Isemann    LLDB_LOG(log, "error: {0}, ::posix_spawnattr_init ( &attr )", error);
11232c77eefeSRaphael Isemann    return error;
11242c77eefeSRaphael Isemann  }
11252c77eefeSRaphael Isemann
11262c77eefeSRaphael Isemann  // Make sure we clean up the posix spawn attributes before exiting this scope.
1127e0ea8d87SJonas Devlieghere  auto cleanup_attr =
1128e0ea8d87SJonas Devlieghere      llvm::make_scope_exit([&]() { posix_spawnattr_destroy(&attr); });
11292c77eefeSRaphael Isemann
11302c77eefeSRaphael Isemann  sigset_t no_signals;
11312c77eefeSRaphael Isemann  sigset_t all_signals;
11322c77eefeSRaphael Isemann  sigemptyset(&no_signals);
11332c77eefeSRaphael Isemann  sigfillset(&all_signals);
11342c77eefeSRaphael Isemann  ::posix_spawnattr_setsigmask(&attr, &no_signals);
11352c77eefeSRaphael Isemann  ::posix_spawnattr_setsigdefault(&attr, &all_signals);
11362c77eefeSRaphael Isemann
11372c77eefeSRaphael Isemann  short flags = GetPosixspawnFlags(launch_info);
11382c77eefeSRaphael Isemann
11390642cd76SAdrian Prantl  error = Status(::posix_spawnattr_setflags(&attr, flags), eErrorTypePOSIX);
11402c77eefeSRaphael Isemann  if (error.Fail()) {
11412c77eefeSRaphael Isemann    LLDB_LOG(log,
11422c77eefeSRaphael Isemann             "error: {0}, ::posix_spawnattr_setflags ( &attr, flags={1:x} )",
11432c77eefeSRaphael Isemann             error, flags);
11442c77eefeSRaphael Isemann    return error;
11452c77eefeSRaphael Isemann  }
11462c77eefeSRaphael Isemann
1147041c7b84SJonas Devlieghere  bool is_graphical = true;
1148041c7b84SJonas Devlieghere
1149041c7b84SJonas Devlieghere#if TARGET_OS_OSX
1150041c7b84SJonas Devlieghere  SecuritySessionId session_id;
1151041c7b84SJonas Devlieghere  SessionAttributeBits session_attributes;
1152041c7b84SJonas Devlieghere  OSStatus status =
1153041c7b84SJonas Devlieghere      SessionGetInfo(callerSecuritySession, &session_id, &session_attributes);
1154041c7b84SJonas Devlieghere  if (status == errSessionSuccess)
1155041c7b84SJonas Devlieghere    is_graphical = session_attributes & sessionHasGraphicAccess;
1156041c7b84SJonas Devlieghere#endif
1157041c7b84SJonas Devlieghere
1158249a1d4fSJonas Devlieghere  //  When lldb is ran through a graphical session, make the debuggee process
1159249a1d4fSJonas Devlieghere  //  responsible for its own TCC permissions instead of inheriting them from
1160249a1d4fSJonas Devlieghere  //  its parent.
1161249a1d4fSJonas Devlieghere  if (is_graphical && launch_info.GetFlags().Test(eLaunchFlagDebug) &&
1162249a1d4fSJonas Devlieghere      !launch_info.GetFlags().Test(eLaunchFlagInheritTCCFromParent)) {
11630642cd76SAdrian Prantl    error = Status(setup_posix_spawn_responsible_flag(&attr), eErrorTypePOSIX);
1164041c7b84SJonas Devlieghere    if (error.Fail()) {
1165041c7b84SJonas Devlieghere      LLDB_LOG(log, "error: {0}, setup_posix_spawn_responsible_flag(&attr)",
1166041c7b84SJonas Devlieghere               error);
1167041c7b84SJonas Devlieghere      return error;
1168041c7b84SJonas Devlieghere    }
1169041c7b84SJonas Devlieghere  }
1170041c7b84SJonas Devlieghere
117198e50a7dSJonas Devlieghere  // Don't set the binpref if a shell was provided. After all, that's only
117298e50a7dSJonas Devlieghere  // going to affect what version of the shell is launched, not what fork of
117398e50a7dSJonas Devlieghere  // the binary is launched.  We insert "arch --arch <ARCH> as part of the
117498e50a7dSJonas Devlieghere  // shell invocation to do that job on OSX.
117598e50a7dSJonas Devlieghere  if (launch_info.GetShell() == FileSpec()) {
117698e50a7dSJonas Devlieghere    const ArchSpec &arch_spec = launch_info.GetArchitecture();
117798e50a7dSJonas Devlieghere    cpu_type_t cpu_type = arch_spec.GetMachOCPUType();
117898e50a7dSJonas Devlieghere    cpu_type_t cpu_subtype = arch_spec.GetMachOCPUSubType();
117998e50a7dSJonas Devlieghere    const bool set_cpu_type =
118098e50a7dSJonas Devlieghere        cpu_type != 0 && cpu_type != static_cast<cpu_type_t>(UINT32_MAX) &&
118198e50a7dSJonas Devlieghere        cpu_type != static_cast<cpu_type_t>(LLDB_INVALID_CPUTYPE);
118298e50a7dSJonas Devlieghere    const bool set_cpu_subtype =
118398e50a7dSJonas Devlieghere        cpu_subtype != 0 &&
118498e50a7dSJonas Devlieghere        cpu_subtype != static_cast<cpu_subtype_t>(UINT32_MAX) &&
118598e50a7dSJonas Devlieghere        cpu_subtype != CPU_SUBTYPE_X86_64_H;
118698e50a7dSJonas Devlieghere    if (set_cpu_type) {
118798e50a7dSJonas Devlieghere      size_t ocount = 0;
118898e50a7dSJonas Devlieghere      typedef int (*posix_spawnattr_setarchpref_np_t)(
118998e50a7dSJonas Devlieghere          posix_spawnattr_t *, size_t, cpu_type_t *, cpu_subtype_t *, size_t *);
119098e50a7dSJonas Devlieghere      posix_spawnattr_setarchpref_np_t posix_spawnattr_setarchpref_np_fn =
119198e50a7dSJonas Devlieghere          (posix_spawnattr_setarchpref_np_t)dlsym(
119298e50a7dSJonas Devlieghere              RTLD_DEFAULT, "posix_spawnattr_setarchpref_np");
119398e50a7dSJonas Devlieghere      if (set_cpu_subtype && posix_spawnattr_setarchpref_np_fn) {
11940642cd76SAdrian Prantl        error = Status((*posix_spawnattr_setarchpref_np_fn)(
119598e50a7dSJonas Devlieghere                           &attr, 1, &cpu_type, &cpu_subtype, &ocount),
119698e50a7dSJonas Devlieghere                       eErrorTypePOSIX);
119798e50a7dSJonas Devlieghere        if (error.Fail())
119898e50a7dSJonas Devlieghere          LLDB_LOG(log,
119998e50a7dSJonas Devlieghere                   "error: {0}, ::posix_spawnattr_setarchpref_np ( &attr, 1, "
120098e50a7dSJonas Devlieghere                   "cpu_type = {1:x}, cpu_subtype = {1:x}, count => {2} )",
120198e50a7dSJonas Devlieghere                   error, cpu_type, cpu_subtype, ocount);
120298e50a7dSJonas Devlieghere
120398e50a7dSJonas Devlieghere        if (error.Fail() || ocount != 1)
120498e50a7dSJonas Devlieghere          return error;
120598e50a7dSJonas Devlieghere      } else {
12060642cd76SAdrian Prantl        error = Status(
120798e50a7dSJonas Devlieghere            ::posix_spawnattr_setbinpref_np(&attr, 1, &cpu_type, &ocount),
120898e50a7dSJonas Devlieghere            eErrorTypePOSIX);
120998e50a7dSJonas Devlieghere        if (error.Fail())
121098e50a7dSJonas Devlieghere          LLDB_LOG(log,
121198e50a7dSJonas Devlieghere                   "error: {0}, ::posix_spawnattr_setbinpref_np ( &attr, 1, "
121298e50a7dSJonas Devlieghere                   "cpu_type = {1:x}, count => {2} )",
121398e50a7dSJonas Devlieghere                   error, cpu_type, ocount);
121498e50a7dSJonas Devlieghere        if (error.Fail() || ocount != 1)
121598e50a7dSJonas Devlieghere          return error;
121698e50a7dSJonas Devlieghere      }
121798e50a7dSJonas Devlieghere    }
121898e50a7dSJonas Devlieghere  }
121998e50a7dSJonas Devlieghere
12202c77eefeSRaphael Isemann  const char *tmp_argv[2];
12212c77eefeSRaphael Isemann  char *const *argv = const_cast<char *const *>(
12222c77eefeSRaphael Isemann      launch_info.GetArguments().GetConstArgumentVector());
12232c77eefeSRaphael Isemann  Environment::Envp envp = launch_info.GetEnvironment().getEnvp();
12242c77eefeSRaphael Isemann  if (argv == NULL) {
12252c77eefeSRaphael Isemann    // posix_spawn gets very unhappy if it doesn't have at least the program
12262c77eefeSRaphael Isemann    // name in argv[0]. One of the side affects I have noticed is the
12272c77eefeSRaphael Isemann    // environment
12282c77eefeSRaphael Isemann    // variables don't make it into the child process if "argv == NULL"!!!
12292c77eefeSRaphael Isemann    tmp_argv[0] = exe_path;
12302c77eefeSRaphael Isemann    tmp_argv[1] = NULL;
12312c77eefeSRaphael Isemann    argv = const_cast<char *const *>(tmp_argv);
12322c77eefeSRaphael Isemann  }
12332c77eefeSRaphael Isemann
12342c77eefeSRaphael Isemann  FileSpec working_dir{launch_info.GetWorkingDirectory()};
12352c77eefeSRaphael Isemann  if (working_dir) {
12362c77eefeSRaphael Isemann    // Set the working directory on this thread only
1237529a3d87SGreg Clayton    std::string working_dir_path = working_dir.GetPath();
1238529a3d87SGreg Clayton    if (__pthread_chdir(working_dir_path.c_str()) < 0) {
12392c77eefeSRaphael Isemann      if (errno == ENOENT) {
12400642cd76SAdrian Prantl        error = Status::FromErrorStringWithFormat(
12410642cd76SAdrian Prantl            "No such file or directory: %s", working_dir_path.c_str());
12422c77eefeSRaphael Isemann      } else if (errno == ENOTDIR) {
12430642cd76SAdrian Prantl        error = Status::FromErrorStringWithFormat(
12440642cd76SAdrian Prantl            "Path doesn't name a directory: %s", working_dir_path.c_str());
12452c77eefeSRaphael Isemann      } else {
12460642cd76SAdrian Prantl        error =
12470642cd76SAdrian Prantl            Status::FromErrorStringWithFormat("An unknown error occurred when "
12482c77eefeSRaphael Isemann                                              "changing directory for process "
12492c77eefeSRaphael Isemann                                              "execution.");
12502c77eefeSRaphael Isemann      }
12512c77eefeSRaphael Isemann      return error;
12522c77eefeSRaphael Isemann    }
12532c77eefeSRaphael Isemann  }
12542c77eefeSRaphael Isemann
12552c77eefeSRaphael Isemann  ::pid_t result_pid = LLDB_INVALID_PROCESS_ID;
12562c77eefeSRaphael Isemann  const size_t num_file_actions = launch_info.GetNumFileActions();
12572c77eefeSRaphael Isemann  if (num_file_actions > 0) {
12582c77eefeSRaphael Isemann    posix_spawn_file_actions_t file_actions;
12590642cd76SAdrian Prantl    error =
12600642cd76SAdrian Prantl        Status(::posix_spawn_file_actions_init(&file_actions), eErrorTypePOSIX);
12612c77eefeSRaphael Isemann    if (error.Fail()) {
12622c77eefeSRaphael Isemann      LLDB_LOG(log,
12632c77eefeSRaphael Isemann               "error: {0}, ::posix_spawn_file_actions_init ( &file_actions )",
12642c77eefeSRaphael Isemann               error);
12652c77eefeSRaphael Isemann      return error;
12662c77eefeSRaphael Isemann    }
12672c77eefeSRaphael Isemann
12682c77eefeSRaphael Isemann    // Make sure we clean up the posix file actions before exiting this scope.
1269e0ea8d87SJonas Devlieghere    auto cleanup_fileact = llvm::make_scope_exit(
1270e0ea8d87SJonas Devlieghere        [&]() { posix_spawn_file_actions_destroy(&file_actions); });
12712c77eefeSRaphael Isemann
12722c77eefeSRaphael Isemann    for (size_t i = 0; i < num_file_actions; ++i) {
12732c77eefeSRaphael Isemann      const FileAction *launch_file_action =
12742c77eefeSRaphael Isemann          launch_info.GetFileActionAtIndex(i);
12752c77eefeSRaphael Isemann      if (launch_file_action) {
12762c77eefeSRaphael Isemann        if (!AddPosixSpawnFileAction(&file_actions, launch_file_action, log,
12772c77eefeSRaphael Isemann                                     error))
12782c77eefeSRaphael Isemann          return error;
12792c77eefeSRaphael Isemann      }
12802c77eefeSRaphael Isemann    }
12812c77eefeSRaphael Isemann
12820642cd76SAdrian Prantl    error = Status(
12832c77eefeSRaphael Isemann        ::posix_spawnp(&result_pid, exe_path, &file_actions, &attr, argv, envp),
12842c77eefeSRaphael Isemann        eErrorTypePOSIX);
12852c77eefeSRaphael Isemann
12862c77eefeSRaphael Isemann    if (error.Fail()) {
12872c77eefeSRaphael Isemann      LLDB_LOG(log,
12882c77eefeSRaphael Isemann               "error: {0}, ::posix_spawnp(pid => {1}, path = '{2}', "
12892c77eefeSRaphael Isemann               "file_actions = {3}, "
12902c77eefeSRaphael Isemann               "attr = {4}, argv = {5}, envp = {6} )",
12912c77eefeSRaphael Isemann               error, result_pid, exe_path, &file_actions, &attr, argv,
12922c77eefeSRaphael Isemann               envp.get());
12932c77eefeSRaphael Isemann      if (log) {
12942c77eefeSRaphael Isemann        for (int ii = 0; argv[ii]; ++ii)
12952c77eefeSRaphael Isemann          LLDB_LOG(log, "argv[{0}] = '{1}'", ii, argv[ii]);
12962c77eefeSRaphael Isemann      }
12972c77eefeSRaphael Isemann    }
12982c77eefeSRaphael Isemann
12992c77eefeSRaphael Isemann  } else {
13000642cd76SAdrian Prantl    error =
13010642cd76SAdrian Prantl        Status(::posix_spawnp(&result_pid, exe_path, NULL, &attr, argv, envp),
13022c77eefeSRaphael Isemann               eErrorTypePOSIX);
13032c77eefeSRaphael Isemann
13042c77eefeSRaphael Isemann    if (error.Fail()) {
13052c77eefeSRaphael Isemann      LLDB_LOG(log,
13062c77eefeSRaphael Isemann               "error: {0}, ::posix_spawnp ( pid => {1}, path = '{2}', "
13072c77eefeSRaphael Isemann               "file_actions = NULL, attr = {3}, argv = {4}, envp = {5} )",
13082c77eefeSRaphael Isemann               error, result_pid, exe_path, &attr, argv, envp.get());
13092c77eefeSRaphael Isemann      if (log) {
13102c77eefeSRaphael Isemann        for (int ii = 0; argv[ii]; ++ii)
13112c77eefeSRaphael Isemann          LLDB_LOG(log, "argv[{0}] = '{1}'", ii, argv[ii]);
13122c77eefeSRaphael Isemann      }
13132c77eefeSRaphael Isemann    }
13142c77eefeSRaphael Isemann  }
13152c77eefeSRaphael Isemann  pid = result_pid;
13162c77eefeSRaphael Isemann
13172c77eefeSRaphael Isemann  if (working_dir) {
13182c77eefeSRaphael Isemann    // No more thread specific current working directory
13192c77eefeSRaphael Isemann    __pthread_fchdir(-1);
13202c77eefeSRaphael Isemann  }
13212c77eefeSRaphael Isemann
13222c77eefeSRaphael Isemann  return error;
13232c77eefeSRaphael Isemann}
13242c77eefeSRaphael Isemann
13252c77eefeSRaphael Isemannstatic bool ShouldLaunchUsingXPC(ProcessLaunchInfo &launch_info) {
13262c77eefeSRaphael Isemann  bool result = false;
13272c77eefeSRaphael Isemann
1328f203100eSVedant Kumar#if TARGET_OS_OSX
13292c77eefeSRaphael Isemann  bool launchingAsRoot = launch_info.GetUserID() == 0;
13302c77eefeSRaphael Isemann  bool currentUserIsRoot = HostInfo::GetEffectiveUserID() == 0;
13312c77eefeSRaphael Isemann
13322c77eefeSRaphael Isemann  if (launchingAsRoot && !currentUserIsRoot) {
13332c77eefeSRaphael Isemann    // If current user is already root, we don't need XPC's help.
13342c77eefeSRaphael Isemann    result = true;
13352c77eefeSRaphael Isemann  }
13362c77eefeSRaphael Isemann#endif
13372c77eefeSRaphael Isemann
13382c77eefeSRaphael Isemann  return result;
13392c77eefeSRaphael Isemann}
13402c77eefeSRaphael Isemann
13412c77eefeSRaphael IsemannStatus Host::LaunchProcess(ProcessLaunchInfo &launch_info) {
13422c77eefeSRaphael Isemann  Status error;
1343046c3903SJonas Devlieghere
1344046c3903SJonas Devlieghere  FileSystem &fs = FileSystem::Instance();
13452c77eefeSRaphael Isemann  FileSpec exe_spec(launch_info.GetExecutableFile());
13462c77eefeSRaphael Isemann
1347046c3903SJonas Devlieghere  if (!fs.Exists(exe_spec))
13488f3be7a3SJonas Devlieghere    FileSystem::Instance().Resolve(exe_spec);
1349046c3903SJonas Devlieghere
1350046c3903SJonas Devlieghere  if (!fs.Exists(exe_spec))
13512c22c800SJonas Devlieghere    FileSystem::Instance().ResolveExecutableLocation(exe_spec);
1352046c3903SJonas Devlieghere
1353046c3903SJonas Devlieghere  if (!fs.Exists(exe_spec)) {
13540642cd76SAdrian Prantl    error = Status::FromErrorStringWithFormatv(
13550642cd76SAdrian Prantl        "executable doesn't exist: '{0}'", exe_spec);
13562c77eefeSRaphael Isemann    return error;
13572c77eefeSRaphael Isemann  }
13582c77eefeSRaphael Isemann
13592c77eefeSRaphael Isemann  if (launch_info.GetFlags().Test(eLaunchFlagLaunchInTTY)) {
1360f203100eSVedant Kumar#if TARGET_OS_OSX
13612c77eefeSRaphael Isemann    return LaunchInNewTerminalWithAppleScript(exe_spec.GetPath().c_str(),
13622c77eefeSRaphael Isemann                                              launch_info);
13632c77eefeSRaphael Isemann#else
13640642cd76SAdrian Prantl    error =
13650642cd76SAdrian Prantl        Status::FromErrorString("launching a process in a new terminal is not "
13662c77eefeSRaphael Isemann                                "supported on iOS devices");
13672c77eefeSRaphael Isemann    return error;
13682c77eefeSRaphael Isemann#endif
13692c77eefeSRaphael Isemann  }
13702c77eefeSRaphael Isemann
13712c77eefeSRaphael Isemann  lldb::pid_t pid = LLDB_INVALID_PROCESS_ID;
13722c77eefeSRaphael Isemann
137359eb7052SJonas Devlieghere  auto exe_path = exe_spec.GetPath();
137446575176SJonas Devlieghere
137546575176SJonas Devlieghere  if (ShouldLaunchUsingXPC(launch_info))
137659eb7052SJonas Devlieghere    error = LaunchProcessXPC(exe_path.c_str(), launch_info, pid);
137746575176SJonas Devlieghere  else
137859eb7052SJonas Devlieghere    error = LaunchProcessPosixSpawn(exe_path.c_str(), launch_info, pid);
13792c77eefeSRaphael Isemann
13802c77eefeSRaphael Isemann  if (pid != LLDB_INVALID_PROCESS_ID) {
13812c77eefeSRaphael Isemann    // If all went well, then set the process ID into the launch info
13822c77eefeSRaphael Isemann    launch_info.SetProcessID(pid);
13832c77eefeSRaphael Isemann
13842c77eefeSRaphael Isemann    // Make sure we reap any processes we spawn or we will have zombies.
13852c77eefeSRaphael Isemann    bool monitoring = launch_info.MonitorProcess();
13862c77eefeSRaphael Isemann    UNUSED_IF_ASSERT_DISABLED(monitoring);
13872c77eefeSRaphael Isemann    assert(monitoring);
13882c77eefeSRaphael Isemann  } else {
13892c77eefeSRaphael Isemann    // Invalid process ID, something didn't go well
13902c77eefeSRaphael Isemann    if (error.Success())
13910642cd76SAdrian Prantl      error =
13920642cd76SAdrian Prantl          Status::FromErrorString("process launch failed for unknown reasons");
13932c77eefeSRaphael Isemann  }
13942c77eefeSRaphael Isemann  return error;
13952c77eefeSRaphael Isemann}
13962c77eefeSRaphael Isemann
13972c77eefeSRaphael IsemannStatus Host::ShellExpandArguments(ProcessLaunchInfo &launch_info) {
13982c77eefeSRaphael Isemann  Status error;
13992c77eefeSRaphael Isemann  if (launch_info.GetFlags().Test(eLaunchFlagShellExpandArguments)) {
1400f918c056SKeith Smiley    FileSpec expand_tool_spec;
1401f918c056SKeith Smiley    Environment host_env = Host::GetEnvironment();
1402f918c056SKeith Smiley    std::string env_argdumper_path = host_env.lookup("LLDB_ARGDUMPER_PATH");
1403f918c056SKeith Smiley    if (!env_argdumper_path.empty()) {
1404f918c056SKeith Smiley      expand_tool_spec.SetFile(env_argdumper_path, FileSpec::Style::native);
1405f918c056SKeith Smiley      Log *log(GetLog(LLDBLog::Host | LLDBLog::Process));
1406f918c056SKeith Smiley      LLDB_LOGF(log,
1407f918c056SKeith Smiley                "lldb-argdumper exe path set from environment variable: %s",
1408f918c056SKeith Smiley                env_argdumper_path.c_str());
1409f918c056SKeith Smiley    }
1410f918c056SKeith Smiley    bool argdumper_exists = FileSystem::Instance().Exists(env_argdumper_path);
1411f918c056SKeith Smiley    if (!argdumper_exists) {
1412f918c056SKeith Smiley      expand_tool_spec = HostInfo::GetSupportExeDir();
141360f028ffSPavel Labath      if (!expand_tool_spec) {
14140642cd76SAdrian Prantl        error = Status::FromErrorString(
14150642cd76SAdrian Prantl            "could not get support executable directory for "
1416f918c056SKeith Smiley            "lldb-argdumper tool");
14172c77eefeSRaphael Isemann        return error;
14182c77eefeSRaphael Isemann      }
14192c77eefeSRaphael Isemann      expand_tool_spec.AppendPathComponent("lldb-argdumper");
1420dbd7fabaSJonas Devlieghere      if (!FileSystem::Instance().Exists(expand_tool_spec)) {
14210642cd76SAdrian Prantl        error = Status::FromErrorStringWithFormat(
14222c77eefeSRaphael Isemann            "could not find the lldb-argdumper tool: %s",
14232c77eefeSRaphael Isemann            expand_tool_spec.GetPath().c_str());
14242c77eefeSRaphael Isemann        return error;
14252c77eefeSRaphael Isemann      }
1426f918c056SKeith Smiley    }
14272c77eefeSRaphael Isemann
14282c77eefeSRaphael Isemann    StreamString expand_tool_spec_stream;
14292c77eefeSRaphael Isemann    expand_tool_spec_stream.Printf("\"%s\"",
14302c77eefeSRaphael Isemann                                   expand_tool_spec.GetPath().c_str());
14312c77eefeSRaphael Isemann
14322c77eefeSRaphael Isemann    Args expand_command(expand_tool_spec_stream.GetData());
14332c77eefeSRaphael Isemann    expand_command.AppendArguments(launch_info.GetArguments());
14342c77eefeSRaphael Isemann
14352c77eefeSRaphael Isemann    int status;
14362c77eefeSRaphael Isemann    std::string output;
14372c77eefeSRaphael Isemann    FileSpec cwd(launch_info.GetWorkingDirectory());
1438dbd7fabaSJonas Devlieghere    if (!FileSystem::Instance().Exists(cwd)) {
14392c77eefeSRaphael Isemann      char *wd = getcwd(nullptr, 0);
14402c77eefeSRaphael Isemann      if (wd == nullptr) {
14410642cd76SAdrian Prantl        error = Status::FromErrorStringWithFormat(
14422c77eefeSRaphael Isemann            "cwd does not exist; cannot launch with shell argument expansion");
14432c77eefeSRaphael Isemann        return error;
14442c77eefeSRaphael Isemann      } else {
14458f3be7a3SJonas Devlieghere        FileSpec working_dir(wd);
14462c77eefeSRaphael Isemann        free(wd);
14472c77eefeSRaphael Isemann        launch_info.SetWorkingDirectory(working_dir);
14482c77eefeSRaphael Isemann      }
14492c77eefeSRaphael Isemann    }
1450addb5148SMed Ismail Bennani    bool run_in_shell = true;
1451b1a5d7d5SAdrian Prantl    bool hide_stderr = true;
1452addb5148SMed Ismail Bennani    Status e =
1453addb5148SMed Ismail Bennani        RunShellCommand(expand_command, cwd, &status, nullptr, &output,
1454addb5148SMed Ismail Bennani                        std::chrono::seconds(10), run_in_shell, hide_stderr);
14552c77eefeSRaphael Isemann
14562a711814SJonas Devlieghere    if (e.Fail())
14572a711814SJonas Devlieghere      return e;
1458a8c04469SJonas Devlieghere
14592c77eefeSRaphael Isemann    if (status != 0) {
14600642cd76SAdrian Prantl      error = Status::FromErrorStringWithFormat(
14610642cd76SAdrian Prantl          "lldb-argdumper exited with error %d", status);
14622c77eefeSRaphael Isemann      return error;
14632c77eefeSRaphael Isemann    }
14642c77eefeSRaphael Isemann
14652c77eefeSRaphael Isemann    auto data_sp = StructuredData::ParseJSON(output);
14662c77eefeSRaphael Isemann    if (!data_sp) {
14670642cd76SAdrian Prantl      error = Status::FromErrorString("invalid JSON");
14682c77eefeSRaphael Isemann      return error;
14692c77eefeSRaphael Isemann    }
14702c77eefeSRaphael Isemann
14712c77eefeSRaphael Isemann    auto dict_sp = data_sp->GetAsDictionary();
14722c77eefeSRaphael Isemann    if (!data_sp) {
14730642cd76SAdrian Prantl      error = Status::FromErrorString("invalid JSON");
14742c77eefeSRaphael Isemann      return error;
14752c77eefeSRaphael Isemann    }
14762c77eefeSRaphael Isemann
14772c77eefeSRaphael Isemann    auto args_sp = dict_sp->GetObjectForDotSeparatedPath("arguments");
14782c77eefeSRaphael Isemann    if (!args_sp) {
14790642cd76SAdrian Prantl      error = Status::FromErrorString("invalid JSON");
14802c77eefeSRaphael Isemann      return error;
14812c77eefeSRaphael Isemann    }
14822c77eefeSRaphael Isemann
14832c77eefeSRaphael Isemann    auto args_array_sp = args_sp->GetAsArray();
14842c77eefeSRaphael Isemann    if (!args_array_sp) {
14850642cd76SAdrian Prantl      error = Status::FromErrorString("invalid JSON");
14862c77eefeSRaphael Isemann      return error;
14872c77eefeSRaphael Isemann    }
14882c77eefeSRaphael Isemann
14892c77eefeSRaphael Isemann    launch_info.GetArguments().Clear();
14902c77eefeSRaphael Isemann
14912c77eefeSRaphael Isemann    for (size_t i = 0; i < args_array_sp->GetSize(); i++) {
14922c77eefeSRaphael Isemann      auto item_sp = args_array_sp->GetItemAtIndex(i);
14932c77eefeSRaphael Isemann      if (!item_sp)
14942c77eefeSRaphael Isemann        continue;
14952c77eefeSRaphael Isemann      auto str_sp = item_sp->GetAsString();
14962c77eefeSRaphael Isemann      if (!str_sp)
14972c77eefeSRaphael Isemann        continue;
14982c77eefeSRaphael Isemann
14992c77eefeSRaphael Isemann      launch_info.GetArguments().AppendArgument(str_sp->GetValue());
15002c77eefeSRaphael Isemann    }
15012c77eefeSRaphael Isemann  }
15022c77eefeSRaphael Isemann
15032c77eefeSRaphael Isemann  return error;
15042c77eefeSRaphael Isemann}
15052c77eefeSRaphael Isemann
15062734f5c8SJonas Devliegherellvm::Expected<HostThread> Host::StartMonitoringChildProcess(
150712c9c4a8SPavel Labath    const Host::MonitorChildProcessCallback &callback, lldb::pid_t pid) {
15082c77eefeSRaphael Isemann  unsigned long mask = DISPATCH_PROC_EXIT;
15092c77eefeSRaphael Isemann
1510a007a6d8SPavel Labath  Log *log(GetLog(LLDBLog::Host | LLDBLog::Process));
15112c77eefeSRaphael Isemann
15122c77eefeSRaphael Isemann  dispatch_source_t source = ::dispatch_source_create(
15132c77eefeSRaphael Isemann      DISPATCH_SOURCE_TYPE_PROC, pid, mask,
15142c77eefeSRaphael Isemann      ::dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
15152c77eefeSRaphael Isemann
151663e5fb76SJonas Devlieghere  LLDB_LOGF(log,
151712c9c4a8SPavel Labath            "Host::StartMonitoringChildProcess(callback, pid=%i) source = %p\n",
151812c9c4a8SPavel Labath            static_cast<int>(pid), static_cast<void *>(source));
15192c77eefeSRaphael Isemann
15202c77eefeSRaphael Isemann  if (source) {
15212c77eefeSRaphael Isemann    Host::MonitorChildProcessCallback callback_copy = callback;
15222c77eefeSRaphael Isemann    ::dispatch_source_set_cancel_handler(source, ^{
15232c77eefeSRaphael Isemann      dispatch_release(source);
15242c77eefeSRaphael Isemann    });
15252c77eefeSRaphael Isemann    ::dispatch_source_set_event_handler(source, ^{
15262c77eefeSRaphael Isemann
15272c77eefeSRaphael Isemann      int status = 0;
15282c77eefeSRaphael Isemann      int wait_pid = 0;
15292c77eefeSRaphael Isemann      wait_pid = llvm::sys::RetryAfterSignal(-1, ::waitpid, pid, &status, 0);
15302c77eefeSRaphael Isemann      if (wait_pid >= 0) {
15312c77eefeSRaphael Isemann        int signal = 0;
15322c77eefeSRaphael Isemann        int exit_status = 0;
15332c77eefeSRaphael Isemann        const char *status_cstr = NULL;
153412c9c4a8SPavel Labath        if (WIFEXITED(status)) {
15352c77eefeSRaphael Isemann          exit_status = WEXITSTATUS(status);
15362c77eefeSRaphael Isemann          status_cstr = "EXITED";
15372c77eefeSRaphael Isemann        } else if (WIFSIGNALED(status)) {
15382c77eefeSRaphael Isemann          signal = WTERMSIG(status);
15392c77eefeSRaphael Isemann          status_cstr = "SIGNALED";
15402c77eefeSRaphael Isemann          exit_status = -1;
15412c77eefeSRaphael Isemann        } else {
154212c9c4a8SPavel Labath          llvm_unreachable("Unknown status");
15432c77eefeSRaphael Isemann        }
15442c77eefeSRaphael Isemann
154563e5fb76SJonas Devlieghere        LLDB_LOGF(log,
154663e5fb76SJonas Devlieghere                  "::waitpid (pid = %llu, &status, 0) => pid = %i, status "
15472c77eefeSRaphael Isemann                  "= 0x%8.8x (%s), signal = %i, exit_status = %i",
15482c77eefeSRaphael Isemann                  pid, wait_pid, status, status_cstr, signal, exit_status);
15492c77eefeSRaphael Isemann
15502c77eefeSRaphael Isemann        if (callback_copy)
155112c9c4a8SPavel Labath          callback_copy(pid, signal, exit_status);
15522c77eefeSRaphael Isemann
15532c77eefeSRaphael Isemann        ::dispatch_source_cancel(source);
15542c77eefeSRaphael Isemann      }
15552c77eefeSRaphael Isemann    });
15562c77eefeSRaphael Isemann
15572c77eefeSRaphael Isemann    ::dispatch_resume(source);
15582c77eefeSRaphael Isemann  }
15592c77eefeSRaphael Isemann  return HostThread();
15602c77eefeSRaphael Isemann}
1561