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. 4 5#include "chrome/service/service_utility_process_host.h" 6 7#include "base/bind.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" 26 27#if defined(OS_WIN) 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 36namespace { 37 38// NOTE: changes to this class need to be reviewed by the security team. 39class ServiceSandboxedProcessLauncherDelegate 40 : public content::SandboxedProcessLauncherDelegate { 41 public: 42 explicit ServiceSandboxedProcessLauncherDelegate( 43 const base::FilePath& exposed_dir) 44 : exposed_dir_(exposed_dir) { 45 } 46 47 virtual void PreSandbox(bool* disable_default_policy, 48 base::FilePath* exposed_dir) OVERRIDE { 49 *exposed_dir = exposed_dir_; 50 } 51 52 private: 53 base::FilePath exposed_dir_; 54}; 55 56} // namespace 57 58#endif // OS_WIN 59 60using content::ChildProcessHost; 61 62namespace { 63enum 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, 76}; 77} // namespace 78 79ServiceUtilityProcessHost::ServiceUtilityProcessHost( 80 Client* client, base::MessageLoopProxy* client_message_loop_proxy) 81 : handle_(base::kNullProcessHandle), 82 client_(client), 83 client_message_loop_proxy_(client_message_loop_proxy), 84 waiting_for_reply_(false) { 85 child_process_host_.reset(ChildProcessHost::Create(this)); 86} 87 88ServiceUtilityProcessHost::~ServiceUtilityProcessHost() { 89 // We need to kill the child process when the host dies. 90 base::KillProcess(handle_, content::RESULT_CODE_NORMAL_EXIT, false); 91} 92 93bool 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(); 101#if !defined(OS_WIN) 102 // This is only implemented on Windows (because currently it is only needed 103 // on Windows). Will add implementations on other platforms when needed. 104 NOTIMPLEMENTED(); 105 return false; 106#else // !defined(OS_WIN) 107 scratch_metafile_dir_.reset(new base::ScopedTempDir); 108 if (!scratch_metafile_dir_->CreateUniqueTempDir()) 109 return false; 110 metafile_path_ = scratch_metafile_dir_->path().AppendASCII("output.emf"); 111 if (!StartProcess(false, scratch_metafile_dir_->path())) 112 return false; 113 114 base::File pdf_file( 115 pdf_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()), 122 metafile_path_, 123 render_settings, 124 page_ranges)); 125#endif // !defined(OS_WIN) 126} 127 128bool 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)) 136 return false; 137 DCHECK(!waiting_for_reply_); 138 waiting_for_reply_ = true; 139 return child_process_host_->Send( 140 new ChromeUtilityMsg_GetPrinterCapsAndDefaults(printer_name)); 141} 142 143bool 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)) 151 return false; 152 DCHECK(!waiting_for_reply_); 153 waiting_for_reply_ = true; 154 return child_process_host_->Send( 155 new ChromeUtilityMsg_GetPrinterSemanticCapsAndDefaults(printer_name)); 156} 157 158bool ServiceUtilityProcessHost::StartProcess( 159 bool no_sandbox, 160 const base::FilePath& exposed_dir) { 161 std::string channel_id = child_process_host_->CreateChannel(); 162 if (channel_id.empty()) 163 return false; 164 165 base::FilePath exe_path = GetUtilityProcessCmd(); 166 if (exe_path.empty()) { 167 NOTREACHED() << "Unable to get utility process binary name."; 168 return false; 169 } 170 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); 175 176 if (Launch(&cmd_line, no_sandbox, exposed_dir)) { 177 UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent", 178 SERVICE_UTILITY_STARTED, 179 SERVICE_UTILITY_EVENT_MAX); 180 return true; 181 } 182 return false; 183} 184 185bool ServiceUtilityProcessHost::Launch(CommandLine* cmd_line, 186 bool no_sandbox, 187 const base::FilePath& exposed_dir) { 188#if !defined(OS_WIN) 189 // TODO(sanjeevr): Implement for non-Windows OSes. 190 NOTIMPLEMENTED(); 191 return false; 192#else // !defined(OS_WIN) 193 194 if (no_sandbox) { 195 base::ProcessHandle process = base::kNullProcessHandle; 196 cmd_line->AppendSwitch(switches::kNoSandbox); 197 base::LaunchProcess(*cmd_line, base::LaunchOptions(), &handle_); 198 } else { 199 ServiceSandboxedProcessLauncherDelegate delegate(exposed_dir); 200 handle_ = content::StartSandboxedProcess(&delegate, cmd_line); 201 } 202 return (handle_ != base::kNullProcessHandle); 203#endif // !defined(OS_WIN) 204} 205 206base::FilePath ServiceUtilityProcessHost::GetUtilityProcessCmd() { 207#if defined(OS_LINUX) 208 int flags = ChildProcessHost::CHILD_ALLOW_SELF; 209#else 210 int flags = ChildProcessHost::CHILD_NORMAL; 211#endif 212 return ChildProcessHost::GetChildPath(flags); 213} 214 215void ServiceUtilityProcessHost::OnChildDisconnected() { 216 if (waiting_for_reply_) { 217 // If we are yet to receive a reply then notify the client that the 218 // child died. 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_); 226 } 227 delete this; 228} 229 230bool ServiceUtilityProcessHost::OnMessageReceived(const IPC::Message& message) { 231 bool handled = true; 232 IPC_BEGIN_MESSAGE_MAP(ServiceUtilityProcessHost, message) 233#if defined(WIN_PDF_METAFILE_FOR_PRINTING) 234 IPC_MESSAGE_HANDLER( 235 ChromeUtilityHostMsg_RenderPDFPagesToMetafiles_Succeeded, 236 OnRenderPDFPagesToMetafilesSucceeded) 237 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_RenderPDFPagesToMetafile_Failed, 238 OnRenderPDFPagesToMetafileFailed) 239#endif 240 IPC_MESSAGE_HANDLER( 241 ChromeUtilityHostMsg_GetPrinterCapsAndDefaults_Succeeded, 242 OnGetPrinterCapsAndDefaultsSucceeded) 243 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_GetPrinterCapsAndDefaults_Failed, 244 OnGetPrinterCapsAndDefaultsFailed) 245 IPC_MESSAGE_HANDLER( 246 ChromeUtilityHostMsg_GetPrinterSemanticCapsAndDefaults_Succeeded, 247 OnGetPrinterSemanticCapsAndDefaultsSucceeded) 248 IPC_MESSAGE_HANDLER( 249 ChromeUtilityHostMsg_GetPrinterSemanticCapsAndDefaults_Failed, 250 OnGetPrinterSemanticCapsAndDefaultsFailed) 251 IPC_MESSAGE_UNHANDLED(handled = false) 252 IPC_END_MESSAGE_MAP() 253 return handled; 254} 255 256base::ProcessHandle ServiceUtilityProcessHost::GetHandle() const { 257 return handle_; 258} 259 260#if defined(WIN_PDF_METAFILE_FOR_PRINTING) 261void 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 273 // metafile. 274 scratch_metafile_dir_->Take(); 275 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 278 // this. 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( 283 FROM_HERE, 284 base::Bind(&Client::MetafileAvailable, 285 client_.get(), 286 metafile_path_.InsertBeforeExtensionASCII( 287 base::StringPrintf(".%d", page_number)), 288 page_number, 289 scale_factor)); 290} 291 292void 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( 301 FROM_HERE, 302 base::Bind(&Client::OnRenderPDFPagesToMetafileFailed, client_.get())); 303} 304#endif // defined(WIN_PDF_METAFILE_FOR_PRINTING) 305 306void 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( 317 FROM_HERE, 318 base::Bind(&Client::OnGetPrinterCapsAndDefaults, client_.get(), true, 319 printer_name, caps_and_defaults)); 320} 321 322void 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( 333 FROM_HERE, 334 base::Bind(&Client::OnGetPrinterSemanticCapsAndDefaults, client_.get(), 335 true, printer_name, caps_and_defaults)); 336} 337 338void 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( 348 FROM_HERE, 349 base::Bind(&Client::OnGetPrinterCapsAndDefaults, client_.get(), false, 350 printer_name, printing::PrinterCapsAndDefaults())); 351} 352 353void 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( 363 FROM_HERE, 364 base::Bind(&Client::OnGetPrinterSemanticCapsAndDefaults, 365 client_.get(), false, printer_name, 366 printing::PrinterSemanticCapsAndDefaults())); 367} 368 369void 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"; 378#if defined(OS_WIN) 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(); 385 } else { 386 OnRenderPDFPagesToMetafileSucceeded(metafile, 387 highest_rendered_page_number, 388 scale_factor); 389 } 390#endif // defined(OS_WIN) 391} 392 393