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