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.
5 #include "chrome/service/service_utility_process_host.h"
8 #include "base/command_line.h"
9 #include "base/file_util.h"
10 #include "base/files/scoped_temp_dir.h"
11 #include "base/logging.h"
12 #include "base/message_loop/message_loop.h"
13 #include "base/message_loop/message_loop_proxy.h"
14 #include "base/metrics/histogram.h"
15 #include "base/process/kill.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "chrome/common/chrome_switches.h"
18 #include "chrome/common/chrome_utility_printing_messages.h"
19 #include "content/public/common/child_process_host.h"
20 #include "content/public/common/result_codes.h"
21 #include "content/public/common/sandbox_init.h"
22 #include "ipc/ipc_switches.h"
23 #include "printing/page_range.h"
24 #include "ui/base/ui_base_switches.h"
25 #include "ui/gfx/rect.h"
28 #include "base/files/file_path.h"
29 #include "base/memory/scoped_ptr.h"
30 #include "base/process/launch.h"
31 #include "base/win/scoped_handle.h"
32 #include "content/public/common/sandbox_init.h"
33 #include "content/public/common/sandboxed_process_launcher_delegate.h"
34 #include "printing/emf_win.h"
35 #include "sandbox/win/src/sandbox_policy_base.h"
39 // NOTE: changes to this class need to be reviewed by the security team.
40 class ServiceSandboxedProcessLauncherDelegate
41 : public content::SandboxedProcessLauncherDelegate {
43 explicit ServiceSandboxedProcessLauncherDelegate(
44 const base::FilePath& exposed_dir)
45 : exposed_dir_(exposed_dir) {
48 virtual void PreSandbox(bool* disable_default_policy,
49 base::FilePath* exposed_dir) OVERRIDE {
50 *exposed_dir = exposed_dir_;
53 virtual void PreSpawnTarget(sandbox::TargetPolicy* policy,
54 bool* success) OVERRIDE {
55 // Service process may run as windows service and it fails to create a
57 policy->SetAlternateDesktop(false);
61 base::FilePath exposed_dir_;
68 using content::ChildProcessHost;
71 enum ServiceUtilityProcessHostEvent {
72 SERVICE_UTILITY_STARTED,
73 SERVICE_UTILITY_DISCONNECTED,
74 SERVICE_UTILITY_METAFILE_REQUEST,
75 SERVICE_UTILITY_METAFILE_SUCCEEDED,
76 SERVICE_UTILITY_METAFILE_FAILED,
77 SERVICE_UTILITY_CAPS_REQUEST,
78 SERVICE_UTILITY_CAPS_SUCCEEDED,
79 SERVICE_UTILITY_CAPS_FAILED,
80 SERVICE_UTILITY_SEMANTIC_CAPS_REQUEST,
81 SERVICE_UTILITY_SEMANTIC_CAPS_SUCCEEDED,
82 SERVICE_UTILITY_SEMANTIC_CAPS_FAILED,
83 SERVICE_UTILITY_FAILED_TO_START,
84 SERVICE_UTILITY_EVENT_MAX,
88 ServiceUtilityProcessHost::ServiceUtilityProcessHost(
89 Client* client, base::MessageLoopProxy* client_message_loop_proxy)
90 : handle_(base::kNullProcessHandle),
92 client_message_loop_proxy_(client_message_loop_proxy),
93 waiting_for_reply_(false) {
94 child_process_host_.reset(ChildProcessHost::Create(this));
97 ServiceUtilityProcessHost::~ServiceUtilityProcessHost() {
98 // We need to kill the child process when the host dies.
99 base::KillProcess(handle_, content::RESULT_CODE_NORMAL_EXIT, false);
102 bool ServiceUtilityProcessHost::StartRenderPDFPagesToMetafile(
103 const base::FilePath& pdf_path,
104 const printing::PdfRenderSettings& render_settings,
105 const std::vector<printing::PageRange>& page_ranges) {
106 UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
107 SERVICE_UTILITY_METAFILE_REQUEST,
108 SERVICE_UTILITY_EVENT_MAX);
109 start_time_ = base::Time::Now();
111 // This is only implemented on Windows (because currently it is only needed
112 // on Windows). Will add implementations on other platforms when needed.
115 #else // !defined(OS_WIN)
116 scratch_metafile_dir_.reset(new base::ScopedTempDir);
117 if (!scratch_metafile_dir_->CreateUniqueTempDir())
119 metafile_path_ = scratch_metafile_dir_->path().AppendASCII("output.emf");
120 if (!StartProcess(false, scratch_metafile_dir_->path()))
125 base::File::FLAG_OPEN | base::File::FLAG_READ | base::File::FLAG_WRITE);
126 DCHECK(!waiting_for_reply_);
127 waiting_for_reply_ = true;
128 return child_process_host_->Send(
129 new ChromeUtilityMsg_RenderPDFPagesToMetafiles(
130 IPC::TakeFileHandleForProcess(pdf_file.Pass(), handle()),
134 #endif // !defined(OS_WIN)
137 bool ServiceUtilityProcessHost::StartGetPrinterCapsAndDefaults(
138 const std::string& printer_name) {
139 UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
140 SERVICE_UTILITY_CAPS_REQUEST,
141 SERVICE_UTILITY_EVENT_MAX);
142 start_time_ = base::Time::Now();
143 base::FilePath exposed_path;
144 if (!StartProcess(true, exposed_path))
146 DCHECK(!waiting_for_reply_);
147 waiting_for_reply_ = true;
148 return child_process_host_->Send(
149 new ChromeUtilityMsg_GetPrinterCapsAndDefaults(printer_name));
152 bool ServiceUtilityProcessHost::StartGetPrinterSemanticCapsAndDefaults(
153 const std::string& printer_name) {
154 UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
155 SERVICE_UTILITY_SEMANTIC_CAPS_REQUEST,
156 SERVICE_UTILITY_EVENT_MAX);
157 start_time_ = base::Time::Now();
158 base::FilePath exposed_path;
159 if (!StartProcess(true, exposed_path))
161 DCHECK(!waiting_for_reply_);
162 waiting_for_reply_ = true;
163 return child_process_host_->Send(
164 new ChromeUtilityMsg_GetPrinterSemanticCapsAndDefaults(printer_name));
167 bool ServiceUtilityProcessHost::StartProcess(
169 const base::FilePath& exposed_dir) {
170 std::string channel_id = child_process_host_->CreateChannel();
171 if (channel_id.empty())
174 base::FilePath exe_path = GetUtilityProcessCmd();
175 if (exe_path.empty()) {
176 NOTREACHED() << "Unable to get utility process binary name.";
180 CommandLine cmd_line(exe_path);
181 cmd_line.AppendSwitchASCII(switches::kProcessType, switches::kUtilityProcess);
182 cmd_line.AppendSwitchASCII(switches::kProcessChannelID, channel_id);
183 cmd_line.AppendSwitch(switches::kLang);
185 if (Launch(&cmd_line, no_sandbox, exposed_dir)) {
186 UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
187 SERVICE_UTILITY_STARTED,
188 SERVICE_UTILITY_EVENT_MAX);
191 UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
192 SERVICE_UTILITY_FAILED_TO_START,
193 SERVICE_UTILITY_EVENT_MAX);
197 bool ServiceUtilityProcessHost::Launch(CommandLine* cmd_line,
199 const base::FilePath& exposed_dir) {
201 // TODO(sanjeevr): Implement for non-Windows OSes.
204 #else // !defined(OS_WIN)
207 base::ProcessHandle process = base::kNullProcessHandle;
208 cmd_line->AppendSwitch(switches::kNoSandbox);
209 base::LaunchProcess(*cmd_line, base::LaunchOptions(), &handle_);
211 ServiceSandboxedProcessLauncherDelegate delegate(exposed_dir);
212 handle_ = content::StartSandboxedProcess(&delegate, cmd_line);
214 return (handle_ != base::kNullProcessHandle);
215 #endif // !defined(OS_WIN)
218 base::FilePath ServiceUtilityProcessHost::GetUtilityProcessCmd() {
219 #if defined(OS_LINUX)
220 int flags = ChildProcessHost::CHILD_ALLOW_SELF;
222 int flags = ChildProcessHost::CHILD_NORMAL;
224 return ChildProcessHost::GetChildPath(flags);
227 void ServiceUtilityProcessHost::OnChildDisconnected() {
228 if (waiting_for_reply_) {
229 // If we are yet to receive a reply then notify the client that the
231 client_message_loop_proxy_->PostTask(
232 FROM_HERE, base::Bind(&Client::OnChildDied, client_.get()));
233 UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
234 SERVICE_UTILITY_DISCONNECTED,
235 SERVICE_UTILITY_EVENT_MAX);
236 UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilityDisconnectTime",
237 base::Time::Now() - start_time_);
242 bool ServiceUtilityProcessHost::OnMessageReceived(const IPC::Message& message) {
244 IPC_BEGIN_MESSAGE_MAP(ServiceUtilityProcessHost, message)
245 #if defined(WIN_PDF_METAFILE_FOR_PRINTING)
247 ChromeUtilityHostMsg_RenderPDFPagesToMetafiles_Succeeded,
248 OnRenderPDFPagesToMetafilesSucceeded)
249 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_RenderPDFPagesToMetafile_Failed,
250 OnRenderPDFPagesToMetafileFailed)
253 ChromeUtilityHostMsg_GetPrinterCapsAndDefaults_Succeeded,
254 OnGetPrinterCapsAndDefaultsSucceeded)
255 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_GetPrinterCapsAndDefaults_Failed,
256 OnGetPrinterCapsAndDefaultsFailed)
258 ChromeUtilityHostMsg_GetPrinterSemanticCapsAndDefaults_Succeeded,
259 OnGetPrinterSemanticCapsAndDefaultsSucceeded)
261 ChromeUtilityHostMsg_GetPrinterSemanticCapsAndDefaults_Failed,
262 OnGetPrinterSemanticCapsAndDefaultsFailed)
263 IPC_MESSAGE_UNHANDLED(handled = false)
264 IPC_END_MESSAGE_MAP()
268 base::ProcessHandle ServiceUtilityProcessHost::GetHandle() const {
272 #if defined(WIN_PDF_METAFILE_FOR_PRINTING)
273 void ServiceUtilityProcessHost::OnRenderPDFPagesToMetafilesSucceeded(
274 const std::vector<printing::PageRange>& page_ranges,
275 double scale_factor) {
276 UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
277 SERVICE_UTILITY_METAFILE_SUCCEEDED,
278 SERVICE_UTILITY_EVENT_MAX);
279 UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilityMetafileTime",
280 base::Time::Now() - start_time_);
281 DCHECK(waiting_for_reply_);
282 waiting_for_reply_ = false;
283 // If the metafile was successfully created, we need to take our hands off the
284 // scratch metafile directory. The client will delete it when it is done with
286 scratch_metafile_dir_->Take();
288 // TODO(vitalybuka|scottmg): http://crbug.com/170859: Currently, only one
289 // page is printed at a time. This would need to be refactored to change
291 CHECK_EQ(1u, page_ranges.size());
292 CHECK_EQ(page_ranges[0].from, page_ranges[0].to);
293 int page_number = page_ranges[0].from;
294 client_message_loop_proxy_->PostTask(
296 base::Bind(&Client::MetafileAvailable,
298 metafile_path_.InsertBeforeExtensionASCII(
299 base::StringPrintf(".%d", page_number)),
304 void ServiceUtilityProcessHost::OnRenderPDFPagesToMetafileFailed() {
305 DCHECK(waiting_for_reply_);
306 UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
307 SERVICE_UTILITY_METAFILE_FAILED,
308 SERVICE_UTILITY_EVENT_MAX);
309 UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilityMetafileFailTime",
310 base::Time::Now() - start_time_);
311 waiting_for_reply_ = false;
312 client_message_loop_proxy_->PostTask(
314 base::Bind(&Client::OnRenderPDFPagesToMetafileFailed, client_.get()));
316 #endif // defined(WIN_PDF_METAFILE_FOR_PRINTING)
318 void ServiceUtilityProcessHost::OnGetPrinterCapsAndDefaultsSucceeded(
319 const std::string& printer_name,
320 const printing::PrinterCapsAndDefaults& caps_and_defaults) {
321 DCHECK(waiting_for_reply_);
322 UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
323 SERVICE_UTILITY_CAPS_SUCCEEDED,
324 SERVICE_UTILITY_EVENT_MAX);
325 UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilityCapsTime",
326 base::Time::Now() - start_time_);
327 waiting_for_reply_ = false;
328 client_message_loop_proxy_->PostTask(
330 base::Bind(&Client::OnGetPrinterCapsAndDefaults, client_.get(), true,
331 printer_name, caps_and_defaults));
334 void ServiceUtilityProcessHost::OnGetPrinterSemanticCapsAndDefaultsSucceeded(
335 const std::string& printer_name,
336 const printing::PrinterSemanticCapsAndDefaults& caps_and_defaults) {
337 DCHECK(waiting_for_reply_);
338 UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
339 SERVICE_UTILITY_SEMANTIC_CAPS_SUCCEEDED,
340 SERVICE_UTILITY_EVENT_MAX);
341 UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilitySemanticCapsTime",
342 base::Time::Now() - start_time_);
343 waiting_for_reply_ = false;
344 client_message_loop_proxy_->PostTask(
346 base::Bind(&Client::OnGetPrinterSemanticCapsAndDefaults, client_.get(),
347 true, printer_name, caps_and_defaults));
350 void ServiceUtilityProcessHost::OnGetPrinterCapsAndDefaultsFailed(
351 const std::string& printer_name) {
352 DCHECK(waiting_for_reply_);
353 UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
354 SERVICE_UTILITY_CAPS_FAILED,
355 SERVICE_UTILITY_EVENT_MAX);
356 UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilityCapsFailTime",
357 base::Time::Now() - start_time_);
358 waiting_for_reply_ = false;
359 client_message_loop_proxy_->PostTask(
361 base::Bind(&Client::OnGetPrinterCapsAndDefaults, client_.get(), false,
362 printer_name, printing::PrinterCapsAndDefaults()));
365 void ServiceUtilityProcessHost::OnGetPrinterSemanticCapsAndDefaultsFailed(
366 const std::string& printer_name) {
367 DCHECK(waiting_for_reply_);
368 UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
369 SERVICE_UTILITY_SEMANTIC_CAPS_FAILED,
370 SERVICE_UTILITY_EVENT_MAX);
371 UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilitySemanticCapsFailTime",
372 base::Time::Now() - start_time_);
373 waiting_for_reply_ = false;
374 client_message_loop_proxy_->PostTask(
376 base::Bind(&Client::OnGetPrinterSemanticCapsAndDefaults,
377 client_.get(), false, printer_name,
378 printing::PrinterSemanticCapsAndDefaults()));
381 void ServiceUtilityProcessHost::Client::MetafileAvailable(
382 const base::FilePath& metafile_path,
383 int highest_rendered_page_number,
384 double scale_factor) {
385 // The metafile was created in a temp folder which needs to get deleted after
386 // we have processed it.
387 base::ScopedTempDir scratch_metafile_dir;
388 if (!scratch_metafile_dir.Set(metafile_path.DirName()))
389 LOG(WARNING) << "Unable to set scratch metafile directory";
391 // It's important that metafile is declared after scratch_metafile_dir so
392 // that the metafile destructor closes the file before the base::ScopedTempDir
393 // destructor tries to remove the directory.
394 printing::Emf metafile;
395 if (!metafile.InitFromFile(metafile_path)) {
396 OnRenderPDFPagesToMetafileFailed();
398 OnRenderPDFPagesToMetafileSucceeded(metafile,
399 highest_rendered_page_number,
402 #endif // defined(OS_WIN)