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_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 if (!base::CreateTemporaryFileInDir(scratch_metafile_dir_
->path(),
115 if (!StartProcess(false, scratch_metafile_dir_
->path()))
118 base::win::ScopedHandle
pdf_file(
119 ::CreateFile(pdf_path
.value().c_str(),
121 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
124 FILE_ATTRIBUTE_NORMAL
,
126 if (pdf_file
== INVALID_HANDLE_VALUE
)
128 HANDLE pdf_file_in_utility_process
= NULL
;
129 ::DuplicateHandle(::GetCurrentProcess(), pdf_file
, handle(),
130 &pdf_file_in_utility_process
, 0, false,
131 DUPLICATE_SAME_ACCESS
);
132 if (!pdf_file_in_utility_process
)
134 DCHECK(!waiting_for_reply_
);
135 waiting_for_reply_
= true;
136 return child_process_host_
->Send(
137 new ChromeUtilityMsg_RenderPDFPagesToMetafile(
138 pdf_file_in_utility_process
,
142 #endif // !defined(OS_WIN)
145 bool ServiceUtilityProcessHost::StartGetPrinterCapsAndDefaults(
146 const std::string
& printer_name
) {
147 UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
148 SERVICE_UTILITY_CAPS_REQUEST
,
149 SERVICE_UTILITY_EVENT_MAX
);
150 start_time_
= base::Time::Now();
151 base::FilePath exposed_path
;
152 if (!StartProcess(true, exposed_path
))
154 DCHECK(!waiting_for_reply_
);
155 waiting_for_reply_
= true;
156 return child_process_host_
->Send(
157 new ChromeUtilityMsg_GetPrinterCapsAndDefaults(printer_name
));
160 bool ServiceUtilityProcessHost::StartGetPrinterSemanticCapsAndDefaults(
161 const std::string
& printer_name
) {
162 UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
163 SERVICE_UTILITY_SEMANTIC_CAPS_REQUEST
,
164 SERVICE_UTILITY_EVENT_MAX
);
165 start_time_
= base::Time::Now();
166 base::FilePath exposed_path
;
167 if (!StartProcess(true, exposed_path
))
169 DCHECK(!waiting_for_reply_
);
170 waiting_for_reply_
= true;
171 return child_process_host_
->Send(
172 new ChromeUtilityMsg_GetPrinterSemanticCapsAndDefaults(printer_name
));
175 bool ServiceUtilityProcessHost::StartProcess(
177 const base::FilePath
& exposed_dir
) {
178 std::string channel_id
= child_process_host_
->CreateChannel();
179 if (channel_id
.empty())
182 base::FilePath exe_path
= GetUtilityProcessCmd();
183 if (exe_path
.empty()) {
184 NOTREACHED() << "Unable to get utility process binary name.";
188 CommandLine
cmd_line(exe_path
);
189 cmd_line
.AppendSwitchASCII(switches::kProcessType
, switches::kUtilityProcess
);
190 cmd_line
.AppendSwitchASCII(switches::kProcessChannelID
, channel_id
);
191 cmd_line
.AppendSwitch(switches::kLang
);
193 if (Launch(&cmd_line
, no_sandbox
, exposed_dir
)) {
194 UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
195 SERVICE_UTILITY_STARTED
,
196 SERVICE_UTILITY_EVENT_MAX
);
202 bool ServiceUtilityProcessHost::Launch(CommandLine
* cmd_line
,
204 const base::FilePath
& exposed_dir
) {
206 // TODO(sanjeevr): Implement for non-Windows OSes.
209 #else // !defined(OS_WIN)
212 base::ProcessHandle process
= base::kNullProcessHandle
;
213 cmd_line
->AppendSwitch(switches::kNoSandbox
);
214 base::LaunchProcess(*cmd_line
, base::LaunchOptions(), &handle_
);
216 ServiceSandboxedProcessLauncherDelegate
delegate(exposed_dir
);
217 handle_
= content::StartSandboxedProcess(&delegate
, cmd_line
);
219 return (handle_
!= base::kNullProcessHandle
);
220 #endif // !defined(OS_WIN)
223 base::FilePath
ServiceUtilityProcessHost::GetUtilityProcessCmd() {
224 #if defined(OS_LINUX)
225 int flags
= ChildProcessHost::CHILD_ALLOW_SELF
;
227 int flags
= ChildProcessHost::CHILD_NORMAL
;
229 return ChildProcessHost::GetChildPath(flags
);
232 void ServiceUtilityProcessHost::OnChildDisconnected() {
233 if (waiting_for_reply_
) {
234 // If we are yet to receive a reply then notify the client that the
236 client_message_loop_proxy_
->PostTask(
237 FROM_HERE
, base::Bind(&Client::OnChildDied
, client_
.get()));
238 UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
239 SERVICE_UTILITY_DISCONNECTED
,
240 SERVICE_UTILITY_EVENT_MAX
);
241 UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilityDisconnectTime",
242 base::Time::Now() - start_time_
);
247 bool ServiceUtilityProcessHost::OnMessageReceived(const IPC::Message
& message
) {
249 IPC_BEGIN_MESSAGE_MAP(ServiceUtilityProcessHost
, message
)
251 ChromeUtilityHostMsg_RenderPDFPagesToMetafile_Succeeded
,
252 OnRenderPDFPagesToMetafileSucceeded
)
253 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_RenderPDFPagesToMetafile_Failed
,
254 OnRenderPDFPagesToMetafileFailed
)
256 ChromeUtilityHostMsg_GetPrinterCapsAndDefaults_Succeeded
,
257 OnGetPrinterCapsAndDefaultsSucceeded
)
258 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_GetPrinterCapsAndDefaults_Failed
,
259 OnGetPrinterCapsAndDefaultsFailed
)
261 ChromeUtilityHostMsg_GetPrinterSemanticCapsAndDefaults_Succeeded
,
262 OnGetPrinterSemanticCapsAndDefaultsSucceeded
)
264 ChromeUtilityHostMsg_GetPrinterSemanticCapsAndDefaults_Failed
,
265 OnGetPrinterSemanticCapsAndDefaultsFailed
)
266 IPC_MESSAGE_UNHANDLED(handled
= false)
267 IPC_END_MESSAGE_MAP()
271 base::ProcessHandle
ServiceUtilityProcessHost::GetHandle() const {
275 void ServiceUtilityProcessHost::OnRenderPDFPagesToMetafileSucceeded(
276 int highest_rendered_page_number
,
277 double scale_factor
) {
278 UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
279 SERVICE_UTILITY_METAFILE_SUCCEEDED
,
280 SERVICE_UTILITY_EVENT_MAX
);
281 UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilityMetafileTime",
282 base::Time::Now() - start_time_
);
283 DCHECK(waiting_for_reply_
);
284 waiting_for_reply_
= false;
285 // If the metafile was successfully created, we need to take our hands off the
286 // scratch metafile directory. The client will delete it when it is done with
288 scratch_metafile_dir_
->Take();
289 client_message_loop_proxy_
->PostTask(
291 base::Bind(&Client::MetafileAvailable
, client_
.get(), metafile_path_
,
292 highest_rendered_page_number
, scale_factor
));
295 void ServiceUtilityProcessHost::OnRenderPDFPagesToMetafileFailed() {
296 DCHECK(waiting_for_reply_
);
297 UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
298 SERVICE_UTILITY_METAFILE_FAILED
,
299 SERVICE_UTILITY_EVENT_MAX
);
300 UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilityMetafileFailTime",
301 base::Time::Now() - start_time_
);
302 waiting_for_reply_
= false;
303 client_message_loop_proxy_
->PostTask(
305 base::Bind(&Client::OnRenderPDFPagesToMetafileFailed
, client_
.get()));
308 void ServiceUtilityProcessHost::OnGetPrinterCapsAndDefaultsSucceeded(
309 const std::string
& printer_name
,
310 const printing::PrinterCapsAndDefaults
& caps_and_defaults
) {
311 DCHECK(waiting_for_reply_
);
312 UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
313 SERVICE_UTILITY_CAPS_SUCCEEDED
,
314 SERVICE_UTILITY_EVENT_MAX
);
315 UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilityCapsTime",
316 base::Time::Now() - start_time_
);
317 waiting_for_reply_
= false;
318 client_message_loop_proxy_
->PostTask(
320 base::Bind(&Client::OnGetPrinterCapsAndDefaults
, client_
.get(), true,
321 printer_name
, caps_and_defaults
));
324 void ServiceUtilityProcessHost::OnGetPrinterSemanticCapsAndDefaultsSucceeded(
325 const std::string
& printer_name
,
326 const printing::PrinterSemanticCapsAndDefaults
& caps_and_defaults
) {
327 DCHECK(waiting_for_reply_
);
328 UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
329 SERVICE_UTILITY_SEMANTIC_CAPS_SUCCEEDED
,
330 SERVICE_UTILITY_EVENT_MAX
);
331 UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilitySemanticCapsTime",
332 base::Time::Now() - start_time_
);
333 waiting_for_reply_
= false;
334 client_message_loop_proxy_
->PostTask(
336 base::Bind(&Client::OnGetPrinterSemanticCapsAndDefaults
, client_
.get(),
337 true, printer_name
, caps_and_defaults
));
340 void ServiceUtilityProcessHost::OnGetPrinterCapsAndDefaultsFailed(
341 const std::string
& printer_name
) {
342 DCHECK(waiting_for_reply_
);
343 UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
344 SERVICE_UTILITY_CAPS_FAILED
,
345 SERVICE_UTILITY_EVENT_MAX
);
346 UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilityCapsFailTime",
347 base::Time::Now() - start_time_
);
348 waiting_for_reply_
= false;
349 client_message_loop_proxy_
->PostTask(
351 base::Bind(&Client::OnGetPrinterCapsAndDefaults
, client_
.get(), false,
352 printer_name
, printing::PrinterCapsAndDefaults()));
355 void ServiceUtilityProcessHost::OnGetPrinterSemanticCapsAndDefaultsFailed(
356 const std::string
& printer_name
) {
357 DCHECK(waiting_for_reply_
);
358 UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
359 SERVICE_UTILITY_SEMANTIC_CAPS_FAILED
,
360 SERVICE_UTILITY_EVENT_MAX
);
361 UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilitySemanticCapsFailTime",
362 base::Time::Now() - start_time_
);
363 waiting_for_reply_
= false;
364 client_message_loop_proxy_
->PostTask(
366 base::Bind(&Client::OnGetPrinterSemanticCapsAndDefaults
,
367 client_
.get(), false, printer_name
,
368 printing::PrinterSemanticCapsAndDefaults()));
371 void ServiceUtilityProcessHost::Client::MetafileAvailable(
372 const base::FilePath
& metafile_path
,
373 int highest_rendered_page_number
,
374 double scale_factor
) {
375 // The metafile was created in a temp folder which needs to get deleted after
376 // we have processed it.
377 base::ScopedTempDir scratch_metafile_dir
;
378 if (!scratch_metafile_dir
.Set(metafile_path
.DirName()))
379 LOG(WARNING
) << "Unable to set scratch metafile directory";
381 // It's important that metafile is declared after scratch_metafile_dir so
382 // that the metafile destructor closes the file before the base::ScopedTempDir
383 // destructor tries to remove the directory.
384 printing::Emf metafile
;
385 if (!metafile
.InitFromFile(metafile_path
)) {
386 OnRenderPDFPagesToMetafileFailed();
388 OnRenderPDFPagesToMetafileSucceeded(metafile
,
389 highest_rendered_page_number
,
392 #endif // defined(OS_WIN)