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