daemon_process.cc revision c2e0dbddbe15c98d52c4786dac06cb8952a8ae6d
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 "remoting/host/daemon_process.h" 6 7#include "base/bind.h" 8#include "base/bind_helpers.h" 9#include "base/command_line.h" 10#include "base/file_util.h" 11#include "base/files/file_path.h" 12#include "base/location.h" 13#include "base/single_thread_task_runner.h" 14#include "net/base/net_util.h" 15#include "remoting/base/auto_thread_task_runner.h" 16#include "remoting/host/branding.h" 17#include "remoting/host/chromoting_messages.h" 18#include "remoting/host/desktop_session.h" 19#include "remoting/host/host_event_logger.h" 20#include "remoting/host/host_status_observer.h" 21#include "remoting/host/screen_resolution.h" 22#include "remoting/protocol/transport.h" 23 24namespace remoting { 25 26namespace { 27 28// This is used for tagging system event logs. 29const char kApplicationName[] = "chromoting"; 30 31std::ostream& operator<<(std::ostream& os, const ScreenResolution& resolution) { 32 return os << resolution.dimensions_.width() << "x" 33 << resolution.dimensions_.height() << " at " 34 << resolution.dpi_.x() << "x" << resolution.dpi_.y() << " DPI"; 35} 36 37} // namespace 38 39DaemonProcess::~DaemonProcess() { 40 DCHECK(!config_watcher_.get()); 41 DCHECK(desktop_sessions_.empty()); 42} 43 44void DaemonProcess::OnConfigUpdated(const std::string& serialized_config) { 45 DCHECK(caller_task_runner()->BelongsToCurrentThread()); 46 47 if (serialized_config_ != serialized_config) { 48 serialized_config_ = serialized_config; 49 SendToNetwork( 50 new ChromotingDaemonNetworkMsg_Configuration(serialized_config_)); 51 } 52} 53 54void DaemonProcess::OnConfigWatcherError() { 55 DCHECK(caller_task_runner()->BelongsToCurrentThread()); 56 57 Stop(); 58} 59 60void DaemonProcess::AddStatusObserver(HostStatusObserver* observer) { 61 DCHECK(caller_task_runner()->BelongsToCurrentThread()); 62 63 status_observers_.AddObserver(observer); 64} 65 66void DaemonProcess::RemoveStatusObserver(HostStatusObserver* observer) { 67 DCHECK(caller_task_runner()->BelongsToCurrentThread()); 68 69 status_observers_.RemoveObserver(observer); 70} 71 72void DaemonProcess::OnChannelConnected(int32 peer_pid) { 73 DCHECK(caller_task_runner()->BelongsToCurrentThread()); 74 75 VLOG(1) << "IPC: daemon <- network (" << peer_pid << ")"; 76 77 DeleteAllDesktopSessions(); 78 79 // Reset the last known terminal ID because no IDs have been allocated 80 // by the the newly started process yet. 81 next_terminal_id_ = 0; 82 83 // Send the configuration to the network process. 84 SendToNetwork( 85 new ChromotingDaemonNetworkMsg_Configuration(serialized_config_)); 86} 87 88bool DaemonProcess::OnMessageReceived(const IPC::Message& message) { 89 DCHECK(caller_task_runner()->BelongsToCurrentThread()); 90 91 bool handled = true; 92 IPC_BEGIN_MESSAGE_MAP(DaemonProcess, message) 93 IPC_MESSAGE_HANDLER(ChromotingNetworkHostMsg_ConnectTerminal, 94 CreateDesktopSession) 95 IPC_MESSAGE_HANDLER(ChromotingNetworkHostMsg_DisconnectTerminal, 96 CloseDesktopSession) 97 IPC_MESSAGE_HANDLER(ChromotingNetworkDaemonMsg_SetScreenResolution, 98 SetScreenResolution) 99 IPC_MESSAGE_HANDLER(ChromotingNetworkDaemonMsg_AccessDenied, 100 OnAccessDenied) 101 IPC_MESSAGE_HANDLER(ChromotingNetworkDaemonMsg_ClientAuthenticated, 102 OnClientAuthenticated) 103 IPC_MESSAGE_HANDLER(ChromotingNetworkDaemonMsg_ClientConnected, 104 OnClientConnected) 105 IPC_MESSAGE_HANDLER(ChromotingNetworkDaemonMsg_ClientDisconnected, 106 OnClientDisconnected) 107 IPC_MESSAGE_HANDLER(ChromotingNetworkDaemonMsg_ClientRouteChange, 108 OnClientRouteChange) 109 IPC_MESSAGE_HANDLER(ChromotingNetworkDaemonMsg_HostStarted, 110 OnHostStarted) 111 IPC_MESSAGE_HANDLER(ChromotingNetworkDaemonMsg_HostShutdown, 112 OnHostShutdown) 113 IPC_MESSAGE_UNHANDLED(handled = false) 114 IPC_END_MESSAGE_MAP() 115 116 if (!handled) { 117 LOG(ERROR) << "Received unexpected IPC type: " << message.type(); 118 CrashNetworkProcess(FROM_HERE); 119 } 120 121 return handled; 122} 123 124void DaemonProcess::OnPermanentError() { 125 DCHECK(caller_task_runner()->BelongsToCurrentThread()); 126 Stop(); 127} 128 129void DaemonProcess::CloseDesktopSession(int terminal_id) { 130 DCHECK(caller_task_runner()->BelongsToCurrentThread()); 131 132 // Validate the supplied terminal ID. An attempt to use a desktop session ID 133 // that couldn't possibly have been allocated is considered a protocol error 134 // and the network process will be restarted. 135 if (!WasTerminalIdAllocated(terminal_id)) { 136 LOG(ERROR) << "Invalid terminal ID: " << terminal_id; 137 CrashNetworkProcess(FROM_HERE); 138 return; 139 } 140 141 DesktopSessionList::iterator i; 142 for (i = desktop_sessions_.begin(); i != desktop_sessions_.end(); ++i) { 143 if ((*i)->id() == terminal_id) { 144 break; 145 } 146 } 147 148 // It is OK if the terminal ID wasn't found. There is a race between 149 // the network and daemon processes. Each frees its own recources first and 150 // notifies the other party if there was something to clean up. 151 if (i == desktop_sessions_.end()) 152 return; 153 154 delete *i; 155 desktop_sessions_.erase(i); 156 157 VLOG(1) << "Daemon: closed desktop session " << terminal_id; 158 SendToNetwork( 159 new ChromotingDaemonNetworkMsg_TerminalDisconnected(terminal_id)); 160} 161 162DaemonProcess::DaemonProcess( 163 scoped_refptr<AutoThreadTaskRunner> caller_task_runner, 164 scoped_refptr<AutoThreadTaskRunner> io_task_runner, 165 const base::Closure& stopped_callback) 166 : Stoppable(caller_task_runner, stopped_callback), 167 caller_task_runner_(caller_task_runner), 168 io_task_runner_(io_task_runner), 169 next_terminal_id_(0), 170 weak_factory_(this) { 171 DCHECK(caller_task_runner->BelongsToCurrentThread()); 172} 173 174void DaemonProcess::CreateDesktopSession(int terminal_id, 175 const ScreenResolution& resolution, 176 bool virtual_terminal) { 177 DCHECK(caller_task_runner()->BelongsToCurrentThread()); 178 179 // Validate the supplied terminal ID. An attempt to create a desktop session 180 // with an ID that could possibly have been allocated already is considered 181 // a protocol error and the network process will be restarted. 182 if (WasTerminalIdAllocated(terminal_id)) { 183 LOG(ERROR) << "Invalid terminal ID: " << terminal_id; 184 CrashNetworkProcess(FROM_HERE); 185 return; 186 } 187 188 // Terminal IDs cannot be reused. Update the expected next terminal ID. 189 next_terminal_id_ = std::max(next_terminal_id_, terminal_id + 1); 190 191 // Validate |resolution| and restart the sender if it is not valid. 192 if (!resolution.IsValid()) { 193 LOG(ERROR) << "Invalid resolution specified: " << resolution; 194 CrashNetworkProcess(FROM_HERE); 195 return; 196 } 197 198 // Create the desktop session. 199 scoped_ptr<DesktopSession> session = DoCreateDesktopSession( 200 terminal_id, resolution, virtual_terminal); 201 if (!session) { 202 LOG(ERROR) << "Failed to create a desktop session."; 203 SendToNetwork( 204 new ChromotingDaemonNetworkMsg_TerminalDisconnected(terminal_id)); 205 return; 206 } 207 208 VLOG(1) << "Daemon: opened desktop session " << terminal_id; 209 desktop_sessions_.push_back(session.release()); 210} 211 212void DaemonProcess::SetScreenResolution(int terminal_id, 213 const ScreenResolution& resolution) { 214 DCHECK(caller_task_runner()->BelongsToCurrentThread()); 215 216 // Validate the supplied terminal ID. An attempt to use a desktop session ID 217 // that couldn't possibly have been allocated is considered a protocol error 218 // and the network process will be restarted. 219 if (!WasTerminalIdAllocated(terminal_id)) { 220 LOG(ERROR) << "Invalid terminal ID: " << terminal_id; 221 CrashNetworkProcess(FROM_HERE); 222 return; 223 } 224 225 // Validate |resolution| and restart the sender if it is not valid. 226 if (!resolution.IsValid()) { 227 LOG(ERROR) << "Invalid resolution specified: " << resolution; 228 CrashNetworkProcess(FROM_HERE); 229 return; 230 } 231 232 DesktopSessionList::iterator i; 233 for (i = desktop_sessions_.begin(); i != desktop_sessions_.end(); ++i) { 234 if ((*i)->id() == terminal_id) { 235 break; 236 } 237 } 238 239 // It is OK if the terminal ID wasn't found. There is a race between 240 // the network and daemon processes. Each frees its own resources first and 241 // notifies the other party if there was something to clean up. 242 if (i == desktop_sessions_.end()) 243 return; 244 245 (*i)->SetScreenResolution(resolution); 246} 247 248void DaemonProcess::CrashNetworkProcess( 249 const tracked_objects::Location& location) { 250 DCHECK(caller_task_runner()->BelongsToCurrentThread()); 251 252 DoCrashNetworkProcess(location); 253 DeleteAllDesktopSessions(); 254} 255 256void DaemonProcess::Initialize() { 257 DCHECK(caller_task_runner()->BelongsToCurrentThread()); 258 259 // Get the name of the host configuration file. 260 base::FilePath default_config_dir = remoting::GetConfigDir(); 261 base::FilePath config_path = default_config_dir.Append(kDefaultHostConfigFile); 262 const CommandLine* command_line = CommandLine::ForCurrentProcess(); 263 if (command_line->HasSwitch(kHostConfigSwitchName)) { 264 config_path = command_line->GetSwitchValuePath(kHostConfigSwitchName); 265 } 266 267 // Start watching the host configuration file. 268 config_watcher_.reset(new ConfigFileWatcher(caller_task_runner(), 269 io_task_runner(), 270 this)); 271 config_watcher_->Watch(config_path); 272 273 host_event_logger_ = 274 HostEventLogger::Create(weak_factory_.GetWeakPtr(), kApplicationName); 275 276 // Launch the process. 277 LaunchNetworkProcess(); 278} 279 280bool DaemonProcess::WasTerminalIdAllocated(int terminal_id) { 281 return terminal_id < next_terminal_id_; 282} 283 284void DaemonProcess::OnAccessDenied(const std::string& jid) { 285 DCHECK(caller_task_runner()->BelongsToCurrentThread()); 286 287 FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, OnAccessDenied(jid)); 288} 289 290void DaemonProcess::OnClientAuthenticated(const std::string& jid) { 291 DCHECK(caller_task_runner()->BelongsToCurrentThread()); 292 293 FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, 294 OnClientAuthenticated(jid)); 295} 296 297void DaemonProcess::OnClientConnected(const std::string& jid) { 298 DCHECK(caller_task_runner()->BelongsToCurrentThread()); 299 300 FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, 301 OnClientConnected(jid)); 302} 303 304void DaemonProcess::OnClientDisconnected(const std::string& jid) { 305 DCHECK(caller_task_runner()->BelongsToCurrentThread()); 306 307 FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, 308 OnClientDisconnected(jid)); 309} 310 311void DaemonProcess::OnClientRouteChange(const std::string& jid, 312 const std::string& channel_name, 313 const SerializedTransportRoute& route) { 314 DCHECK(caller_task_runner()->BelongsToCurrentThread()); 315 316 // Validate |route|. 317 if (route.type != protocol::TransportRoute::DIRECT && 318 route.type != protocol::TransportRoute::STUN && 319 route.type != protocol::TransportRoute::RELAY) { 320 LOG(ERROR) << "An invalid RouteType " << route.type << " passed."; 321 CrashNetworkProcess(FROM_HERE); 322 return; 323 } 324 if (route.remote_address.size() != net::kIPv4AddressSize && 325 route.remote_address.size() != net::kIPv6AddressSize) { 326 LOG(ERROR) << "An invalid net::IPAddressNumber size " 327 << route.remote_address.size() << " passed."; 328 CrashNetworkProcess(FROM_HERE); 329 return; 330 } 331 if (route.local_address.size() != net::kIPv4AddressSize && 332 route.local_address.size() != net::kIPv6AddressSize) { 333 LOG(ERROR) << "An invalid net::IPAddressNumber size " 334 << route.local_address.size() << " passed."; 335 CrashNetworkProcess(FROM_HERE); 336 return; 337 } 338 339 protocol::TransportRoute parsed_route; 340 parsed_route.type = 341 static_cast<protocol::TransportRoute::RouteType>(route.type); 342 parsed_route.remote_address = 343 net::IPEndPoint(route.remote_address, route.remote_port); 344 parsed_route.local_address = 345 net::IPEndPoint(route.local_address, route.local_port); 346 FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, 347 OnClientRouteChange(jid, channel_name, parsed_route)); 348} 349 350void DaemonProcess::OnHostStarted(const std::string& xmpp_login) { 351 DCHECK(caller_task_runner()->BelongsToCurrentThread()); 352 353 FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, OnStart(xmpp_login)); 354} 355 356void DaemonProcess::OnHostShutdown() { 357 DCHECK(caller_task_runner()->BelongsToCurrentThread()); 358 359 FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, OnShutdown()); 360} 361 362void DaemonProcess::DoStop() { 363 DCHECK(caller_task_runner()->BelongsToCurrentThread()); 364 365 host_event_logger_.reset(); 366 weak_factory_.InvalidateWeakPtrs(); 367 368 config_watcher_.reset(); 369 DeleteAllDesktopSessions(); 370 371 CompleteStopping(); 372} 373 374void DaemonProcess::DeleteAllDesktopSessions() { 375 while (!desktop_sessions_.empty()) { 376 delete desktop_sessions_.front(); 377 desktop_sessions_.pop_front(); 378 } 379} 380 381} // namespace remoting 382