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 <queue> 8 9 #include "base/bind.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" 31 32 namespace { 33 34 using content::ChildProcessHost; 35 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, 50 }; 51 52 void ReportUmaEvent(ServiceUtilityProcessHostEvent id) { 53 UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent", 54 id, 55 SERVICE_UTILITY_EVENT_MAX); 56 } 57 58 // NOTE: changes to this class need to be reviewed by the security team. 59 class ServiceSandboxedProcessLauncherDelegate 60 : public content::SandboxedProcessLauncherDelegate { 61 public: 62 ServiceSandboxedProcessLauncherDelegate() {} 63 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 67 // window station. 68 policy->SetAlternateDesktop(false); 69 } 70 71 private: 72 DISALLOW_COPY_AND_ASSIGN(ServiceSandboxedProcessLauncherDelegate); 73 }; 74 75 } // namespace 76 77 class ServiceUtilityProcessHost::PdfToEmfState { 78 public: 79 explicit PdfToEmfState(ServiceUtilityProcessHost* host) 80 : host_(host), page_count_(0), current_page_(0), pages_in_progress_(0) {} 81 ~PdfToEmfState() { Stop(); } 82 83 bool Start(base::File pdf_file, 84 const printing::PdfRenderSettings& conversion_settings) { 85 if (!temp_dir_.CreateUniqueTempDir()) 86 return false; 87 return host_->Send(new ChromeUtilityMsg_RenderPDFPagesToMetafiles( 88 IPC::TakeFileHandleForProcess(pdf_file.Pass(), host_->handle()), 89 conversion_settings)); 90 } 91 92 void GetMorePages() { 93 const int kMaxNumberOfTempFilesPerDocument = 3; 94 while (pages_in_progress_ < kMaxNumberOfTempFilesPerDocument && 95 current_page_ < page_count_) { 96 ++pages_in_progress_; 97 emf_files_.push(CreateTempFile()); 98 host_->Send(new ChromeUtilityMsg_RenderPDFPagesToMetafiles_GetPage( 99 current_page_++, 100 IPC::GetFileHandleForProcess( 101 emf_files_.back().GetPlatformFile(), host_->handle(), false))); 102 } 103 } 104 105 // Returns true if all pages processed and client should not expect more 106 // results. 107 bool OnPageProcessed() { 108 --pages_in_progress_; 109 GetMorePages(); 110 if (pages_in_progress_ || current_page_ < page_count_) 111 return false; 112 Stop(); 113 return true; 114 } 115 116 base::File TakeNextFile() { 117 DCHECK(!emf_files_.empty()); 118 base::File file; 119 if (!emf_files_.empty()) 120 file = emf_files_.front().Pass(); 121 emf_files_.pop(); 122 return file.Pass(); 123 } 124 125 void set_page_count(int page_count) { page_count_ = page_count; } 126 bool has_page_count() { return page_count_ > 0; } 127 128 private: 129 void Stop() { 130 host_->Send(new ChromeUtilityMsg_RenderPDFPagesToMetafiles_Stop()); 131 } 132 133 base::File CreateTempFile() { 134 base::FilePath path; 135 if (!base::CreateTemporaryFileInDir(temp_dir_.path(), &path)) 136 return base::File(); 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); 143 } 144 145 base::ScopedTempDir temp_dir_; 146 ServiceUtilityProcessHost* host_; 147 std::queue<base::File> emf_files_; 148 int page_count_; 149 int current_page_; 150 int pages_in_progress_; 151 }; 152 153 ServiceUtilityProcessHost::ServiceUtilityProcessHost( 154 Client* client, 155 base::MessageLoopProxy* client_message_loop_proxy) 156 : handle_(base::kNullProcessHandle), 157 client_(client), 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)); 162 } 163 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); 167 } 168 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)) 176 return false; 177 178 DCHECK(!waiting_for_reply_); 179 waiting_for_reply_ = true; 180 181 pdf_to_emf_state_.reset(new PdfToEmfState(this)); 182 return pdf_to_emf_state_->Start(pdf_file.Pass(), render_settings); 183 } 184 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)) 190 return false; 191 DCHECK(!waiting_for_reply_); 192 waiting_for_reply_ = true; 193 return Send(new ChromeUtilityMsg_GetPrinterCapsAndDefaults(printer_name)); 194 } 195 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)) 201 return false; 202 DCHECK(!waiting_for_reply_); 203 waiting_for_reply_ = true; 204 return Send( 205 new ChromeUtilityMsg_GetPrinterSemanticCapsAndDefaults(printer_name)); 206 } 207 208 bool ServiceUtilityProcessHost::StartProcess(bool no_sandbox) { 209 std::string channel_id = child_process_host_->CreateChannel(); 210 if (channel_id.empty()) 211 return false; 212 213 base::FilePath exe_path = GetUtilityProcessCmd(); 214 if (exe_path.empty()) { 215 NOTREACHED() << "Unable to get utility process binary name."; 216 return false; 217 } 218 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); 223 224 if (Launch(&cmd_line, no_sandbox)) { 225 ReportUmaEvent(SERVICE_UTILITY_STARTED); 226 return true; 227 } 228 ReportUmaEvent(SERVICE_UTILITY_FAILED_TO_START); 229 return false; 230 } 231 232 bool ServiceUtilityProcessHost::Launch(base::CommandLine* cmd_line, 233 bool no_sandbox) { 234 if (no_sandbox) { 235 base::ProcessHandle process = base::kNullProcessHandle; 236 cmd_line->AppendSwitch(switches::kNoSandbox); 237 base::LaunchProcess(*cmd_line, base::LaunchOptions(), &handle_); 238 } else { 239 ServiceSandboxedProcessLauncherDelegate delegate; 240 handle_ = content::StartSandboxedProcess(&delegate, cmd_line); 241 } 242 return (handle_ != base::kNullProcessHandle); 243 } 244 245 bool ServiceUtilityProcessHost::Send(IPC::Message* msg) { 246 if (child_process_host_) 247 return child_process_host_->Send(msg); 248 delete msg; 249 return false; 250 } 251 252 base::FilePath ServiceUtilityProcessHost::GetUtilityProcessCmd() { 253 #if defined(OS_LINUX) 254 int flags = ChildProcessHost::CHILD_ALLOW_SELF; 255 #else 256 int flags = ChildProcessHost::CHILD_NORMAL; 257 #endif 258 return ChildProcessHost::GetChildPath(flags); 259 } 260 261 void ServiceUtilityProcessHost::OnChildDisconnected() { 262 if (waiting_for_reply_) { 263 // If we are yet to receive a reply then notify the client that the 264 // child died. 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_); 270 } 271 delete this; 272 } 273 274 bool ServiceUtilityProcessHost::OnMessageReceived(const IPC::Message& message) { 275 bool handled = true; 276 IPC_BEGIN_MESSAGE_MAP(ServiceUtilityProcessHost, message) 277 IPC_MESSAGE_HANDLER( 278 ChromeUtilityHostMsg_RenderPDFPagesToMetafiles_PageCount, 279 OnRenderPDFPagesToMetafilesPageCount) 280 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_RenderPDFPagesToMetafiles_PageDone, 281 OnRenderPDFPagesToMetafilesPageDone) 282 IPC_MESSAGE_HANDLER( 283 ChromeUtilityHostMsg_GetPrinterCapsAndDefaults_Succeeded, 284 OnGetPrinterCapsAndDefaultsSucceeded) 285 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_GetPrinterCapsAndDefaults_Failed, 286 OnGetPrinterCapsAndDefaultsFailed) 287 IPC_MESSAGE_HANDLER( 288 ChromeUtilityHostMsg_GetPrinterSemanticCapsAndDefaults_Succeeded, 289 OnGetPrinterSemanticCapsAndDefaultsSucceeded) 290 IPC_MESSAGE_HANDLER( 291 ChromeUtilityHostMsg_GetPrinterSemanticCapsAndDefaults_Failed, 292 OnGetPrinterSemanticCapsAndDefaultsFailed) 293 IPC_MESSAGE_UNHANDLED(handled = false) 294 IPC_END_MESSAGE_MAP() 295 return handled; 296 } 297 298 base::ProcessHandle ServiceUtilityProcessHost::GetHandle() const { 299 return handle_; 300 } 301 302 void ServiceUtilityProcessHost::OnMetafileSpooled(bool success) { 303 if (!success || pdf_to_emf_state_->OnPageProcessed()) 304 OnPDFToEmfFinished(success); 305 } 306 307 void ServiceUtilityProcessHost::OnRenderPDFPagesToMetafilesPageCount( 308 int page_count) { 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); 313 } 314 pdf_to_emf_state_->set_page_count(page_count); 315 pdf_to_emf_state_->GetMorePages(); 316 } 317 318 void ServiceUtilityProcessHost::OnRenderPDFPagesToMetafilesPageDone( 319 bool success, 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_, 327 FROM_HERE, 328 base::Bind(&Client::MetafileAvailable, 329 client_.get(), 330 scale_factor, 331 base::Passed(&emf_file)), 332 base::Bind(&ServiceUtilityProcessHost::OnMetafileSpooled, 333 weak_ptr_factory_.GetWeakPtr())); 334 } 335 336 void ServiceUtilityProcessHost::OnPDFToEmfFinished(bool success) { 337 if (!waiting_for_reply_) 338 return; 339 waiting_for_reply_ = false; 340 if (success) { 341 ReportUmaEvent(SERVICE_UTILITY_METAFILE_SUCCEEDED); 342 UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilityMetafileTime", 343 base::Time::Now() - start_time_); 344 } else { 345 ReportUmaEvent(SERVICE_UTILITY_METAFILE_FAILED); 346 UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilityMetafileFailTime", 347 base::Time::Now() - start_time_); 348 } 349 client_message_loop_proxy_->PostTask( 350 FROM_HERE, 351 base::Bind( 352 &Client::OnRenderPDFPagesToMetafileDone, client_.get(), success)); 353 pdf_to_emf_state_.reset(); 354 } 355 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( 365 FROM_HERE, 366 base::Bind(&Client::OnGetPrinterCapsAndDefaults, client_.get(), true, 367 printer_name, caps_and_defaults)); 368 } 369 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( 379 FROM_HERE, 380 base::Bind(&Client::OnGetPrinterSemanticCapsAndDefaults, client_.get(), 381 true, printer_name, caps_and_defaults)); 382 } 383 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( 392 FROM_HERE, 393 base::Bind(&Client::OnGetPrinterCapsAndDefaults, client_.get(), false, 394 printer_name, printing::PrinterCapsAndDefaults())); 395 } 396 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( 405 FROM_HERE, 406 base::Bind(&Client::OnGetPrinterSemanticCapsAndDefaults, 407 client_.get(), false, printer_name, 408 printing::PrinterSemanticCapsAndDefaults())); 409 } 410 411 bool ServiceUtilityProcessHost::Client::MetafileAvailable(double scale_factor, 412 base::File file) { 413 file.Seek(base::File::FROM_BEGIN, 0); 414 int64 size = file.GetLength(); 415 if (size <= 0) { 416 OnRenderPDFPagesToMetafileDone(false); 417 return false; 418 } 419 std::vector<char> data(size); 420 if (file.ReadAtCurrentPos(data.data(), data.size()) != size) { 421 OnRenderPDFPagesToMetafileDone(false); 422 return false; 423 } 424 printing::Emf emf; 425 if (!emf.InitFromData(data.data(), data.size())) { 426 OnRenderPDFPagesToMetafileDone(false); 427 return false; 428 } 429 OnRenderPDFPagesToMetafilePageDone(scale_factor, emf); 430 return true; 431 } 432