1// Copyright 2014 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 "mojo/shell/child_process_host.h"
6
7#include "base/base_switches.h"
8#include "base/bind.h"
9#include "base/command_line.h"
10#include "base/location.h"
11#include "base/logging.h"
12#include "base/macros.h"
13#include "base/process/kill.h"
14#include "base/process/launch.h"
15#include "base/strings/string_number_conversions.h"
16#include "base/task_runner.h"
17#include "base/task_runner_util.h"
18#include "mojo/shell/context.h"
19#include "mojo/shell/switches.h"
20
21namespace mojo {
22namespace shell {
23
24ChildProcessHost::ChildProcessHost(Context* context,
25                                   Delegate* delegate,
26                                   ChildProcess::Type type)
27    : context_(context),
28      delegate_(delegate),
29      type_(type),
30      child_process_handle_(base::kNullProcessHandle) {
31  DCHECK(delegate);
32  platform_channel_ = platform_channel_pair_.PassServerHandle();
33  CHECK(platform_channel_.is_valid());
34}
35
36ChildProcessHost::~ChildProcessHost() {
37  if (child_process_handle_ != base::kNullProcessHandle) {
38    LOG(WARNING) << "Destroying ChildProcessHost with unjoined child";
39    base::CloseProcessHandle(child_process_handle_);
40    child_process_handle_ = base::kNullProcessHandle;
41  }
42}
43
44void ChildProcessHost::Start() {
45  DCHECK_EQ(child_process_handle_, base::kNullProcessHandle);
46
47  delegate_->WillStart();
48
49  CHECK(base::PostTaskAndReplyWithResult(
50      context_->task_runners()->blocking_pool(),
51      FROM_HERE,
52      base::Bind(&ChildProcessHost::DoLaunch, base::Unretained(this)),
53      base::Bind(&ChildProcessHost::DidLaunch, base::Unretained(this))));
54}
55
56int ChildProcessHost::Join() {
57  DCHECK_NE(child_process_handle_, base::kNullProcessHandle);
58  int rv = -1;
59  // Note: |WaitForExitCode()| closes the process handle.
60  LOG_IF(ERROR, !base::WaitForExitCode(child_process_handle_, &rv))
61      << "Failed to wait for child process";
62  child_process_handle_ = base::kNullProcessHandle;
63  return rv;
64}
65
66bool ChildProcessHost::DoLaunch() {
67  static const char* kForwardSwitches[] = {
68    switches::kTraceToConsole,
69    switches::kV,
70    switches::kVModule,
71  };
72
73  const base::CommandLine* parent_command_line =
74      base::CommandLine::ForCurrentProcess();
75  base::CommandLine child_command_line(parent_command_line->GetProgram());
76  child_command_line.CopySwitchesFrom(*parent_command_line, kForwardSwitches,
77                                      arraysize(kForwardSwitches));
78  child_command_line.AppendSwitchASCII(
79      switches::kChildProcessType, base::IntToString(static_cast<int>(type_)));
80
81  embedder::HandlePassingInformation handle_passing_info;
82  platform_channel_pair_.PrepareToPassClientHandleToChildProcess(
83      &child_command_line, &handle_passing_info);
84
85  base::LaunchOptions options;
86#if defined(OS_WIN)
87  options.start_hidden = true;
88  options.handles_to_inherit = &handle_passing_info;
89#elif defined(OS_POSIX)
90  options.fds_to_remap = &handle_passing_info;
91#endif
92
93  if (!base::LaunchProcess(child_command_line, options, &child_process_handle_))
94    return false;
95
96  platform_channel_pair_.ChildProcessLaunched();
97  return true;
98}
99
100void ChildProcessHost::DidLaunch(bool success) {
101  delegate_->DidStart(success);
102}
103
104}  // namespace shell
105}  // namespace mojo
106