1061da546Spatrick//===-- Host.mm -------------------------------------------------*- C++ -*-===// 2061da546Spatrick// 3061da546Spatrick// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4061da546Spatrick// See https://llvm.org/LICENSE.txt for license information. 5061da546Spatrick// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6061da546Spatrick// 7061da546Spatrick//===----------------------------------------------------------------------===// 8061da546Spatrick 9061da546Spatrick#include "lldb/Host/Host.h" 10be691f3bSpatrick#include "PosixSpawnResponsible.h" 11061da546Spatrick 12061da546Spatrick#include <AvailabilityMacros.h> 13dda28197Spatrick#include <TargetConditionals.h> 14061da546Spatrick 15dda28197Spatrick#if TARGET_OS_OSX 16061da546Spatrick#define __XPC_PRIVATE_H__ 17061da546Spatrick#include <xpc/xpc.h> 18061da546Spatrick 19061da546Spatrick#define LaunchUsingXPCRightName "com.apple.lldb.RootDebuggingXPCService" 20061da546Spatrick 21061da546Spatrick// These XPC messaging keys are used for communication between Host.mm and the 22061da546Spatrick// XPC service. 23061da546Spatrick#define LauncherXPCServiceAuthKey "auth-key" 24061da546Spatrick#define LauncherXPCServiceArgPrefxKey "arg" 25061da546Spatrick#define LauncherXPCServiceEnvPrefxKey "env" 26061da546Spatrick#define LauncherXPCServiceCPUTypeKey "cpuType" 27061da546Spatrick#define LauncherXPCServicePosixspawnFlagsKey "posixspawnFlags" 28061da546Spatrick#define LauncherXPCServiceStdInPathKeyKey "stdInPath" 29061da546Spatrick#define LauncherXPCServiceStdOutPathKeyKey "stdOutPath" 30061da546Spatrick#define LauncherXPCServiceStdErrPathKeyKey "stdErrPath" 31061da546Spatrick#define LauncherXPCServiceChildPIDKey "childPID" 32061da546Spatrick#define LauncherXPCServiceErrorTypeKey "errorType" 33061da546Spatrick#define LauncherXPCServiceCodeTypeKey "errorCode" 34061da546Spatrick 35*f6aab3d8Srobert#include <bsm/audit.h> 36*f6aab3d8Srobert#include <bsm/audit_session.h> 37061da546Spatrick#endif 38061da546Spatrick 39061da546Spatrick#include "llvm/Support/Host.h" 40061da546Spatrick 41061da546Spatrick#include <asl.h> 42061da546Spatrick#include <crt_externs.h> 43be691f3bSpatrick#include <cstdio> 44be691f3bSpatrick#include <cstdlib> 45be691f3bSpatrick#include <dlfcn.h> 46061da546Spatrick#include <grp.h> 47061da546Spatrick#include <libproc.h> 48061da546Spatrick#include <pwd.h> 49061da546Spatrick#include <spawn.h> 50061da546Spatrick#include <sys/proc.h> 51061da546Spatrick#include <sys/stat.h> 52061da546Spatrick#include <sys/sysctl.h> 53061da546Spatrick#include <sys/types.h> 54061da546Spatrick#include <unistd.h> 55061da546Spatrick 56061da546Spatrick#include "lldb/Host/ConnectionFileDescriptor.h" 57061da546Spatrick#include "lldb/Host/FileSystem.h" 58061da546Spatrick#include "lldb/Host/HostInfo.h" 59061da546Spatrick#include "lldb/Host/ProcessLaunchInfo.h" 60061da546Spatrick#include "lldb/Host/ThreadLauncher.h" 61061da546Spatrick#include "lldb/Utility/ArchSpec.h" 62*f6aab3d8Srobert#include "lldb/Utility/LLDBLog.h" 63061da546Spatrick#include "lldb/Utility/DataBufferHeap.h" 64061da546Spatrick#include "lldb/Utility/DataExtractor.h" 65061da546Spatrick#include "lldb/Utility/Endian.h" 66061da546Spatrick#include "lldb/Utility/FileSpec.h" 67061da546Spatrick#include "lldb/Utility/Log.h" 68061da546Spatrick#include "lldb/Utility/NameMatches.h" 69061da546Spatrick#include "lldb/Utility/ProcessInfo.h" 70061da546Spatrick#include "lldb/Utility/StreamString.h" 71061da546Spatrick#include "lldb/Utility/StructuredData.h" 72061da546Spatrick#include "lldb/lldb-defines.h" 73061da546Spatrick 74061da546Spatrick#include "llvm/ADT/ScopeExit.h" 75061da546Spatrick#include "llvm/Support/Errno.h" 76061da546Spatrick#include "llvm/Support/FileSystem.h" 77061da546Spatrick 78061da546Spatrick#include "../cfcpp/CFCBundle.h" 79061da546Spatrick#include "../cfcpp/CFCMutableArray.h" 80061da546Spatrick#include "../cfcpp/CFCMutableDictionary.h" 81061da546Spatrick#include "../cfcpp/CFCReleaser.h" 82061da546Spatrick#include "../cfcpp/CFCString.h" 83061da546Spatrick 84061da546Spatrick#include <objc/objc-auto.h> 85*f6aab3d8Srobert#include <os/log.h> 86061da546Spatrick 87061da546Spatrick#include <CoreFoundation/CoreFoundation.h> 88061da546Spatrick#include <Foundation/Foundation.h> 89061da546Spatrick 90061da546Spatrick#ifndef _POSIX_SPAWN_DISABLE_ASLR 91061da546Spatrick#define _POSIX_SPAWN_DISABLE_ASLR 0x0100 92061da546Spatrick#endif 93061da546Spatrick 94061da546Spatrickextern "C" { 95061da546Spatrickint __pthread_chdir(const char *path); 96061da546Spatrickint __pthread_fchdir(int fildes); 97061da546Spatrick} 98061da546Spatrick 99061da546Spatrickusing namespace lldb; 100061da546Spatrickusing namespace lldb_private; 101061da546Spatrick 102*f6aab3d8Srobertstatic os_log_t g_os_log; 103*f6aab3d8Srobertstatic std::once_flag g_os_log_once; 104*f6aab3d8Srobert 105*f6aab3d8Srobertvoid Host::SystemLog(llvm::StringRef message) { 106*f6aab3d8Srobert if (__builtin_available(macos 10.12, iOS 10, tvOS 10, watchOS 3, *)) { 107*f6aab3d8Srobert std::call_once(g_os_log_once, []() { 108*f6aab3d8Srobert g_os_log = os_log_create("com.apple.dt.lldb", "lldb"); 109*f6aab3d8Srobert }); 110*f6aab3d8Srobert os_log(g_os_log, "%{public}s", message.str().c_str()); 111*f6aab3d8Srobert } else { 112*f6aab3d8Srobert llvm::errs() << message; 113*f6aab3d8Srobert } 114*f6aab3d8Srobert} 115*f6aab3d8Srobert 116061da546Spatrickbool Host::GetBundleDirectory(const FileSpec &file, 117061da546Spatrick FileSpec &bundle_directory) { 118061da546Spatrick#if defined(__APPLE__) 119061da546Spatrick if (FileSystem::Instance().IsDirectory(file)) { 120061da546Spatrick char path[PATH_MAX]; 121061da546Spatrick if (file.GetPath(path, sizeof(path))) { 122061da546Spatrick CFCBundle bundle(path); 123061da546Spatrick if (bundle.GetPath(path, sizeof(path))) { 124061da546Spatrick bundle_directory.SetFile(path, FileSpec::Style::native); 125061da546Spatrick return true; 126061da546Spatrick } 127061da546Spatrick } 128061da546Spatrick } 129061da546Spatrick#endif 130061da546Spatrick bundle_directory.Clear(); 131061da546Spatrick return false; 132061da546Spatrick} 133061da546Spatrick 134061da546Spatrickbool Host::ResolveExecutableInBundle(FileSpec &file) { 135061da546Spatrick#if defined(__APPLE__) 136061da546Spatrick if (FileSystem::Instance().IsDirectory(file)) { 137061da546Spatrick char path[PATH_MAX]; 138061da546Spatrick if (file.GetPath(path, sizeof(path))) { 139061da546Spatrick CFCBundle bundle(path); 140061da546Spatrick CFCReleaser<CFURLRef> url(bundle.CopyExecutableURL()); 141061da546Spatrick if (url.get()) { 142061da546Spatrick if (::CFURLGetFileSystemRepresentation(url.get(), YES, (UInt8 *)path, 143061da546Spatrick sizeof(path))) { 144061da546Spatrick file.SetFile(path, FileSpec::Style::native); 145061da546Spatrick return true; 146061da546Spatrick } 147061da546Spatrick } 148061da546Spatrick } 149061da546Spatrick } 150061da546Spatrick#endif 151061da546Spatrick return false; 152061da546Spatrick} 153061da546Spatrick 154dda28197Spatrick#if TARGET_OS_OSX 155dda28197Spatrick 156*f6aab3d8Srobertstatic void *AcceptPIDFromInferior(const char *connect_url) { 157061da546Spatrick ConnectionFileDescriptor file_conn; 158061da546Spatrick Status error; 159061da546Spatrick if (file_conn.Connect(connect_url, &error) == eConnectionStatusSuccess) { 160061da546Spatrick char pid_str[256]; 161061da546Spatrick ::memset(pid_str, 0, sizeof(pid_str)); 162061da546Spatrick ConnectionStatus status; 163061da546Spatrick const size_t pid_str_len = file_conn.Read( 164061da546Spatrick pid_str, sizeof(pid_str), std::chrono::seconds(0), status, NULL); 165061da546Spatrick if (pid_str_len > 0) { 166061da546Spatrick int pid = atoi(pid_str); 167061da546Spatrick return (void *)(intptr_t)pid; 168061da546Spatrick } 169061da546Spatrick } 170061da546Spatrick return NULL; 171061da546Spatrick} 172061da546Spatrick 173061da546Spatrickconst char *applscript_in_new_tty = "tell application \"Terminal\"\n" 174061da546Spatrick " activate\n" 175061da546Spatrick " do script \"/bin/bash -c '%s';exit\"\n" 176061da546Spatrick "end tell\n"; 177061da546Spatrick 178061da546Spatrickconst char *applscript_in_existing_tty = "\ 179061da546Spatrickset the_shell_script to \"/bin/bash -c '%s';exit\"\n\ 180061da546Spatricktell application \"Terminal\"\n\ 181061da546Spatrick repeat with the_window in (get windows)\n\ 182061da546Spatrick repeat with the_tab in tabs of the_window\n\ 183061da546Spatrick set the_tty to tty in the_tab\n\ 184061da546Spatrick if the_tty contains \"%s\" then\n\ 185061da546Spatrick if the_tab is not busy then\n\ 186061da546Spatrick set selected of the_tab to true\n\ 187061da546Spatrick set frontmost of the_window to true\n\ 188061da546Spatrick do script the_shell_script in the_tab\n\ 189061da546Spatrick return\n\ 190061da546Spatrick end if\n\ 191061da546Spatrick end if\n\ 192061da546Spatrick end repeat\n\ 193061da546Spatrick end repeat\n\ 194061da546Spatrick do script the_shell_script\n\ 195061da546Spatrickend tell\n"; 196061da546Spatrick 197061da546Spatrickstatic Status 198061da546SpatrickLaunchInNewTerminalWithAppleScript(const char *exe_path, 199061da546Spatrick ProcessLaunchInfo &launch_info) { 200061da546Spatrick Status error; 201061da546Spatrick char unix_socket_name[PATH_MAX] = "/tmp/XXXXXX"; 202061da546Spatrick if (::mktemp(unix_socket_name) == NULL) { 203061da546Spatrick error.SetErrorString("failed to make temporary path for a unix socket"); 204061da546Spatrick return error; 205061da546Spatrick } 206061da546Spatrick 207061da546Spatrick StreamString command; 208061da546Spatrick FileSpec darwin_debug_file_spec = HostInfo::GetSupportExeDir(); 209061da546Spatrick if (!darwin_debug_file_spec) { 210061da546Spatrick error.SetErrorString("can't locate the 'darwin-debug' executable"); 211061da546Spatrick return error; 212061da546Spatrick } 213061da546Spatrick 214*f6aab3d8Srobert darwin_debug_file_spec.SetFilename("darwin-debug"); 215061da546Spatrick 216061da546Spatrick if (!FileSystem::Instance().Exists(darwin_debug_file_spec)) { 217061da546Spatrick error.SetErrorStringWithFormat( 218061da546Spatrick "the 'darwin-debug' executable doesn't exists at '%s'", 219061da546Spatrick darwin_debug_file_spec.GetPath().c_str()); 220061da546Spatrick return error; 221061da546Spatrick } 222061da546Spatrick 223061da546Spatrick char launcher_path[PATH_MAX]; 224061da546Spatrick darwin_debug_file_spec.GetPath(launcher_path, sizeof(launcher_path)); 225061da546Spatrick 226061da546Spatrick const ArchSpec &arch_spec = launch_info.GetArchitecture(); 227061da546Spatrick // Only set the architecture if it is valid and if it isn't Haswell (x86_64h). 228061da546Spatrick if (arch_spec.IsValid() && 229061da546Spatrick arch_spec.GetCore() != ArchSpec::eCore_x86_64_x86_64h) 230061da546Spatrick command.Printf("arch -arch %s ", arch_spec.GetArchitectureName()); 231061da546Spatrick 232*f6aab3d8Srobert command.Printf(R"(\"%s\" --unix-socket=%s)", launcher_path, unix_socket_name); 233061da546Spatrick 234061da546Spatrick if (arch_spec.IsValid()) 235061da546Spatrick command.Printf(" --arch=%s", arch_spec.GetArchitectureName()); 236061da546Spatrick 237061da546Spatrick FileSpec working_dir{launch_info.GetWorkingDirectory()}; 238061da546Spatrick if (working_dir) 239*f6aab3d8Srobert command.Printf(R"( --working-dir \"%s\")", working_dir.GetPath().c_str()); 240061da546Spatrick else { 241061da546Spatrick char cwd[PATH_MAX]; 242061da546Spatrick if (getcwd(cwd, PATH_MAX)) 243*f6aab3d8Srobert command.Printf(R"( --working-dir \"%s\")", cwd); 244061da546Spatrick } 245061da546Spatrick 246061da546Spatrick if (launch_info.GetFlags().Test(eLaunchFlagDisableASLR)) 247061da546Spatrick command.PutCString(" --disable-aslr"); 248061da546Spatrick 249061da546Spatrick // We are launching on this host in a terminal. So compare the environment on 250061da546Spatrick // the host to what is supplied in the launch_info. Any items that aren't in 251061da546Spatrick // the host environment need to be sent to darwin-debug. If we send all 252061da546Spatrick // environment entries, we might blow the max command line length, so we only 253061da546Spatrick // send user modified entries. 254061da546Spatrick Environment host_env = Host::GetEnvironment(); 255061da546Spatrick 256061da546Spatrick for (const auto &KV : launch_info.GetEnvironment()) { 257061da546Spatrick auto host_entry = host_env.find(KV.first()); 258061da546Spatrick if (host_entry == host_env.end() || host_entry->second != KV.second) 259*f6aab3d8Srobert command.Format(R"( --env=\"{0}\")", Environment::compose(KV)); 260061da546Spatrick } 261061da546Spatrick 262061da546Spatrick command.PutCString(" -- "); 263061da546Spatrick 264061da546Spatrick const char **argv = launch_info.GetArguments().GetConstArgumentVector(); 265061da546Spatrick if (argv) { 266061da546Spatrick for (size_t i = 0; argv[i] != NULL; ++i) { 267061da546Spatrick if (i == 0) 268*f6aab3d8Srobert command.Printf(R"( \"%s\")", exe_path); 269061da546Spatrick else 270*f6aab3d8Srobert command.Printf(R"( \"%s\")", argv[i]); 271061da546Spatrick } 272061da546Spatrick } else { 273*f6aab3d8Srobert command.Printf(R"( \"%s\")", exe_path); 274061da546Spatrick } 275061da546Spatrick command.PutCString(" ; echo Process exited with status $?"); 276061da546Spatrick if (launch_info.GetFlags().Test(lldb::eLaunchFlagCloseTTYOnExit)) 277061da546Spatrick command.PutCString(" ; exit"); 278061da546Spatrick 279061da546Spatrick StreamString applescript_source; 280061da546Spatrick 281061da546Spatrick applescript_source.Printf(applscript_in_new_tty, 282061da546Spatrick command.GetString().str().c_str()); 283*f6aab3d8Srobert 284061da546Spatrick NSAppleScript *applescript = [[NSAppleScript alloc] 285061da546Spatrick initWithSource:[NSString stringWithCString:applescript_source.GetString() 286061da546Spatrick .str() 287061da546Spatrick .c_str() 288061da546Spatrick encoding:NSUTF8StringEncoding]]; 289061da546Spatrick 290061da546Spatrick lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; 291061da546Spatrick 292061da546Spatrick Status lldb_error; 293061da546Spatrick // Sleep and wait a bit for debugserver to start to listen... 294061da546Spatrick ConnectionFileDescriptor file_conn; 295061da546Spatrick char connect_url[128]; 296061da546Spatrick ::snprintf(connect_url, sizeof(connect_url), "unix-accept://%s", 297061da546Spatrick unix_socket_name); 298061da546Spatrick 299061da546Spatrick // Spawn a new thread to accept incoming connection on the connect_url 300061da546Spatrick // so we can grab the pid from the inferior. We have to do this because we 301061da546Spatrick // are sending an AppleScript that will launch a process in Terminal.app, 302061da546Spatrick // in a shell and the shell will fork/exec a couple of times before we get 303061da546Spatrick // to the process that we wanted to launch. So when our process actually 304061da546Spatrick // gets launched, we will handshake with it and get the process ID for it. 305061da546Spatrick llvm::Expected<HostThread> accept_thread = ThreadLauncher::LaunchThread( 306*f6aab3d8Srobert unix_socket_name, [&] { return AcceptPIDFromInferior(connect_url); }); 307061da546Spatrick 308061da546Spatrick if (!accept_thread) 309061da546Spatrick return Status(accept_thread.takeError()); 310061da546Spatrick 311061da546Spatrick [applescript executeAndReturnError:nil]; 312061da546Spatrick 313061da546Spatrick thread_result_t accept_thread_result = NULL; 314061da546Spatrick lldb_error = accept_thread->Join(&accept_thread_result); 315061da546Spatrick if (lldb_error.Success() && accept_thread_result) { 316061da546Spatrick pid = (intptr_t)accept_thread_result; 317061da546Spatrick } 318061da546Spatrick 319061da546Spatrick llvm::sys::fs::remove(unix_socket_name); 320061da546Spatrick [applescript release]; 321061da546Spatrick if (pid != LLDB_INVALID_PROCESS_ID) 322061da546Spatrick launch_info.SetProcessID(pid); 323061da546Spatrick return error; 324061da546Spatrick} 325061da546Spatrick 326dda28197Spatrick#endif // TARGET_OS_OSX 327061da546Spatrick 328061da546Spatrickbool Host::OpenFileInExternalEditor(const FileSpec &file_spec, 329061da546Spatrick uint32_t line_no) { 330dda28197Spatrick#if !TARGET_OS_OSX 331061da546Spatrick return false; 332dda28197Spatrick#else // !TARGET_OS_OSX 333061da546Spatrick // We attach this to an 'odoc' event to specify a particular selection 334061da546Spatrick typedef struct { 335061da546Spatrick int16_t reserved0; // must be zero 336061da546Spatrick int16_t fLineNumber; 337061da546Spatrick int32_t fSelStart; 338061da546Spatrick int32_t fSelEnd; 339061da546Spatrick uint32_t reserved1; // must be zero 340061da546Spatrick uint32_t reserved2; // must be zero 341061da546Spatrick } BabelAESelInfo; 342061da546Spatrick 343*f6aab3d8Srobert Log *log = GetLog(LLDBLog::Host); 344061da546Spatrick char file_path[PATH_MAX]; 345061da546Spatrick file_spec.GetPath(file_path, PATH_MAX); 346061da546Spatrick CFCString file_cfstr(file_path, kCFStringEncodingUTF8); 347061da546Spatrick CFCReleaser<CFURLRef> file_URL(::CFURLCreateWithFileSystemPath( 348061da546Spatrick NULL, file_cfstr.get(), kCFURLPOSIXPathStyle, false)); 349061da546Spatrick 350061da546Spatrick LLDB_LOGF(log, 351061da546Spatrick "Sending source file: \"%s\" and line: %d to external editor.\n", 352061da546Spatrick file_path, line_no); 353061da546Spatrick 354061da546Spatrick long error; 355061da546Spatrick BabelAESelInfo file_and_line_info = { 356061da546Spatrick 0, // reserved0 357061da546Spatrick (int16_t)(line_no - 1), // fLineNumber (zero based line number) 358061da546Spatrick 1, // fSelStart 359061da546Spatrick 1024, // fSelEnd 360061da546Spatrick 0, // reserved1 361061da546Spatrick 0 // reserved2 362061da546Spatrick }; 363061da546Spatrick 364061da546Spatrick AEKeyDesc file_and_line_desc; 365061da546Spatrick 366061da546Spatrick error = ::AECreateDesc(typeUTF8Text, &file_and_line_info, 367061da546Spatrick sizeof(file_and_line_info), 368061da546Spatrick &(file_and_line_desc.descContent)); 369061da546Spatrick 370061da546Spatrick if (error != noErr) { 371061da546Spatrick LLDB_LOGF(log, "Error creating AEDesc: %ld.\n", error); 372061da546Spatrick return false; 373061da546Spatrick } 374061da546Spatrick 375061da546Spatrick file_and_line_desc.descKey = keyAEPosition; 376061da546Spatrick 377061da546Spatrick static std::string g_app_name; 378061da546Spatrick static FSRef g_app_fsref; 379061da546Spatrick 380061da546Spatrick LSApplicationParameters app_params; 381061da546Spatrick ::memset(&app_params, 0, sizeof(app_params)); 382061da546Spatrick app_params.flags = 383061da546Spatrick kLSLaunchDefaults | kLSLaunchDontAddToRecents | kLSLaunchDontSwitch; 384061da546Spatrick 385061da546Spatrick char *external_editor = ::getenv("LLDB_EXTERNAL_EDITOR"); 386061da546Spatrick 387061da546Spatrick if (external_editor) { 388061da546Spatrick LLDB_LOGF(log, "Looking for external editor \"%s\".\n", external_editor); 389061da546Spatrick 390061da546Spatrick if (g_app_name.empty() || 391061da546Spatrick strcmp(g_app_name.c_str(), external_editor) != 0) { 392061da546Spatrick CFCString editor_name(external_editor, kCFStringEncodingUTF8); 393061da546Spatrick error = ::LSFindApplicationForInfo(kLSUnknownCreator, NULL, 394061da546Spatrick editor_name.get(), &g_app_fsref, NULL); 395061da546Spatrick 396061da546Spatrick // If we found the app, then store away the name so we don't have to 397061da546Spatrick // re-look it up. 398061da546Spatrick if (error != noErr) { 399061da546Spatrick LLDB_LOGF(log, 400061da546Spatrick "Could not find External Editor application, error: %ld.\n", 401061da546Spatrick error); 402061da546Spatrick return false; 403061da546Spatrick } 404061da546Spatrick } 405061da546Spatrick app_params.application = &g_app_fsref; 406061da546Spatrick } 407061da546Spatrick 408061da546Spatrick ProcessSerialNumber psn; 409061da546Spatrick CFCReleaser<CFArrayRef> file_array( 410061da546Spatrick CFArrayCreate(NULL, (const void **)file_URL.ptr_address(false), 1, NULL)); 411061da546Spatrick error = ::LSOpenURLsWithRole(file_array.get(), kLSRolesAll, 412061da546Spatrick &file_and_line_desc, &app_params, &psn, 1); 413061da546Spatrick 414061da546Spatrick AEDisposeDesc(&(file_and_line_desc.descContent)); 415061da546Spatrick 416061da546Spatrick if (error != noErr) { 417061da546Spatrick LLDB_LOGF(log, "LSOpenURLsWithRole failed, error: %ld.\n", error); 418061da546Spatrick 419061da546Spatrick return false; 420061da546Spatrick } 421061da546Spatrick 422061da546Spatrick return true; 423dda28197Spatrick#endif // TARGET_OS_OSX 424061da546Spatrick} 425061da546Spatrick 426*f6aab3d8Srobertbool Host::IsInteractiveGraphicSession() { 427*f6aab3d8Srobert#if !TARGET_OS_OSX 428*f6aab3d8Srobert return false; 429*f6aab3d8Srobert#else 430*f6aab3d8Srobert auditinfo_addr_t info; 431*f6aab3d8Srobert getaudit_addr(&info, sizeof(info)); 432*f6aab3d8Srobert return info.ai_flags & AU_SESSION_FLAG_HAS_GRAPHIC_ACCESS; 433*f6aab3d8Srobert#endif 434*f6aab3d8Srobert} 435*f6aab3d8Srobert 436061da546SpatrickEnvironment Host::GetEnvironment() { return Environment(*_NSGetEnviron()); } 437061da546Spatrick 438061da546Spatrickstatic bool GetMacOSXProcessCPUType(ProcessInstanceInfo &process_info) { 439061da546Spatrick if (process_info.ProcessIDIsValid()) { 440061da546Spatrick // Make a new mib to stay thread safe 441061da546Spatrick int mib[CTL_MAXNAME] = { 442061da546Spatrick 0, 443061da546Spatrick }; 444061da546Spatrick size_t mib_len = CTL_MAXNAME; 445061da546Spatrick if (::sysctlnametomib("sysctl.proc_cputype", mib, &mib_len)) 446061da546Spatrick return false; 447061da546Spatrick 448061da546Spatrick mib[mib_len] = process_info.GetProcessID(); 449061da546Spatrick mib_len++; 450061da546Spatrick 451061da546Spatrick cpu_type_t cpu, sub = 0; 452061da546Spatrick size_t len = sizeof(cpu); 453061da546Spatrick if (::sysctl(mib, mib_len, &cpu, &len, 0, 0) == 0) { 454061da546Spatrick switch (cpu) { 455061da546Spatrick case CPU_TYPE_I386: 456061da546Spatrick sub = CPU_SUBTYPE_I386_ALL; 457061da546Spatrick break; 458061da546Spatrick case CPU_TYPE_X86_64: 459061da546Spatrick sub = CPU_SUBTYPE_X86_64_ALL; 460061da546Spatrick break; 461061da546Spatrick 462061da546Spatrick#if defined(CPU_TYPE_ARM64) && defined(CPU_SUBTYPE_ARM64_ALL) 463061da546Spatrick case CPU_TYPE_ARM64: 464061da546Spatrick sub = CPU_SUBTYPE_ARM64_ALL; 465061da546Spatrick break; 466061da546Spatrick#endif 467061da546Spatrick 468061da546Spatrick#if defined(CPU_TYPE_ARM64_32) && defined(CPU_SUBTYPE_ARM64_32_ALL) 469061da546Spatrick case CPU_TYPE_ARM64_32: 470061da546Spatrick sub = CPU_SUBTYPE_ARM64_32_ALL; 471061da546Spatrick break; 472061da546Spatrick#endif 473061da546Spatrick 474061da546Spatrick case CPU_TYPE_ARM: { 475061da546Spatrick // Note that we fetched the cpu type from the PROCESS but we can't get a 476061da546Spatrick // cpusubtype of the 477061da546Spatrick // process -- we can only get the host's cpu subtype. 478061da546Spatrick uint32_t cpusubtype = 0; 479061da546Spatrick len = sizeof(cpusubtype); 480061da546Spatrick if (::sysctlbyname("hw.cpusubtype", &cpusubtype, &len, NULL, 0) == 0) 481061da546Spatrick sub = cpusubtype; 482061da546Spatrick 483061da546Spatrick bool host_cpu_is_64bit; 484061da546Spatrick uint32_t is64bit_capable; 485061da546Spatrick size_t is64bit_capable_len = sizeof(is64bit_capable); 486061da546Spatrick host_cpu_is_64bit = 487061da546Spatrick sysctlbyname("hw.cpu64bit_capable", &is64bit_capable, 488061da546Spatrick &is64bit_capable_len, NULL, 0) == 0; 489061da546Spatrick 490061da546Spatrick // if the host is an armv8 device, its cpusubtype will be in 491061da546Spatrick // CPU_SUBTYPE_ARM64 numbering 492061da546Spatrick // and we need to rewrite it to a reasonable CPU_SUBTYPE_ARM value 493061da546Spatrick // instead. 494061da546Spatrick 495061da546Spatrick if (host_cpu_is_64bit) { 496061da546Spatrick sub = CPU_SUBTYPE_ARM_V7; 497061da546Spatrick } 498061da546Spatrick } break; 499061da546Spatrick 500061da546Spatrick default: 501061da546Spatrick break; 502061da546Spatrick } 503061da546Spatrick process_info.GetArchitecture().SetArchitecture(eArchTypeMachO, cpu, sub); 504061da546Spatrick return true; 505061da546Spatrick } 506061da546Spatrick } 507061da546Spatrick process_info.GetArchitecture().Clear(); 508061da546Spatrick return false; 509061da546Spatrick} 510061da546Spatrick 511061da546Spatrickstatic bool GetMacOSXProcessArgs(const ProcessInstanceInfoMatch *match_info_ptr, 512061da546Spatrick ProcessInstanceInfo &process_info) { 513061da546Spatrick if (process_info.ProcessIDIsValid()) { 514061da546Spatrick int proc_args_mib[3] = {CTL_KERN, KERN_PROCARGS2, 515061da546Spatrick (int)process_info.GetProcessID()}; 516061da546Spatrick 517061da546Spatrick size_t arg_data_size = 0; 518061da546Spatrick if (::sysctl(proc_args_mib, 3, nullptr, &arg_data_size, NULL, 0) || 519061da546Spatrick arg_data_size == 0) 520061da546Spatrick arg_data_size = 8192; 521061da546Spatrick 522061da546Spatrick // Add a few bytes to the calculated length, I know we need to add at least 523061da546Spatrick // one byte 524061da546Spatrick // to this number otherwise we get junk back, so add 128 just in case... 525061da546Spatrick DataBufferHeap arg_data(arg_data_size + 128, 0); 526061da546Spatrick arg_data_size = arg_data.GetByteSize(); 527061da546Spatrick if (::sysctl(proc_args_mib, 3, arg_data.GetBytes(), &arg_data_size, NULL, 528061da546Spatrick 0) == 0) { 529061da546Spatrick DataExtractor data(arg_data.GetBytes(), arg_data_size, 530061da546Spatrick endian::InlHostByteOrder(), sizeof(void *)); 531061da546Spatrick lldb::offset_t offset = 0; 532061da546Spatrick uint32_t argc = data.GetU32(&offset); 533061da546Spatrick llvm::Triple &triple = process_info.GetArchitecture().GetTriple(); 534061da546Spatrick const llvm::Triple::ArchType triple_arch = triple.getArch(); 535061da546Spatrick const bool check_for_ios_simulator = 536061da546Spatrick (triple_arch == llvm::Triple::x86 || 537061da546Spatrick triple_arch == llvm::Triple::x86_64); 538061da546Spatrick const char *cstr = data.GetCStr(&offset); 539061da546Spatrick if (cstr) { 540061da546Spatrick process_info.GetExecutableFile().SetFile(cstr, FileSpec::Style::native); 541061da546Spatrick 542061da546Spatrick if (match_info_ptr == NULL || 543061da546Spatrick NameMatches( 544061da546Spatrick process_info.GetExecutableFile().GetFilename().GetCString(), 545061da546Spatrick match_info_ptr->GetNameMatchType(), 546061da546Spatrick match_info_ptr->GetProcessInfo().GetName())) { 547061da546Spatrick // Skip NULLs 548061da546Spatrick while (true) { 549061da546Spatrick const uint8_t *p = data.PeekData(offset, 1); 550061da546Spatrick if ((p == NULL) || (*p != '\0')) 551061da546Spatrick break; 552061da546Spatrick ++offset; 553061da546Spatrick } 554061da546Spatrick // Now extract all arguments 555061da546Spatrick Args &proc_args = process_info.GetArguments(); 556061da546Spatrick for (int i = 0; i < static_cast<int>(argc); ++i) { 557061da546Spatrick cstr = data.GetCStr(&offset); 558061da546Spatrick if (cstr) 559061da546Spatrick proc_args.AppendArgument(llvm::StringRef(cstr)); 560061da546Spatrick } 561061da546Spatrick 562061da546Spatrick Environment &proc_env = process_info.GetEnvironment(); 563061da546Spatrick while ((cstr = data.GetCStr(&offset))) { 564061da546Spatrick if (cstr[0] == '\0') 565061da546Spatrick break; 566061da546Spatrick 567061da546Spatrick if (check_for_ios_simulator) { 568061da546Spatrick if (strncmp(cstr, "SIMULATOR_UDID=", strlen("SIMULATOR_UDID=")) == 569061da546Spatrick 0) 570061da546Spatrick process_info.GetArchitecture().GetTriple().setOS( 571061da546Spatrick llvm::Triple::IOS); 572061da546Spatrick else 573061da546Spatrick process_info.GetArchitecture().GetTriple().setOS( 574061da546Spatrick llvm::Triple::MacOSX); 575061da546Spatrick } 576061da546Spatrick 577061da546Spatrick proc_env.insert(cstr); 578061da546Spatrick } 579061da546Spatrick return true; 580061da546Spatrick } 581061da546Spatrick } 582061da546Spatrick } 583061da546Spatrick } 584061da546Spatrick return false; 585061da546Spatrick} 586061da546Spatrick 587061da546Spatrickstatic bool GetMacOSXProcessUserAndGroup(ProcessInstanceInfo &process_info) { 588061da546Spatrick if (process_info.ProcessIDIsValid()) { 589061da546Spatrick int mib[4]; 590061da546Spatrick mib[0] = CTL_KERN; 591061da546Spatrick mib[1] = KERN_PROC; 592061da546Spatrick mib[2] = KERN_PROC_PID; 593061da546Spatrick mib[3] = process_info.GetProcessID(); 594061da546Spatrick struct kinfo_proc proc_kinfo; 595061da546Spatrick size_t proc_kinfo_size = sizeof(struct kinfo_proc); 596061da546Spatrick 597061da546Spatrick if (::sysctl(mib, 4, &proc_kinfo, &proc_kinfo_size, NULL, 0) == 0) { 598061da546Spatrick if (proc_kinfo_size > 0) { 599061da546Spatrick process_info.SetParentProcessID(proc_kinfo.kp_eproc.e_ppid); 600061da546Spatrick process_info.SetUserID(proc_kinfo.kp_eproc.e_pcred.p_ruid); 601061da546Spatrick process_info.SetGroupID(proc_kinfo.kp_eproc.e_pcred.p_rgid); 602061da546Spatrick process_info.SetEffectiveUserID(proc_kinfo.kp_eproc.e_ucred.cr_uid); 603061da546Spatrick if (proc_kinfo.kp_eproc.e_ucred.cr_ngroups > 0) 604061da546Spatrick process_info.SetEffectiveGroupID( 605061da546Spatrick proc_kinfo.kp_eproc.e_ucred.cr_groups[0]); 606061da546Spatrick else 607061da546Spatrick process_info.SetEffectiveGroupID(UINT32_MAX); 608061da546Spatrick return true; 609061da546Spatrick } 610061da546Spatrick } 611061da546Spatrick } 612061da546Spatrick process_info.SetParentProcessID(LLDB_INVALID_PROCESS_ID); 613061da546Spatrick process_info.SetUserID(UINT32_MAX); 614061da546Spatrick process_info.SetGroupID(UINT32_MAX); 615061da546Spatrick process_info.SetEffectiveUserID(UINT32_MAX); 616061da546Spatrick process_info.SetEffectiveGroupID(UINT32_MAX); 617061da546Spatrick return false; 618061da546Spatrick} 619061da546Spatrick 620dda28197Spatrickuint32_t Host::FindProcessesImpl(const ProcessInstanceInfoMatch &match_info, 621061da546Spatrick ProcessInstanceInfoList &process_infos) { 622061da546Spatrick std::vector<struct kinfo_proc> kinfos; 623061da546Spatrick 624061da546Spatrick int mib[3] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL}; 625061da546Spatrick 626061da546Spatrick size_t pid_data_size = 0; 627061da546Spatrick if (::sysctl(mib, 3, nullptr, &pid_data_size, nullptr, 0) != 0) 628061da546Spatrick return 0; 629061da546Spatrick 630061da546Spatrick // Add a few extra in case a few more show up 631061da546Spatrick const size_t estimated_pid_count = 632061da546Spatrick (pid_data_size / sizeof(struct kinfo_proc)) + 10; 633061da546Spatrick 634061da546Spatrick kinfos.resize(estimated_pid_count); 635061da546Spatrick pid_data_size = kinfos.size() * sizeof(struct kinfo_proc); 636061da546Spatrick 637061da546Spatrick if (::sysctl(mib, 3, &kinfos[0], &pid_data_size, nullptr, 0) != 0) 638061da546Spatrick return 0; 639061da546Spatrick 640061da546Spatrick const size_t actual_pid_count = (pid_data_size / sizeof(struct kinfo_proc)); 641061da546Spatrick 642061da546Spatrick bool all_users = match_info.GetMatchAllUsers(); 643061da546Spatrick const lldb::pid_t our_pid = getpid(); 644061da546Spatrick const uid_t our_uid = getuid(); 645061da546Spatrick for (size_t i = 0; i < actual_pid_count; i++) { 646061da546Spatrick const struct kinfo_proc &kinfo = kinfos[i]; 647061da546Spatrick 648061da546Spatrick bool kinfo_user_matches = false; 649061da546Spatrick if (all_users) 650061da546Spatrick kinfo_user_matches = true; 651061da546Spatrick else 652061da546Spatrick kinfo_user_matches = kinfo.kp_eproc.e_pcred.p_ruid == our_uid; 653061da546Spatrick 654061da546Spatrick // Special case, if lldb is being run as root we can attach to anything. 655061da546Spatrick if (our_uid == 0) 656061da546Spatrick kinfo_user_matches = true; 657061da546Spatrick 658061da546Spatrick if (!kinfo_user_matches || // Make sure the user is acceptable 659061da546Spatrick static_cast<lldb::pid_t>(kinfo.kp_proc.p_pid) == 660061da546Spatrick our_pid || // Skip this process 661061da546Spatrick kinfo.kp_proc.p_pid == 0 || // Skip kernel (kernel pid is zero) 662061da546Spatrick kinfo.kp_proc.p_stat == SZOMB || // Zombies are bad, they like brains... 663061da546Spatrick kinfo.kp_proc.p_flag & P_TRACED || // Being debugged? 664dda28197Spatrick kinfo.kp_proc.p_flag & P_WEXIT) 665061da546Spatrick continue; 666061da546Spatrick 667061da546Spatrick ProcessInstanceInfo process_info; 668061da546Spatrick process_info.SetProcessID(kinfo.kp_proc.p_pid); 669061da546Spatrick process_info.SetParentProcessID(kinfo.kp_eproc.e_ppid); 670061da546Spatrick process_info.SetUserID(kinfo.kp_eproc.e_pcred.p_ruid); 671061da546Spatrick process_info.SetGroupID(kinfo.kp_eproc.e_pcred.p_rgid); 672061da546Spatrick process_info.SetEffectiveUserID(kinfo.kp_eproc.e_ucred.cr_uid); 673061da546Spatrick if (kinfo.kp_eproc.e_ucred.cr_ngroups > 0) 674061da546Spatrick process_info.SetEffectiveGroupID(kinfo.kp_eproc.e_ucred.cr_groups[0]); 675061da546Spatrick else 676061da546Spatrick process_info.SetEffectiveGroupID(UINT32_MAX); 677061da546Spatrick 678061da546Spatrick // Make sure our info matches before we go fetch the name and cpu type 679061da546Spatrick if (!match_info.UserIDsMatch(process_info) || 680061da546Spatrick !match_info.ProcessIDsMatch(process_info)) 681061da546Spatrick continue; 682061da546Spatrick 683061da546Spatrick // Get CPU type first so we can know to look for iOS simulator is we have 684061da546Spatrick // x86 or x86_64 685061da546Spatrick if (GetMacOSXProcessCPUType(process_info)) { 686061da546Spatrick if (GetMacOSXProcessArgs(&match_info, process_info)) { 687061da546Spatrick if (match_info.Matches(process_info)) 688dda28197Spatrick process_infos.push_back(process_info); 689061da546Spatrick } 690061da546Spatrick } 691061da546Spatrick } 692dda28197Spatrick return process_infos.size(); 693061da546Spatrick} 694061da546Spatrick 695061da546Spatrickbool Host::GetProcessInfo(lldb::pid_t pid, ProcessInstanceInfo &process_info) { 696061da546Spatrick process_info.SetProcessID(pid); 697061da546Spatrick bool success = false; 698061da546Spatrick 699061da546Spatrick // Get CPU type first so we can know to look for iOS simulator is we have x86 700061da546Spatrick // or x86_64 701061da546Spatrick if (GetMacOSXProcessCPUType(process_info)) 702061da546Spatrick success = true; 703061da546Spatrick 704061da546Spatrick if (GetMacOSXProcessArgs(NULL, process_info)) 705061da546Spatrick success = true; 706061da546Spatrick 707061da546Spatrick if (GetMacOSXProcessUserAndGroup(process_info)) 708061da546Spatrick success = true; 709061da546Spatrick 710061da546Spatrick if (success) 711061da546Spatrick return true; 712061da546Spatrick 713061da546Spatrick process_info.Clear(); 714061da546Spatrick return false; 715061da546Spatrick} 716061da546Spatrick 717dda28197Spatrick#if TARGET_OS_OSX 718061da546Spatrickstatic void PackageXPCArguments(xpc_object_t message, const char *prefix, 719061da546Spatrick const Args &args) { 720061da546Spatrick size_t count = args.GetArgumentCount(); 721061da546Spatrick char buf[50]; // long enough for 'argXXX' 722061da546Spatrick memset(buf, 0, 50); 723061da546Spatrick sprintf(buf, "%sCount", prefix); 724061da546Spatrick xpc_dictionary_set_int64(message, buf, count); 725061da546Spatrick for (size_t i = 0; i < count; i++) { 726061da546Spatrick memset(buf, 0, 50); 727061da546Spatrick sprintf(buf, "%s%zi", prefix, i); 728061da546Spatrick xpc_dictionary_set_string(message, buf, args.GetArgumentAtIndex(i)); 729061da546Spatrick } 730061da546Spatrick} 731061da546Spatrick 732061da546Spatrickstatic void PackageXPCEnvironment(xpc_object_t message, llvm::StringRef prefix, 733061da546Spatrick const Environment &env) { 734061da546Spatrick xpc_dictionary_set_int64(message, (prefix + "Count").str().c_str(), 735061da546Spatrick env.size()); 736061da546Spatrick size_t i = 0; 737061da546Spatrick for (const auto &KV : env) { 738061da546Spatrick xpc_dictionary_set_string(message, (prefix + llvm::Twine(i)).str().c_str(), 739061da546Spatrick Environment::compose(KV).c_str()); 740061da546Spatrick } 741061da546Spatrick} 742061da546Spatrick 743061da546Spatrick/* 744061da546Spatrick A valid authorizationRef means that 745061da546Spatrick - there is the LaunchUsingXPCRightName rights in the /etc/authorization 746061da546Spatrick - we have successfully copied the rights to be send over the XPC wire 747061da546Spatrick Once obtained, it will be valid for as long as the process lives. 748061da546Spatrick */ 749061da546Spatrickstatic AuthorizationRef authorizationRef = NULL; 750061da546Spatrickstatic Status getXPCAuthorization(ProcessLaunchInfo &launch_info) { 751061da546Spatrick Status error; 752*f6aab3d8Srobert Log *log(GetLog(LLDBLog::Host | LLDBLog::Process)); 753061da546Spatrick 754061da546Spatrick if ((launch_info.GetUserID() == 0) && !authorizationRef) { 755061da546Spatrick OSStatus createStatus = 756061da546Spatrick AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, 757061da546Spatrick kAuthorizationFlagDefaults, &authorizationRef); 758061da546Spatrick if (createStatus != errAuthorizationSuccess) { 759061da546Spatrick error.SetError(1, eErrorTypeGeneric); 760061da546Spatrick error.SetErrorString("Can't create authorizationRef."); 761061da546Spatrick LLDB_LOG(log, "error: {0}", error); 762061da546Spatrick return error; 763061da546Spatrick } 764061da546Spatrick 765061da546Spatrick OSStatus rightsStatus = 766061da546Spatrick AuthorizationRightGet(LaunchUsingXPCRightName, NULL); 767061da546Spatrick if (rightsStatus != errAuthorizationSuccess) { 768061da546Spatrick // No rights in the security database, Create it with the right prompt. 769061da546Spatrick CFStringRef prompt = 770061da546Spatrick CFSTR("Xcode is trying to take control of a root process."); 771061da546Spatrick CFStringRef keys[] = {CFSTR("en")}; 772061da546Spatrick CFTypeRef values[] = {prompt}; 773061da546Spatrick CFDictionaryRef promptDict = CFDictionaryCreate( 774061da546Spatrick kCFAllocatorDefault, (const void **)keys, (const void **)values, 1, 775061da546Spatrick &kCFCopyStringDictionaryKeyCallBacks, 776061da546Spatrick &kCFTypeDictionaryValueCallBacks); 777061da546Spatrick 778061da546Spatrick CFStringRef keys1[] = {CFSTR("class"), CFSTR("group"), CFSTR("comment"), 779061da546Spatrick CFSTR("default-prompt"), CFSTR("shared")}; 780061da546Spatrick CFTypeRef values1[] = {CFSTR("user"), CFSTR("admin"), 781061da546Spatrick CFSTR(LaunchUsingXPCRightName), promptDict, 782061da546Spatrick kCFBooleanFalse}; 783061da546Spatrick CFDictionaryRef dict = CFDictionaryCreate( 784061da546Spatrick kCFAllocatorDefault, (const void **)keys1, (const void **)values1, 5, 785061da546Spatrick &kCFCopyStringDictionaryKeyCallBacks, 786061da546Spatrick &kCFTypeDictionaryValueCallBacks); 787061da546Spatrick rightsStatus = AuthorizationRightSet( 788061da546Spatrick authorizationRef, LaunchUsingXPCRightName, dict, NULL, NULL, NULL); 789061da546Spatrick CFRelease(promptDict); 790061da546Spatrick CFRelease(dict); 791061da546Spatrick } 792061da546Spatrick 793061da546Spatrick OSStatus copyRightStatus = errAuthorizationDenied; 794061da546Spatrick if (rightsStatus == errAuthorizationSuccess) { 795061da546Spatrick AuthorizationItem item1 = {LaunchUsingXPCRightName, 0, NULL, 0}; 796061da546Spatrick AuthorizationItem items[] = {item1}; 797061da546Spatrick AuthorizationRights requestedRights = {1, items}; 798061da546Spatrick AuthorizationFlags authorizationFlags = 799061da546Spatrick kAuthorizationFlagInteractionAllowed | kAuthorizationFlagExtendRights; 800061da546Spatrick copyRightStatus = AuthorizationCopyRights( 801061da546Spatrick authorizationRef, &requestedRights, kAuthorizationEmptyEnvironment, 802061da546Spatrick authorizationFlags, NULL); 803061da546Spatrick } 804061da546Spatrick 805061da546Spatrick if (copyRightStatus != errAuthorizationSuccess) { 806061da546Spatrick // Eventually when the commandline supports running as root and the user 807061da546Spatrick // is not 808061da546Spatrick // logged in in the current audit session, we will need the trick in gdb 809061da546Spatrick // where 810061da546Spatrick // we ask the user to type in the root passwd in the terminal. 811061da546Spatrick error.SetError(2, eErrorTypeGeneric); 812061da546Spatrick error.SetErrorStringWithFormat( 813061da546Spatrick "Launching as root needs root authorization."); 814061da546Spatrick LLDB_LOG(log, "error: {0}", error); 815061da546Spatrick 816061da546Spatrick if (authorizationRef) { 817061da546Spatrick AuthorizationFree(authorizationRef, kAuthorizationFlagDefaults); 818061da546Spatrick authorizationRef = NULL; 819061da546Spatrick } 820061da546Spatrick } 821061da546Spatrick } 822061da546Spatrick 823061da546Spatrick return error; 824061da546Spatrick} 825061da546Spatrick#endif 826061da546Spatrick 827061da546Spatrickstatic short GetPosixspawnFlags(const ProcessLaunchInfo &launch_info) { 828061da546Spatrick short flags = POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK; 829061da546Spatrick 830061da546Spatrick if (launch_info.GetFlags().Test(eLaunchFlagExec)) 831061da546Spatrick flags |= POSIX_SPAWN_SETEXEC; // Darwin specific posix_spawn flag 832061da546Spatrick 833061da546Spatrick if (launch_info.GetFlags().Test(eLaunchFlagDebug)) 834061da546Spatrick flags |= POSIX_SPAWN_START_SUSPENDED; // Darwin specific posix_spawn flag 835061da546Spatrick 836061da546Spatrick if (launch_info.GetFlags().Test(eLaunchFlagDisableASLR)) 837061da546Spatrick flags |= _POSIX_SPAWN_DISABLE_ASLR; // Darwin specific posix_spawn flag 838061da546Spatrick 839061da546Spatrick if (launch_info.GetLaunchInSeparateProcessGroup()) 840061da546Spatrick flags |= POSIX_SPAWN_SETPGROUP; 841061da546Spatrick 842061da546Spatrick#ifdef POSIX_SPAWN_CLOEXEC_DEFAULT 843061da546Spatrick#if defined(__x86_64__) || defined(__i386__) 844061da546Spatrick static LazyBool g_use_close_on_exec_flag = eLazyBoolCalculate; 845061da546Spatrick if (g_use_close_on_exec_flag == eLazyBoolCalculate) { 846061da546Spatrick g_use_close_on_exec_flag = eLazyBoolNo; 847061da546Spatrick 848061da546Spatrick llvm::VersionTuple version = HostInfo::GetOSVersion(); 849061da546Spatrick if (version > llvm::VersionTuple(10, 7)) { 850061da546Spatrick // Kernel panic if we use the POSIX_SPAWN_CLOEXEC_DEFAULT on 10.7 or 851061da546Spatrick // earlier 852061da546Spatrick g_use_close_on_exec_flag = eLazyBoolYes; 853061da546Spatrick } 854061da546Spatrick } 855061da546Spatrick#else 856061da546Spatrick static LazyBool g_use_close_on_exec_flag = eLazyBoolYes; 857061da546Spatrick#endif // defined(__x86_64__) || defined(__i386__) 858061da546Spatrick // Close all files exception those with file actions if this is supported. 859061da546Spatrick if (g_use_close_on_exec_flag == eLazyBoolYes) 860061da546Spatrick flags |= POSIX_SPAWN_CLOEXEC_DEFAULT; 861061da546Spatrick#endif // ifdef POSIX_SPAWN_CLOEXEC_DEFAULT 862061da546Spatrick return flags; 863061da546Spatrick} 864061da546Spatrick 865061da546Spatrickstatic Status LaunchProcessXPC(const char *exe_path, 866061da546Spatrick ProcessLaunchInfo &launch_info, 867061da546Spatrick lldb::pid_t &pid) { 868dda28197Spatrick#if TARGET_OS_OSX 869061da546Spatrick Status error = getXPCAuthorization(launch_info); 870061da546Spatrick if (error.Fail()) 871061da546Spatrick return error; 872061da546Spatrick 873*f6aab3d8Srobert Log *log(GetLog(LLDBLog::Host | LLDBLog::Process)); 874061da546Spatrick 875061da546Spatrick uid_t requested_uid = launch_info.GetUserID(); 876061da546Spatrick const char *xpc_service = nil; 877061da546Spatrick bool send_auth = false; 878061da546Spatrick AuthorizationExternalForm extForm; 879061da546Spatrick if (requested_uid == 0) { 880061da546Spatrick if (AuthorizationMakeExternalForm(authorizationRef, &extForm) == 881061da546Spatrick errAuthorizationSuccess) { 882061da546Spatrick send_auth = true; 883061da546Spatrick } else { 884061da546Spatrick error.SetError(3, eErrorTypeGeneric); 885061da546Spatrick error.SetErrorStringWithFormat("Launching root via XPC needs to " 886061da546Spatrick "externalize authorization reference."); 887061da546Spatrick LLDB_LOG(log, "error: {0}", error); 888061da546Spatrick return error; 889061da546Spatrick } 890061da546Spatrick xpc_service = LaunchUsingXPCRightName; 891061da546Spatrick } else { 892061da546Spatrick error.SetError(4, eErrorTypeGeneric); 893061da546Spatrick error.SetErrorStringWithFormat( 894061da546Spatrick "Launching via XPC is only currently available for root."); 895061da546Spatrick LLDB_LOG(log, "error: {0}", error); 896061da546Spatrick return error; 897061da546Spatrick } 898061da546Spatrick 899061da546Spatrick xpc_connection_t conn = xpc_connection_create(xpc_service, NULL); 900061da546Spatrick 901061da546Spatrick xpc_connection_set_event_handler(conn, ^(xpc_object_t event) { 902061da546Spatrick xpc_type_t type = xpc_get_type(event); 903061da546Spatrick 904061da546Spatrick if (type == XPC_TYPE_ERROR) { 905061da546Spatrick if (event == XPC_ERROR_CONNECTION_INTERRUPTED) { 906061da546Spatrick // The service has either canceled itself, crashed, or been terminated. 907061da546Spatrick // The XPC connection is still valid and sending a message to it will 908061da546Spatrick // re-launch the service. 909061da546Spatrick // If the service is state-full, this is the time to initialize the new 910061da546Spatrick // service. 911061da546Spatrick return; 912061da546Spatrick } else if (event == XPC_ERROR_CONNECTION_INVALID) { 913061da546Spatrick // The service is invalid. Either the service name supplied to 914061da546Spatrick // xpc_connection_create() is incorrect 915061da546Spatrick // or we (this process) have canceled the service; we can do any cleanup 916061da546Spatrick // of application state at this point. 917061da546Spatrick // printf("Service disconnected"); 918061da546Spatrick return; 919061da546Spatrick } else { 920061da546Spatrick // printf("Unexpected error from service: %s", 921061da546Spatrick // xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION)); 922061da546Spatrick } 923061da546Spatrick 924061da546Spatrick } else { 925061da546Spatrick // printf("Received unexpected event in handler"); 926061da546Spatrick } 927061da546Spatrick }); 928061da546Spatrick 929061da546Spatrick xpc_connection_set_finalizer_f(conn, xpc_finalizer_t(xpc_release)); 930061da546Spatrick xpc_connection_resume(conn); 931061da546Spatrick xpc_object_t message = xpc_dictionary_create(nil, nil, 0); 932061da546Spatrick 933061da546Spatrick if (send_auth) { 934061da546Spatrick xpc_dictionary_set_data(message, LauncherXPCServiceAuthKey, extForm.bytes, 935061da546Spatrick sizeof(AuthorizationExternalForm)); 936061da546Spatrick } 937061da546Spatrick 938061da546Spatrick PackageXPCArguments(message, LauncherXPCServiceArgPrefxKey, 939061da546Spatrick launch_info.GetArguments()); 940061da546Spatrick PackageXPCEnvironment(message, LauncherXPCServiceEnvPrefxKey, 941061da546Spatrick launch_info.GetEnvironment()); 942061da546Spatrick 943061da546Spatrick // Posix spawn stuff. 944061da546Spatrick xpc_dictionary_set_int64(message, LauncherXPCServiceCPUTypeKey, 945061da546Spatrick launch_info.GetArchitecture().GetMachOCPUType()); 946061da546Spatrick xpc_dictionary_set_int64(message, LauncherXPCServicePosixspawnFlagsKey, 947061da546Spatrick GetPosixspawnFlags(launch_info)); 948061da546Spatrick const FileAction *file_action = launch_info.GetFileActionForFD(STDIN_FILENO); 949061da546Spatrick if (file_action && !file_action->GetPath().empty()) { 950061da546Spatrick xpc_dictionary_set_string(message, LauncherXPCServiceStdInPathKeyKey, 951061da546Spatrick file_action->GetPath().str().c_str()); 952061da546Spatrick } 953061da546Spatrick file_action = launch_info.GetFileActionForFD(STDOUT_FILENO); 954061da546Spatrick if (file_action && !file_action->GetPath().empty()) { 955061da546Spatrick xpc_dictionary_set_string(message, LauncherXPCServiceStdOutPathKeyKey, 956061da546Spatrick file_action->GetPath().str().c_str()); 957061da546Spatrick } 958061da546Spatrick file_action = launch_info.GetFileActionForFD(STDERR_FILENO); 959061da546Spatrick if (file_action && !file_action->GetPath().empty()) { 960061da546Spatrick xpc_dictionary_set_string(message, LauncherXPCServiceStdErrPathKeyKey, 961061da546Spatrick file_action->GetPath().str().c_str()); 962061da546Spatrick } 963061da546Spatrick 964061da546Spatrick xpc_object_t reply = 965061da546Spatrick xpc_connection_send_message_with_reply_sync(conn, message); 966061da546Spatrick xpc_type_t returnType = xpc_get_type(reply); 967061da546Spatrick if (returnType == XPC_TYPE_DICTIONARY) { 968061da546Spatrick pid = xpc_dictionary_get_int64(reply, LauncherXPCServiceChildPIDKey); 969061da546Spatrick if (pid == 0) { 970061da546Spatrick int errorType = 971061da546Spatrick xpc_dictionary_get_int64(reply, LauncherXPCServiceErrorTypeKey); 972061da546Spatrick int errorCode = 973061da546Spatrick xpc_dictionary_get_int64(reply, LauncherXPCServiceCodeTypeKey); 974061da546Spatrick 975061da546Spatrick error.SetError(errorCode, eErrorTypeGeneric); 976061da546Spatrick error.SetErrorStringWithFormat( 977061da546Spatrick "Problems with launching via XPC. Error type : %i, code : %i", 978061da546Spatrick errorType, errorCode); 979061da546Spatrick LLDB_LOG(log, "error: {0}", error); 980061da546Spatrick 981061da546Spatrick if (authorizationRef) { 982061da546Spatrick AuthorizationFree(authorizationRef, kAuthorizationFlagDefaults); 983061da546Spatrick authorizationRef = NULL; 984061da546Spatrick } 985061da546Spatrick } 986061da546Spatrick } else if (returnType == XPC_TYPE_ERROR) { 987061da546Spatrick error.SetError(5, eErrorTypeGeneric); 988061da546Spatrick error.SetErrorStringWithFormat( 989061da546Spatrick "Problems with launching via XPC. XPC error : %s", 990061da546Spatrick xpc_dictionary_get_string(reply, XPC_ERROR_KEY_DESCRIPTION)); 991061da546Spatrick LLDB_LOG(log, "error: {0}", error); 992061da546Spatrick } 993061da546Spatrick 994061da546Spatrick return error; 995061da546Spatrick#else 996061da546Spatrick Status error; 997061da546Spatrick return error; 998061da546Spatrick#endif 999061da546Spatrick} 1000061da546Spatrick 1001061da546Spatrickstatic bool AddPosixSpawnFileAction(void *_file_actions, const FileAction *info, 1002061da546Spatrick Log *log, Status &error) { 1003061da546Spatrick if (info == NULL) 1004061da546Spatrick return false; 1005061da546Spatrick 1006061da546Spatrick posix_spawn_file_actions_t *file_actions = 1007061da546Spatrick static_cast<posix_spawn_file_actions_t *>(_file_actions); 1008061da546Spatrick 1009061da546Spatrick switch (info->GetAction()) { 1010061da546Spatrick case FileAction::eFileActionNone: 1011061da546Spatrick error.Clear(); 1012061da546Spatrick break; 1013061da546Spatrick 1014061da546Spatrick case FileAction::eFileActionClose: 1015061da546Spatrick if (info->GetFD() == -1) 1016061da546Spatrick error.SetErrorString( 1017061da546Spatrick "invalid fd for posix_spawn_file_actions_addclose(...)"); 1018061da546Spatrick else { 1019061da546Spatrick error.SetError( 1020061da546Spatrick ::posix_spawn_file_actions_addclose(file_actions, info->GetFD()), 1021061da546Spatrick eErrorTypePOSIX); 1022061da546Spatrick if (error.Fail()) 1023061da546Spatrick LLDB_LOG(log, 1024061da546Spatrick "error: {0}, posix_spawn_file_actions_addclose " 1025061da546Spatrick "(action={1}, fd={2})", 1026061da546Spatrick error, file_actions, info->GetFD()); 1027061da546Spatrick } 1028061da546Spatrick break; 1029061da546Spatrick 1030061da546Spatrick case FileAction::eFileActionDuplicate: 1031061da546Spatrick if (info->GetFD() == -1) 1032061da546Spatrick error.SetErrorString( 1033061da546Spatrick "invalid fd for posix_spawn_file_actions_adddup2(...)"); 1034061da546Spatrick else if (info->GetActionArgument() == -1) 1035061da546Spatrick error.SetErrorString( 1036061da546Spatrick "invalid duplicate fd for posix_spawn_file_actions_adddup2(...)"); 1037061da546Spatrick else { 1038061da546Spatrick error.SetError( 1039061da546Spatrick ::posix_spawn_file_actions_adddup2(file_actions, info->GetFD(), 1040061da546Spatrick info->GetActionArgument()), 1041061da546Spatrick eErrorTypePOSIX); 1042061da546Spatrick if (error.Fail()) 1043061da546Spatrick LLDB_LOG(log, 1044061da546Spatrick "error: {0}, posix_spawn_file_actions_adddup2 " 1045061da546Spatrick "(action={1}, fd={2}, dup_fd={3})", 1046061da546Spatrick error, file_actions, info->GetFD(), info->GetActionArgument()); 1047061da546Spatrick } 1048061da546Spatrick break; 1049061da546Spatrick 1050061da546Spatrick case FileAction::eFileActionOpen: 1051061da546Spatrick if (info->GetFD() == -1) 1052061da546Spatrick error.SetErrorString( 1053061da546Spatrick "invalid fd in posix_spawn_file_actions_addopen(...)"); 1054061da546Spatrick else { 1055061da546Spatrick int oflag = info->GetActionArgument(); 1056061da546Spatrick 1057061da546Spatrick mode_t mode = 0; 1058061da546Spatrick 1059061da546Spatrick if (oflag & O_CREAT) 1060061da546Spatrick mode = 0640; 1061061da546Spatrick 1062061da546Spatrick error.SetError(::posix_spawn_file_actions_addopen( 1063061da546Spatrick file_actions, info->GetFD(), 1064061da546Spatrick info->GetPath().str().c_str(), oflag, mode), 1065061da546Spatrick eErrorTypePOSIX); 1066061da546Spatrick if (error.Fail()) 1067061da546Spatrick LLDB_LOG(log, 1068061da546Spatrick "error: {0}, posix_spawn_file_actions_addopen (action={1}, " 1069061da546Spatrick "fd={2}, path='{3}', oflag={4}, mode={5})", 1070061da546Spatrick error, file_actions, info->GetFD(), info->GetPath(), oflag, 1071061da546Spatrick mode); 1072061da546Spatrick } 1073061da546Spatrick break; 1074061da546Spatrick } 1075061da546Spatrick return error.Success(); 1076061da546Spatrick} 1077061da546Spatrick 1078061da546Spatrickstatic Status LaunchProcessPosixSpawn(const char *exe_path, 1079061da546Spatrick const ProcessLaunchInfo &launch_info, 1080061da546Spatrick lldb::pid_t &pid) { 1081061da546Spatrick Status error; 1082*f6aab3d8Srobert Log *log(GetLog(LLDBLog::Host | LLDBLog::Process)); 1083061da546Spatrick 1084061da546Spatrick posix_spawnattr_t attr; 1085061da546Spatrick error.SetError(::posix_spawnattr_init(&attr), eErrorTypePOSIX); 1086061da546Spatrick 1087061da546Spatrick if (error.Fail()) { 1088061da546Spatrick LLDB_LOG(log, "error: {0}, ::posix_spawnattr_init ( &attr )", error); 1089061da546Spatrick return error; 1090061da546Spatrick } 1091061da546Spatrick 1092061da546Spatrick // Make sure we clean up the posix spawn attributes before exiting this scope. 1093061da546Spatrick auto cleanup_attr = 1094061da546Spatrick llvm::make_scope_exit([&]() { posix_spawnattr_destroy(&attr); }); 1095061da546Spatrick 1096061da546Spatrick sigset_t no_signals; 1097061da546Spatrick sigset_t all_signals; 1098061da546Spatrick sigemptyset(&no_signals); 1099061da546Spatrick sigfillset(&all_signals); 1100061da546Spatrick ::posix_spawnattr_setsigmask(&attr, &no_signals); 1101061da546Spatrick ::posix_spawnattr_setsigdefault(&attr, &all_signals); 1102061da546Spatrick 1103061da546Spatrick short flags = GetPosixspawnFlags(launch_info); 1104061da546Spatrick 1105061da546Spatrick error.SetError(::posix_spawnattr_setflags(&attr, flags), eErrorTypePOSIX); 1106061da546Spatrick if (error.Fail()) { 1107061da546Spatrick LLDB_LOG(log, 1108061da546Spatrick "error: {0}, ::posix_spawnattr_setflags ( &attr, flags={1:x} )", 1109061da546Spatrick error, flags); 1110061da546Spatrick return error; 1111061da546Spatrick } 1112061da546Spatrick 1113be691f3bSpatrick bool is_graphical = true; 1114be691f3bSpatrick 1115be691f3bSpatrick#if TARGET_OS_OSX 1116be691f3bSpatrick SecuritySessionId session_id; 1117be691f3bSpatrick SessionAttributeBits session_attributes; 1118be691f3bSpatrick OSStatus status = 1119be691f3bSpatrick SessionGetInfo(callerSecuritySession, &session_id, &session_attributes); 1120be691f3bSpatrick if (status == errSessionSuccess) 1121be691f3bSpatrick is_graphical = session_attributes & sessionHasGraphicAccess; 1122be691f3bSpatrick#endif 1123be691f3bSpatrick 1124be691f3bSpatrick // When lldb is ran through a graphical session, make the debuggee process 1125be691f3bSpatrick // responsible for its own TCC permissions instead of inheriting them from 1126be691f3bSpatrick // its parent. 1127be691f3bSpatrick if (is_graphical && launch_info.GetFlags().Test(eLaunchFlagDebug) && 1128be691f3bSpatrick !launch_info.GetFlags().Test(eLaunchFlagInheritTCCFromParent)) { 1129be691f3bSpatrick error.SetError(setup_posix_spawn_responsible_flag(&attr), eErrorTypePOSIX); 1130be691f3bSpatrick if (error.Fail()) { 1131be691f3bSpatrick LLDB_LOG(log, "error: {0}, setup_posix_spawn_responsible_flag(&attr)", 1132be691f3bSpatrick error); 1133be691f3bSpatrick return error; 1134be691f3bSpatrick } 1135be691f3bSpatrick } 1136be691f3bSpatrick 1137be691f3bSpatrick // Don't set the binpref if a shell was provided. After all, that's only 1138be691f3bSpatrick // going to affect what version of the shell is launched, not what fork of 1139be691f3bSpatrick // the binary is launched. We insert "arch --arch <ARCH> as part of the 1140be691f3bSpatrick // shell invocation to do that job on OSX. 1141be691f3bSpatrick if (launch_info.GetShell() == FileSpec()) { 1142be691f3bSpatrick const ArchSpec &arch_spec = launch_info.GetArchitecture(); 1143be691f3bSpatrick cpu_type_t cpu_type = arch_spec.GetMachOCPUType(); 1144be691f3bSpatrick cpu_type_t cpu_subtype = arch_spec.GetMachOCPUSubType(); 1145be691f3bSpatrick const bool set_cpu_type = 1146be691f3bSpatrick cpu_type != 0 && cpu_type != static_cast<cpu_type_t>(UINT32_MAX) && 1147be691f3bSpatrick cpu_type != static_cast<cpu_type_t>(LLDB_INVALID_CPUTYPE); 1148be691f3bSpatrick const bool set_cpu_subtype = 1149be691f3bSpatrick cpu_subtype != 0 && 1150be691f3bSpatrick cpu_subtype != static_cast<cpu_subtype_t>(UINT32_MAX) && 1151be691f3bSpatrick cpu_subtype != CPU_SUBTYPE_X86_64_H; 1152be691f3bSpatrick if (set_cpu_type) { 1153be691f3bSpatrick size_t ocount = 0; 1154be691f3bSpatrick typedef int (*posix_spawnattr_setarchpref_np_t)( 1155be691f3bSpatrick posix_spawnattr_t *, size_t, cpu_type_t *, cpu_subtype_t *, size_t *); 1156be691f3bSpatrick posix_spawnattr_setarchpref_np_t posix_spawnattr_setarchpref_np_fn = 1157be691f3bSpatrick (posix_spawnattr_setarchpref_np_t)dlsym( 1158be691f3bSpatrick RTLD_DEFAULT, "posix_spawnattr_setarchpref_np"); 1159be691f3bSpatrick if (set_cpu_subtype && posix_spawnattr_setarchpref_np_fn) { 1160be691f3bSpatrick error.SetError((*posix_spawnattr_setarchpref_np_fn)( 1161be691f3bSpatrick &attr, 1, &cpu_type, &cpu_subtype, &ocount), 1162be691f3bSpatrick eErrorTypePOSIX); 1163be691f3bSpatrick if (error.Fail()) 1164be691f3bSpatrick LLDB_LOG(log, 1165be691f3bSpatrick "error: {0}, ::posix_spawnattr_setarchpref_np ( &attr, 1, " 1166be691f3bSpatrick "cpu_type = {1:x}, cpu_subtype = {1:x}, count => {2} )", 1167be691f3bSpatrick error, cpu_type, cpu_subtype, ocount); 1168be691f3bSpatrick 1169be691f3bSpatrick if (error.Fail() || ocount != 1) 1170be691f3bSpatrick return error; 1171be691f3bSpatrick } else { 1172be691f3bSpatrick error.SetError( 1173be691f3bSpatrick ::posix_spawnattr_setbinpref_np(&attr, 1, &cpu_type, &ocount), 1174be691f3bSpatrick eErrorTypePOSIX); 1175be691f3bSpatrick if (error.Fail()) 1176be691f3bSpatrick LLDB_LOG(log, 1177be691f3bSpatrick "error: {0}, ::posix_spawnattr_setbinpref_np ( &attr, 1, " 1178be691f3bSpatrick "cpu_type = {1:x}, count => {2} )", 1179be691f3bSpatrick error, cpu_type, ocount); 1180be691f3bSpatrick if (error.Fail() || ocount != 1) 1181be691f3bSpatrick return error; 1182be691f3bSpatrick } 1183be691f3bSpatrick } 1184be691f3bSpatrick } 1185be691f3bSpatrick 1186061da546Spatrick const char *tmp_argv[2]; 1187061da546Spatrick char *const *argv = const_cast<char *const *>( 1188061da546Spatrick launch_info.GetArguments().GetConstArgumentVector()); 1189061da546Spatrick Environment::Envp envp = launch_info.GetEnvironment().getEnvp(); 1190061da546Spatrick if (argv == NULL) { 1191061da546Spatrick // posix_spawn gets very unhappy if it doesn't have at least the program 1192061da546Spatrick // name in argv[0]. One of the side affects I have noticed is the 1193061da546Spatrick // environment 1194061da546Spatrick // variables don't make it into the child process if "argv == NULL"!!! 1195061da546Spatrick tmp_argv[0] = exe_path; 1196061da546Spatrick tmp_argv[1] = NULL; 1197061da546Spatrick argv = const_cast<char *const *>(tmp_argv); 1198061da546Spatrick } 1199061da546Spatrick 1200061da546Spatrick FileSpec working_dir{launch_info.GetWorkingDirectory()}; 1201061da546Spatrick if (working_dir) { 1202061da546Spatrick // Set the working directory on this thread only 1203*f6aab3d8Srobert std::string working_dir_path = working_dir.GetPath(); 1204*f6aab3d8Srobert if (__pthread_chdir(working_dir_path.c_str()) < 0) { 1205061da546Spatrick if (errno == ENOENT) { 1206061da546Spatrick error.SetErrorStringWithFormat("No such file or directory: %s", 1207*f6aab3d8Srobert working_dir_path.c_str()); 1208061da546Spatrick } else if (errno == ENOTDIR) { 1209061da546Spatrick error.SetErrorStringWithFormat("Path doesn't name a directory: %s", 1210*f6aab3d8Srobert working_dir_path.c_str()); 1211061da546Spatrick } else { 1212061da546Spatrick error.SetErrorStringWithFormat("An unknown error occurred when " 1213061da546Spatrick "changing directory for process " 1214061da546Spatrick "execution."); 1215061da546Spatrick } 1216061da546Spatrick return error; 1217061da546Spatrick } 1218061da546Spatrick } 1219061da546Spatrick 1220061da546Spatrick ::pid_t result_pid = LLDB_INVALID_PROCESS_ID; 1221061da546Spatrick const size_t num_file_actions = launch_info.GetNumFileActions(); 1222061da546Spatrick if (num_file_actions > 0) { 1223061da546Spatrick posix_spawn_file_actions_t file_actions; 1224061da546Spatrick error.SetError(::posix_spawn_file_actions_init(&file_actions), 1225061da546Spatrick eErrorTypePOSIX); 1226061da546Spatrick if (error.Fail()) { 1227061da546Spatrick LLDB_LOG(log, 1228061da546Spatrick "error: {0}, ::posix_spawn_file_actions_init ( &file_actions )", 1229061da546Spatrick error); 1230061da546Spatrick return error; 1231061da546Spatrick } 1232061da546Spatrick 1233061da546Spatrick // Make sure we clean up the posix file actions before exiting this scope. 1234061da546Spatrick auto cleanup_fileact = llvm::make_scope_exit( 1235061da546Spatrick [&]() { posix_spawn_file_actions_destroy(&file_actions); }); 1236061da546Spatrick 1237061da546Spatrick for (size_t i = 0; i < num_file_actions; ++i) { 1238061da546Spatrick const FileAction *launch_file_action = 1239061da546Spatrick launch_info.GetFileActionAtIndex(i); 1240061da546Spatrick if (launch_file_action) { 1241061da546Spatrick if (!AddPosixSpawnFileAction(&file_actions, launch_file_action, log, 1242061da546Spatrick error)) 1243061da546Spatrick return error; 1244061da546Spatrick } 1245061da546Spatrick } 1246061da546Spatrick 1247061da546Spatrick error.SetError( 1248061da546Spatrick ::posix_spawnp(&result_pid, exe_path, &file_actions, &attr, argv, envp), 1249061da546Spatrick eErrorTypePOSIX); 1250061da546Spatrick 1251061da546Spatrick if (error.Fail()) { 1252061da546Spatrick LLDB_LOG(log, 1253061da546Spatrick "error: {0}, ::posix_spawnp(pid => {1}, path = '{2}', " 1254061da546Spatrick "file_actions = {3}, " 1255061da546Spatrick "attr = {4}, argv = {5}, envp = {6} )", 1256061da546Spatrick error, result_pid, exe_path, &file_actions, &attr, argv, 1257061da546Spatrick envp.get()); 1258061da546Spatrick if (log) { 1259061da546Spatrick for (int ii = 0; argv[ii]; ++ii) 1260061da546Spatrick LLDB_LOG(log, "argv[{0}] = '{1}'", ii, argv[ii]); 1261061da546Spatrick } 1262061da546Spatrick } 1263061da546Spatrick 1264061da546Spatrick } else { 1265061da546Spatrick error.SetError( 1266061da546Spatrick ::posix_spawnp(&result_pid, exe_path, NULL, &attr, argv, envp), 1267061da546Spatrick eErrorTypePOSIX); 1268061da546Spatrick 1269061da546Spatrick if (error.Fail()) { 1270061da546Spatrick LLDB_LOG(log, 1271061da546Spatrick "error: {0}, ::posix_spawnp ( pid => {1}, path = '{2}', " 1272061da546Spatrick "file_actions = NULL, attr = {3}, argv = {4}, envp = {5} )", 1273061da546Spatrick error, result_pid, exe_path, &attr, argv, envp.get()); 1274061da546Spatrick if (log) { 1275061da546Spatrick for (int ii = 0; argv[ii]; ++ii) 1276061da546Spatrick LLDB_LOG(log, "argv[{0}] = '{1}'", ii, argv[ii]); 1277061da546Spatrick } 1278061da546Spatrick } 1279061da546Spatrick } 1280061da546Spatrick pid = result_pid; 1281061da546Spatrick 1282061da546Spatrick if (working_dir) { 1283061da546Spatrick // No more thread specific current working directory 1284061da546Spatrick __pthread_fchdir(-1); 1285061da546Spatrick } 1286061da546Spatrick 1287061da546Spatrick return error; 1288061da546Spatrick} 1289061da546Spatrick 1290061da546Spatrickstatic bool ShouldLaunchUsingXPC(ProcessLaunchInfo &launch_info) { 1291061da546Spatrick bool result = false; 1292061da546Spatrick 1293dda28197Spatrick#if TARGET_OS_OSX 1294061da546Spatrick bool launchingAsRoot = launch_info.GetUserID() == 0; 1295061da546Spatrick bool currentUserIsRoot = HostInfo::GetEffectiveUserID() == 0; 1296061da546Spatrick 1297061da546Spatrick if (launchingAsRoot && !currentUserIsRoot) { 1298061da546Spatrick // If current user is already root, we don't need XPC's help. 1299061da546Spatrick result = true; 1300061da546Spatrick } 1301061da546Spatrick#endif 1302061da546Spatrick 1303061da546Spatrick return result; 1304061da546Spatrick} 1305061da546Spatrick 1306061da546SpatrickStatus Host::LaunchProcess(ProcessLaunchInfo &launch_info) { 1307061da546Spatrick Status error; 1308061da546Spatrick 1309061da546Spatrick FileSystem &fs = FileSystem::Instance(); 1310061da546Spatrick FileSpec exe_spec(launch_info.GetExecutableFile()); 1311061da546Spatrick 1312061da546Spatrick if (!fs.Exists(exe_spec)) 1313061da546Spatrick FileSystem::Instance().Resolve(exe_spec); 1314061da546Spatrick 1315061da546Spatrick if (!fs.Exists(exe_spec)) 1316061da546Spatrick FileSystem::Instance().ResolveExecutableLocation(exe_spec); 1317061da546Spatrick 1318061da546Spatrick if (!fs.Exists(exe_spec)) { 1319061da546Spatrick error.SetErrorStringWithFormatv("executable doesn't exist: '{0}'", 1320061da546Spatrick exe_spec); 1321061da546Spatrick return error; 1322061da546Spatrick } 1323061da546Spatrick 1324061da546Spatrick if (launch_info.GetFlags().Test(eLaunchFlagLaunchInTTY)) { 1325dda28197Spatrick#if TARGET_OS_OSX 1326061da546Spatrick return LaunchInNewTerminalWithAppleScript(exe_spec.GetPath().c_str(), 1327061da546Spatrick launch_info); 1328061da546Spatrick#else 1329061da546Spatrick error.SetErrorString("launching a process in a new terminal is not " 1330061da546Spatrick "supported on iOS devices"); 1331061da546Spatrick return error; 1332061da546Spatrick#endif 1333061da546Spatrick } 1334061da546Spatrick 1335061da546Spatrick lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; 1336061da546Spatrick 1337*f6aab3d8Srobert auto exe_path = exe_spec.GetPath(); 1338061da546Spatrick 1339061da546Spatrick if (ShouldLaunchUsingXPC(launch_info)) 1340*f6aab3d8Srobert error = LaunchProcessXPC(exe_path.c_str(), launch_info, pid); 1341061da546Spatrick else 1342*f6aab3d8Srobert error = LaunchProcessPosixSpawn(exe_path.c_str(), launch_info, pid); 1343061da546Spatrick 1344061da546Spatrick if (pid != LLDB_INVALID_PROCESS_ID) { 1345061da546Spatrick // If all went well, then set the process ID into the launch info 1346061da546Spatrick launch_info.SetProcessID(pid); 1347061da546Spatrick 1348061da546Spatrick // Make sure we reap any processes we spawn or we will have zombies. 1349061da546Spatrick bool monitoring = launch_info.MonitorProcess(); 1350061da546Spatrick UNUSED_IF_ASSERT_DISABLED(monitoring); 1351061da546Spatrick assert(monitoring); 1352061da546Spatrick } else { 1353061da546Spatrick // Invalid process ID, something didn't go well 1354061da546Spatrick if (error.Success()) 1355061da546Spatrick error.SetErrorString("process launch failed for unknown reasons"); 1356061da546Spatrick } 1357061da546Spatrick return error; 1358061da546Spatrick} 1359061da546Spatrick 1360061da546SpatrickStatus Host::ShellExpandArguments(ProcessLaunchInfo &launch_info) { 1361061da546Spatrick Status error; 1362061da546Spatrick if (launch_info.GetFlags().Test(eLaunchFlagShellExpandArguments)) { 1363061da546Spatrick FileSpec expand_tool_spec = HostInfo::GetSupportExeDir(); 1364061da546Spatrick if (!expand_tool_spec) { 1365061da546Spatrick error.SetErrorString( 1366061da546Spatrick "could not get support executable directory for lldb-argdumper tool"); 1367061da546Spatrick return error; 1368061da546Spatrick } 1369061da546Spatrick expand_tool_spec.AppendPathComponent("lldb-argdumper"); 1370061da546Spatrick if (!FileSystem::Instance().Exists(expand_tool_spec)) { 1371061da546Spatrick error.SetErrorStringWithFormat( 1372061da546Spatrick "could not find the lldb-argdumper tool: %s", 1373061da546Spatrick expand_tool_spec.GetPath().c_str()); 1374061da546Spatrick return error; 1375061da546Spatrick } 1376061da546Spatrick 1377061da546Spatrick StreamString expand_tool_spec_stream; 1378061da546Spatrick expand_tool_spec_stream.Printf("\"%s\"", 1379061da546Spatrick expand_tool_spec.GetPath().c_str()); 1380061da546Spatrick 1381061da546Spatrick Args expand_command(expand_tool_spec_stream.GetData()); 1382061da546Spatrick expand_command.AppendArguments(launch_info.GetArguments()); 1383061da546Spatrick 1384061da546Spatrick int status; 1385061da546Spatrick std::string output; 1386061da546Spatrick FileSpec cwd(launch_info.GetWorkingDirectory()); 1387061da546Spatrick if (!FileSystem::Instance().Exists(cwd)) { 1388061da546Spatrick char *wd = getcwd(nullptr, 0); 1389061da546Spatrick if (wd == nullptr) { 1390061da546Spatrick error.SetErrorStringWithFormat( 1391061da546Spatrick "cwd does not exist; cannot launch with shell argument expansion"); 1392061da546Spatrick return error; 1393061da546Spatrick } else { 1394061da546Spatrick FileSpec working_dir(wd); 1395061da546Spatrick free(wd); 1396061da546Spatrick launch_info.SetWorkingDirectory(working_dir); 1397061da546Spatrick } 1398061da546Spatrick } 1399be691f3bSpatrick bool run_in_shell = true; 1400061da546Spatrick bool hide_stderr = true; 1401be691f3bSpatrick Status e = 1402be691f3bSpatrick RunShellCommand(expand_command, cwd, &status, nullptr, &output, 1403be691f3bSpatrick std::chrono::seconds(10), run_in_shell, hide_stderr); 1404061da546Spatrick 1405061da546Spatrick if (e.Fail()) 1406061da546Spatrick return e; 1407061da546Spatrick 1408061da546Spatrick if (status != 0) { 1409061da546Spatrick error.SetErrorStringWithFormat("lldb-argdumper exited with error %d", 1410061da546Spatrick status); 1411061da546Spatrick return error; 1412061da546Spatrick } 1413061da546Spatrick 1414061da546Spatrick auto data_sp = StructuredData::ParseJSON(output); 1415061da546Spatrick if (!data_sp) { 1416061da546Spatrick error.SetErrorString("invalid JSON"); 1417061da546Spatrick return error; 1418061da546Spatrick } 1419061da546Spatrick 1420061da546Spatrick auto dict_sp = data_sp->GetAsDictionary(); 1421061da546Spatrick if (!data_sp) { 1422061da546Spatrick error.SetErrorString("invalid JSON"); 1423061da546Spatrick return error; 1424061da546Spatrick } 1425061da546Spatrick 1426061da546Spatrick auto args_sp = dict_sp->GetObjectForDotSeparatedPath("arguments"); 1427061da546Spatrick if (!args_sp) { 1428061da546Spatrick error.SetErrorString("invalid JSON"); 1429061da546Spatrick return error; 1430061da546Spatrick } 1431061da546Spatrick 1432061da546Spatrick auto args_array_sp = args_sp->GetAsArray(); 1433061da546Spatrick if (!args_array_sp) { 1434061da546Spatrick error.SetErrorString("invalid JSON"); 1435061da546Spatrick return error; 1436061da546Spatrick } 1437061da546Spatrick 1438061da546Spatrick launch_info.GetArguments().Clear(); 1439061da546Spatrick 1440061da546Spatrick for (size_t i = 0; i < args_array_sp->GetSize(); i++) { 1441061da546Spatrick auto item_sp = args_array_sp->GetItemAtIndex(i); 1442061da546Spatrick if (!item_sp) 1443061da546Spatrick continue; 1444061da546Spatrick auto str_sp = item_sp->GetAsString(); 1445061da546Spatrick if (!str_sp) 1446061da546Spatrick continue; 1447061da546Spatrick 1448061da546Spatrick launch_info.GetArguments().AppendArgument(str_sp->GetValue()); 1449061da546Spatrick } 1450061da546Spatrick } 1451061da546Spatrick 1452061da546Spatrick return error; 1453061da546Spatrick} 1454061da546Spatrick 1455061da546Spatrickllvm::Expected<HostThread> Host::StartMonitoringChildProcess( 1456*f6aab3d8Srobert const Host::MonitorChildProcessCallback &callback, lldb::pid_t pid) { 1457061da546Spatrick unsigned long mask = DISPATCH_PROC_EXIT; 1458061da546Spatrick 1459*f6aab3d8Srobert Log *log(GetLog(LLDBLog::Host | LLDBLog::Process)); 1460061da546Spatrick 1461061da546Spatrick dispatch_source_t source = ::dispatch_source_create( 1462061da546Spatrick DISPATCH_SOURCE_TYPE_PROC, pid, mask, 1463061da546Spatrick ::dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)); 1464061da546Spatrick 1465061da546Spatrick LLDB_LOGF(log, 1466*f6aab3d8Srobert "Host::StartMonitoringChildProcess(callback, pid=%i) source = %p\n", 1467*f6aab3d8Srobert static_cast<int>(pid), static_cast<void *>(source)); 1468061da546Spatrick 1469061da546Spatrick if (source) { 1470061da546Spatrick Host::MonitorChildProcessCallback callback_copy = callback; 1471061da546Spatrick ::dispatch_source_set_cancel_handler(source, ^{ 1472061da546Spatrick dispatch_release(source); 1473061da546Spatrick }); 1474061da546Spatrick ::dispatch_source_set_event_handler(source, ^{ 1475061da546Spatrick 1476061da546Spatrick int status = 0; 1477061da546Spatrick int wait_pid = 0; 1478061da546Spatrick wait_pid = llvm::sys::RetryAfterSignal(-1, ::waitpid, pid, &status, 0); 1479061da546Spatrick if (wait_pid >= 0) { 1480061da546Spatrick int signal = 0; 1481061da546Spatrick int exit_status = 0; 1482061da546Spatrick const char *status_cstr = NULL; 1483*f6aab3d8Srobert if (WIFEXITED(status)) { 1484061da546Spatrick exit_status = WEXITSTATUS(status); 1485061da546Spatrick status_cstr = "EXITED"; 1486061da546Spatrick } else if (WIFSIGNALED(status)) { 1487061da546Spatrick signal = WTERMSIG(status); 1488061da546Spatrick status_cstr = "SIGNALED"; 1489061da546Spatrick exit_status = -1; 1490061da546Spatrick } else { 1491*f6aab3d8Srobert llvm_unreachable("Unknown status"); 1492061da546Spatrick } 1493061da546Spatrick 1494061da546Spatrick LLDB_LOGF(log, 1495061da546Spatrick "::waitpid (pid = %llu, &status, 0) => pid = %i, status " 1496061da546Spatrick "= 0x%8.8x (%s), signal = %i, exit_status = %i", 1497061da546Spatrick pid, wait_pid, status, status_cstr, signal, exit_status); 1498061da546Spatrick 1499061da546Spatrick if (callback_copy) 1500*f6aab3d8Srobert callback_copy(pid, signal, exit_status); 1501061da546Spatrick 1502061da546Spatrick ::dispatch_source_cancel(source); 1503061da546Spatrick } 1504061da546Spatrick }); 1505061da546Spatrick 1506061da546Spatrick ::dispatch_resume(source); 1507061da546Spatrick } 1508061da546Spatrick return HostThread(); 1509061da546Spatrick} 1510