service_process_launcher.cc 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. // Copyright 2016 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 "services/service_manager/service_process_launcher.h"
  5. #include <utility>
  6. #include "base/base_paths.h"
  7. #include "base/base_switches.h"
  8. #include "base/bind.h"
  9. #include "base/command_line.h"
  10. #include "base/feature_list.h"
  11. #include "base/location.h"
  12. #include "base/logging.h"
  13. #include "base/memory/ptr_util.h"
  14. #include "base/path_service.h"
  15. #include "base/process/kill.h"
  16. #include "base/process/launch.h"
  17. #include "base/rand_util.h"
  18. #include "base/run_loop.h"
  19. #include "base/sequence_checker.h"
  20. #include "base/strings/string_number_conversions.h"
  21. #include "base/synchronization/lock.h"
  22. #include "base/task/task_runner_util.h"
  23. #include "base/task/thread_pool.h"
  24. #include "base/threading/thread.h"
  25. #include "build/build_config.h"
  26. #include "build/chromeos_buildflags.h"
  27. #include "mojo/public/cpp/platform/platform_channel.h"
  28. #include "mojo/public/cpp/system/core.h"
  29. #include "mojo/public/cpp/system/invitation.h"
  30. #include "sandbox/policy/mojom/sandbox.mojom.h"
  31. #include "sandbox/policy/sandbox_type.h"
  32. #include "sandbox/policy/switches.h"
  33. #include "services/service_manager/public/cpp/service_executable/switches.h"
  34. #include "services/service_manager/public/mojom/service.mojom.h"
  35. #include "services/service_manager/switches.h"
  36. #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
  37. #include "sandbox/linux/services/namespace_sandbox.h"
  38. #endif
  39. #if BUILDFLAG(IS_WIN)
  40. #include "base/win/windows_version.h"
  41. #include <windows.h>
  42. #endif
  43. namespace service_manager {
  44. // Thread-safe owner of state related to a service process. This facilitates
  45. // safely scheduling the launching and joining of a service process in the
  46. // background.
  47. class ServiceProcessLauncher::ProcessState
  48. : public base::RefCountedThreadSafe<ProcessState> {
  49. public:
  50. ProcessState() { DETACH_FROM_SEQUENCE(sequence_checker_); }
  51. ProcessState(const ProcessState&) = delete;
  52. ProcessState& operator=(const ProcessState&) = delete;
  53. base::ProcessId LaunchInBackground(
  54. const Identity& target,
  55. sandbox::mojom::Sandbox sandbox_type,
  56. std::unique_ptr<base::CommandLine> child_command_line,
  57. mojo::PlatformChannel::HandlePassingInfo handle_passing_info,
  58. mojo::PlatformChannel channel,
  59. mojo::OutgoingInvitation invitation);
  60. void StopInBackground();
  61. private:
  62. friend class base::RefCountedThreadSafe<ProcessState>;
  63. ~ProcessState() = default;
  64. base::Process child_process_;
  65. SEQUENCE_CHECKER(sequence_checker_);
  66. };
  67. ServiceProcessLauncher::ServiceProcessLauncher(
  68. ServiceProcessLauncherDelegate* delegate,
  69. const base::FilePath& service_path)
  70. : delegate_(delegate),
  71. service_path_(service_path.empty()
  72. ? base::CommandLine::ForCurrentProcess()->GetProgram()
  73. : service_path),
  74. background_task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
  75. {base::TaskPriority::USER_VISIBLE, base::WithBaseSyncPrimitives(),
  76. base::MayBlock()})) {}
  77. ServiceProcessLauncher::~ServiceProcessLauncher() {
  78. // We don't really need to wait for the process to be joined, as long as it
  79. // eventually happens. Schedule a task to do it, and move on.
  80. background_task_runner_->PostTask(
  81. FROM_HERE, base::BindOnce(&ProcessState::StopInBackground, state_));
  82. }
  83. mojo::PendingRemote<mojom::Service> ServiceProcessLauncher::Start(
  84. const Identity& target,
  85. sandbox::mojom::Sandbox sandbox_type,
  86. ProcessReadyCallback callback) {
  87. DCHECK(!state_);
  88. const base::CommandLine& parent_command_line =
  89. *base::CommandLine::ForCurrentProcess();
  90. std::unique_ptr<base::CommandLine> child_command_line(
  91. new base::CommandLine(service_path_));
  92. child_command_line->AppendArguments(parent_command_line, false);
  93. // Add enabled/disabled features from base::FeatureList. These will take
  94. // precedence over existing ones (if there is any copied from the
  95. // |parent_command_line| above) as they appear later in the arguments list.
  96. std::string enabled_features;
  97. std::string disabled_features;
  98. base::FeatureList::GetInstance()->GetFeatureOverrides(&enabled_features,
  99. &disabled_features);
  100. if (!enabled_features.empty()) {
  101. child_command_line->AppendSwitchASCII(::switches::kEnableFeatures,
  102. enabled_features);
  103. }
  104. if (!disabled_features.empty()) {
  105. child_command_line->AppendSwitchASCII(::switches::kDisableFeatures,
  106. disabled_features);
  107. }
  108. child_command_line->AppendSwitchASCII(switches::kServiceName, target.name());
  109. #ifndef NDEBUG
  110. child_command_line->AppendSwitchASCII("g",
  111. target.instance_group().ToString());
  112. #endif
  113. if (!sandbox::policy::IsUnsandboxedSandboxType(sandbox_type)) {
  114. child_command_line->AppendSwitchASCII(
  115. sandbox::policy::switches::kServiceSandboxType,
  116. sandbox::policy::StringFromUtilitySandboxType(sandbox_type));
  117. }
  118. mojo::PlatformChannel::HandlePassingInfo handle_passing_info;
  119. mojo::PlatformChannel channel;
  120. channel.PrepareToPassRemoteEndpoint(&handle_passing_info,
  121. child_command_line.get());
  122. mojo::OutgoingInvitation invitation;
  123. auto client =
  124. PassServiceRequestOnCommandLine(&invitation, child_command_line.get());
  125. if (delegate_) {
  126. delegate_->AdjustCommandLineArgumentsForTarget(target,
  127. child_command_line.get());
  128. }
  129. state_ = base::WrapRefCounted(new ProcessState);
  130. base::PostTaskAndReplyWithResult(
  131. background_task_runner_.get(), FROM_HERE,
  132. base::BindOnce(&ProcessState::LaunchInBackground, state_, target,
  133. sandbox_type, std::move(child_command_line),
  134. std::move(handle_passing_info), std::move(channel),
  135. std::move(invitation)),
  136. std::move(callback));
  137. return client;
  138. }
  139. // static
  140. mojo::PendingRemote<mojom::Service>
  141. ServiceProcessLauncher::PassServiceRequestOnCommandLine(
  142. mojo::OutgoingInvitation* invitation,
  143. base::CommandLine* command_line) {
  144. const auto attachment_name = base::NumberToString(base::RandUint64());
  145. command_line->AppendSwitchASCII(switches::kServiceRequestAttachmentName,
  146. attachment_name);
  147. return mojo::PendingRemote<mojom::Service>(
  148. invitation->AttachMessagePipe(attachment_name), 0);
  149. }
  150. base::ProcessId ServiceProcessLauncher::ProcessState::LaunchInBackground(
  151. const Identity& target,
  152. sandbox::mojom::Sandbox sandbox_type,
  153. std::unique_ptr<base::CommandLine> child_command_line,
  154. mojo::PlatformChannel::HandlePassingInfo handle_passing_info,
  155. mojo::PlatformChannel channel,
  156. mojo::OutgoingInvitation invitation) {
  157. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  158. base::LaunchOptions options;
  159. #if BUILDFLAG(IS_WIN)
  160. options.handles_to_inherit = handle_passing_info;
  161. options.stdin_handle = INVALID_HANDLE_VALUE;
  162. options.stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE);
  163. options.stderr_handle = GetStdHandle(STD_ERROR_HANDLE);
  164. // Always inherit stdout/stderr as a pair.
  165. if (!options.stdout_handle || !options.stdin_handle)
  166. options.stdin_handle = options.stdout_handle = nullptr;
  167. // Pseudo handles are used when stdout and stderr redirect to the console. In
  168. // that case, they're automatically inherited by child processes. See
  169. // https://msdn.microsoft.com/en-us/library/windows/desktop/ms682075.aspx
  170. // Trying to add them to the list of handles to inherit causes CreateProcess
  171. // to fail. When this process is launched from Python then a real handle is
  172. // used. In that case, we do want to add it to the list of handles that is
  173. // inherited.
  174. if (options.stdout_handle &&
  175. GetFileType(options.stdout_handle) != FILE_TYPE_CHAR) {
  176. options.handles_to_inherit.push_back(options.stdout_handle);
  177. }
  178. if (options.stderr_handle &&
  179. GetFileType(options.stderr_handle) != FILE_TYPE_CHAR &&
  180. options.stdout_handle != options.stderr_handle) {
  181. options.handles_to_inherit.push_back(options.stderr_handle);
  182. }
  183. #elif BUILDFLAG(IS_FUCHSIA)
  184. // LaunchProcess will share stdin/out/err with the child process by default.
  185. if (!sandbox::policy::IsUnsandboxedSandboxType(sandbox_type))
  186. NOTIMPLEMENTED();
  187. options.handles_to_transfer = std::move(handle_passing_info);
  188. #elif BUILDFLAG(IS_POSIX)
  189. const base::FileHandleMappingVector fd_mapping{
  190. {STDIN_FILENO, STDIN_FILENO},
  191. {STDOUT_FILENO, STDOUT_FILENO},
  192. {STDERR_FILENO, STDERR_FILENO},
  193. };
  194. #if BUILDFLAG(IS_MAC)
  195. options.fds_to_remap = fd_mapping;
  196. options.mach_ports_for_rendezvous = handle_passing_info;
  197. #else
  198. handle_passing_info.insert(handle_passing_info.end(), fd_mapping.begin(),
  199. fd_mapping.end());
  200. options.fds_to_remap = handle_passing_info;
  201. #endif // BUILDFLAG(IS_MAC)
  202. #endif
  203. DVLOG(2) << "Launching child with command line: "
  204. << child_command_line->GetCommandLineString();
  205. #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
  206. if (!sandbox::policy::IsUnsandboxedSandboxType(sandbox_type)) {
  207. child_process_ =
  208. sandbox::NamespaceSandbox::LaunchProcess(*child_command_line, options);
  209. if (!child_process_.IsValid())
  210. LOG(ERROR) << "Starting the process with a sandbox failed.";
  211. } else
  212. #endif
  213. {
  214. child_process_ = base::LaunchProcess(*child_command_line, options);
  215. }
  216. channel.RemoteProcessLaunchAttempted();
  217. if (!child_process_.IsValid()) {
  218. LOG(ERROR) << "Failed to start child process for service: "
  219. << target.name();
  220. return base::kNullProcessId;
  221. }
  222. #if BUILDFLAG(IS_CHROMEOS_ASH)
  223. // Always log instead of DVLOG because knowing which pid maps to which
  224. // service is vital for interpreting crashes after-the-fact and Chrome OS
  225. // devices generally run release builds, even in development.
  226. VLOG(0)
  227. #else
  228. DVLOG(0)
  229. #endif
  230. << "Launched child process pid=" << child_process_.Pid()
  231. << " id=" << target.ToString();
  232. mojo::OutgoingInvitation::Send(std::move(invitation), child_process_.Handle(),
  233. channel.TakeLocalEndpoint());
  234. return child_process_.Pid();
  235. }
  236. void ServiceProcessLauncher::ProcessState::StopInBackground() {
  237. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  238. if (!child_process_.IsValid())
  239. return;
  240. int rv = -1;
  241. LOG_IF(ERROR, !child_process_.WaitForExit(&rv))
  242. << "Failed to wait for child process";
  243. child_process_.Close();
  244. }
  245. } // namespace service_manager