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.
5 #include "remoting/host/daemon_process.h"
7 #include "base/base_switches.h"
9 #include "base/bind_helpers.h"
10 #include "base/location.h"
11 #include "base/logging.h"
12 #include "base/memory/ref_counted.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/process/process.h"
15 #include "base/single_thread_task_runner.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "base/time/time.h"
18 #include "base/timer/timer.h"
19 #include "base/win/scoped_handle.h"
20 #include "ipc/ipc_message.h"
21 #include "ipc/ipc_message_macros.h"
22 #include "remoting/base/auto_thread_task_runner.h"
23 #include "remoting/base/scoped_sc_handle_win.h"
24 #include "remoting/host/branding.h"
25 #include "remoting/host/chromoting_messages.h"
26 #include "remoting/host/desktop_session_win.h"
27 #include "remoting/host/host_exit_codes.h"
28 #include "remoting/host/host_main.h"
29 #include "remoting/host/ipc_constants.h"
30 #include "remoting/host/screen_resolution.h"
31 #include "remoting/host/win/launch_process_with_token.h"
32 #include "remoting/host/win/unprivileged_process_delegate.h"
33 #include "remoting/host/win/worker_process_launcher.h"
35 using base::win::ScopedHandle
;
36 using base::TimeDelta
;
40 class WtsTerminalMonitor
;
42 // The command line parameters that should be copied from the service's command
43 // line to the host process.
44 const char* kCopiedSwitchNames
[] = { switches::kV
, switches::kVModule
};
46 class DaemonProcessWin
: public DaemonProcess
{
49 scoped_refptr
<AutoThreadTaskRunner
> caller_task_runner
,
50 scoped_refptr
<AutoThreadTaskRunner
> io_task_runner
,
51 const base::Closure
& stopped_callback
);
52 virtual ~DaemonProcessWin();
54 // WorkerProcessIpcDelegate implementation.
55 virtual void OnChannelConnected(int32 peer_pid
) OVERRIDE
;
56 virtual void OnPermanentError(int exit_code
) OVERRIDE
;
58 // DaemonProcess overrides.
59 virtual void SendToNetwork(IPC::Message
* message
) OVERRIDE
;
60 virtual bool OnDesktopSessionAgentAttached(
62 base::ProcessHandle desktop_process
,
63 IPC::PlatformFileForTransit desktop_pipe
) OVERRIDE
;
66 // DaemonProcess implementation.
67 virtual scoped_ptr
<DesktopSession
> DoCreateDesktopSession(
69 const ScreenResolution
& resolution
,
70 bool virtual_terminal
) OVERRIDE
;
71 virtual void DoCrashNetworkProcess(
72 const tracked_objects::Location
& location
) OVERRIDE
;
73 virtual void LaunchNetworkProcess() OVERRIDE
;
75 // Changes the service start type to 'manual'.
76 void DisableAutoStart();
79 scoped_ptr
<WorkerProcessLauncher
> network_launcher_
;
81 // Handle of the network process.
82 ScopedHandle network_process_
;
84 DISALLOW_COPY_AND_ASSIGN(DaemonProcessWin
);
87 DaemonProcessWin::DaemonProcessWin(
88 scoped_refptr
<AutoThreadTaskRunner
> caller_task_runner
,
89 scoped_refptr
<AutoThreadTaskRunner
> io_task_runner
,
90 const base::Closure
& stopped_callback
)
91 : DaemonProcess(caller_task_runner
, io_task_runner
, stopped_callback
) {
94 DaemonProcessWin::~DaemonProcessWin() {
97 void DaemonProcessWin::OnChannelConnected(int32 peer_pid
) {
98 // Obtain the handle of the network process.
99 network_process_
.Set(OpenProcess(PROCESS_DUP_HANDLE
, false, peer_pid
));
100 if (!network_process_
.IsValid()) {
101 CrashNetworkProcess(FROM_HERE
);
105 DaemonProcess::OnChannelConnected(peer_pid
);
108 void DaemonProcessWin::OnPermanentError(int exit_code
) {
109 // Change the service start type to 'manual' if the host has been deleted
110 // remotely. This way the host will not be started every time the machine
111 // boots until the user re-enable it again.
112 if (exit_code
== kInvalidHostIdExitCode
)
115 DaemonProcess::OnPermanentError(exit_code
);
118 void DaemonProcessWin::SendToNetwork(IPC::Message
* message
) {
119 if (network_launcher_
) {
120 network_launcher_
->Send(message
);
126 bool DaemonProcessWin::OnDesktopSessionAgentAttached(
128 base::ProcessHandle desktop_process
,
129 IPC::PlatformFileForTransit desktop_pipe
) {
130 // Prepare |desktop_process| handle for sending over to the network process.
131 base::ProcessHandle desktop_process_for_transit
;
132 if (!DuplicateHandle(GetCurrentProcess(),
135 &desktop_process_for_transit
,
138 DUPLICATE_SAME_ACCESS
)) {
139 LOG_GETLASTERROR(ERROR
) << "Failed to duplicate the desktop process handle";
143 // |desktop_pipe| is a handle in the desktop process. It will be duplicated
144 // by the network process directly from the desktop process.
145 SendToNetwork(new ChromotingDaemonNetworkMsg_DesktopAttached(
146 terminal_id
, desktop_process_for_transit
, desktop_pipe
));
150 scoped_ptr
<DesktopSession
> DaemonProcessWin::DoCreateDesktopSession(
152 const ScreenResolution
& resolution
,
153 bool virtual_terminal
) {
154 DCHECK(caller_task_runner()->BelongsToCurrentThread());
156 if (virtual_terminal
) {
157 return DesktopSessionWin::CreateForVirtualTerminal(
158 caller_task_runner(), io_task_runner(), this, terminal_id
, resolution
);
160 return DesktopSessionWin::CreateForConsole(
161 caller_task_runner(), io_task_runner(), this, terminal_id
, resolution
);
165 void DaemonProcessWin::DoCrashNetworkProcess(
166 const tracked_objects::Location
& location
) {
167 DCHECK(caller_task_runner()->BelongsToCurrentThread());
169 network_launcher_
->Crash(location
);
172 void DaemonProcessWin::LaunchNetworkProcess() {
173 DCHECK(caller_task_runner()->BelongsToCurrentThread());
174 DCHECK(!network_launcher_
);
176 // Construct the host binary name.
177 base::FilePath host_binary
;
178 if (!GetInstalledBinaryPath(kHostBinaryName
, &host_binary
)) {
183 scoped_ptr
<CommandLine
> target(new CommandLine(host_binary
));
184 target
->AppendSwitchASCII(kProcessTypeSwitchName
, kProcessTypeHost
);
185 target
->CopySwitchesFrom(*CommandLine::ForCurrentProcess(),
187 arraysize(kCopiedSwitchNames
));
189 scoped_ptr
<UnprivilegedProcessDelegate
> delegate(
190 new UnprivilegedProcessDelegate(io_task_runner(), target
.Pass()));
191 network_launcher_
.reset(new WorkerProcessLauncher(delegate
.Pass(), this));
194 scoped_ptr
<DaemonProcess
> DaemonProcess::Create(
195 scoped_refptr
<AutoThreadTaskRunner
> caller_task_runner
,
196 scoped_refptr
<AutoThreadTaskRunner
> io_task_runner
,
197 const base::Closure
& stopped_callback
) {
198 scoped_ptr
<DaemonProcessWin
> daemon_process(
199 new DaemonProcessWin(caller_task_runner
, io_task_runner
,
201 daemon_process
->Initialize();
202 return daemon_process
.PassAs
<DaemonProcess
>();
205 void DaemonProcessWin::DisableAutoStart() {
206 ScopedScHandle
scmanager(
207 OpenSCManager(NULL
, SERVICES_ACTIVE_DATABASE
,
208 SC_MANAGER_CONNECT
| SC_MANAGER_ENUMERATE_SERVICE
));
209 if (!scmanager
.IsValid()) {
210 LOG_GETLASTERROR(INFO
)
211 << "Failed to connect to the service control manager";
215 DWORD desired_access
= SERVICE_CHANGE_CONFIG
| SERVICE_QUERY_STATUS
;
216 ScopedScHandle
service(
217 OpenService(scmanager
, kWindowsServiceName
, desired_access
));
218 if (!service
.IsValid()) {
219 LOG_GETLASTERROR(INFO
)
220 << "Failed to open to the '" << kWindowsServiceName
<< "' service";
224 // Change the service start type to 'manual'. All |NULL| parameters below mean
225 // that there is no change to the corresponding service parameter.
226 if (!ChangeServiceConfig(service
,
228 SERVICE_DEMAND_START
,
237 LOG_GETLASTERROR(INFO
)
238 << "Failed to change the '" << kWindowsServiceName
239 << "'service start type to 'manual'";
243 } // namespace remoting