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