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