Home | History | Annotate | Download | only in service
      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