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