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