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"
38 // NOTE: changes to this class need to be reviewed by the security team.
39 class ServiceSandboxedProcessLauncherDelegate
40 : public content::SandboxedProcessLauncherDelegate
{
42 explicit ServiceSandboxedProcessLauncherDelegate(
43 const base::FilePath
& exposed_dir
)
44 : exposed_dir_(exposed_dir
) {
47 virtual void PreSandbox(bool* disable_default_policy
,
48 base::FilePath
* exposed_dir
) OVERRIDE
{
49 *exposed_dir
= exposed_dir_
;
53 base::FilePath exposed_dir_
;
60 using content::ChildProcessHost
;
63 enum ServiceUtilityProcessHostEvent
{
64 SERVICE_UTILITY_STARTED
,
65 SERVICE_UTILITY_DISCONNECTED
,
66 SERVICE_UTILITY_METAFILE_REQUEST
,
67 SERVICE_UTILITY_METAFILE_SUCCEEDED
,
68 SERVICE_UTILITY_METAFILE_FAILED
,
69 SERVICE_UTILITY_CAPS_REQUEST
,
70 SERVICE_UTILITY_CAPS_SUCCEEDED
,
71 SERVICE_UTILITY_CAPS_FAILED
,
72 SERVICE_UTILITY_SEMANTIC_CAPS_REQUEST
,
73 SERVICE_UTILITY_SEMANTIC_CAPS_SUCCEEDED
,
74 SERVICE_UTILITY_SEMANTIC_CAPS_FAILED
,
75 SERVICE_UTILITY_EVENT_MAX
,
79 ServiceUtilityProcessHost::ServiceUtilityProcessHost(
80 Client
* client
, base::MessageLoopProxy
* client_message_loop_proxy
)
81 : handle_(base::kNullProcessHandle
),
83 client_message_loop_proxy_(client_message_loop_proxy
),
84 waiting_for_reply_(false) {
85 child_process_host_
.reset(ChildProcessHost::Create(this));
88 ServiceUtilityProcessHost::~ServiceUtilityProcessHost() {
89 // We need to kill the child process when the host dies.
90 base::KillProcess(handle_
, content::RESULT_CODE_NORMAL_EXIT
, false);
93 bool ServiceUtilityProcessHost::StartRenderPDFPagesToMetafile(
94 const base::FilePath
& pdf_path
,
95 const printing::PdfRenderSettings
& render_settings
,
96 const std::vector
<printing::PageRange
>& page_ranges
) {
97 UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
98 SERVICE_UTILITY_METAFILE_REQUEST
,
99 SERVICE_UTILITY_EVENT_MAX
);
100 start_time_
= base::Time::Now();
102 // This is only implemented on Windows (because currently it is only needed
103 // on Windows). Will add implementations on other platforms when needed.
106 #else // !defined(OS_WIN)
107 scratch_metafile_dir_
.reset(new base::ScopedTempDir
);
108 if (!scratch_metafile_dir_
->CreateUniqueTempDir())
110 metafile_path_
= scratch_metafile_dir_
->path().AppendASCII("output.emf");
111 if (!StartProcess(false, scratch_metafile_dir_
->path()))
116 base::File::FLAG_OPEN
| base::File::FLAG_READ
| base::File::FLAG_WRITE
);
117 DCHECK(!waiting_for_reply_
);
118 waiting_for_reply_
= true;
119 return child_process_host_
->Send(
120 new ChromeUtilityMsg_RenderPDFPagesToMetafiles(
121 IPC::TakeFileHandleForProcess(pdf_file
.Pass(), handle()),
125 #endif // !defined(OS_WIN)
128 bool ServiceUtilityProcessHost::StartGetPrinterCapsAndDefaults(
129 const std::string
& printer_name
) {
130 UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
131 SERVICE_UTILITY_CAPS_REQUEST
,
132 SERVICE_UTILITY_EVENT_MAX
);
133 start_time_
= base::Time::Now();
134 base::FilePath exposed_path
;
135 if (!StartProcess(true, exposed_path
))
137 DCHECK(!waiting_for_reply_
);
138 waiting_for_reply_
= true;
139 return child_process_host_
->Send(
140 new ChromeUtilityMsg_GetPrinterCapsAndDefaults(printer_name
));
143 bool ServiceUtilityProcessHost::StartGetPrinterSemanticCapsAndDefaults(
144 const std::string
& printer_name
) {
145 UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
146 SERVICE_UTILITY_SEMANTIC_CAPS_REQUEST
,
147 SERVICE_UTILITY_EVENT_MAX
);
148 start_time_
= base::Time::Now();
149 base::FilePath exposed_path
;
150 if (!StartProcess(true, exposed_path
))
152 DCHECK(!waiting_for_reply_
);
153 waiting_for_reply_
= true;
154 return child_process_host_
->Send(
155 new ChromeUtilityMsg_GetPrinterSemanticCapsAndDefaults(printer_name
));
158 bool ServiceUtilityProcessHost::StartProcess(
160 const base::FilePath
& exposed_dir
) {
161 std::string channel_id
= child_process_host_
->CreateChannel();
162 if (channel_id
.empty())
165 base::FilePath exe_path
= GetUtilityProcessCmd();
166 if (exe_path
.empty()) {
167 NOTREACHED() << "Unable to get utility process binary name.";
171 CommandLine
cmd_line(exe_path
);
172 cmd_line
.AppendSwitchASCII(switches::kProcessType
, switches::kUtilityProcess
);
173 cmd_line
.AppendSwitchASCII(switches::kProcessChannelID
, channel_id
);
174 cmd_line
.AppendSwitch(switches::kLang
);
176 if (Launch(&cmd_line
, no_sandbox
, exposed_dir
)) {
177 UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
178 SERVICE_UTILITY_STARTED
,
179 SERVICE_UTILITY_EVENT_MAX
);
185 bool ServiceUtilityProcessHost::Launch(CommandLine
* cmd_line
,
187 const base::FilePath
& exposed_dir
) {
189 // TODO(sanjeevr): Implement for non-Windows OSes.
192 #else // !defined(OS_WIN)
195 base::ProcessHandle process
= base::kNullProcessHandle
;
196 cmd_line
->AppendSwitch(switches::kNoSandbox
);
197 base::LaunchProcess(*cmd_line
, base::LaunchOptions(), &handle_
);
199 ServiceSandboxedProcessLauncherDelegate
delegate(exposed_dir
);
200 handle_
= content::StartSandboxedProcess(&delegate
, cmd_line
);
202 return (handle_
!= base::kNullProcessHandle
);
203 #endif // !defined(OS_WIN)
206 base::FilePath
ServiceUtilityProcessHost::GetUtilityProcessCmd() {
207 #if defined(OS_LINUX)
208 int flags
= ChildProcessHost::CHILD_ALLOW_SELF
;
210 int flags
= ChildProcessHost::CHILD_NORMAL
;
212 return ChildProcessHost::GetChildPath(flags
);
215 void ServiceUtilityProcessHost::OnChildDisconnected() {
216 if (waiting_for_reply_
) {
217 // If we are yet to receive a reply then notify the client that the
219 client_message_loop_proxy_
->PostTask(
220 FROM_HERE
, base::Bind(&Client::OnChildDied
, client_
.get()));
221 UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
222 SERVICE_UTILITY_DISCONNECTED
,
223 SERVICE_UTILITY_EVENT_MAX
);
224 UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilityDisconnectTime",
225 base::Time::Now() - start_time_
);
230 bool ServiceUtilityProcessHost::OnMessageReceived(const IPC::Message
& message
) {
232 IPC_BEGIN_MESSAGE_MAP(ServiceUtilityProcessHost
, message
)
233 #if defined(WIN_PDF_METAFILE_FOR_PRINTING)
235 ChromeUtilityHostMsg_RenderPDFPagesToMetafiles_Succeeded
,
236 OnRenderPDFPagesToMetafilesSucceeded
)
237 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_RenderPDFPagesToMetafile_Failed
,
238 OnRenderPDFPagesToMetafileFailed
)
241 ChromeUtilityHostMsg_GetPrinterCapsAndDefaults_Succeeded
,
242 OnGetPrinterCapsAndDefaultsSucceeded
)
243 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_GetPrinterCapsAndDefaults_Failed
,
244 OnGetPrinterCapsAndDefaultsFailed
)
246 ChromeUtilityHostMsg_GetPrinterSemanticCapsAndDefaults_Succeeded
,
247 OnGetPrinterSemanticCapsAndDefaultsSucceeded
)
249 ChromeUtilityHostMsg_GetPrinterSemanticCapsAndDefaults_Failed
,
250 OnGetPrinterSemanticCapsAndDefaultsFailed
)
251 IPC_MESSAGE_UNHANDLED(handled
= false)
252 IPC_END_MESSAGE_MAP()
256 base::ProcessHandle
ServiceUtilityProcessHost::GetHandle() const {
260 #if defined(WIN_PDF_METAFILE_FOR_PRINTING)
261 void ServiceUtilityProcessHost::OnRenderPDFPagesToMetafilesSucceeded(
262 const std::vector
<printing::PageRange
>& page_ranges
,
263 double scale_factor
) {
264 UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
265 SERVICE_UTILITY_METAFILE_SUCCEEDED
,
266 SERVICE_UTILITY_EVENT_MAX
);
267 UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilityMetafileTime",
268 base::Time::Now() - start_time_
);
269 DCHECK(waiting_for_reply_
);
270 waiting_for_reply_
= false;
271 // If the metafile was successfully created, we need to take our hands off the
272 // scratch metafile directory. The client will delete it when it is done with
274 scratch_metafile_dir_
->Take();
276 // TODO(vitalybuka|scottmg): http://crbug.com/170859: Currently, only one
277 // page is printed at a time. This would need to be refactored to change
279 CHECK_EQ(1u, page_ranges
.size());
280 CHECK_EQ(page_ranges
[0].from
, page_ranges
[0].to
);
281 int page_number
= page_ranges
[0].from
;
282 client_message_loop_proxy_
->PostTask(
284 base::Bind(&Client::MetafileAvailable
,
286 metafile_path_
.InsertBeforeExtensionASCII(
287 base::StringPrintf(".%d", page_number
)),
292 void ServiceUtilityProcessHost::OnRenderPDFPagesToMetafileFailed() {
293 DCHECK(waiting_for_reply_
);
294 UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
295 SERVICE_UTILITY_METAFILE_FAILED
,
296 SERVICE_UTILITY_EVENT_MAX
);
297 UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilityMetafileFailTime",
298 base::Time::Now() - start_time_
);
299 waiting_for_reply_
= false;
300 client_message_loop_proxy_
->PostTask(
302 base::Bind(&Client::OnRenderPDFPagesToMetafileFailed
, client_
.get()));
304 #endif // defined(WIN_PDF_METAFILE_FOR_PRINTING)
306 void ServiceUtilityProcessHost::OnGetPrinterCapsAndDefaultsSucceeded(
307 const std::string
& printer_name
,
308 const printing::PrinterCapsAndDefaults
& caps_and_defaults
) {
309 DCHECK(waiting_for_reply_
);
310 UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
311 SERVICE_UTILITY_CAPS_SUCCEEDED
,
312 SERVICE_UTILITY_EVENT_MAX
);
313 UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilityCapsTime",
314 base::Time::Now() - start_time_
);
315 waiting_for_reply_
= false;
316 client_message_loop_proxy_
->PostTask(
318 base::Bind(&Client::OnGetPrinterCapsAndDefaults
, client_
.get(), true,
319 printer_name
, caps_and_defaults
));
322 void ServiceUtilityProcessHost::OnGetPrinterSemanticCapsAndDefaultsSucceeded(
323 const std::string
& printer_name
,
324 const printing::PrinterSemanticCapsAndDefaults
& caps_and_defaults
) {
325 DCHECK(waiting_for_reply_
);
326 UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
327 SERVICE_UTILITY_SEMANTIC_CAPS_SUCCEEDED
,
328 SERVICE_UTILITY_EVENT_MAX
);
329 UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilitySemanticCapsTime",
330 base::Time::Now() - start_time_
);
331 waiting_for_reply_
= false;
332 client_message_loop_proxy_
->PostTask(
334 base::Bind(&Client::OnGetPrinterSemanticCapsAndDefaults
, client_
.get(),
335 true, printer_name
, caps_and_defaults
));
338 void ServiceUtilityProcessHost::OnGetPrinterCapsAndDefaultsFailed(
339 const std::string
& printer_name
) {
340 DCHECK(waiting_for_reply_
);
341 UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
342 SERVICE_UTILITY_CAPS_FAILED
,
343 SERVICE_UTILITY_EVENT_MAX
);
344 UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilityCapsFailTime",
345 base::Time::Now() - start_time_
);
346 waiting_for_reply_
= false;
347 client_message_loop_proxy_
->PostTask(
349 base::Bind(&Client::OnGetPrinterCapsAndDefaults
, client_
.get(), false,
350 printer_name
, printing::PrinterCapsAndDefaults()));
353 void ServiceUtilityProcessHost::OnGetPrinterSemanticCapsAndDefaultsFailed(
354 const std::string
& printer_name
) {
355 DCHECK(waiting_for_reply_
);
356 UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
357 SERVICE_UTILITY_SEMANTIC_CAPS_FAILED
,
358 SERVICE_UTILITY_EVENT_MAX
);
359 UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilitySemanticCapsFailTime",
360 base::Time::Now() - start_time_
);
361 waiting_for_reply_
= false;
362 client_message_loop_proxy_
->PostTask(
364 base::Bind(&Client::OnGetPrinterSemanticCapsAndDefaults
,
365 client_
.get(), false, printer_name
,
366 printing::PrinterSemanticCapsAndDefaults()));
369 void ServiceUtilityProcessHost::Client::MetafileAvailable(
370 const base::FilePath
& metafile_path
,
371 int highest_rendered_page_number
,
372 double scale_factor
) {
373 // The metafile was created in a temp folder which needs to get deleted after
374 // we have processed it.
375 base::ScopedTempDir scratch_metafile_dir
;
376 if (!scratch_metafile_dir
.Set(metafile_path
.DirName()))
377 LOG(WARNING
) << "Unable to set scratch metafile directory";
379 // It's important that metafile is declared after scratch_metafile_dir so
380 // that the metafile destructor closes the file before the base::ScopedTempDir
381 // destructor tries to remove the directory.
382 printing::Emf metafile
;
383 if (!metafile
.InitFromFile(metafile_path
)) {
384 OnRenderPDFPagesToMetafileFailed();
386 OnRenderPDFPagesToMetafileSucceeded(metafile
,
387 highest_rendered_page_number
,
390 #endif // defined(OS_WIN)