launch_mac.cc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "base/process/launch.h"
  5. #include <crt_externs.h>
  6. #include <mach/mach.h>
  7. #include <os/availability.h>
  8. #include <spawn.h>
  9. #include <string.h>
  10. #include <sys/wait.h>
  11. #include "base/command_line.h"
  12. #include "base/files/scoped_file.h"
  13. #include "base/logging.h"
  14. #include "base/posix/eintr_wrapper.h"
  15. #include "base/process/environment_internal.h"
  16. #include "base/threading/scoped_blocking_call.h"
  17. #include "base/threading/thread_restrictions.h"
  18. #include "base/trace_event/base_tracing.h"
  19. extern "C" {
  20. // Changes the current thread's directory to a path or directory file
  21. // descriptor. libpthread only exposes a syscall wrapper starting in
  22. // macOS 10.12, but the system call dates back to macOS 10.5. On older OSes,
  23. // the syscall is issued directly.
  24. int pthread_chdir_np(const char* dir) API_AVAILABLE(macosx(10.12));
  25. int pthread_fchdir_np(int fd) API_AVAILABLE(macosx(10.12));
  26. int responsibility_spawnattrs_setdisclaim(posix_spawnattr_t attrs, int disclaim)
  27. API_AVAILABLE(macosx(10.14));
  28. } // extern "C"
  29. namespace base {
  30. namespace {
  31. // DPSXCHECK is a Debug Posix Spawn Check macro. The posix_spawn* family of
  32. // functions return an errno value, as opposed to setting errno directly. This
  33. // macro emulates a DPCHECK().
  34. #define DPSXCHECK(expr) \
  35. do { \
  36. int rv = (expr); \
  37. DCHECK_EQ(rv, 0) << #expr << ": -" << rv << " " << strerror(rv); \
  38. } while (0)
  39. class PosixSpawnAttr {
  40. public:
  41. PosixSpawnAttr() { DPSXCHECK(posix_spawnattr_init(&attr_)); }
  42. ~PosixSpawnAttr() { DPSXCHECK(posix_spawnattr_destroy(&attr_)); }
  43. posix_spawnattr_t* get() { return &attr_; }
  44. private:
  45. posix_spawnattr_t attr_;
  46. };
  47. class PosixSpawnFileActions {
  48. public:
  49. PosixSpawnFileActions() {
  50. DPSXCHECK(posix_spawn_file_actions_init(&file_actions_));
  51. }
  52. PosixSpawnFileActions(const PosixSpawnFileActions&) = delete;
  53. PosixSpawnFileActions& operator=(const PosixSpawnFileActions&) = delete;
  54. ~PosixSpawnFileActions() {
  55. DPSXCHECK(posix_spawn_file_actions_destroy(&file_actions_));
  56. }
  57. void Open(int filedes, const char* path, int mode) {
  58. DPSXCHECK(posix_spawn_file_actions_addopen(&file_actions_, filedes, path,
  59. mode, 0));
  60. }
  61. void Dup2(int filedes, int newfiledes) {
  62. DPSXCHECK(
  63. posix_spawn_file_actions_adddup2(&file_actions_, filedes, newfiledes));
  64. }
  65. void Inherit(int filedes) {
  66. DPSXCHECK(posix_spawn_file_actions_addinherit_np(&file_actions_, filedes));
  67. }
  68. void Chdir(const char* path) API_AVAILABLE(macos(10.15)) {
  69. DPSXCHECK(posix_spawn_file_actions_addchdir_np(&file_actions_, path));
  70. }
  71. const posix_spawn_file_actions_t* get() const { return &file_actions_; }
  72. private:
  73. posix_spawn_file_actions_t file_actions_;
  74. };
  75. int ChangeCurrentThreadDirectory(const char* path) {
  76. return pthread_chdir_np(path);
  77. }
  78. // The recommended way to unset a per-thread cwd is to set a new value to an
  79. // invalid file descriptor, per libpthread-218.1.3/private/private.h.
  80. int ResetCurrentThreadDirectory() {
  81. return pthread_fchdir_np(-1);
  82. }
  83. struct GetAppOutputOptions {
  84. // Whether to pipe stderr to stdout in |output|.
  85. bool include_stderr = false;
  86. // Caller-supplied string poiter for the output.
  87. std::string* output = nullptr;
  88. // Result exit code of Process::Wait().
  89. int exit_code = 0;
  90. };
  91. bool GetAppOutputInternal(const std::vector<std::string>& argv,
  92. GetAppOutputOptions* gao_options) {
  93. TRACE_EVENT0("base", "GetAppOutput");
  94. ScopedFD read_fd, write_fd;
  95. {
  96. int pipefds[2];
  97. if (pipe(pipefds) != 0) {
  98. DPLOG(ERROR) << "pipe";
  99. return false;
  100. }
  101. read_fd.reset(pipefds[0]);
  102. write_fd.reset(pipefds[1]);
  103. }
  104. LaunchOptions launch_options;
  105. launch_options.fds_to_remap.emplace_back(write_fd.get(), STDOUT_FILENO);
  106. if (gao_options->include_stderr) {
  107. launch_options.fds_to_remap.emplace_back(write_fd.get(), STDERR_FILENO);
  108. }
  109. Process process = LaunchProcess(argv, launch_options);
  110. // Close the parent process' write descriptor, so that EOF is generated in
  111. // read loop below.
  112. write_fd.reset();
  113. // Read the child's output before waiting for its exit, otherwise the pipe
  114. // buffer may fill up if the process is producing a lot of output.
  115. std::string* output = gao_options->output;
  116. output->clear();
  117. const size_t kBufferSize = 1024;
  118. size_t total_bytes_read = 0;
  119. ssize_t read_this_pass = 0;
  120. do {
  121. output->resize(output->size() + kBufferSize);
  122. read_this_pass = HANDLE_EINTR(
  123. read(read_fd.get(), &(*output)[total_bytes_read], kBufferSize));
  124. if (read_this_pass >= 0) {
  125. total_bytes_read += static_cast<size_t>(read_this_pass);
  126. output->resize(total_bytes_read);
  127. }
  128. } while (read_this_pass > 0);
  129. // It is okay to allow this process to wait on the launched process as a
  130. // process launched with GetAppOutput*() shouldn't wait back on the process
  131. // that launched it.
  132. internal::GetAppOutputScopedAllowBaseSyncPrimitives allow_wait;
  133. if (!process.WaitForExit(&gao_options->exit_code)) {
  134. return false;
  135. }
  136. return read_this_pass == 0;
  137. }
  138. } // namespace
  139. Process LaunchProcess(const CommandLine& cmdline,
  140. const LaunchOptions& options) {
  141. return LaunchProcess(cmdline.argv(), options);
  142. }
  143. Process LaunchProcess(const std::vector<std::string>& argv,
  144. const LaunchOptions& options) {
  145. TRACE_EVENT0("base", "LaunchProcess");
  146. PosixSpawnAttr attr;
  147. short flags = POSIX_SPAWN_CLOEXEC_DEFAULT;
  148. if (options.new_process_group) {
  149. flags |= POSIX_SPAWN_SETPGROUP;
  150. DPSXCHECK(posix_spawnattr_setpgroup(attr.get(), 0));
  151. }
  152. DPSXCHECK(posix_spawnattr_setflags(attr.get(), flags));
  153. PosixSpawnFileActions file_actions;
  154. // Process file descriptors for the child. By default, LaunchProcess will
  155. // open stdin to /dev/null and inherit stdout and stderr.
  156. bool inherit_stdout = true, inherit_stderr = true;
  157. bool null_stdin = true;
  158. for (const auto& dup2_pair : options.fds_to_remap) {
  159. if (dup2_pair.second == STDIN_FILENO) {
  160. null_stdin = false;
  161. } else if (dup2_pair.second == STDOUT_FILENO) {
  162. inherit_stdout = false;
  163. } else if (dup2_pair.second == STDERR_FILENO) {
  164. inherit_stderr = false;
  165. }
  166. if (dup2_pair.first == dup2_pair.second) {
  167. file_actions.Inherit(dup2_pair.second);
  168. } else {
  169. file_actions.Dup2(dup2_pair.first, dup2_pair.second);
  170. }
  171. }
  172. if (null_stdin) {
  173. file_actions.Open(STDIN_FILENO, "/dev/null", O_RDONLY);
  174. }
  175. if (inherit_stdout) {
  176. file_actions.Inherit(STDOUT_FILENO);
  177. }
  178. if (inherit_stderr) {
  179. file_actions.Inherit(STDERR_FILENO);
  180. }
  181. if (options.disclaim_responsibility) {
  182. if (__builtin_available(macOS 10.14, *)) {
  183. DPSXCHECK(responsibility_spawnattrs_setdisclaim(attr.get(), 1));
  184. }
  185. }
  186. std::vector<char*> argv_cstr;
  187. argv_cstr.reserve(argv.size() + 1);
  188. for (const auto& arg : argv)
  189. argv_cstr.push_back(const_cast<char*>(arg.c_str()));
  190. argv_cstr.push_back(nullptr);
  191. std::unique_ptr<char*[]> owned_environ;
  192. char* empty_environ = nullptr;
  193. char** new_environ =
  194. options.clear_environment ? &empty_environ : *_NSGetEnviron();
  195. if (!options.environment.empty()) {
  196. owned_environ =
  197. internal::AlterEnvironment(new_environ, options.environment);
  198. new_environ = owned_environ.get();
  199. }
  200. const char* executable_path = !options.real_path.empty()
  201. ? options.real_path.value().c_str()
  202. : argv_cstr[0];
  203. if (__builtin_available(macOS 11.0, *)) {
  204. if (options.enable_cpu_security_mitigations) {
  205. DPSXCHECK(posix_spawnattr_set_csm_np(attr.get(), POSIX_SPAWN_NP_CSM_ALL));
  206. }
  207. }
  208. if (!options.current_directory.empty()) {
  209. const char* chdir_str = options.current_directory.value().c_str();
  210. if (__builtin_available(macOS 10.15, *)) {
  211. file_actions.Chdir(chdir_str);
  212. } else {
  213. // If the chdir posix_spawn_file_actions extension is not available,
  214. // change the thread-specific working directory. The new process will
  215. // inherit it during posix_spawnp().
  216. int rv = ChangeCurrentThreadDirectory(chdir_str);
  217. if (rv != 0) {
  218. DPLOG(ERROR) << "pthread_chdir_np";
  219. return Process();
  220. }
  221. }
  222. }
  223. int rv;
  224. pid_t pid;
  225. {
  226. // If |options.mach_ports_for_rendezvous| is specified : the server's lock
  227. // must be held for the duration of posix_spawnp() so that new child's PID
  228. // can be recorded with the set of ports.
  229. const bool has_mach_ports_for_rendezvous =
  230. !options.mach_ports_for_rendezvous.empty();
  231. AutoLockMaybe rendezvous_lock(
  232. has_mach_ports_for_rendezvous
  233. ? &MachPortRendezvousServer::GetInstance()->GetLock()
  234. : nullptr);
  235. // Use posix_spawnp as some callers expect to have PATH consulted.
  236. rv = posix_spawnp(&pid, executable_path, file_actions.get(), attr.get(),
  237. &argv_cstr[0], new_environ);
  238. if (has_mach_ports_for_rendezvous) {
  239. if (rv == 0) {
  240. MachPortRendezvousServer::GetInstance()->GetLock().AssertAcquired();
  241. MachPortRendezvousServer::GetInstance()->RegisterPortsForPid(
  242. pid, options.mach_ports_for_rendezvous);
  243. } else {
  244. // Because |options| is const-ref, the collection has to be copied here.
  245. // The caller expects to relinquish ownership of any strong rights if
  246. // LaunchProcess() were to succeed, so these rights should be manually
  247. // destroyed on failure.
  248. MachPortsForRendezvous ports = options.mach_ports_for_rendezvous;
  249. for (auto& port : ports) {
  250. port.second.Destroy();
  251. }
  252. }
  253. }
  254. }
  255. // Restore the thread's working directory if it was changed.
  256. if (!options.current_directory.empty()) {
  257. if (__builtin_available(macOS 10.15, *)) {
  258. // Nothing to do because no global state was changed, but
  259. // __builtin_available is special and cannot be negated.
  260. } else {
  261. ResetCurrentThreadDirectory();
  262. }
  263. }
  264. if (rv != 0) {
  265. DLOG(ERROR) << "posix_spawnp(" << executable_path << "): -" << rv << " "
  266. << strerror(rv);
  267. return Process();
  268. }
  269. if (options.wait) {
  270. // While this isn't strictly disk IO, waiting for another process to
  271. // finish is the sort of thing ThreadRestrictions is trying to prevent.
  272. ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
  273. pid_t ret = HANDLE_EINTR(waitpid(pid, nullptr, 0));
  274. DPCHECK(ret > 0);
  275. }
  276. return Process(pid);
  277. }
  278. bool GetAppOutput(const CommandLine& cl, std::string* output) {
  279. return GetAppOutput(cl.argv(), output);
  280. }
  281. bool GetAppOutputAndError(const CommandLine& cl, std::string* output) {
  282. return GetAppOutputAndError(cl.argv(), output);
  283. }
  284. bool GetAppOutputWithExitCode(const CommandLine& cl,
  285. std::string* output,
  286. int* exit_code) {
  287. GetAppOutputOptions options;
  288. options.output = output;
  289. bool rv = GetAppOutputInternal(cl.argv(), &options);
  290. *exit_code = options.exit_code;
  291. return rv;
  292. }
  293. bool GetAppOutput(const std::vector<std::string>& argv, std::string* output) {
  294. GetAppOutputOptions options;
  295. options.output = output;
  296. return GetAppOutputInternal(argv, &options) &&
  297. options.exit_code == EXIT_SUCCESS;
  298. }
  299. bool GetAppOutputAndError(const std::vector<std::string>& argv,
  300. std::string* output) {
  301. GetAppOutputOptions options;
  302. options.include_stderr = true;
  303. options.output = output;
  304. return GetAppOutputInternal(argv, &options) &&
  305. options.exit_code == EXIT_SUCCESS;
  306. }
  307. void RaiseProcessToHighPriority() {
  308. // Historically this has not been implemented on POSIX and macOS. This could
  309. // influence the Mach task policy in the future.
  310. }
  311. } // namespace base