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/curtain_mode.h"
6
7#include <X11/extensions/XInput.h>
8
9#include "base/callback.h"
10#include "base/single_thread_task_runner.h"
11#include "remoting/base/logging.h"
12#include "remoting/host/client_session_control.h"
13
14namespace remoting {
15
16class CurtainModeLinux : public CurtainMode {
17 public:
18  CurtainModeLinux();
19
20  // Overriden from CurtainMode.
21  virtual bool Activate() OVERRIDE;
22
23 private:
24  // Returns true if the host is running under an Xvfb session.
25  bool IsXvfbSession();
26
27  DISALLOW_COPY_AND_ASSIGN(CurtainModeLinux);
28};
29
30CurtainModeLinux::CurtainModeLinux() {
31}
32
33bool CurtainModeLinux::Activate() {
34  // We can't curtain the session in run-time in Linux.
35  // Either the session is running on Xvfb (i.e. always curtained), or it is
36  // attached to the physical console (i.e. impossible to curtain).
37  bool activated = IsXvfbSession();
38  if (!activated) {
39    LOG(ERROR) << "Curtain-mode is not supported when running on non-Xvfb "
40                  "X server";
41  }
42
43  return activated;
44}
45
46bool CurtainModeLinux::IsXvfbSession() {
47  // Try to identify an Xvfb session. There's no way to query what X server we
48  // are running under, so we check for the Xvfb input devices.
49  // TODO(rmsousa): Find a similar way to determine that the *output* is secure.
50  Display* display = XOpenDisplay(NULL);
51  int opcode, event, error;
52  if (!XQueryExtension(display, "XInputExtension", &opcode, &event, &error)) {
53    // If XInput is not available, assume it is not an Xvfb session.
54    LOG(ERROR) << "X Input extension not available: " << error;
55    XCloseDisplay(display);
56    return false;
57  }
58  int num_devices;
59  XDeviceInfo* devices;
60  bool found_xvfb_mouse = false;
61  bool found_xvfb_keyboard = false;
62  bool found_other_devices = false;
63  devices = XListInputDevices(display, &num_devices);
64  for (int i = 0; i < num_devices; i++) {
65    XDeviceInfo* device_info = &devices[i];
66    if (device_info->use == IsXExtensionPointer) {
67      if (strcmp(device_info->name, "Xvfb mouse") == 0) {
68        found_xvfb_mouse = true;
69      } else if (strcmp(device_info->name, "Virtual core XTEST pointer") != 0) {
70        found_other_devices = true;
71        HOST_LOG << "Non Xvfb mouse found: " << device_info->name;
72      }
73    } else if (device_info->use == IsXExtensionKeyboard) {
74      if (strcmp(device_info->name, "Xvfb keyboard") == 0) {
75        found_xvfb_keyboard = true;
76      } else if (strcmp(device_info->name,
77                        "Virtual core XTEST keyboard") != 0) {
78        found_other_devices = true;
79        HOST_LOG << "Non Xvfb keyboard found: " << device_info->name;
80      }
81    } else if (device_info->use == IsXPointer) {
82      if (strcmp(device_info->name, "Virtual core pointer") != 0) {
83        found_other_devices = true;
84        HOST_LOG << "Non Xvfb mouse found: " << device_info->name;
85      }
86    } else if (device_info->use == IsXKeyboard) {
87      if (strcmp(device_info->name, "Virtual core keyboard") != 0) {
88        found_other_devices = true;
89        HOST_LOG << "Non Xvfb keyboard found: " << device_info->name;
90      }
91    } else {
92      found_other_devices = true;
93      HOST_LOG << "Non Xvfb device found: " << device_info->name;
94    }
95  }
96  XFreeDeviceList(devices);
97  XCloseDisplay(display);
98  return found_xvfb_mouse && found_xvfb_keyboard && !found_other_devices;
99}
100
101// static
102scoped_ptr<CurtainMode> CurtainMode::Create(
103    scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
104    scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
105    base::WeakPtr<ClientSessionControl> client_session_control) {
106  return scoped_ptr<CurtainMode>(new CurtainModeLinux());
107}
108
109}  // namespace remoting
110