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"
10 #include "base/command_line.h"
11 #include "base/files/file.h"
12 #include "base/files/file_path.h"
13 #include "base/files/file_util.h"
14 #include "base/files/scoped_temp_dir.h"
15 #include "base/logging.h"
16 #include "base/message_loop/message_loop_proxy.h"
17 #include "base/metrics/histogram.h"
18 #include "base/process/kill.h"
19 #include "base/process/launch.h"
20 #include "base/task_runner_util.h"
21 #include "chrome/common/chrome_switches.h"
22 #include "chrome/common/chrome_utility_printing_messages.h"
23 #include "content/public/common/child_process_host.h"
24 #include "content/public/common/result_codes.h"
25 #include "content/public/common/sandbox_init.h"
26 #include "content/public/common/sandboxed_process_launcher_delegate.h"
27 #include "ipc/ipc_switches.h"
28 #include "printing/emf_win.h"
29 #include "sandbox/win/src/sandbox_policy_base.h"
30 #include "ui/base/ui_base_switches.h"
34 using content::ChildProcessHost
;
36 enum ServiceUtilityProcessHostEvent
{
37 SERVICE_UTILITY_STARTED
,
38 SERVICE_UTILITY_DISCONNECTED
,
39 SERVICE_UTILITY_METAFILE_REQUEST
,
40 SERVICE_UTILITY_METAFILE_SUCCEEDED
,
41 SERVICE_UTILITY_METAFILE_FAILED
,
42 SERVICE_UTILITY_CAPS_REQUEST
,
43 SERVICE_UTILITY_CAPS_SUCCEEDED
,
44 SERVICE_UTILITY_CAPS_FAILED
,
45 SERVICE_UTILITY_SEMANTIC_CAPS_REQUEST
,
46 SERVICE_UTILITY_SEMANTIC_CAPS_SUCCEEDED
,
47 SERVICE_UTILITY_SEMANTIC_CAPS_FAILED
,
48 SERVICE_UTILITY_FAILED_TO_START
,
49 SERVICE_UTILITY_EVENT_MAX
,
52 void ReportUmaEvent(ServiceUtilityProcessHostEvent id
) {
53 UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
55 SERVICE_UTILITY_EVENT_MAX
);
58 // NOTE: changes to this class need to be reviewed by the security team.
59 class ServiceSandboxedProcessLauncherDelegate
60 : public content::SandboxedProcessLauncherDelegate
{
62 ServiceSandboxedProcessLauncherDelegate() {}
64 virtual void PreSpawnTarget(sandbox::TargetPolicy
* policy
,
65 bool* success
) OVERRIDE
{
66 // Service process may run as windows service and it fails to create a
68 policy
->SetAlternateDesktop(false);
72 DISALLOW_COPY_AND_ASSIGN(ServiceSandboxedProcessLauncherDelegate
);
77 class ServiceUtilityProcessHost::PdfToEmfState
{
79 explicit PdfToEmfState(ServiceUtilityProcessHost
* host
)
80 : host_(host
), page_count_(0), current_page_(0), pages_in_progress_(0) {}
81 ~PdfToEmfState() { Stop(); }
83 bool Start(base::File pdf_file
,
84 const printing::PdfRenderSettings
& conversion_settings
) {
85 if (!temp_dir_
.CreateUniqueTempDir())
87 return host_
->Send(new ChromeUtilityMsg_RenderPDFPagesToMetafiles(
88 IPC::TakeFileHandleForProcess(pdf_file
.Pass(), host_
->handle()),
89 conversion_settings
));
93 const int kMaxNumberOfTempFilesPerDocument
= 3;
94 while (pages_in_progress_
< kMaxNumberOfTempFilesPerDocument
&&
95 current_page_
< page_count_
) {
97 emf_files_
.push(CreateTempFile());
98 host_
->Send(new ChromeUtilityMsg_RenderPDFPagesToMetafiles_GetPage(
100 IPC::GetFileHandleForProcess(
101 emf_files_
.back().GetPlatformFile(), host_
->handle(), false)));
105 // Returns true if all pages processed and client should not expect more
107 bool OnPageProcessed() {
108 --pages_in_progress_
;
110 if (pages_in_progress_
|| current_page_
< page_count_
)
116 base::File
TakeNextFile() {
117 DCHECK(!emf_files_
.empty());
119 if (!emf_files_
.empty())
120 file
= emf_files_
.front().Pass();
125 void set_page_count(int page_count
) { page_count_
= page_count
; }
126 bool has_page_count() { return page_count_
> 0; }
130 host_
->Send(new ChromeUtilityMsg_RenderPDFPagesToMetafiles_Stop());
133 base::File
CreateTempFile() {
135 if (!base::CreateTemporaryFileInDir(temp_dir_
.path(), &path
))
137 return base::File(path
,
138 base::File::FLAG_CREATE_ALWAYS
|
139 base::File::FLAG_WRITE
|
140 base::File::FLAG_READ
|
141 base::File::FLAG_DELETE_ON_CLOSE
|
142 base::File::FLAG_TEMPORARY
);
145 base::ScopedTempDir temp_dir_
;
146 ServiceUtilityProcessHost
* host_
;
147 std::queue
<base::File
> emf_files_
;
150 int pages_in_progress_
;
153 ServiceUtilityProcessHost::ServiceUtilityProcessHost(
155 base::MessageLoopProxy
* client_message_loop_proxy
)
156 : handle_(base::kNullProcessHandle
),
158 client_message_loop_proxy_(client_message_loop_proxy
),
159 waiting_for_reply_(false),
160 weak_ptr_factory_(this) {
161 child_process_host_
.reset(ChildProcessHost::Create(this));
164 ServiceUtilityProcessHost::~ServiceUtilityProcessHost() {
165 // We need to kill the child process when the host dies.
166 base::KillProcess(handle_
, content::RESULT_CODE_NORMAL_EXIT
, false);
169 bool ServiceUtilityProcessHost::StartRenderPDFPagesToMetafile(
170 const base::FilePath
& pdf_path
,
171 const printing::PdfRenderSettings
& render_settings
) {
172 ReportUmaEvent(SERVICE_UTILITY_METAFILE_REQUEST
);
173 start_time_
= base::Time::Now();
174 base::File
pdf_file(pdf_path
, base::File::FLAG_OPEN
| base::File::FLAG_READ
);
175 if (!pdf_file
.IsValid() || !StartProcess(false))
178 DCHECK(!waiting_for_reply_
);
179 waiting_for_reply_
= true;
181 pdf_to_emf_state_
.reset(new PdfToEmfState(this));
182 return pdf_to_emf_state_
->Start(pdf_file
.Pass(), render_settings
);
185 bool ServiceUtilityProcessHost::StartGetPrinterCapsAndDefaults(
186 const std::string
& printer_name
) {
187 ReportUmaEvent(SERVICE_UTILITY_CAPS_REQUEST
);
188 start_time_
= base::Time::Now();
189 if (!StartProcess(true))
191 DCHECK(!waiting_for_reply_
);
192 waiting_for_reply_
= true;
193 return Send(new ChromeUtilityMsg_GetPrinterCapsAndDefaults(printer_name
));
196 bool ServiceUtilityProcessHost::StartGetPrinterSemanticCapsAndDefaults(
197 const std::string
& printer_name
) {
198 ReportUmaEvent(SERVICE_UTILITY_SEMANTIC_CAPS_REQUEST
);
199 start_time_
= base::Time::Now();
200 if (!StartProcess(true))
202 DCHECK(!waiting_for_reply_
);
203 waiting_for_reply_
= true;
205 new ChromeUtilityMsg_GetPrinterSemanticCapsAndDefaults(printer_name
));
208 bool ServiceUtilityProcessHost::StartProcess(bool no_sandbox
) {
209 std::string channel_id
= child_process_host_
->CreateChannel();
210 if (channel_id
.empty())
213 base::FilePath exe_path
= GetUtilityProcessCmd();
214 if (exe_path
.empty()) {
215 NOTREACHED() << "Unable to get utility process binary name.";
219 base::CommandLine
cmd_line(exe_path
);
220 cmd_line
.AppendSwitchASCII(switches::kProcessType
, switches::kUtilityProcess
);
221 cmd_line
.AppendSwitchASCII(switches::kProcessChannelID
, channel_id
);
222 cmd_line
.AppendSwitch(switches::kLang
);
224 if (Launch(&cmd_line
, no_sandbox
)) {
225 ReportUmaEvent(SERVICE_UTILITY_STARTED
);
228 ReportUmaEvent(SERVICE_UTILITY_FAILED_TO_START
);
232 bool ServiceUtilityProcessHost::Launch(base::CommandLine
* cmd_line
,
235 base::ProcessHandle process
= base::kNullProcessHandle
;
236 cmd_line
->AppendSwitch(switches::kNoSandbox
);
237 base::LaunchProcess(*cmd_line
, base::LaunchOptions(), &handle_
);
239 ServiceSandboxedProcessLauncherDelegate delegate
;
240 handle_
= content::StartSandboxedProcess(&delegate
, cmd_line
);
242 return (handle_
!= base::kNullProcessHandle
);
245 bool ServiceUtilityProcessHost::Send(IPC::Message
* msg
) {
246 if (child_process_host_
)
247 return child_process_host_
->Send(msg
);
252 base::FilePath
ServiceUtilityProcessHost::GetUtilityProcessCmd() {
253 #if defined(OS_LINUX)
254 int flags
= ChildProcessHost::CHILD_ALLOW_SELF
;
256 int flags
= ChildProcessHost::CHILD_NORMAL
;
258 return ChildProcessHost::GetChildPath(flags
);
261 void ServiceUtilityProcessHost::OnChildDisconnected() {
262 if (waiting_for_reply_
) {
263 // If we are yet to receive a reply then notify the client that the
265 client_message_loop_proxy_
->PostTask(
266 FROM_HERE
, base::Bind(&Client::OnChildDied
, client_
.get()));
267 ReportUmaEvent(SERVICE_UTILITY_DISCONNECTED
);
268 UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilityDisconnectTime",
269 base::Time::Now() - start_time_
);
274 bool ServiceUtilityProcessHost::OnMessageReceived(const IPC::Message
& message
) {
276 IPC_BEGIN_MESSAGE_MAP(ServiceUtilityProcessHost
, message
)
278 ChromeUtilityHostMsg_RenderPDFPagesToMetafiles_PageCount
,
279 OnRenderPDFPagesToMetafilesPageCount
)
280 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_RenderPDFPagesToMetafiles_PageDone
,
281 OnRenderPDFPagesToMetafilesPageDone
)
283 ChromeUtilityHostMsg_GetPrinterCapsAndDefaults_Succeeded
,
284 OnGetPrinterCapsAndDefaultsSucceeded
)
285 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_GetPrinterCapsAndDefaults_Failed
,
286 OnGetPrinterCapsAndDefaultsFailed
)
288 ChromeUtilityHostMsg_GetPrinterSemanticCapsAndDefaults_Succeeded
,
289 OnGetPrinterSemanticCapsAndDefaultsSucceeded
)
291 ChromeUtilityHostMsg_GetPrinterSemanticCapsAndDefaults_Failed
,
292 OnGetPrinterSemanticCapsAndDefaultsFailed
)
293 IPC_MESSAGE_UNHANDLED(handled
= false)
294 IPC_END_MESSAGE_MAP()
298 base::ProcessHandle
ServiceUtilityProcessHost::GetHandle() const {
302 void ServiceUtilityProcessHost::OnMetafileSpooled(bool success
) {
303 if (!success
|| pdf_to_emf_state_
->OnPageProcessed())
304 OnPDFToEmfFinished(success
);
307 void ServiceUtilityProcessHost::OnRenderPDFPagesToMetafilesPageCount(
309 DCHECK(waiting_for_reply_
);
310 if (!pdf_to_emf_state_
|| page_count
<= 0 ||
311 pdf_to_emf_state_
->has_page_count()) {
312 return OnPDFToEmfFinished(false);
314 pdf_to_emf_state_
->set_page_count(page_count
);
315 pdf_to_emf_state_
->GetMorePages();
318 void ServiceUtilityProcessHost::OnRenderPDFPagesToMetafilesPageDone(
320 double scale_factor
) {
321 DCHECK(waiting_for_reply_
);
322 if (!pdf_to_emf_state_
|| !success
)
323 return OnPDFToEmfFinished(false);
324 base::File emf_file
= pdf_to_emf_state_
->TakeNextFile();
325 base::PostTaskAndReplyWithResult(
326 client_message_loop_proxy_
,
328 base::Bind(&Client::MetafileAvailable
,
331 base::Passed(&emf_file
)),
332 base::Bind(&ServiceUtilityProcessHost::OnMetafileSpooled
,
333 weak_ptr_factory_
.GetWeakPtr()));
336 void ServiceUtilityProcessHost::OnPDFToEmfFinished(bool success
) {
337 if (!waiting_for_reply_
)
339 waiting_for_reply_
= false;
341 ReportUmaEvent(SERVICE_UTILITY_METAFILE_SUCCEEDED
);
342 UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilityMetafileTime",
343 base::Time::Now() - start_time_
);
345 ReportUmaEvent(SERVICE_UTILITY_METAFILE_FAILED
);
346 UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilityMetafileFailTime",
347 base::Time::Now() - start_time_
);
349 client_message_loop_proxy_
->PostTask(
352 &Client::OnRenderPDFPagesToMetafileDone
, client_
.get(), success
));
353 pdf_to_emf_state_
.reset();
356 void ServiceUtilityProcessHost::OnGetPrinterCapsAndDefaultsSucceeded(
357 const std::string
& printer_name
,
358 const printing::PrinterCapsAndDefaults
& caps_and_defaults
) {
359 DCHECK(waiting_for_reply_
);
360 ReportUmaEvent(SERVICE_UTILITY_CAPS_SUCCEEDED
);
361 UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilityCapsTime",
362 base::Time::Now() - start_time_
);
363 waiting_for_reply_
= false;
364 client_message_loop_proxy_
->PostTask(
366 base::Bind(&Client::OnGetPrinterCapsAndDefaults
, client_
.get(), true,
367 printer_name
, caps_and_defaults
));
370 void ServiceUtilityProcessHost::OnGetPrinterSemanticCapsAndDefaultsSucceeded(
371 const std::string
& printer_name
,
372 const printing::PrinterSemanticCapsAndDefaults
& caps_and_defaults
) {
373 DCHECK(waiting_for_reply_
);
374 ReportUmaEvent(SERVICE_UTILITY_SEMANTIC_CAPS_SUCCEEDED
);
375 UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilitySemanticCapsTime",
376 base::Time::Now() - start_time_
);
377 waiting_for_reply_
= false;
378 client_message_loop_proxy_
->PostTask(
380 base::Bind(&Client::OnGetPrinterSemanticCapsAndDefaults
, client_
.get(),
381 true, printer_name
, caps_and_defaults
));
384 void ServiceUtilityProcessHost::OnGetPrinterCapsAndDefaultsFailed(
385 const std::string
& printer_name
) {
386 DCHECK(waiting_for_reply_
);
387 ReportUmaEvent(SERVICE_UTILITY_CAPS_FAILED
);
388 UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilityCapsFailTime",
389 base::Time::Now() - start_time_
);
390 waiting_for_reply_
= false;
391 client_message_loop_proxy_
->PostTask(
393 base::Bind(&Client::OnGetPrinterCapsAndDefaults
, client_
.get(), false,
394 printer_name
, printing::PrinterCapsAndDefaults()));
397 void ServiceUtilityProcessHost::OnGetPrinterSemanticCapsAndDefaultsFailed(
398 const std::string
& printer_name
) {
399 DCHECK(waiting_for_reply_
);
400 ReportUmaEvent(SERVICE_UTILITY_SEMANTIC_CAPS_FAILED
);
401 UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilitySemanticCapsFailTime",
402 base::Time::Now() - start_time_
);
403 waiting_for_reply_
= false;
404 client_message_loop_proxy_
->PostTask(
406 base::Bind(&Client::OnGetPrinterSemanticCapsAndDefaults
,
407 client_
.get(), false, printer_name
,
408 printing::PrinterSemanticCapsAndDefaults()));
411 bool ServiceUtilityProcessHost::Client::MetafileAvailable(double scale_factor
,
413 file
.Seek(base::File::FROM_BEGIN
, 0);
414 int64 size
= file
.GetLength();
416 OnRenderPDFPagesToMetafileDone(false);
419 std::vector
<char> data(size
);
420 if (file
.ReadAtCurrentPos(data
.data(), data
.size()) != size
) {
421 OnRenderPDFPagesToMetafileDone(false);
425 if (!emf
.InitFromData(data
.data(), data
.size())) {
426 OnRenderPDFPagesToMetafileDone(false);
429 OnRenderPDFPagesToMetafilePageDone(scale_factor
, emf
);