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