process_util_mac.mm (5827B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 // Copyright (c) 2008 The Chromium Authors. All rights reserved. 4 // Use of this source code is governed by a BSD-style license that can be 5 // found in the LICENSE file. 6 7 #include "base/process_util.h" 8 9 #include <fcntl.h> 10 #include <os/availability.h> 11 #include <spawn.h> 12 #include <sys/wait.h> 13 #include <unistd.h> 14 15 #include <string> 16 17 #include "base/command_line.h" 18 #include "base/eintr_wrapper.h" 19 #include "base/logging.h" 20 #include "mozilla/ipc/FileDescriptorShuffle.h" 21 #include "mozilla/ScopeExit.h" 22 #include "mozilla/Result.h" 23 24 #include "mozilla/ipc/LaunchError.h" 25 26 extern "C" { 27 // N.B. the syscalls are available back to 10.5, but the C wrappers 28 // only in 10.12. Fortunately, 10.15 is our current baseline. 29 int pthread_fchdir_np(int fd) API_AVAILABLE(macosx(10.12)); 30 31 int responsibility_spawnattrs_setdisclaim(posix_spawnattr_t attrs, int disclaim) 32 API_AVAILABLE(macosx(10.14)); 33 } 34 35 namespace { 36 37 MOZ_RUNINIT static mozilla::EnvironmentLog gProcessLog("MOZ_PROCESS_LOG"); 38 39 } // namespace 40 41 namespace base { 42 43 Result<Ok, LaunchError> LaunchApp(const std::vector<std::string>& argv, 44 LaunchOptions&& options, 45 ProcessHandle* process_handle) { 46 Result<Ok, LaunchError> retval = Ok(); 47 48 char* argv_copy[argv.size() + 1]; 49 for (size_t i = 0; i < argv.size(); i++) { 50 argv_copy[i] = const_cast<char*>(argv[i].c_str()); 51 } 52 argv_copy[argv.size()] = NULL; 53 54 EnvironmentArray env_storage; 55 const EnvironmentArray& vars = 56 options.full_env ? options.full_env 57 : (env_storage = BuildEnvironmentArray(options.env_map)); 58 59 posix_spawn_file_actions_t file_actions; 60 int err = posix_spawn_file_actions_init(&file_actions); 61 if (err != 0) { 62 DLOG(WARNING) << "posix_spawn_file_actions_init failed"; 63 return Err(LaunchError("posix_spawn_file_actions_init", err)); 64 } 65 auto file_actions_guard = mozilla::MakeScopeExit( 66 [&file_actions] { posix_spawn_file_actions_destroy(&file_actions); }); 67 68 // Turn fds_to_remap array into a set of dup2 calls. 69 // 70 // Init() there will call fcntl(F_DUPFD/F_DUPFD_CLOEXEC) under the hood in 71 // https://searchfox.org/mozilla-central/rev/55d5c4b9dffe5e59eb6b019c1a930ec9ada47e10/ipc/glue/FileDescriptorShuffle.cpp#72 72 // so it will set errno. 73 mozilla::ipc::FileDescriptorShuffle shuffle; 74 if (!shuffle.Init(options.fds_to_remap)) { 75 DLOG(WARNING) << "FileDescriptorShuffle::Init failed"; 76 return Err(LaunchError("FileDescriptorShuffle", errno)); 77 } 78 for (const auto& fd_map : shuffle.Dup2Sequence()) { 79 int src_fd = fd_map.first; 80 int dest_fd = fd_map.second; 81 82 int rv = posix_spawn_file_actions_adddup2(&file_actions, src_fd, dest_fd); 83 if (rv != 0) { 84 DLOG(WARNING) << "posix_spawn_file_actions_adddup2 failed"; 85 return Err(LaunchError("posix_spawn_file_actions_adddup2", rv)); 86 } 87 } 88 89 if (!options.workdir.empty()) { 90 int rv = posix_spawn_file_actions_addchdir_np(&file_actions, 91 options.workdir.c_str()); 92 if (rv != 0) { 93 DLOG(WARNING) << "posix_spawn_file_actions_addchdir_np failed"; 94 return Err(LaunchError("posix_spawn_file_actions_addchdir", rv)); 95 } 96 } 97 98 // Initialize spawn attributes. 99 posix_spawnattr_t spawnattr; 100 err = posix_spawnattr_init(&spawnattr); 101 if (err != 0) { 102 DLOG(WARNING) << "posix_spawnattr_init failed"; 103 return Err(LaunchError("posix_spawnattr_init", err)); 104 } 105 auto spawnattr_guard = mozilla::MakeScopeExit( 106 [&spawnattr] { posix_spawnattr_destroy(&spawnattr); }); 107 108 #if defined(XP_MACOSX) && defined(__aarch64__) 109 if (options.arch == PROCESS_ARCH_X86_64) { 110 cpu_type_t cpu_pref = CPU_TYPE_X86_64; 111 size_t count = 1; 112 size_t ocount = 0; 113 int rv = 114 posix_spawnattr_setbinpref_np(&spawnattr, count, &cpu_pref, &ocount); 115 if ((rv != 0) || (ocount != count)) { 116 DLOG(WARNING) << "posix_spawnattr_setbinpref_np failed"; 117 return Err(LaunchError("posix_spawnattr_setbinpref_np", rv)); 118 } 119 } 120 #endif 121 122 if (options.disclaim) { 123 int err = responsibility_spawnattrs_setdisclaim(&spawnattr, 1); 124 if (err != 0) { 125 DLOG(WARNING) << "responsibility_spawnattrs_setdisclaim failed"; 126 return Err(LaunchError("responsibility_spawnattrs_setdisclaim", err)); 127 } 128 } 129 130 // Prevent the child process from inheriting any file descriptors 131 // that aren't named in `file_actions`. (This is an Apple-specific 132 // extension to posix_spawn.) 133 err = posix_spawnattr_setflags(&spawnattr, POSIX_SPAWN_CLOEXEC_DEFAULT); 134 if (err != 0) { 135 DLOG(WARNING) << "posix_spawnattr_setflags failed"; 136 return Err(LaunchError("posix_spawnattr_setflags", err)); 137 } 138 139 // Exempt std{in,out,err} from being closed by POSIX_SPAWN_CLOEXEC_DEFAULT. 140 for (int fd = 0; fd <= STDERR_FILENO; ++fd) { 141 err = posix_spawn_file_actions_addinherit_np(&file_actions, fd); 142 if (err != 0) { 143 DLOG(WARNING) << "posix_spawn_file_actions_addinherit_np failed"; 144 return Err(LaunchError("posix_spawn_file_actions_addinherit_np", err)); 145 } 146 } 147 148 int pid = 0; 149 int spawn_succeeded = (posix_spawnp(&pid, argv_copy[0], &file_actions, 150 &spawnattr, argv_copy, vars.get()) == 0); 151 152 bool process_handle_valid = pid > 0; 153 if (!spawn_succeeded || !process_handle_valid) { 154 DLOG(WARNING) << "posix_spawnp failed"; 155 retval = Err(LaunchError("posix_spawnp", spawn_succeeded)); 156 } else { 157 gProcessLog.print("==> process %d launched child process %d\n", 158 GetCurrentProcId(), pid); 159 if (options.wait) HANDLE_EINTR(waitpid(pid, 0, 0)); 160 161 if (process_handle) *process_handle = pid; 162 } 163 164 return retval; 165 } 166 167 } // namespace base