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/process/kill.h"
15#include "base/strings/utf_string_conversions.h"
16#include "chrome/common/chrome_switches.h"
17#include "chrome/common/chrome_utility_messages.h"
18#include "content/public/common/child_process_host.h"
19#include "content/public/common/result_codes.h"
20#include "content/public/common/sandbox_init.h"
21#include "ipc/ipc_switches.h"
22#include "printing/page_range.h"
23#include "ui/base/ui_base_switches.h"
24#include "ui/gfx/rect.h"
25
26#if defined(OS_WIN)
27#include "base/files/file_path.h"
28#include "base/memory/scoped_ptr.h"
29#include "base/process/launch.h"
30#include "base/win/scoped_handle.h"
31#include "content/public/common/sandbox_init.h"
32#include "content/public/common/sandboxed_process_launcher_delegate.h"
33#include "printing/emf_win.h"
34
35namespace {
36// NOTE: changes to this class need to be reviewed by the security team.
37class ServiceSandboxedProcessLauncherDelegate
38    : public content::SandboxedProcessLauncherDelegate {
39 public:
40  explicit ServiceSandboxedProcessLauncherDelegate(
41      const base::FilePath& exposed_dir)
42    : exposed_dir_(exposed_dir) {
43  }
44
45  virtual void PreSandbox(bool* disable_default_policy,
46                          base::FilePath* exposed_dir) OVERRIDE {
47    *exposed_dir = exposed_dir_;
48  }
49
50 private:
51  base::FilePath exposed_dir_;
52};
53}
54
55#endif  // OS_WIN
56
57using content::ChildProcessHost;
58
59ServiceUtilityProcessHost::ServiceUtilityProcessHost(
60    Client* client, base::MessageLoopProxy* client_message_loop_proxy)
61        : handle_(base::kNullProcessHandle),
62          client_(client),
63          client_message_loop_proxy_(client_message_loop_proxy),
64          waiting_for_reply_(false) {
65  child_process_host_.reset(ChildProcessHost::Create(this));
66}
67
68ServiceUtilityProcessHost::~ServiceUtilityProcessHost() {
69  // We need to kill the child process when the host dies.
70  base::KillProcess(handle_, content::RESULT_CODE_NORMAL_EXIT, false);
71}
72
73bool ServiceUtilityProcessHost::StartRenderPDFPagesToMetafile(
74    const base::FilePath& pdf_path,
75    const printing::PdfRenderSettings& render_settings,
76    const std::vector<printing::PageRange>& page_ranges) {
77#if !defined(OS_WIN)
78  // This is only implemented on Windows (because currently it is only needed
79  // on Windows). Will add implementations on other platforms when needed.
80  NOTIMPLEMENTED();
81  return false;
82#else  // !defined(OS_WIN)
83  scratch_metafile_dir_.reset(new base::ScopedTempDir);
84  if (!scratch_metafile_dir_->CreateUniqueTempDir())
85    return false;
86  if (!file_util::CreateTemporaryFileInDir(scratch_metafile_dir_->path(),
87                                           &metafile_path_)) {
88    return false;
89  }
90
91  if (!StartProcess(false, scratch_metafile_dir_->path()))
92    return false;
93
94  base::win::ScopedHandle pdf_file(
95      ::CreateFile(pdf_path.value().c_str(),
96                   GENERIC_READ,
97                   FILE_SHARE_READ | FILE_SHARE_WRITE,
98                   NULL,
99                   OPEN_EXISTING,
100                   FILE_ATTRIBUTE_NORMAL,
101                   NULL));
102  if (pdf_file == INVALID_HANDLE_VALUE)
103    return false;
104  HANDLE pdf_file_in_utility_process = NULL;
105  ::DuplicateHandle(::GetCurrentProcess(), pdf_file, handle(),
106                    &pdf_file_in_utility_process, 0, false,
107                    DUPLICATE_SAME_ACCESS);
108  if (!pdf_file_in_utility_process)
109    return false;
110  waiting_for_reply_ = true;
111  return child_process_host_->Send(
112      new ChromeUtilityMsg_RenderPDFPagesToMetafile(
113          pdf_file_in_utility_process,
114          metafile_path_,
115          render_settings,
116          page_ranges));
117#endif  // !defined(OS_WIN)
118}
119
120bool ServiceUtilityProcessHost::StartGetPrinterCapsAndDefaults(
121    const std::string& printer_name) {
122  base::FilePath exposed_path;
123  if (!StartProcess(true, exposed_path))
124    return false;
125  waiting_for_reply_ = true;
126  return child_process_host_->Send(
127      new ChromeUtilityMsg_GetPrinterCapsAndDefaults(printer_name));
128}
129
130bool ServiceUtilityProcessHost::StartProcess(
131    bool no_sandbox,
132    const base::FilePath& exposed_dir) {
133  std::string channel_id = child_process_host_->CreateChannel();
134  if (channel_id.empty())
135    return false;
136
137  base::FilePath exe_path = GetUtilityProcessCmd();
138  if (exe_path.empty()) {
139    NOTREACHED() << "Unable to get utility process binary name.";
140    return false;
141  }
142
143  CommandLine cmd_line(exe_path);
144  cmd_line.AppendSwitchASCII(switches::kProcessType, switches::kUtilityProcess);
145  cmd_line.AppendSwitchASCII(switches::kProcessChannelID, channel_id);
146  cmd_line.AppendSwitch(switches::kLang);
147
148  return Launch(&cmd_line, no_sandbox, exposed_dir);
149}
150
151bool ServiceUtilityProcessHost::Launch(CommandLine* cmd_line,
152                                       bool no_sandbox,
153                                       const base::FilePath& exposed_dir) {
154#if !defined(OS_WIN)
155  // TODO(sanjeevr): Implement for non-Windows OSes.
156  NOTIMPLEMENTED();
157  return false;
158#else  // !defined(OS_WIN)
159
160  if (no_sandbox) {
161    base::ProcessHandle process = base::kNullProcessHandle;
162    cmd_line->AppendSwitch(switches::kNoSandbox);
163    base::LaunchProcess(*cmd_line, base::LaunchOptions(), &handle_);
164  } else {
165    ServiceSandboxedProcessLauncherDelegate delegate(exposed_dir);
166    handle_ = content::StartSandboxedProcess(&delegate, cmd_line);
167  }
168  return (handle_ != base::kNullProcessHandle);
169#endif  // !defined(OS_WIN)
170}
171
172base::FilePath ServiceUtilityProcessHost::GetUtilityProcessCmd() {
173#if defined(OS_LINUX)
174  int flags = ChildProcessHost::CHILD_ALLOW_SELF;
175#else
176  int flags = ChildProcessHost::CHILD_NORMAL;
177#endif
178  return ChildProcessHost::GetChildPath(flags);
179}
180
181void ServiceUtilityProcessHost::OnChildDisconnected() {
182  if (waiting_for_reply_) {
183    // If we are yet to receive a reply then notify the client that the
184    // child died.
185    client_message_loop_proxy_->PostTask(
186        FROM_HERE, base::Bind(&Client::OnChildDied, client_.get()));
187  }
188  delete this;
189}
190
191bool ServiceUtilityProcessHost::OnMessageReceived(const IPC::Message& message) {
192  bool handled = true;
193  IPC_BEGIN_MESSAGE_MAP(ServiceUtilityProcessHost, message)
194    IPC_MESSAGE_HANDLER(
195        ChromeUtilityHostMsg_RenderPDFPagesToMetafile_Succeeded,
196        OnRenderPDFPagesToMetafileSucceeded)
197    IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_RenderPDFPagesToMetafile_Failed,
198                        OnRenderPDFPagesToMetafileFailed)
199    IPC_MESSAGE_HANDLER(
200        ChromeUtilityHostMsg_GetPrinterCapsAndDefaults_Succeeded,
201        OnGetPrinterCapsAndDefaultsSucceeded)
202    IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_GetPrinterCapsAndDefaults_Failed,
203                        OnGetPrinterCapsAndDefaultsFailed)
204    IPC_MESSAGE_UNHANDLED(handled = false)
205  IPC_END_MESSAGE_MAP()
206  return handled;
207}
208
209void ServiceUtilityProcessHost::OnRenderPDFPagesToMetafileSucceeded(
210    int highest_rendered_page_number,
211    double scale_factor) {
212  DCHECK(waiting_for_reply_);
213  waiting_for_reply_ = false;
214  // If the metafile was successfully created, we need to take our hands off the
215  // scratch metafile directory. The client will delete it when it is done with
216  // metafile.
217  scratch_metafile_dir_->Take();
218  client_message_loop_proxy_->PostTask(
219      FROM_HERE,
220      base::Bind(&Client::MetafileAvailable, client_.get(), metafile_path_,
221                 highest_rendered_page_number, scale_factor));
222}
223
224void ServiceUtilityProcessHost::OnRenderPDFPagesToMetafileFailed() {
225  DCHECK(waiting_for_reply_);
226  waiting_for_reply_ = false;
227  client_message_loop_proxy_->PostTask(
228      FROM_HERE,
229      base::Bind(&Client::OnRenderPDFPagesToMetafileFailed, client_.get()));
230}
231
232void ServiceUtilityProcessHost::OnGetPrinterCapsAndDefaultsSucceeded(
233    const std::string& printer_name,
234    const printing::PrinterCapsAndDefaults& caps_and_defaults) {
235  DCHECK(waiting_for_reply_);
236  waiting_for_reply_ = false;
237  client_message_loop_proxy_->PostTask(
238      FROM_HERE,
239      base::Bind(&Client::OnGetPrinterCapsAndDefaultsSucceeded, client_.get(),
240                 printer_name, caps_and_defaults));
241}
242
243void ServiceUtilityProcessHost::OnGetPrinterCapsAndDefaultsFailed(
244    const std::string& printer_name) {
245  DCHECK(waiting_for_reply_);
246  waiting_for_reply_ = false;
247  client_message_loop_proxy_->PostTask(
248      FROM_HERE,
249      base::Bind(&Client::OnGetPrinterCapsAndDefaultsFailed, client_.get(),
250                 printer_name));
251}
252
253void ServiceUtilityProcessHost::Client::MetafileAvailable(
254    const base::FilePath& metafile_path,
255    int highest_rendered_page_number,
256    double scale_factor) {
257  // The metafile was created in a temp folder which needs to get deleted after
258  // we have processed it.
259  base::ScopedTempDir scratch_metafile_dir;
260  if (!scratch_metafile_dir.Set(metafile_path.DirName()))
261    LOG(WARNING) << "Unable to set scratch metafile directory";
262#if defined(OS_WIN)
263  // It's important that metafile is declared after scratch_metafile_dir so
264  // that the metafile destructor closes the file before the base::ScopedTempDir
265  // destructor tries to remove the directory.
266  printing::Emf metafile;
267  if (!metafile.InitFromFile(metafile_path)) {
268    OnRenderPDFPagesToMetafileFailed();
269  } else {
270    OnRenderPDFPagesToMetafileSucceeded(metafile,
271                                        highest_rendered_page_number,
272                                        scale_factor);
273  }
274#endif  // defined(OS_WIN)
275}
276
277