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"
10 #include "base/bind.h"
11 #include "base/bind_helpers.h"
12 #include "base/callback_helpers.h"
13 #include "base/command_line.h"
14 #include "base/files/file_path.h"
15 #include "base/files/file_util.h"
16 #include "base/location.h"
17 #include "base/single_thread_task_runner.h"
18 #include "net/base/net_util.h"
19 #include "remoting/base/auto_thread_task_runner.h"
20 #include "remoting/host/branding.h"
21 #include "remoting/host/chromoting_messages.h"
22 #include "remoting/host/config_file_watcher.h"
23 #include "remoting/host/desktop_session.h"
24 #include "remoting/host/host_event_logger.h"
25 #include "remoting/host/host_exit_codes.h"
26 #include "remoting/host/host_status_observer.h"
27 #include "remoting/host/screen_resolution.h"
28 #include "remoting/protocol/transport.h"
34 // This is used for tagging system event logs.
35 const char kApplicationName
[] = "chromoting";
37 std::ostream
& operator<<(std::ostream
& os
, const ScreenResolution
& resolution
) {
38 return os
<< resolution
.dimensions().width() << "x"
39 << resolution
.dimensions().height() << " at "
40 << resolution
.dpi().x() << "x" << resolution
.dpi().y() << " DPI";
45 DaemonProcess::~DaemonProcess() {
46 DCHECK(caller_task_runner()->BelongsToCurrentThread());
48 host_event_logger_
.reset();
49 weak_factory_
.InvalidateWeakPtrs();
51 config_watcher_
.reset();
52 DeleteAllDesktopSessions();
55 void DaemonProcess::OnConfigUpdated(const std::string
& serialized_config
) {
56 DCHECK(caller_task_runner()->BelongsToCurrentThread());
58 if (serialized_config_
!= serialized_config
) {
59 serialized_config_
= serialized_config
;
61 new ChromotingDaemonNetworkMsg_Configuration(serialized_config_
));
65 void DaemonProcess::OnConfigWatcherError() {
66 DCHECK(caller_task_runner()->BelongsToCurrentThread());
71 void DaemonProcess::AddStatusObserver(HostStatusObserver
* observer
) {
72 DCHECK(caller_task_runner()->BelongsToCurrentThread());
74 status_observers_
.AddObserver(observer
);
77 void DaemonProcess::RemoveStatusObserver(HostStatusObserver
* observer
) {
78 DCHECK(caller_task_runner()->BelongsToCurrentThread());
80 status_observers_
.RemoveObserver(observer
);
83 void DaemonProcess::OnChannelConnected(int32 peer_pid
) {
84 DCHECK(caller_task_runner()->BelongsToCurrentThread());
86 VLOG(1) << "IPC: daemon <- network (" << peer_pid
<< ")";
88 DeleteAllDesktopSessions();
90 // Reset the last known terminal ID because no IDs have been allocated
91 // by the the newly started process yet.
92 next_terminal_id_
= 0;
94 // Send the configuration to the network process.
96 new ChromotingDaemonNetworkMsg_Configuration(serialized_config_
));
99 bool DaemonProcess::OnMessageReceived(const IPC::Message
& message
) {
100 DCHECK(caller_task_runner()->BelongsToCurrentThread());
103 IPC_BEGIN_MESSAGE_MAP(DaemonProcess
, message
)
104 IPC_MESSAGE_HANDLER(ChromotingNetworkHostMsg_ConnectTerminal
,
105 CreateDesktopSession
)
106 IPC_MESSAGE_HANDLER(ChromotingNetworkHostMsg_DisconnectTerminal
,
108 IPC_MESSAGE_HANDLER(ChromotingNetworkDaemonMsg_SetScreenResolution
,
110 IPC_MESSAGE_HANDLER(ChromotingNetworkDaemonMsg_AccessDenied
,
112 IPC_MESSAGE_HANDLER(ChromotingNetworkDaemonMsg_ClientAuthenticated
,
113 OnClientAuthenticated
)
114 IPC_MESSAGE_HANDLER(ChromotingNetworkDaemonMsg_ClientConnected
,
116 IPC_MESSAGE_HANDLER(ChromotingNetworkDaemonMsg_ClientDisconnected
,
117 OnClientDisconnected
)
118 IPC_MESSAGE_HANDLER(ChromotingNetworkDaemonMsg_ClientRouteChange
,
120 IPC_MESSAGE_HANDLER(ChromotingNetworkDaemonMsg_HostStarted
,
122 IPC_MESSAGE_HANDLER(ChromotingNetworkDaemonMsg_HostShutdown
,
124 IPC_MESSAGE_UNHANDLED(handled
= false)
125 IPC_END_MESSAGE_MAP()
128 LOG(ERROR
) << "Received unexpected IPC type: " << message
.type();
129 CrashNetworkProcess(FROM_HERE
);
135 void DaemonProcess::OnPermanentError(int exit_code
) {
136 DCHECK(caller_task_runner()->BelongsToCurrentThread());
137 DCHECK(kMinPermanentErrorExitCode
<= exit_code
&&
138 exit_code
<= kMaxPermanentErrorExitCode
);
143 void DaemonProcess::CloseDesktopSession(int terminal_id
) {
144 DCHECK(caller_task_runner()->BelongsToCurrentThread());
146 // Validate the supplied terminal ID. An attempt to use a desktop session ID
147 // that couldn't possibly have been allocated is considered a protocol error
148 // and the network process will be restarted.
149 if (!WasTerminalIdAllocated(terminal_id
)) {
150 LOG(ERROR
) << "Invalid terminal ID: " << terminal_id
;
151 CrashNetworkProcess(FROM_HERE
);
155 DesktopSessionList::iterator i
;
156 for (i
= desktop_sessions_
.begin(); i
!= desktop_sessions_
.end(); ++i
) {
157 if ((*i
)->id() == terminal_id
) {
162 // It is OK if the terminal ID wasn't found. There is a race between
163 // the network and daemon processes. Each frees its own recources first and
164 // notifies the other party if there was something to clean up.
165 if (i
== desktop_sessions_
.end())
169 desktop_sessions_
.erase(i
);
171 VLOG(1) << "Daemon: closed desktop session " << terminal_id
;
173 new ChromotingDaemonNetworkMsg_TerminalDisconnected(terminal_id
));
176 DaemonProcess::DaemonProcess(
177 scoped_refptr
<AutoThreadTaskRunner
> caller_task_runner
,
178 scoped_refptr
<AutoThreadTaskRunner
> io_task_runner
,
179 const base::Closure
& stopped_callback
)
180 : caller_task_runner_(caller_task_runner
),
181 io_task_runner_(io_task_runner
),
182 next_terminal_id_(0),
183 stopped_callback_(stopped_callback
),
184 weak_factory_(this) {
185 DCHECK(caller_task_runner
->BelongsToCurrentThread());
188 void DaemonProcess::CreateDesktopSession(int terminal_id
,
189 const ScreenResolution
& resolution
,
190 bool virtual_terminal
) {
191 DCHECK(caller_task_runner()->BelongsToCurrentThread());
193 // Validate the supplied terminal ID. An attempt to create a desktop session
194 // with an ID that could possibly have been allocated already is considered
195 // a protocol error and the network process will be restarted.
196 if (WasTerminalIdAllocated(terminal_id
)) {
197 LOG(ERROR
) << "Invalid terminal ID: " << terminal_id
;
198 CrashNetworkProcess(FROM_HERE
);
202 // Terminal IDs cannot be reused. Update the expected next terminal ID.
203 next_terminal_id_
= std::max(next_terminal_id_
, terminal_id
+ 1);
205 // Create the desktop session.
206 scoped_ptr
<DesktopSession
> session
= DoCreateDesktopSession(
207 terminal_id
, resolution
, virtual_terminal
);
209 LOG(ERROR
) << "Failed to create a desktop session.";
211 new ChromotingDaemonNetworkMsg_TerminalDisconnected(terminal_id
));
215 VLOG(1) << "Daemon: opened desktop session " << terminal_id
;
216 desktop_sessions_
.push_back(session
.release());
219 void DaemonProcess::SetScreenResolution(int terminal_id
,
220 const ScreenResolution
& resolution
) {
221 DCHECK(caller_task_runner()->BelongsToCurrentThread());
223 // Validate the supplied terminal ID. An attempt to use a desktop session ID
224 // that couldn't possibly have been allocated is considered a protocol error
225 // and the network process will be restarted.
226 if (!WasTerminalIdAllocated(terminal_id
)) {
227 LOG(ERROR
) << "Invalid terminal ID: " << terminal_id
;
228 CrashNetworkProcess(FROM_HERE
);
232 // Validate |resolution| and restart the sender if it is not valid.
233 if (resolution
.IsEmpty()) {
234 LOG(ERROR
) << "Invalid resolution specified: " << resolution
;
235 CrashNetworkProcess(FROM_HERE
);
239 DesktopSessionList::iterator i
;
240 for (i
= desktop_sessions_
.begin(); i
!= desktop_sessions_
.end(); ++i
) {
241 if ((*i
)->id() == terminal_id
) {
246 // It is OK if the terminal ID wasn't found. There is a race between
247 // the network and daemon processes. Each frees its own resources first and
248 // notifies the other party if there was something to clean up.
249 if (i
== desktop_sessions_
.end())
252 (*i
)->SetScreenResolution(resolution
);
255 void DaemonProcess::CrashNetworkProcess(
256 const tracked_objects::Location
& location
) {
257 DCHECK(caller_task_runner()->BelongsToCurrentThread());
259 DoCrashNetworkProcess(location
);
260 DeleteAllDesktopSessions();
263 void DaemonProcess::Initialize() {
264 DCHECK(caller_task_runner()->BelongsToCurrentThread());
266 const base::CommandLine
* command_line
=
267 base::CommandLine::ForCurrentProcess();
268 // Get the name of the host configuration file.
269 base::FilePath default_config_dir
= remoting::GetConfigDir();
270 base::FilePath config_path
= default_config_dir
.Append(
271 kDefaultHostConfigFile
);
272 if (command_line
->HasSwitch(kHostConfigSwitchName
)) {
273 config_path
= command_line
->GetSwitchValuePath(kHostConfigSwitchName
);
275 config_watcher_
.reset(new ConfigFileWatcher(
276 caller_task_runner(), io_task_runner(), config_path
));
277 config_watcher_
->Watch(this);
279 HostEventLogger::Create(weak_factory_
.GetWeakPtr(), kApplicationName
);
281 // Launch the process.
282 LaunchNetworkProcess();
285 void DaemonProcess::Stop() {
286 DCHECK(caller_task_runner()->BelongsToCurrentThread());
288 if (!stopped_callback_
.is_null()) {
289 base::ResetAndReturn(&stopped_callback_
).Run();
293 bool DaemonProcess::WasTerminalIdAllocated(int terminal_id
) {
294 return terminal_id
< next_terminal_id_
;
297 void DaemonProcess::OnAccessDenied(const std::string
& jid
) {
298 DCHECK(caller_task_runner()->BelongsToCurrentThread());
300 FOR_EACH_OBSERVER(HostStatusObserver
, status_observers_
, OnAccessDenied(jid
));
303 void DaemonProcess::OnClientAuthenticated(const std::string
& jid
) {
304 DCHECK(caller_task_runner()->BelongsToCurrentThread());
306 FOR_EACH_OBSERVER(HostStatusObserver
, status_observers_
,
307 OnClientAuthenticated(jid
));
310 void DaemonProcess::OnClientConnected(const std::string
& jid
) {
311 DCHECK(caller_task_runner()->BelongsToCurrentThread());
313 FOR_EACH_OBSERVER(HostStatusObserver
, status_observers_
,
314 OnClientConnected(jid
));
317 void DaemonProcess::OnClientDisconnected(const std::string
& jid
) {
318 DCHECK(caller_task_runner()->BelongsToCurrentThread());
320 FOR_EACH_OBSERVER(HostStatusObserver
, status_observers_
,
321 OnClientDisconnected(jid
));
324 void DaemonProcess::OnClientRouteChange(const std::string
& jid
,
325 const std::string
& channel_name
,
326 const SerializedTransportRoute
& route
) {
327 DCHECK(caller_task_runner()->BelongsToCurrentThread());
330 if (route
.type
!= protocol::TransportRoute::DIRECT
&&
331 route
.type
!= protocol::TransportRoute::STUN
&&
332 route
.type
!= protocol::TransportRoute::RELAY
) {
333 LOG(ERROR
) << "An invalid RouteType " << route
.type
<< " passed.";
334 CrashNetworkProcess(FROM_HERE
);
337 if (route
.remote_address
.size() != net::kIPv4AddressSize
&&
338 route
.remote_address
.size() != net::kIPv6AddressSize
) {
339 LOG(ERROR
) << "An invalid net::IPAddressNumber size "
340 << route
.remote_address
.size() << " passed.";
341 CrashNetworkProcess(FROM_HERE
);
344 if (route
.local_address
.size() != net::kIPv4AddressSize
&&
345 route
.local_address
.size() != net::kIPv6AddressSize
) {
346 LOG(ERROR
) << "An invalid net::IPAddressNumber size "
347 << route
.local_address
.size() << " passed.";
348 CrashNetworkProcess(FROM_HERE
);
352 protocol::TransportRoute parsed_route
;
354 static_cast<protocol::TransportRoute::RouteType
>(route
.type
);
355 parsed_route
.remote_address
=
356 net::IPEndPoint(route
.remote_address
, route
.remote_port
);
357 parsed_route
.local_address
=
358 net::IPEndPoint(route
.local_address
, route
.local_port
);
359 FOR_EACH_OBSERVER(HostStatusObserver
, status_observers_
,
360 OnClientRouteChange(jid
, channel_name
, parsed_route
));
363 void DaemonProcess::OnHostStarted(const std::string
& xmpp_login
) {
364 DCHECK(caller_task_runner()->BelongsToCurrentThread());
366 FOR_EACH_OBSERVER(HostStatusObserver
, status_observers_
, OnStart(xmpp_login
));
369 void DaemonProcess::OnHostShutdown() {
370 DCHECK(caller_task_runner()->BelongsToCurrentThread());
372 FOR_EACH_OBSERVER(HostStatusObserver
, status_observers_
, OnShutdown());
375 void DaemonProcess::DeleteAllDesktopSessions() {
376 while (!desktop_sessions_
.empty()) {
377 delete desktop_sessions_
.front();
378 desktop_sessions_
.pop_front();
382 } // namespace remoting