summaryrefslogtreecommitdiffstats
path: root/src/input
diff options
context:
space:
mode:
authorFabian Schillinger2010-11-01 17:35:27 +0100
committerFabian Schillinger2010-11-01 17:35:27 +0100
commitea3fb17345e5f82db9f2e98a8062e95797700ace (patch)
tree1da0d1a8ec9455364386af78762d0f6fed187824 /src/input
parentProcess start/stop/view functionality (diff)
parent[PVSGUI] No X required for --help and --version (diff)
downloadpvs-ea3fb17345e5f82db9f2e98a8062e95797700ace.tar.gz
pvs-ea3fb17345e5f82db9f2e98a8062e95797700ace.tar.xz
pvs-ea3fb17345e5f82db9f2e98a8062e95797700ace.zip
Merge branch 'master' of openslx.org:pvs
Conflicts: CMakeLists.txt src/core/pvsConnectionManager.cpp src/pvs.cpp src/pvs.h
Diffstat (limited to 'src/input')
-rw-r--r--src/input/CMakeLists.txt114
-rw-r--r--src/input/inputEvent.cpp99
-rw-r--r--src/input/inputEvent.h219
-rw-r--r--src/input/inputEventHandler.cpp36
-rw-r--r--src/input/inputEventHandler.h295
-rw-r--r--src/input/inputEventNonQt.cpp17
-rw-r--r--src/input/inputHandlerChain.h49
-rw-r--r--src/input/killX11Handler.cpp89
-rw-r--r--src/input/killX11Handler.h34
-rw-r--r--src/input/magicSysRqHandler.cpp28
-rw-r--r--src/input/magicSysRqHandler.h72
-rw-r--r--src/input/org.openslx.pvs.input.policy18
-rw-r--r--src/input/privilegedHandlerForwarder.cpp82
-rw-r--r--src/input/privilegedHandlerForwarder.h32
-rw-r--r--src/input/pvsCheckPrivileges.cpp550
-rw-r--r--src/input/pvsCheckPrivileges.h151
-rw-r--r--src/input/pvsPrivInputHandler.cpp108
-rw-r--r--src/input/pvsPrivInputHandler.h45
-rw-r--r--src/input/pvsPrivInputSignalHandler.cpp69
-rw-r--r--src/input/pvsPrivInputSignalHandler.h47
-rw-r--r--src/input/pvsPrivInputSocket.cpp233
-rw-r--r--src/input/pvsPrivInputSocket.h37
-rw-r--r--src/input/pvsSyslog.cpp133
-rw-r--r--src/input/pvsSyslog.h63
-rw-r--r--src/input/pvsprivinputd.conf28
-rw-r--r--src/input/pvsprivinputd.cpp262
-rw-r--r--src/input/rebootSystemHandler.cpp32
-rw-r--r--src/input/rebootSystemHandler.h34
-rw-r--r--src/input/sayHelloHandler.cpp25
-rw-r--r--src/input/sayHelloHandler.h34
-rw-r--r--src/input/x11FakeKeyboardHandler.cpp906
-rw-r--r--src/input/x11FakeKeyboardHandler.h29
-rw-r--r--src/input/x11FakeMouseHandler.cpp59
-rw-r--r--src/input/x11FakeMouseHandler.h34
-rw-r--r--src/input/x11InputUtils.cpp29
-rw-r--r--src/input/x11InputUtils.h27
36 files changed, 4119 insertions, 0 deletions
diff --git a/src/input/CMakeLists.txt b/src/input/CMakeLists.txt
new file mode 100644
index 0000000..20fd531
--- /dev/null
+++ b/src/input/CMakeLists.txt
@@ -0,0 +1,114 @@
+include(${QT_USE_FILE})
+
+set(pvsinput_SRCS
+ inputEvent.cpp
+ )
+
+if(UNIX)
+ find_file(XINPUT2_HDR X11/extensions/XInput2.h)
+ if(XINPUT2_HDR)
+ set_property(SOURCE x11FakeKeyboardHandler.cpp
+ APPEND
+ PROPERTY COMPILE_DEFINITIONS HAVE_XINPUT2_H
+ )
+ endif()
+
+ set(pvsprivinputd_SRCS
+ pvsprivinputd.cpp
+ pvsPrivInputHandler.cpp
+ pvsCheckPrivileges.cpp
+ pvsSyslog.cpp
+ pvsPrivInputSignalHandler.cpp
+ rebootSystemHandler.cpp
+ killX11Handler.cpp
+ magicSysRqHandler.cpp
+ sayHelloHandler.cpp
+ inputEventHandler.cpp
+ )
+
+ set(pvsprivinputd_MOC_HDRS
+ pvsPrivInputHandler.h
+ pvsCheckPrivileges.h
+ pvsSyslog.h
+ pvsPrivInputSignalHandler.h
+ )
+
+ qt4_wrap_cpp(pvsprivinputd_MOC_SRCS
+ ${pvsprivinputd_MOC_HDRS}
+ )
+
+ add_executable(pvsprivinputd
+ ${pvsprivinputd_SRCS}
+ ${pvsprivinputd_MOC_SRCS}
+ )
+
+ set_property(SOURCE ${pvsprivinputd_SRCS} ${pvsprivinputd_MOC_SRCS}
+ APPEND
+ PROPERTY COMPILE_FLAGS " -I${QT_QTCORE_INCLUDE_DIR} -I${QT_QTDBUS_INCLUDE_DIR} -I${QT_QTNETWORK_INCLUDE_DIR}")
+
+ target_link_libraries(pvsprivinputd
+ pvsinput
+ ${QT_QTCORE_LIBRARY}
+ ${QT_QTDBUS_LIBRARY}
+ ${QT_QTNETWORK_LIBRARY}
+ )
+
+ list(APPEND pvsinput_SRCS
+ pvsPrivInputSocket.cpp
+ x11InputUtils.cpp
+ x11FakeKeyboardHandler.cpp
+ x11FakeMouseHandler.cpp
+ privilegedHandlerForwarder.cpp
+ )
+
+ # we need pkg-config to find out where to install the action file:
+ find_package(PkgConfig)
+ if(NOT PKG_CONFIG_FOUND)
+ # we will try to make a best effort and put our policy file into
+ # ${PREFIX}/share/polkit-1, but only if we can find
+ # the pkexec executable.
+ find_program(PKEXEC_LOCATION pkexec)
+ if(PKEXEC_LOCATION OR ENABLE_POLKIT)
+ set(POLKIT_FOUND ON)
+ set(POLKIT_PREFIX ${CMAKE_INSTALL_PREFIX})
+ set(POLKIT_POLICY_DIR ${POLKIT_PREFIX}/share/polkit-1/actions)
+ endif()
+ else()
+ pkg_check_modules(POLKIT "polkit-gobject-1")
+ if(POLKIT_FOUND)
+ execute_process(COMMAND ${PKG_CONFIG_EXECUTABLE} polkit-gobject-1 --variable=policydir
+ OUTPUT_VARIABLE POLKIT_POLICY_DIR
+ OUTPUT_STRIP_TRAILING_WHITESPACE)
+ if(NOT POLKIT_POLICY_DIR)
+ set(POLKIT_FOUND OFF)
+ endif()
+ endif()
+ endif()
+
+ # now, arrange for policykit integration:
+ if(POLKIT_FOUND OR ENABLE_POLKIT)
+ if(NOT POLKIT_POLICY_DIR)
+ message(SEND_ERROR "PolicyKit integration is enabled, but cannot find PolicyKit's actions directory. Please set POLKIT_POLICY_DIR to the right value")
+ endif()
+
+ install(FILES org.openslx.pvs.input.policy
+ DESTINATION ${POLKIT_POLICY_DIR})
+ set_property(SOURCE ${pvsprivinputd_SRCS}
+ APPEND
+ PROPERTY COMPILE_DEFINITIONS HAVE_POLKIT)
+ message(STATUS "PolicyKit integration: enabled")
+ else()
+ message(STATUS "PolicyKit integration: disabled")
+ endif()
+
+ # Install a pvsprivinputd.conf template
+ install(FILES pvsprivinputd.conf
+ DESTINATION /etc
+ PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ)
+endif()
+
+add_library(
+ pvsinput
+ STATIC
+ ${pvsinput_SRCS}
+)
diff --git a/src/input/inputEvent.cpp b/src/input/inputEvent.cpp
new file mode 100644
index 0000000..9b69a3a
--- /dev/null
+++ b/src/input/inputEvent.cpp
@@ -0,0 +1,99 @@
+/*
+ * inputEvent.cpp
+ *
+ * Created on: 06.09.2010
+ * Author: brs
+ */
+
+#include <QBuffer>
+#include <QByteArray>
+#include <QDataStream>
+#include <QKeyEvent>
+#include <QMouseEvent>
+#include <QString>
+#include "inputEvent.h"
+#include <src/util/consoleLogger.h>
+
+// We implement operators to serialize and load an event:
+QDataStream& operator <<(QDataStream& ostrm, InputEvent const& evt)
+{
+ ostrm << evt.type_ << evt.code_ << evt.value_;
+ return ostrm;
+}
+
+QDataStream& operator >>(QDataStream& istrm, InputEvent& evt)
+{
+ istrm >> evt.type_ >> evt.code_ >> evt.value_;
+ return istrm;
+}
+
+void eventToString(InputEvent const& evt, QString& str)
+{
+ QByteArray ba;
+ QBuffer buf(&ba);
+ buf.open(QIODevice::WriteOnly);
+ QDataStream out(&buf);
+ out << evt;
+ str = QString::fromAscii(ba.toBase64());
+}
+
+bool eventFromString(QString const& str, InputEvent& evt)
+{
+ // TODO This does not do proper error checking. Only use from trusted sources!
+ QByteArray ba = QByteArray::fromBase64(str.toAscii());
+ QBuffer buf(&ba);
+ buf.open(QIODevice::ReadOnly);
+ QDataStream in(&buf);
+ in >> evt;
+ return true;
+}
+
+quint16 InputEvent::mouseButtonsFromQt(int b)
+{
+ quint16 ret = 0;
+ if(b & Qt::LeftButton)
+ {
+ ret |= EB_LEFT;
+ }
+ if(b & Qt::RightButton)
+ {
+ ret |= EB_RIGHT;
+ }
+ if(b & Qt::MidButton)
+ {
+ ret |= EB_MIDDLE;
+ }
+ return ret;
+}
+
+InputEvent InputEvent::mousePressRelease(int qt_button, int qt_buttons)
+{
+ quint16 button = mouseButtonsFromQt(qt_button);
+ quint16 buttons = mouseButtonsFromQt(qt_buttons);
+ quint16 code;
+
+ if(buttons & button)
+ {
+ code = EC_PRESS;
+ }
+ else
+ {
+ code = EC_RELEASE;
+ }
+
+ quint32 value = ((quint32)button << 16) | buttons;
+ return InputEvent(ET_BUTTON, code, value);
+}
+
+InputEvent InputEvent::keyboardPress(int key, int mods)
+{
+ quint32 value = key | mods;
+ return InputEvent(ET_KEY, EC_PRESS, value);
+}
+
+InputEvent InputEvent::keyboardRelease(int key, int mods)
+{
+ quint32 value = key | mods;
+ return InputEvent(ET_KEY, EC_RELEASE, value);
+}
+
diff --git a/src/input/inputEvent.h b/src/input/inputEvent.h
new file mode 100644
index 0000000..7a64bfc
--- /dev/null
+++ b/src/input/inputEvent.h
@@ -0,0 +1,219 @@
+/*
+ # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # --------------------------------------------------------------------------
+ # inputEvent.h:
+ # - Definition of an input event
+ # --------------------------------------------------------------------------
+ */
+
+#ifndef INPUTEVENT_H_
+#define INPUTEVENT_H_
+
+#include <cassert>
+#include <string>
+#include <sstream>
+#include <stdint.h>
+
+#ifndef __linux
+# error "This will only run on a Linux system. Porting is required for other systems."
+#endif
+
+struct QDataStream;
+struct QString;
+struct QMouseEvent;
+struct QKeyEvent;
+
+class InputEvent
+{
+private:
+ friend QDataStream& operator<<(QDataStream&, InputEvent const&);
+ friend QDataStream& operator>>(QDataStream&, InputEvent&);
+
+ friend void eventToString(InputEvent const& evt, QString& str);
+ friend bool eventFromString(QString const& str, InputEvent& evt);
+
+ uint16_t type_;
+ uint16_t code_;
+ uint32_t value_;
+
+ // InputEvents are immutable. Prohibit assignment:
+ InputEvent& operator=(InputEvent const&); // There intentionally is no implementation.
+public:
+ InputEvent(uint16_t type, uint16_t code, uint32_t value) : type_(type), code_(code), value_(value)
+ {
+ }
+
+ InputEvent()
+ {
+ }
+
+ static InputEvent mouseMotion(uint16_t x, uint16_t y)
+ {
+ return InputEvent(ET_POINTER, 0, ((uint32_t)x << 16) | y);
+ }
+
+ static uint16_t mouseButtonsFromQt(int b);
+
+ static InputEvent mousePressRelease(int button, int buttons);
+ static InputEvent keyboardPress(int key, int mods);
+ static InputEvent keyboardRelease(int key, int mods);
+
+ static const uint16_t ET_KEY = 0;
+ static const uint16_t ET_BUTTON = 1;
+ static const uint16_t ET_POINTER = 2;
+ static const uint16_t ET_SPECIAL = 3;
+
+ static const uint16_t EC_PRESS = 0;
+ static const uint16_t EC_RELEASE = 1;
+ static const uint16_t EC_REBOOT = 2;
+ static const uint16_t EC_SYSRQ = 3;
+ static const uint16_t EC_KILL_X = 4;
+ static const uint16_t EC_SAY_HELLO = 5; //< for debugging purposes
+
+ typedef uint32_t event_key;
+
+ typedef uint32_t event_key_modifiers;
+
+ static const uint16_t EB_LEFT = 1;
+ static const uint16_t EB_MIDDLE = 2;
+ static const uint16_t EB_RIGHT = 4;
+
+ static const uint32_t MODIFIER_MASK =
+ 0x7e000000;
+
+ uint16_t type() const
+ {
+ return type_;
+ }
+
+ uint16_t code() const
+ {
+ return code_;
+ }
+
+ uint32_t value() const
+ {
+ return value_;
+ }
+
+ bool isKeyboard() const
+ {
+ return type_ == ET_KEY;
+ }
+
+ bool isButton() const
+ {
+ return type_ == ET_BUTTON;
+ }
+
+ bool isPointer() const
+ {
+ return type_ == ET_POINTER;
+ }
+
+ bool isSpecial() const
+ {
+ return type_ == ET_SPECIAL;
+ }
+
+ bool isPress() const
+ {
+ return code_ == EC_PRESS;
+ }
+
+ bool isRelease() const
+ {
+ return code_ == EC_RELEASE;
+ }
+
+ uint16_t pressedButton() const
+ {
+ assert(type_ == ET_BUTTON);
+ return (value_ >> 16);
+ }
+
+ uint16_t heldButtons() const
+ {
+ assert(type_ == ET_BUTTON);
+ return (value_ & 0xffff);
+ }
+
+ uint16_t xCoord() const
+ {
+ assert(type_ == ET_POINTER);
+ return (value_ >> 16);
+ }
+
+ uint16_t yCoord() const
+ {
+ assert(type_ == ET_POINTER);
+ return (value_ & 0xffff);
+ }
+
+ static std::string typeToString(uint16_t type)
+ {
+ switch(type)
+ {
+ case ET_BUTTON:
+ return "BUTTON";
+ case ET_KEY:
+ return "KEY";
+ case ET_POINTER:
+ return "POINTER";
+ case ET_SPECIAL:
+ return "SPECIAL";
+ default:
+ std::ostringstream s;
+ s << std::hex << type;
+ return s.str();
+ }
+ }
+
+ static std::string codeToString(uint16_t code)
+ {
+ switch(code)
+ {
+ case EC_PRESS:
+ return "PRESS";
+ case EC_RELEASE:
+ return "RELEASE";
+ case EC_SYSRQ:
+ return "SYSRQ";
+ case EC_REBOOT:
+ return "REBOOT";
+ case EC_KILL_X:
+ return "KILL_X";
+ default:
+ std::ostringstream s;
+ s << std::hex << code;
+ return s.str();
+ }
+ }
+
+ std::string toString() const
+ {
+ std::ostringstream s;
+ s << typeToString(type_) << ':' << codeToString(code_) << ':' << std::hex << value_;
+ return s.str();
+ }
+
+ uint32_t qt_keysym() const
+ {
+ return value_ & ~MODIFIER_MASK;
+ }
+
+ uint32_t qt_modifiers() const
+ {
+ return value_ & MODIFIER_MASK;
+ }
+};
+
+#endif /* INPUTEVENT_H_ */
diff --git a/src/input/inputEventHandler.cpp b/src/input/inputEventHandler.cpp
new file mode 100644
index 0000000..c16c358
--- /dev/null
+++ b/src/input/inputEventHandler.cpp
@@ -0,0 +1,36 @@
+/*
+ # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # --------------------------------------------------------------------------
+ # inputEventHandler.h:
+ # - Common definitions for input event handlers - implementation
+ # --------------------------------------------------------------------------
+ */
+
+#include "inputEventHandler.h"
+#include "pvsCheckPrivileges.h"
+
+bool policy::allowPrivilegedUser(InputEvent const& evt, InputEventContext const* ctx)
+{
+ if(ctx)
+ return PVSCheckPrivileges::instance()->require(PVSCheckPrivileges::SESSION_UNKNOWN, PVSCheckPrivileges::USER_PRIVILEGED,
+ ctx);
+ else
+ return false;
+}
+
+bool policy::allowPhysicalSeat(InputEvent const& evt, InputEventContext const* ctx)
+{
+ if(ctx)
+ return PVSCheckPrivileges::instance()->require(PVSCheckPrivileges::SESSION_LOCAL, PVSCheckPrivileges::USER_UNKNOWN,
+ ctx);
+ else
+ return false;
+}
diff --git a/src/input/inputEventHandler.h b/src/input/inputEventHandler.h
new file mode 100644
index 0000000..52e3338
--- /dev/null
+++ b/src/input/inputEventHandler.h
@@ -0,0 +1,295 @@
+/*
+ # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # --------------------------------------------------------------------------
+ # inputEventHandler.h:
+ # - Common definitions for input event handlers
+ # --------------------------------------------------------------------------
+ */
+
+#ifndef INPUTEVENTHANDLER_H_
+#define INPUTEVENTHANDLER_H_
+
+#include <QtGlobal>
+#include <QtDebug>
+#include <QList>
+#include <QString>
+#include <QCoreApplication>
+#include <boost/mpl/contains.hpp>
+#include <boost/mpl/if.hpp>
+#include <boost/mpl/vector.hpp>
+#include <src/input/inputEvent.h>
+
+#define HANDLER_TYPE_DONT_CARE 0xffff
+#define HANDLER_CODE_DONT_CARE 0xffff
+#define HANDLER_VALUE_DONT_CARE 0xffffffff
+
+class InputEventContext
+{
+public:
+ virtual pid_t getSenderPid() const = 0;
+ virtual uid_t getSenderUid() const = 0;
+ virtual gid_t getSenderGid() const = 0;
+};
+
+struct SpecialInputEventDescription
+{
+ SpecialInputEventDescription(QString const& d, quint16 t, quint16 c, quint32 v = 0)
+ : descriptionString(d), evtType(t), evtCode(c), evtValue(v)
+ {
+ }
+
+ QString descriptionString;
+ quint16 evtType;
+ quint16 evtCode;
+ quint32 evtValue;
+
+ InputEvent toEvent() const
+ {
+ return InputEvent(evtType, evtCode, evtValue);
+ }
+};
+
+template<quint16 Type = HANDLER_TYPE_DONT_CARE,
+ quint16 Code = HANDLER_CODE_DONT_CARE,
+ quint32 Value = HANDLER_VALUE_DONT_CARE>
+class DefaultInputEventHandler {
+protected:
+ static QString tr(char const* string)
+ {
+ return QCoreApplication::translate("InputEventHandler", string);
+ }
+
+public:
+ virtual bool matches(InputEvent const& evt, InputEventContext const*) {
+ if(Type != HANDLER_TYPE_DONT_CARE) {
+ if(evt.type() != Type)
+ return false;
+ }
+ if(Code != HANDLER_CODE_DONT_CARE) {
+ if(evt.code() != Code)
+ return false;
+ }
+ if(Value != HANDLER_VALUE_DONT_CARE) {
+ if(evt.value() != Value)
+ return false;
+ }
+ return true;
+ }
+
+ virtual void initialize()
+ {
+ }
+
+ virtual void handle(InputEvent const& evt, InputEventContext const*) = 0;
+
+ static void describeInto(QList<SpecialInputEventDescription>& description)
+ {
+ }
+};
+
+namespace policy {
+
+enum SecurityFlags {
+ SEC_FREE_FOR_ALL,
+ SEC_PHYSICAL_OR_PRIVILEGED
+};
+
+bool allowPhysicalSeat(InputEvent const& evt, InputEventContext const* ctx);
+bool allowPrivilegedUser(InputEvent const& evt, InputEventContext const* ctx);
+
+struct SecurityAllowAny
+{
+ bool allow(InputEvent const& evt, InputEventContext const* ctx)
+ {
+ return true;
+ }
+};
+
+struct SecurityAllowPhysicalOrPrivileged
+{
+ bool allow(InputEvent const& evt, InputEventContext const* ctx)
+ {
+ if(allowPhysicalSeat(evt, ctx))
+ return true;
+ else if(allowPrivilegedUser(evt, ctx))
+ return true;
+ return false;
+ }
+};
+
+struct UnixLike;
+struct Linux;
+struct Windows;
+
+#if defined(__linux)
+typedef boost::mpl::vector2<UnixLike,Linux>::type Systems;
+#elif defined(_WIN32) || defined(__WIN32__) || defined(__TOS_WIN__) || defined(__WINDOWS__)
+typedef boost::mpl::vector1<Windows>::type Systems;
+#else
+# error "Porting is needed!"
+#endif
+
+struct SystemEnabled;
+struct SystemDisabled;
+
+template<typename System>
+struct RequireSystem
+{
+ typedef typename boost::mpl::contains<Systems, System>::type enabled_type;
+ static const bool enabled = enabled_type::value;
+};
+
+struct RequireNoSystem
+{
+ typedef boost::mpl::bool_<true>::type enabled_type;
+ static const bool enabled = enabled_type::value;
+};
+
+}
+
+template<bool Enabled, typename Delegate, typename SecurityPolicy>
+class HandlerHelper
+{
+public:
+ bool handle(InputEvent const& evt, InputEventContext const* context = 0) {
+ if(!securityPolicy.allow(evt, context))
+ {
+ std::string evtStr = evt.toString();
+ qWarning("Input Event %s has been denied by security policy", evtStr.c_str());
+ return true;
+ }
+ if(delegate.matches(evt, context)) {
+ delegate.handle(evt, context);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ void initialize()
+ {
+ delegate.initialize();
+ }
+
+ static void describeInto(QList<SpecialInputEventDescription>& list)
+ {
+ Delegate::describeInto(list);
+ }
+
+private:
+ Delegate delegate;
+ SecurityPolicy securityPolicy;
+};
+
+template<typename Delegate, typename SecurityPolicy>
+class HandlerHelper<false, Delegate, SecurityPolicy>
+{
+public:
+ bool handle(InputEvent const& evt, InputEventContext const* context = 0) {
+ return false;
+ }
+
+ void initialize()
+ {
+ }
+
+ static void describeInto(QList<SpecialInputEventDescription>&)
+ {
+ }
+};
+
+template<typename Delegate, typename SystemPolicy = policy::RequireNoSystem, typename SecurityPolicy = void>
+struct Handler : public HandlerHelper<SystemPolicy::enabled, Delegate, SecurityPolicy>
+{
+};
+
+template<typename DefaultSecurityPolicy, typename HandlerType>
+struct ApplyDefaultSecurityPolicy
+{
+ typedef HandlerType type;
+};
+
+template<typename DefaultSecurityPolicy, typename Delegate, typename SystemPolicy>
+struct ApplyDefaultSecurityPolicy<DefaultSecurityPolicy, Handler<Delegate, SystemPolicy, void> >
+{
+ typedef Handler<Delegate, SystemPolicy, DefaultSecurityPolicy> type;
+};
+
+template<typename DefaultSecurityPolicy, typename Begin, typename End>
+struct InputEventHandlerChainHelper
+{
+private:
+ typedef typename boost::mpl::next<Begin>::type next_iterator_type;
+ typedef InputEventHandlerChainHelper<DefaultSecurityPolicy, next_iterator_type, End> next_in_chain;
+
+ typedef typename boost::mpl::deref<Begin>::type handler_entry_type;
+ typedef typename ApplyDefaultSecurityPolicy<DefaultSecurityPolicy, handler_entry_type>::type handler_type;
+
+ handler_type _handler;
+ next_in_chain _next;
+
+public:
+ void handle(InputEvent const& evt, InputEventContext const* context = 0) {
+ if(!_handler.handle(evt, context)) {
+ _next.handle(evt, context);
+ }
+ }
+
+ void initialize() {
+ _handler.initialize();
+ _next.initialize();
+ }
+
+ static void describeInto(QList<SpecialInputEventDescription>& list)
+ {
+ handler_type::describeInto(list);
+ next_in_chain::describeInto(list);
+ }
+
+ static QList<SpecialInputEventDescription> describe()
+ {
+ QList<SpecialInputEventDescription> list;
+ describeInto(list);
+ return list;
+ }
+};
+
+template<typename DefaultSecurityPolicy, typename End>
+struct InputEventHandlerChainHelper<DefaultSecurityPolicy, End, End>
+{
+ void handle(InputEvent const&, InputEventContext const* context = 0) {
+ // do nothing
+ }
+
+ void initialize() {
+ // do nothing
+ }
+
+ static void describeInto(QList<SpecialInputEventDescription>&)
+ {
+ // do nothing
+ }
+
+ static QList<SpecialInputEventDescription> describe()
+ {
+ return QList<SpecialInputEventDescription>();
+ }
+};
+
+template<typename DefaultSecurityPolicy, typename Collection>
+struct InputEventHandlerChain :
+ public InputEventHandlerChainHelper<DefaultSecurityPolicy,
+ typename boost::mpl::begin<Collection>::type,
+ typename boost::mpl::end<Collection>::type>
+{
+};
+
+#endif /* INPUTEVENTHANDLER_H_ */
diff --git a/src/input/inputEventNonQt.cpp b/src/input/inputEventNonQt.cpp
new file mode 100644
index 0000000..a7455fd
--- /dev/null
+++ b/src/input/inputEventNonQt.cpp
@@ -0,0 +1,17 @@
+/*
+ # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # --------------------------------------------------------------------------
+ # inputEvent.h:
+ # - Input Event routines that need not be linked against Qt
+ # --------------------------------------------------------------------------
+ */
+
+
diff --git a/src/input/inputHandlerChain.h b/src/input/inputHandlerChain.h
new file mode 100644
index 0000000..3c9446c
--- /dev/null
+++ b/src/input/inputHandlerChain.h
@@ -0,0 +1,49 @@
+/*
+ # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # --------------------------------------------------------------------------
+ # inputHandlerChain.h:
+ # - Definition of the input handler chain
+ # --------------------------------------------------------------------------
+ */
+
+#ifndef INPUTHANDLERCHAIN_H_
+#define INPUTHANDLERCHAIN_H_
+
+#include <boost/mpl/list.hpp>
+#include "inputEventHandler.h"
+
+#include "x11FakeKeyboardHandler.h"
+#include "x11FakeMouseHandler.h"
+#include "privilegedHandlerForwarder.h"
+#include "rebootSystemHandler.h"
+#include "sayHelloHandler.h"
+#include "killX11Handler.h"
+#include "magicSysRqHandler.h"
+
+typedef boost::mpl::list<
+ Handler<X11FakeKeyboardHandler, policy::RequireSystem<policy::UnixLike> >,
+ Handler<X11FakeMouseButtonHandler, policy::RequireSystem<policy::UnixLike> >,
+ Handler<X11FakeMouseMovementHandler, policy::RequireSystem<policy::UnixLike> >,
+ Handler<PrivilegedHandlerForwarder>
+>::type unprivileged_handler_list;
+
+typedef InputEventHandlerChain<policy::SecurityAllowAny, unprivileged_handler_list> unprivileged_handler_chain;
+
+typedef boost::mpl::list<
+ Handler<SayHelloHandler, policy::RequireNoSystem, policy::SecurityAllowAny >,
+ Handler<KillX11Handler, policy::RequireSystem<policy::Linux> >,
+ Handler<RebootLinuxSystemHandler, policy::RequireSystem<policy::Linux> >,
+ Handler<MagicSysRqHandler, policy::RequireSystem<policy::Linux> >
+>::type privileged_handler_list;
+
+typedef InputEventHandlerChain<policy::SecurityAllowPhysicalOrPrivileged, privileged_handler_list> privileged_handler_chain;
+
+#endif /* INPUTHANDLERCHAIN_H_ */
diff --git a/src/input/killX11Handler.cpp b/src/input/killX11Handler.cpp
new file mode 100644
index 0000000..7ac75a1
--- /dev/null
+++ b/src/input/killX11Handler.cpp
@@ -0,0 +1,89 @@
+/*
+ # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # --------------------------------------------------------------------------
+ # killX11Handler.h
+ # - Kill the X11 Server - implementation
+ # --------------------------------------------------------------------------
+ */
+
+#include <sys/types.h>
+#include <signal.h>
+#include <cerrno>
+#include <QDir>
+#include <QFileInfo>
+#include <QStringList>
+#include "pvsCheckPrivileges.h"
+#include "killX11Handler.h"
+
+using namespace std;
+
+void KillX11Handler::handle(InputEvent const&, InputEventContext const* ctx)
+{
+ // Find out which display device is used:
+ QString displayDevice = PVSCheckPrivileges::instance()->getX11DisplayDevice(ctx);
+ QString displayDeviceAbs = QFileInfo(displayDevice).canonicalFilePath();
+
+ if(displayDevice.isNull())
+ {
+ qWarning("Can not kill X server for %d,%d,%d: No Display Device", ctx->getSenderPid(), ctx->getSenderUid(), ctx->getSenderGid());
+ return;
+ }
+
+ QList<pid_t> pids;
+
+ // Find all processes that have it opened:
+ QDir proc("/proc");
+ QFileInfoList entries = proc.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
+ foreach(QFileInfo fi, entries)
+ {
+ // We are only interested in numerical ids:
+ bool ok;
+ pid_t pid = fi.fileName().toUInt(&ok);
+ if(!ok)
+ continue;
+
+ // We have a pid. Now check open files:
+ QDir fds(QString("/proc/%1/fd").arg(pid));
+ qDebug("Searching for X server in %s", fds.absolutePath().toLocal8Bit().constData());
+ QFileInfoList fdfiles = fds.entryInfoList(QDir::Files);
+ foreach(QFileInfo fdfile, fdfiles)
+ {
+ qDebug(" Looking for terminal in %s", fdfile.absoluteFilePath().toLocal8Bit().constData());
+ QFileInfo fdTarget(fdfile.readLink());
+ if(fdTarget.canonicalFilePath() == displayDeviceAbs)
+ {
+ qDebug(" ... found");
+ pids << pid;
+ }
+ }
+ }
+
+ // If everything is well, we have exactly one pid:
+ if(pids.size() != 1)
+ {
+ QStringList pidStrs;
+ foreach(pid_t pid, pids)
+ {
+ pidStrs << QString::number(pid);
+ }
+
+ qWarning("Display device %s is open by multiple or zero processes (%s). We don't know which is X. Aborting.",
+ displayDeviceAbs.toLocal8Bit().constData(),
+ pidStrs.join(", ").toLocal8Bit().constData());
+ return;
+ }
+
+ // We found the PID for the X server. Now kill it.
+ QString exe = QFileInfo(QString("/proc/%1/exe").arg(pids[0])).readLink();
+ qDebug("Killing X server, PID %d, exe name %s with SIGTERM", pids[0], exe.toLocal8Bit().constData());
+
+// kill(pids[0], SIGTERM);
+}
diff --git a/src/input/killX11Handler.h b/src/input/killX11Handler.h
new file mode 100644
index 0000000..2f3ef44
--- /dev/null
+++ b/src/input/killX11Handler.h
@@ -0,0 +1,34 @@
+/*
+ # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # --------------------------------------------------------------------------
+ # killX11Handler.h
+ # - Kill the X11 Server - interface
+ # --------------------------------------------------------------------------
+ */
+
+#ifndef KILLX11HANDLER_H_
+#define KILLX11HANDLER_H_
+
+#include <QCoreApplication>
+#include "inputEventHandler.h"
+
+class KillX11Handler : public DefaultInputEventHandler<InputEvent::ET_SPECIAL, InputEvent::EC_KILL_X>
+{
+public:
+ void handle(InputEvent const&, InputEventContext const*);
+
+ static void describeInto(QList<SpecialInputEventDescription>& list)
+ {
+ list << SpecialInputEventDescription(tr(QT_TRANSLATE_NOOP("InputEventHandler", "Kill X Server")), InputEvent::ET_SPECIAL, InputEvent::EC_KILL_X);
+ }
+};
+
+#endif /* KILLX11HANDLER_H_ */
diff --git a/src/input/magicSysRqHandler.cpp b/src/input/magicSysRqHandler.cpp
new file mode 100644
index 0000000..108bfca
--- /dev/null
+++ b/src/input/magicSysRqHandler.cpp
@@ -0,0 +1,28 @@
+/*
+ # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # --------------------------------------------------------------------------
+ # inputEventHandler.h:
+ # - Common definitions for input event handlers
+ # --------------------------------------------------------------------------
+ */
+
+#include <QFile>
+#include "magicSysRqHandler.h"
+
+void MagicSysRqHandler::handle(InputEvent const& evt, InputEventContext const*)
+{
+ QFile trigger("/proc/sysrq-trigger");
+ trigger.open(QIODevice::WriteOnly);
+ char c = (char)(evt.value() & 0xff);
+ trigger.write(&c, 1);
+ trigger.flush();
+ trigger.close();
+}
diff --git a/src/input/magicSysRqHandler.h b/src/input/magicSysRqHandler.h
new file mode 100644
index 0000000..563d091
--- /dev/null
+++ b/src/input/magicSysRqHandler.h
@@ -0,0 +1,72 @@
+/*
+ # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # --------------------------------------------------------------------------
+ # magicSysRqHandler.h
+ # - Trigger Magic-SysRq functions - interface
+ # --------------------------------------------------------------------------
+ */
+
+#ifndef MAGICSYSRQHANDLER_H_
+#define MAGICSYSRQHANDLER_H_
+
+#include "inputEventHandler.h"
+
+class MagicSysRqHandler : public DefaultInputEventHandler<InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ>
+{
+public:
+ void handle(InputEvent const& evt, InputEventContext const* ctx);
+
+ static void describeInto(QList<SpecialInputEventDescription>& list)
+ {
+ list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Reboot immediately"),
+ InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'b');
+ list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Crash system"),
+ InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'c');
+ list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Show all held logs"),
+ InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'd');
+ list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Send SIGTERM to all"),
+ InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'e');
+ list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Activate OOM killer"),
+ InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'f');
+ list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Send SIGKILL to all"),
+ InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'i');
+ list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Force thaw filesystems"),
+ InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'j');
+ list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Kill all on terminal"),
+ InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'k');
+ list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Show stack traces"),
+ InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'l');
+ list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Dump memory info"),
+ InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'm');
+ list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Make real-time tasks niceable"),
+ InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'n');
+ list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Power off immediately"),
+ InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'o');
+ list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Dump registers and flags"),
+ InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'p');
+ list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Dump timers and clockevents"),
+ InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'q');
+ list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Turn off raw keyboard mode"),
+ InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'r');
+ list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Sync all mounted filesystems"),
+ InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 's');
+ list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Dump task list"),
+ InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 't');
+ list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Remount all read-only"),
+ InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'u');
+ list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Dump uninterruptible tasks"),
+ InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'w');
+ list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Dump ftrace buffer"),
+ InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'z');
+ }
+};
+
+#endif /* MAGICSYSRQHANDLER_H_ */
diff --git a/src/input/org.openslx.pvs.input.policy b/src/input/org.openslx.pvs.input.policy
new file mode 100644
index 0000000..f0de9f2
--- /dev/null
+++ b/src/input/org.openslx.pvs.input.policy
@@ -0,0 +1,18 @@
+<?xml version="1.0" ?>
+<!DOCTYPE policyconfig PUBLIC
+ "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/PolicyKit/1/policyconfig.dtd">
+<policyconfig>
+ <vendor>The OpenSLX project</vendor>
+ <vendor_url>http://lab.openslx.org/pvs</vendor_url>
+
+ <action id="org.openslx.pvs.privilegedinput">
+ <description>Use privileged input actions in PVS</description>
+ <message>Authentication is required to let PVS use privileged input actions</message>
+ <defaults>
+ <allow_inactive>auth_self_keep</allow_inactive>
+ <allow_active>auth_self_keep</allow_active>
+ <allow_any>auth_admin_keep</allow_any>
+ </defaults>
+ </action>
+</policyconfig>
diff --git a/src/input/privilegedHandlerForwarder.cpp b/src/input/privilegedHandlerForwarder.cpp
new file mode 100644
index 0000000..3c0b3bd
--- /dev/null
+++ b/src/input/privilegedHandlerForwarder.cpp
@@ -0,0 +1,82 @@
+/*
+ # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # --------------------------------------------------------------------------
+ # inputHandlerChain.cpp:
+ # - Forward privileged input events to a special handler process - implementation
+ # --------------------------------------------------------------------------
+ */
+
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <cassert>
+#include <cerrno>
+#include <cstring>
+#include <string>
+#include <QSettings>
+#include <QtDebug>
+#include "pvsPrivInputSocket.h"
+#include "privilegedHandlerForwarder.h"
+
+using namespace std;
+
+#ifndef UNIX_PATH_MAX
+# define UNIX_PATH_MAX 108 /* according to unix(7) */
+#endif
+
+void PrivilegedHandlerForwarder::initialize()
+{
+ QSettings settings(QSettings::NativeFormat, QSettings::SystemScope, "openslx", "pvsinputd");
+
+ QString defaultPath = "/tmp/pvsprivinputd.sock";
+ QByteArray socketPath = settings.value("socketpath", defaultPath).toString().toLocal8Bit();
+
+ _socket = pvsPrivInputMakeClientSocket();
+ if(_socket < 0)
+ {
+ return;
+ }
+}
+
+void PrivilegedHandlerForwarder::handle(InputEvent const& evt, InputEventContext const*)
+{
+ qDebug("Trying to handle %s in PrivilegedHandlerForwarder", evt.toString().c_str());
+ if(_socket < 0)
+ {
+ initialize();
+ }
+
+ QByteArray data;
+ QDataStream strm(&data, QIODevice::WriteOnly);
+ strm.setByteOrder(QDataStream::BigEndian);
+ strm << evt;
+
+ assert(data.size() == 8);
+
+ int delta = 0;
+ int count = 0;
+ do {
+ delta = write(_socket, data.constData(), data.size());
+ if(delta < 0)
+ {
+ qWarning("Error while communicating with pvsprivinputd: %s", strerror(errno));
+ close(_socket);
+
+ // Try again:
+ initialize();
+ }
+ else if(delta != 8)
+ {
+ // This should normally not happen.
+ qWarning("Could not send a complete packet. Only %d bytes sent", delta);
+ }
+ count++;
+ } while(delta != 8 && count < 3);
+}
diff --git a/src/input/privilegedHandlerForwarder.h b/src/input/privilegedHandlerForwarder.h
new file mode 100644
index 0000000..37e8f24
--- /dev/null
+++ b/src/input/privilegedHandlerForwarder.h
@@ -0,0 +1,32 @@
+/*
+ # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # --------------------------------------------------------------------------
+ # inputHandlerChain.h:
+ # - Forward privileged input events to a special handler process.
+ # --------------------------------------------------------------------------
+ */
+
+#ifndef PRIVILEGEDHANDLERFORWARDER_H_
+#define PRIVILEGEDHANDLERFORWARDER_H_
+
+#include "inputEventHandler.h"
+
+class PrivilegedHandlerForwarder : public DefaultInputEventHandler<InputEvent::ET_SPECIAL>
+{
+public:
+ void handle(InputEvent const& evt, InputEventContext const* = 0);
+ void initialize();
+
+private:
+ int _socket;
+};
+
+#endif /* PRIVILEGEDHANDLERFORWARDER_H_ */
diff --git a/src/input/pvsCheckPrivileges.cpp b/src/input/pvsCheckPrivileges.cpp
new file mode 100644
index 0000000..2026c0a
--- /dev/null
+++ b/src/input/pvsCheckPrivileges.cpp
@@ -0,0 +1,550 @@
+/*
+ # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # --------------------------------------------------------------------------
+ # pvsCheckPrivileges.cpp:
+ # - A small program that checks whether or not it is called from
+ # a physical seat and conditionally executes the pvs input daemon.
+ # Additional security-relevant conditions should be checked here.
+ #
+ # The program is called with exactly one parameter, specifying the
+ # number of the file descriptor which is to be passed to its child.
+ # --------------------------------------------------------------------------
+ */
+
+#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
+#include <unistd.h>
+#include <iostream>
+#include <cerrno>
+#include <cstdlib>
+#include <cstring>
+#include <QCoreApplication>
+#include <QDir>
+#include <QFileInfo>
+#include <QFileSystemWatcher>
+#include <QSettings>
+#include <QTimer>
+#include <QtDBus/QDBusArgument>
+#include <QtDBus/QDBusConnection>
+#include <QtDBus/QDBusInterface>
+#include <QtDBus/QDBusMetaType>
+#include <QtDBus/QDBusReply>
+#include <QtGlobal>
+#include <QDebug>
+#include <QUuid>
+#include "pvsPrivInputSocket.h"
+#include "pvsCheckPrivileges.h"
+
+using namespace std;
+
+#define TIMEOUT_VALUE 10000 /* Wait for max. 10 seconds */
+
+// We need these classes for PolicyKit access:
+struct PolKitSubject {
+ QString subject_kind;
+ QMap<QString, QVariant> subject_details;
+};
+Q_DECLARE_METATYPE(PolKitSubject);
+
+QDBusArgument& operator<<(QDBusArgument& arg, PolKitSubject const& subj)
+{
+ arg.beginStructure();
+ arg << subj.subject_kind << subj.subject_details;
+ arg.endStructure();
+ return arg;
+}
+
+QDBusArgument const& operator>>(QDBusArgument const& arg, PolKitSubject& subj)
+{
+ arg.beginStructure();
+ arg >> subj.subject_kind >> subj.subject_details;
+ arg.endStructure();
+ return arg;
+}
+
+struct PolKitAuthReply {
+ bool isAuthorized;
+ bool isChallenge;
+ QMap<QString, QString> details;
+};
+Q_DECLARE_METATYPE(PolKitAuthReply);
+
+QDBusArgument& operator<<(QDBusArgument& arg, PolKitAuthReply const& reply)
+{
+ arg.beginStructure();
+ arg << reply.isAuthorized << reply.isChallenge << reply.details;
+ arg.endStructure();
+ return arg;
+}
+
+QDBusArgument const& operator>>(QDBusArgument const& arg, PolKitAuthReply& reply)
+{
+ arg.beginStructure();
+ arg >> reply.isAuthorized >> reply.isChallenge >> reply.details;
+ arg.endStructure();
+ return arg;
+}
+
+// We need to pass QMap<QString, QString> to QVariant:
+typedef QMap<QString, QString> QStringStringMap;
+Q_DECLARE_METATYPE(QStringStringMap)
+
+PVSCheckPrivileges* PVSCheckPrivileges::instance()
+{
+ static PVSCheckPrivileges* static_instance = 0;
+ if(!static_instance)
+ {
+ static_instance = new PVSCheckPrivileges();
+ }
+ return static_instance;
+}
+
+void PVSCheckPrivileges::deleteInstance()
+{
+ delete instance();
+}
+
+PVSCheckPrivileges::PVSCheckPrivileges(QObject* parent)
+ : QObject(parent)
+{
+ // initialise our cache:
+ updateCachedUserDatabase();
+ rereadConfiguration();
+
+ // and make it update itself:
+ QStringList paths;
+ paths << "/etc/passwd" << "/etc/group" << pvsPrivInputGetSettingsPath();
+ _watcher = new QFileSystemWatcher(paths, this);
+ connect(_watcher, SIGNAL(fileChanged(QString const&)), this, SLOT(updateCachedUserDatabase()));
+ connect(_watcher, SIGNAL(fileChanged(QString const&)), this, SLOT(rereadConfiguration()));
+}
+
+PVSCheckPrivileges::~PVSCheckPrivileges()
+{
+}
+
+QString PVSCheckPrivileges::getSessionReference(CachedInputContext const& sender)
+{
+ if(!sender.isValid())
+ {
+ return QString();
+ }
+
+ QString sessionReference = _savedConsoleKitSession.value(sender, QString());
+ if(sessionReference.isNull())
+ {
+ QDBusConnection conn = QDBusConnection::systemBus();
+ // Find the name of the current session:
+ QDBusInterface manager("org.freedesktop.ConsoleKit", "/org/freedesktop/ConsoleKit/Manager", "org.freedesktop.ConsoleKit.Manager", conn);
+ QDBusReply<QDBusObjectPath> replyGetSession = manager.call(QDBus::Block, "GetSessionForUnixProcess", (quint32)sender.pid);
+ if(!replyGetSession.isValid())
+ {
+ qWarning("Reply to GetSessionForUnixProcess is invalid: %s: %s", replyGetSession.error().name().toLocal8Bit().constData(), replyGetSession.error().message().toLocal8Bit().constData());
+ return QString();
+ }
+ _savedConsoleKitSession[sender] = sessionReference = replyGetSession.value().path();
+ }
+ return sessionReference;
+}
+
+PVSCheckPrivileges::SessionKind PVSCheckPrivileges::getSessionKind(CachedInputContext const& sender)
+{
+ if(!sender.isValid())
+ {
+ return SESSION_UNKNOWN;
+ }
+
+ // if the sender is root, we always suppose he works locally.
+ if(sender.uid == 0)
+ {
+ return SESSION_LOCAL;
+ }
+
+ QString sessionReference = getSessionReference(sender);
+ if(sessionReference.isNull())
+ {
+ return SESSION_LOOKUP_FAILURE;
+ }
+
+ QDBusConnection conn = QDBusConnection::systemBus();
+ QDBusInterface session("org.freedesktop.ConsoleKit", sessionReference, "org.freedesktop.ConsoleKit.Session", conn);
+ QDBusReply<bool> replyIsLocal = session.call(QDBus::Block, "IsLocal");
+
+ if(!replyIsLocal.isValid())
+ {
+ qWarning("Unable to find out whether the current session is local: %s: %s", replyIsLocal.error().name().toLocal8Bit().constData(), replyIsLocal.error().message().toLocal8Bit().constData());
+ return SESSION_LOOKUP_FAILURE;
+ }
+ return replyIsLocal.value() ? SESSION_LOCAL : SESSION_NONLOCAL;
+}
+
+PVSCheckPrivileges::UserPrivilege PVSCheckPrivileges::getUserPrivilege(CachedInputContext const& sender)
+{
+ // Always allow root:
+ if(sender.uid == 0)
+ {
+ return USER_PRIVILEGED;
+ }
+
+ // Or if the user is one of those enumerated in the privileged-users configuration value:
+ if(_privilegedUsers.contains(sender.uid))
+ {
+ return USER_PRIVILEGED;
+ }
+
+ // Or if the user is a member of one of the groups enumerated in the privileged-groups configuration value:
+ foreach(gid_t gid, _privilegedGroups)
+ {
+ if(_userGroupMap.contains(sender.uid, gid))
+ {
+ return USER_PRIVILEGED;
+ }
+ }
+
+ // User is not trivially privileged, so try to check with PolicyKit.
+#ifdef HAVE_POLKIT // but only if it is present
+ // For PolKit, we need the start-time of the process.
+ // On Linux, we can get it from /proc:
+ QString procStat = QString("/proc/%1/stat").arg(sender.pid);
+ QFile procStatFile(procStat);
+ if(!procStatFile.exists())
+ {
+ qWarning("Could not look up any info on process %d, its %s file does not exist", sender.pid, procStat.toLocal8Bit().constData());
+ return USER_LOOKUP_FAILURE;
+ }
+ procStatFile.open(QIODevice::ReadOnly);
+ QByteArray procStatBytes = procStatFile.readAll();
+ qDebug() << "Read stat file: " << procStatBytes;
+ QString procStatLine = QString::fromLocal8Bit(procStatBytes.constData(), procStatBytes.length());
+ QStringList procStatFields = procStatLine.split(QRegExp("\\s+"));
+ qDebug() << "Found stat fields: " << procStatFields;
+ bool ok;
+ quint64 startTime = procStatFields[21].toULongLong(&ok);
+ if(!ok)
+ {
+ qWarning("Could not find out start time for process %d", (int)sender.pid);
+ return USER_LOOKUP_FAILURE;
+ }
+
+ // Okay, we got the startTime. Now ask PolicyKit:
+
+ QDBusConnection conn = QDBusConnection::systemBus();
+ QDBusInterface intf("org.freedesktop.PolicyKit1", "/org/freedesktop/PolicyKit1/Authority", "org.freedesktop.PolicyKit1.Authority", conn);
+ PolKitSubject subj;
+ subj.subject_kind = "unix-process";
+ subj.subject_details["pid"] = (quint32)sender.pid;
+ subj.subject_details["start-time"] = startTime;
+ QDBusReply<PolKitAuthReply> reply = intf.call(QDBus::Block,
+ QLatin1String("CheckAuthorization"),
+ QVariant::fromValue(subj),
+ "org.openslx.pvs.privilegedinput",
+ QVariant::fromValue(QMap<QString, QString>()) /* No details */,
+ (quint32)1 /* Allow Interaction */,
+ QUuid::createUuid().toString() /* Cancellation ID */);
+ if(!reply.isValid())
+ {
+ QDBusError err = reply.error();
+
+ qWarning("Reply to CheckAuthorization is invalid: %s: %s", err.name().toLocal8Bit().constData(), err.message().toLocal8Bit().constData());
+ return USER_LOOKUP_FAILURE;
+ }
+ return reply.value().isAuthorized ? USER_PRIVILEGED : USER_UNPRIVILEGED;
+#else
+ return USER_UNPRIVILEGED;
+#endif
+}
+
+QString PVSCheckPrivileges::getX11SessionName(CachedInputContext const& sender)
+{
+ QString sessionReference = getSessionReference(sender);
+ if(sessionReference.isNull())
+ {
+ return QString();
+ }
+
+ QDBusConnection conn = QDBusConnection::systemBus();
+ QDBusInterface intf("org.freedesktop.ConsoleKit", sessionReference, "org.freedesktop.ConsoleKit.Session", conn);
+ QDBusReply<QString> reply = intf.call(QDBus::Block, QLatin1String("GetX11Display"));
+ if(!reply.isValid())
+ {
+ QDBusError err = reply.error();
+
+ qWarning("Reply to GetX11Display is invalid: %s: %s", err.name().toLocal8Bit().constData(), err.message().toLocal8Bit().constData());
+ return QString();
+ }
+
+ return reply.value();
+}
+
+QString PVSCheckPrivileges::getX11DisplayDevice(CachedInputContext const& sender)
+{
+ QString sessionReference = getSessionReference(sender);
+ if(sessionReference.isNull())
+ {
+ return QString();
+ }
+
+ QDBusConnection conn = QDBusConnection::systemBus();
+ QDBusInterface intf("org.freedesktop.ConsoleKit", sessionReference, "org.freedesktop.ConsoleKit.Session", conn);
+ QDBusReply<QString> reply = intf.call(QDBus::Block, QLatin1String("GetX11DisplayDevice"));
+ if(!reply.isValid())
+ {
+ QDBusError err = reply.error();
+
+ qWarning("Reply to GetX11DisplayDevice is invalid: %s: %s", err.name().toLocal8Bit().constData(), err.message().toLocal8Bit().constData());
+ return QString();
+ }
+
+ return reply.value();
+}
+
+bool PVSCheckPrivileges::require(SessionKind sessionKind, CachedInputContext const& sender)
+{
+ SessionKind cachedSessionKind;
+
+ if(sessionKind == SESSION_NONLOCAL)
+ {
+ // All sessions are at least non-local
+ return true;
+ }
+ else if(sessionKind == SESSION_LOCAL)
+ {
+ if((cachedSessionKind = _savedSessionKind.value(sender, SESSION_UNKNOWN)) == SESSION_UNKNOWN)
+ {
+ cachedSessionKind = getSessionKind(sender);
+ if(cachedSessionKind != SESSION_LOOKUP_FAILURE)
+ _savedSessionKind[sender] = cachedSessionKind;
+ qDebug("Got session kind: %s", toString(cachedSessionKind).toLocal8Bit().constData());
+ }
+
+ switch(cachedSessionKind)
+ {
+ case SESSION_LOOKUP_FAILURE:
+ case SESSION_UNKNOWN:
+ {
+ // If we cannot find out the correct session kind, look up what we should do in
+ // the configuration:
+ QSettings* config = pvsPrivInputGetSettings();
+ QVariant assumeLocal = config->value("assume-session-local", false);
+ if(!assumeLocal.canConvert(QVariant::Bool))
+ {
+ qWarning("There is an assume-session-local setting, but cannot convert it to boolean");
+ return false;
+ }
+ return assumeLocal.toBool();
+ }
+ case SESSION_LOCAL:
+ return true;
+ case SESSION_NONLOCAL:
+ return false;
+ default:
+ qWarning("Internal error: Undefined session kind %d", (int)cachedSessionKind);
+ return false;
+ }
+ }
+ else
+ {
+ qWarning("Internal error: It does not make sense to require an unknown session or undefined session kind %d", (int)sessionKind);
+ return false;
+ }
+}
+
+bool PVSCheckPrivileges::require(UserPrivilege userPrivilege, CachedInputContext const& sender)
+{
+ UserPrivilege cachedUserPrivilege;
+
+ if(userPrivilege == USER_UNPRIVILEGED)
+ {
+ // All users are unprivileged
+ return true;
+ }
+ else if(userPrivilege == USER_PRIVILEGED)
+ {
+ if((cachedUserPrivilege = _savedUserPrivilege.value(sender, USER_UNKNOWN)) == USER_UNKNOWN)
+ {
+ cachedUserPrivilege = getUserPrivilege(sender);
+ if(cachedUserPrivilege != USER_LOOKUP_FAILURE)
+ _savedUserPrivilege[sender] = cachedUserPrivilege;
+ qDebug("Got user privilege: %s", toString(cachedUserPrivilege).toLocal8Bit().constData());
+ }
+
+ switch(cachedUserPrivilege)
+ {
+ case USER_LOOKUP_FAILURE:
+ case USER_UNKNOWN:
+ {
+ // If we cannot find out the correct user privilege level, look up what we should do in
+ // the configuration:
+ QSettings* config = pvsPrivInputGetSettings();
+ QVariant assumePrivileged = config->value("assume-user-privileged", false);
+ if(!assumePrivileged.canConvert(QVariant::Bool))
+ {
+ qWarning("There is an assume-session-local setting, but cannot convert it to boolean");
+ return false;
+ }
+ return assumePrivileged.toBool();
+ }
+ case USER_PRIVILEGED:
+ return true;
+ case USER_UNPRIVILEGED:
+ return false;
+ default:
+ qWarning("Internal error: Found undefined user privilege level %d", (int)cachedUserPrivilege);
+ _savedUserPrivilege.remove(sender);
+ return false;
+ }
+ }
+ else
+ {
+ qWarning("Internal error: It does not make sense to require an unknown or undefined user privilege level %d", (int)userPrivilege);
+ return false;
+ }
+}
+
+bool PVSCheckPrivileges::require(SessionKind sessionKind,
+ UserPrivilege userPrivilege,
+ CachedInputContext const& sender)
+{
+ if(!require(sessionKind, sender))
+ return false;
+ if(!require(userPrivilege, sender))
+ return false;
+ return true;
+}
+
+uint qHash(CachedInputContext const& p)
+{
+ return qHash(qMakePair(p.pid, qMakePair(p.uid, p.gid)));
+}
+
+void PVSCheckPrivileges::updateCachedUserDatabase()
+{
+ QHash<QString, uid_t> userNames;
+
+ _userGroupMap.clear();
+
+ // assemble a list of known users and their primary groups:
+ setpwent(); // open the user database
+ struct passwd* userrec;
+ while((userrec = getpwent()))
+ {
+ userNames.insert(userrec->pw_name, userrec->pw_uid);
+ _userGroupMap.insert(userrec->pw_uid, userrec->pw_gid);
+ }
+ endpwent(); // close the database
+
+ // augment with secondary groups:
+ setgrent(); // open the group database
+ struct group* grouprec;
+ while((grouprec = getgrent()))
+ {
+ char** membername = grouprec->gr_mem;
+ while(*membername)
+ {
+ uid_t uid = userNames.value(*membername, (uid_t)-1);
+ if(uid != (uid_t)-1)
+ {
+ _userGroupMap.insert(uid, grouprec->gr_gid);
+ }
+ membername++;
+ }
+ }
+ endgrent();
+
+ // decisions may have changed, so clear the caches:
+ _savedUserPrivilege.clear();
+}
+
+void PVSCheckPrivileges::rereadConfiguration()
+{
+ QSettings* settings = pvsPrivInputReopenSettings();
+
+ _privilegedGroups.clear();
+ QVariant groupList = settings->value("privileged-groups");
+ if(groupList.isValid())
+ {
+ if(!groupList.canConvert(QVariant::StringList))
+ {
+ qWarning("There is a privileged-groups setting, but it cannot be converted to a list of strings.");
+ goto endGroupListScan;
+ }
+
+ QStringList groups = groupList.toStringList();
+ foreach(QString groupName, groups)
+ {
+ bool ok;
+ gid_t gid = groupName.toUInt(&ok);
+ if(ok)
+ {
+ _privilegedGroups.append(gid);
+ }
+ else
+ {
+ // lookup the name:
+ QByteArray groupNameBytes = groupName.toLocal8Bit();
+ struct group* group = getgrnam(groupNameBytes.constData());
+ if(group)
+ {
+ _privilegedGroups.append(group->gr_gid);
+ }
+ else
+ {
+ qWarning("privileged-groups setting contains %s which is neither a numeric GID "
+ "nor a valid group name. Skipping.",
+ groupNameBytes.constData());
+ }
+ }
+ }
+ }
+
+endGroupListScan:
+ _privilegedUsers.clear();
+ QVariant userList = settings->value("privileged-users");
+ if(userList.isValid())
+ {
+ if(!userList.canConvert(QVariant::StringList))
+ {
+ qWarning("There is a privileged-users setting, but it cannot be converted to a list of strings.");
+ goto endUserListScan;
+ }
+
+ QStringList users = userList.toStringList();
+ foreach(QString userName, users)
+ {
+ bool ok;
+ uid_t uid = userName.toUInt(&ok);
+ if(ok)
+ {
+ _privilegedUsers.append(uid);
+ }
+ else
+ {
+ // lookup the name:
+ QByteArray userNameBytes = userName.toLocal8Bit();
+ struct passwd* user = getpwnam(userNameBytes.constData());
+ if(user)
+ {
+ _privilegedUsers.append(user->pw_uid);
+ }
+ else
+ {
+ qWarning("privileged-users setting contains %s which is neither a numeric UID "
+ "nor a valid user name. Skipping.",
+ userNameBytes.constData());
+ }
+ }
+ }
+ }
+
+endUserListScan:
+ return;
+}
diff --git a/src/input/pvsCheckPrivileges.h b/src/input/pvsCheckPrivileges.h
new file mode 100644
index 0000000..62b463c
--- /dev/null
+++ b/src/input/pvsCheckPrivileges.h
@@ -0,0 +1,151 @@
+/*
+ # Copyright (c) 2009,2010 - OpenSLX Project, Computer Center University of
+ # Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # -----------------------------------------------------------------------------
+ # src/net/pvsCheckPrivileges_linux.h
+ # - Linux implementation of privilege checking
+ # -----------------------------------------------------------------------------
+ */
+
+#ifndef PVSCHECKPRIVILEGES_H_
+#define PVSCHECKPRIVILEGES_H_
+
+#include <sys/types.h>
+#include <QObject>
+#include <QString>
+#include <QList>
+#include <QMultiMap>
+#include <QHash>
+#include "inputEventHandler.h"
+
+struct CachedInputContext
+{
+ CachedInputContext(InputEventContext const* source)
+ {
+ if(source)
+ {
+ pid = source->getSenderPid();
+ uid = source->getSenderUid();
+ gid = source->getSenderGid();
+ }
+ else
+ {
+ pid = (pid_t)-1;
+ uid = (uid_t)-1;
+ gid = (gid_t)-1;
+ }
+ }
+
+ CachedInputContext()
+ {
+ pid = (pid_t)-1;
+ uid = (uid_t)-1;
+ gid = (gid_t)-1;
+ }
+
+ pid_t pid;
+ uid_t uid;
+ gid_t gid;
+
+ bool isValid() const
+ {
+ return (pid != (pid_t)-1) && (uid != (uid_t)-1) && (gid != (gid_t)-1);
+ }
+
+ bool operator==(CachedInputContext const& other) const
+ {
+ return (other.pid == pid) && (other.uid == uid) && (other.gid == gid);
+ }
+};
+uint qHash(CachedInputContext const& p);
+
+class QFileSystemWatcher;
+
+class PVSCheckPrivileges : public QObject
+{
+ Q_OBJECT
+public:
+ typedef enum {
+ SESSION_LOCAL,
+ SESSION_NONLOCAL,
+ SESSION_LOOKUP_FAILURE,
+ SESSION_UNKNOWN
+ } SessionKind;
+ static QString toString(SessionKind k)
+ {
+ switch(k)
+ {
+ case SESSION_LOOKUP_FAILURE: return "SESSION_LOOKUP_FAILURE";
+ case SESSION_LOCAL: return "SESSION_LOCAL";
+ case SESSION_NONLOCAL: return "SESSION_NONLOCAL";
+ case SESSION_UNKNOWN: return "SESSION_UNKNOWN";
+ default: return QString("unknown value (%1)").arg(k);
+ }
+ }
+
+ typedef enum {
+ USER_PRIVILEGED,
+ USER_UNPRIVILEGED,
+ USER_LOOKUP_FAILURE,
+ USER_UNKNOWN
+ } UserPrivilege;
+ static QString toString(UserPrivilege k)
+ {
+ switch(k)
+ {
+ case USER_PRIVILEGED: return "USER_PRIVILEGED";
+ case USER_UNPRIVILEGED: return "USER_UNPRIVILEGED";
+ case USER_LOOKUP_FAILURE: return "USER_LOOKUP_FAILURE";
+ case USER_UNKNOWN: return "USER_UNKNOWN";
+ default: return QString("unknown value (%1)").arg(k);
+ }
+ }
+
+ static PVSCheckPrivileges* instance();
+ static void deleteInstance();
+
+ bool require(SessionKind sessionKind, CachedInputContext const& sender);
+ bool require(UserPrivilege userPrivilege, CachedInputContext const& sender);
+ bool require(SessionKind sessionKind, UserPrivilege userPrivilege, CachedInputContext const& sender);
+ QString getX11SessionName(CachedInputContext const& sender);
+ QString getX11DisplayDevice(CachedInputContext const& sender);
+
+public slots:
+ void updateCachedUserDatabase();
+ void rereadConfiguration();
+
+private:
+ PVSCheckPrivileges(QObject* parent = 0);
+ virtual ~PVSCheckPrivileges();
+
+ typedef QPair<pid_t, QPair<uid_t, gid_t> > piduidgid;
+ piduidgid makePidUidGid(pid_t pid, uid_t uid, gid_t gid)
+ {
+ return qMakePair(pid, qMakePair(uid, gid));
+ }
+
+ QString getSessionReference(CachedInputContext const& sender);
+ SessionKind getSessionKind(CachedInputContext const& sender);
+ UserPrivilege getUserPrivilege(CachedInputContext const& sender);
+
+ static PVSCheckPrivileges* _instance;
+
+ QHash<CachedInputContext, UserPrivilege> _savedUserPrivilege;
+ QHash<CachedInputContext, SessionKind> _savedSessionKind;
+ QHash<CachedInputContext, QString> _savedConsoleKitSession;
+
+ QList<uid_t> _privilegedUsers;
+ QList<gid_t> _privilegedGroups;
+ QMultiMap<uid_t, gid_t> _userGroupMap;
+ QFileSystemWatcher* _watcher;
+};
+
+#endif /* PVSCHECKPRIVILEGES_H_ */
diff --git a/src/input/pvsPrivInputHandler.cpp b/src/input/pvsPrivInputHandler.cpp
new file mode 100644
index 0000000..70ed1bc
--- /dev/null
+++ b/src/input/pvsPrivInputHandler.cpp
@@ -0,0 +1,108 @@
+/*
+ # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # --------------------------------------------------------------------------
+ # pvsPrivInputHandler.h
+ # - Handle privileged input connection requests - implementation
+ # --------------------------------------------------------------------------
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <cerrno>
+#include <QtDebug>
+#include <QSocketNotifier>
+#include "inputEvent.h"
+#include "inputEventHandler.h"
+#include "pvsPrivInputSocket.h"
+#include "pvsPrivInputHandler.h"
+
+using namespace std;
+
+class PrivInputContext : public InputEventContext
+{
+public:
+ PrivInputContext(pid_t pid, uid_t uid, gid_t gid)
+ : _pid(pid), _uid(uid), _gid(gid)
+ {
+ }
+
+ pid_t getSenderPid() const
+ {
+ return _pid;
+ }
+
+ uid_t getSenderUid() const
+ {
+ return _uid;
+ }
+
+ gid_t getSenderGid() const
+ {
+ return _gid;
+ }
+
+private:
+ pid_t _pid;
+ uid_t _uid;
+ gid_t _gid;
+};
+
+PVSPrivInputHandler::PVSPrivInputHandler(int fd, QObject* parent) :
+ QObject(parent)
+{
+ _fd = fd;
+ _notifier = new QSocketNotifier(fd, QSocketNotifier::Read, this);
+ _notifier->setEnabled(true);
+ connect(_notifier, SIGNAL(activated(int)), this, SLOT(canRead()));
+}
+
+PVSPrivInputHandler::~PVSPrivInputHandler()
+{
+ delete _notifier;
+}
+
+void PVSPrivInputHandler::canRead()
+{
+ _notifier->setEnabled(false);
+
+ // We need to retrieve the credentials of the process:
+ size_t siz = 8;
+ _messageAssembly.clear();
+ _messageAssembly.resize(8);
+ pid_t pid;
+ uid_t uid;
+ gid_t gid;
+ int err;
+
+ if(!pvsPrivInputRecvMessage(_fd, _messageAssembly.data(), siz, pid, uid, gid, &err))
+ {
+ close(_fd);
+ deleteLater();
+ return;
+ }
+ else
+ {
+ if(siz != 8)
+ {
+ qWarning("Malformed PVS Input Event packet");
+ return;
+ }
+ QDataStream strm(&_messageAssembly, QIODevice::ReadOnly);
+ InputEvent evt;
+ strm.setByteOrder(QDataStream::BigEndian);
+ strm >> evt;
+ PrivInputContext ctx(pid, uid, gid);
+ _handlerChain.handle(evt, &ctx);
+ }
+
+ _notifier->setEnabled(true);
+}
diff --git a/src/input/pvsPrivInputHandler.h b/src/input/pvsPrivInputHandler.h
new file mode 100644
index 0000000..9980cdf
--- /dev/null
+++ b/src/input/pvsPrivInputHandler.h
@@ -0,0 +1,45 @@
+/*
+ # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # --------------------------------------------------------------------------
+ # pvsPrivInputHandler.h
+ # - Handle privileged input connection requests - interface
+ # --------------------------------------------------------------------------
+ */
+
+#ifndef PVSPRIVINPUTHANDLER_H_
+#define PVSPRIVINPUTHANDLER_H_
+
+#include <QObject>
+#include <QHash>
+#include <QPointer>
+#include "inputHandlerChain.h"
+
+class QSocketNotifier;
+
+class PVSPrivInputHandler : public QObject
+{
+ Q_OBJECT
+public:
+ PVSPrivInputHandler(int fd, QObject* parent = 0);
+ virtual ~PVSPrivInputHandler();
+
+private slots:
+ void canRead();
+
+private:
+ QSocketNotifier* _notifier;
+ QByteArray _messageAssembly;
+ int _bytes;
+ int _fd;
+ privileged_handler_chain _handlerChain;
+};
+
+#endif /* PVSPRIVINPUTHANDLER_H_ */
diff --git a/src/input/pvsPrivInputSignalHandler.cpp b/src/input/pvsPrivInputSignalHandler.cpp
new file mode 100644
index 0000000..b09c335
--- /dev/null
+++ b/src/input/pvsPrivInputSignalHandler.cpp
@@ -0,0 +1,69 @@
+/*
+ # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # --------------------------------------------------------------------------
+ # pvsPrivInputSignalHandler.cpp:
+ # - Handle signals forwarded over a file descriptor - implementation
+ # --------------------------------------------------------------------------
+ */
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <signal.h>
+#include <cerrno>
+#include "pvsPrivInputSocket.h"
+#include "pvsPrivInputSignalHandler.h"
+
+void PVSPrivInputSignalHandler::signalReceived(int sigfd)
+{
+ int signum;
+
+ pid_t pid;
+ uid_t uid;
+ gid_t gid;
+ int err;
+ size_t siz = sizeof(signum);
+
+ if(!pvsPrivInputRecvMessage(sigfd, &signum, siz, pid, uid, gid, &err))
+ {
+ qWarning("Should have received a signal but could not read message: %s", strerror(err));
+ return;
+ }
+
+ if(siz != sizeof(signum))
+ {
+ qWarning("Sould have received a message of size %d bytes but got %d bytes instead. Discarding message.", (int)sizeof(signum), (int)siz);
+ return;
+ }
+
+ if(!_allowUnauthenticatedKilling && pid != getpid())
+ {
+ qCritical("SOMETHING STRANGE IS GOING ON!\nReceived signal %d from PID %d\nPlease kill me instead of sending me signal notifications.", signum, (int)pid);
+ return;
+ }
+
+ switch(signum)
+ {
+ case SIGINT:
+ qDebug("Caught SIGINT. Quitting.");
+ emit terminate();
+ break;
+ case SIGTERM:
+ qDebug("Caught SIGTERM. Quitting.");
+ emit terminate();
+ break;
+ case SIGHUP:
+ qDebug("Caught SIGHUP. Reloading configuration.");
+ emit reloadConfiguration();
+ break;
+ default:
+ qWarning("Received signal %d, but don't know how to handle it.", signum);
+ }
+}
diff --git a/src/input/pvsPrivInputSignalHandler.h b/src/input/pvsPrivInputSignalHandler.h
new file mode 100644
index 0000000..cb75c86
--- /dev/null
+++ b/src/input/pvsPrivInputSignalHandler.h
@@ -0,0 +1,47 @@
+/*
+ # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # --------------------------------------------------------------------------
+ # pvsPrivInputSignalHandler.h:
+ # - Handle signals forwarded over a file descriptor - interface
+ # --------------------------------------------------------------------------
+ */
+
+#ifndef PVSPRIVINPUTSIGNALHANDLER_H_
+#define PVSPRIVINPUTSIGNALHANDLER_H_
+
+#include <QObject>
+
+class PVSPrivInputSignalHandler : public QObject
+{
+ Q_OBJECT
+public:
+ PVSPrivInputSignalHandler(QObject* parent = 0)
+ : QObject(parent)
+ {
+ }
+
+ void allowUnauthenticatedKilling(bool value)
+ {
+ _allowUnauthenticatedKilling = value;
+ }
+
+public slots:
+ void signalReceived(int sigfd);
+
+signals:
+ void terminate();
+ void reloadConfiguration();
+
+private:
+ bool _allowUnauthenticatedKilling;
+};
+
+#endif /* PVSPRIVINPUTSIGNALHANDLER_H_ */
diff --git a/src/input/pvsPrivInputSocket.cpp b/src/input/pvsPrivInputSocket.cpp
new file mode 100644
index 0000000..df5dff5
--- /dev/null
+++ b/src/input/pvsPrivInputSocket.cpp
@@ -0,0 +1,233 @@
+/*
+ # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # --------------------------------------------------------------------------
+ # pvsPrivInputSocket.h:
+ # - Centralize knowledge of socket address and connection options
+ # for pvsprivinputd - implementation
+ # --------------------------------------------------------------------------
+ */
+
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <cerrno>
+#include <QtDebug>
+#include <QSettings>
+#include "pvsPrivInputSocket.h"
+
+using namespace std;
+
+#ifndef UNIX_PATH_MAX
+# define UNIX_PATH_MAX 108 /* according to unix(7) */
+#endif
+
+static QSettings* pvsPrivInputSettings = 0;
+
+QString pvsPrivInputGetSettingsPath()
+{
+ return "/etc/pvsprivinputd.conf";
+}
+
+QSettings* pvsPrivInputGetSettings()
+{
+ if(!pvsPrivInputSettings)
+ {
+ pvsPrivInputSettings = new QSettings(pvsPrivInputGetSettingsPath(), QSettings::IniFormat);
+ }
+ return pvsPrivInputSettings;
+}
+
+QSettings* pvsPrivInputReopenSettings()
+{
+ if(pvsPrivInputSettings)
+ {
+ delete pvsPrivInputSettings;
+ pvsPrivInputSettings = 0;
+ }
+ return pvsPrivInputGetSettings();
+}
+
+QString pvsPrivInputGetSocketAddress()
+{
+ return pvsPrivInputGetSettings()->value("socketpath", "/tmp/pvsprivinputd.sock").toString();
+}
+
+bool pvsPrivInputEnableReceiveCredentials(int sock)
+{
+ int passcred = 1;
+ if(setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &passcred, sizeof(passcred)) < 0)
+ {
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+}
+
+int pvsPrivInputMakeClientSocket()
+{
+ int sock = socket(AF_UNIX, SOCK_DGRAM, 0);
+ if(sock < 0)
+ {
+ qWarning("Could not create a socket: %s", strerror(errno));
+ return -1;
+ }
+
+ QByteArray socketPath = pvsPrivInputGetSocketAddress().toLocal8Bit();
+ struct sockaddr_un addr;
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ strncpy(addr.sun_path, socketPath.constData(), UNIX_PATH_MAX - 1);
+ if(connect(sock, reinterpret_cast<struct sockaddr*>(&addr),
+ sizeof(addr)) < 0)
+ {
+ qWarning("Could not connect to pvsprivinputd at %s: %s", socketPath.constData(), strerror(errno));
+ close(sock);
+ return -1;
+ }
+
+ return sock;
+}
+
+int pvsPrivInputMakeServerSocket()
+{
+ int sock = socket(AF_UNIX, SOCK_DGRAM, 0);
+ if(sock < 0)
+ {
+ qCritical("Could not create a socket: %s", strerror(errno));
+ return -1;
+ }
+
+ // Bind to the address:
+ QByteArray socketPath = pvsPrivInputGetSocketAddress().toLocal8Bit();
+ struct sockaddr_un addr;
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ strncpy(addr.sun_path, socketPath.constData(), UNIX_PATH_MAX - 1);
+ if(bind(sock, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)) < 0)
+ {
+ qCritical("Could not bind socket to %s", strerror(errno));
+ close(sock);
+ return -1;
+ }
+
+ // Announce that credentials are requested:
+ if(!pvsPrivInputEnableReceiveCredentials(sock))
+ {
+ // We will not operate without credentials.
+ qCritical("Could not request peer credentials: %s", strerror(errno));
+ close(sock);
+ return -1;
+ }
+
+#if 0 /* Only for SOCK_STREAM: */
+ // Listen for connections:
+ if(listen(sock, 1) < 0)
+ {
+ qCritical("Could not listen for connections to %s: %s", socketPath.constData(), strerror(errno));
+ close(sock);
+ return -1;
+ }
+#endif
+
+ return sock;
+}
+
+bool pvsPrivInputSendMessage(int sock, void* buf, size_t _len, int* err)
+{
+ /*
+ * Portability note: All UNIX-like systems can transmit credentials over UNIX
+ * sockets, but only Linux does it automagically.
+ */
+
+ long len = (long)_len;
+
+ // send(2) does not split messages on a SOCK_DGRAM socket.
+ int e = send(sock, buf, len, 0);
+ if(e < 0)
+ {
+ qWarning("Failed to send message of length %d over socket %d: %s", (unsigned)len, e, strerror(errno));
+ if(err)
+ *err = errno;
+ return false;
+ }
+ else if(e < len)
+ {
+ qWarning("Failed to send a complete message of length %d over socket %d, only %d bytes were sent", (unsigned)len, sock, e);
+ if(err)
+ *err = errno;
+ return false;
+ }
+
+ return true;
+}
+
+bool pvsPrivInputRecvMessage(int sock, void* buf, size_t& len,
+ pid_t& pid, uid_t& uid, gid_t& gid, int* err)
+{
+ struct iovec iov;
+ struct msghdr msg;
+ char ctlbuf[1024];
+ iov.iov_base = buf;
+ iov.iov_len = len;
+ msg.msg_name = 0;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = &ctlbuf;
+ msg.msg_controllen = sizeof(ctlbuf);
+ msg.msg_flags = 0;
+ int bytes_read = recvmsg(sock, &msg, 0);
+ if(bytes_read < 0)
+ {
+ qWarning("Could not read from socket: %s", strerror(errno));
+ if(err)
+ *err = errno;
+ return false;
+ }
+
+ pid = -1;
+ uid = -1;
+ gid = -1;
+
+ struct cmsghdr* cmsg;
+ for(cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg))
+ {
+ if(cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_CREDENTIALS)
+ {
+ struct ucred* creds = reinterpret_cast<struct ucred*>(CMSG_DATA(cmsg));
+ pid = creds->pid;
+ uid = creds->uid;
+ gid = creds->gid;
+ break;
+ }
+ else if(cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS)
+ {
+ // We need to close passed file descriptors. If we don't, we
+ // have a denial-of-service vulnerability.
+ int* fds = reinterpret_cast<int*>(CMSG_DATA(cmsg));
+ unsigned num_fds = cmsg->cmsg_len / sizeof(int);
+ for(unsigned i = 0; i < num_fds; i++)
+ {
+ close(fds[i]);
+ }
+ }
+ }
+
+ if(pid == (pid_t)-1 || uid == (uid_t)-1 || gid == (gid_t)-1)
+ {
+ *err = 0;
+ return false;
+ }
+
+ return true;
+}
diff --git a/src/input/pvsPrivInputSocket.h b/src/input/pvsPrivInputSocket.h
new file mode 100644
index 0000000..447360b
--- /dev/null
+++ b/src/input/pvsPrivInputSocket.h
@@ -0,0 +1,37 @@
+/*
+ # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # --------------------------------------------------------------------------
+ # pvsPrivInputSocket.h:
+ # - Centralize knowledge of socket address and connection options
+ # for pvsprivinputd - interface
+ # --------------------------------------------------------------------------
+ */
+
+#ifndef PVSPRIVINPUTSOCKET_H_
+#define PVSPRIVINPUTSOCKET_H_
+
+#include <sys/types.h>
+#include <cstring>
+#include <QString>
+
+class QSettings;
+
+QSettings* pvsPrivInputGetSettings();
+QSettings* pvsPrivInputReopenSettings();
+QString pvsPrivInputGetSettingsPath();
+QString pvsPrivInputGetSocketAddress();
+bool pvsPrivInputEnableReceiveCredentials(int sock);
+int pvsPrivInputMakeClientSocket();
+int pvsPrivInputMakeServerSocket();
+bool pvsPrivInputSendMessage(int sock, void* buf, size_t len, int* err = 0);
+bool pvsPrivInputRecvMessage(int sock, void* buf, size_t& len, pid_t& pid, uid_t& uid, gid_t& gid, int* err = 0);
+
+#endif /* PVSPRIVINPUTSOCKET_H_ */
diff --git a/src/input/pvsSyslog.cpp b/src/input/pvsSyslog.cpp
new file mode 100644
index 0000000..6a9be31
--- /dev/null
+++ b/src/input/pvsSyslog.cpp
@@ -0,0 +1,133 @@
+/*
+ # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # --------------------------------------------------------------------------
+ # pvsSyslog.cpp:
+ # - Send output to syslog or to a file - implementation
+ # --------------------------------------------------------------------------
+ */
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <cerrno>
+#include <syslog.h>
+#include <QFile>
+#include <QTextStream>
+#include "pvsSyslog.h"
+
+using namespace std;
+
+#define PVS_LOG_BUFSIZ 8192
+
+void PVSLogRedirector::inputAvailableOn(int fd)
+{
+ QByteArray readbuf;
+ readbuf.resize(PVS_LOG_BUFSIZ);
+ ssize_t siz;
+ bool cont = true;
+
+ do
+ {
+ siz = recv(fd, readbuf.data(), readbuf.size(), MSG_DONTWAIT);
+ if(siz > 0)
+ {
+ _buf.append(readbuf.constData(), siz);
+
+ // Send lines:
+ int oldnpos = -1;
+ int npos = -1;
+ while((npos = _buf.indexOf('\n', oldnpos + 1)) >= 0)
+ {
+ if((oldnpos + 1) == npos)
+ {
+ oldnpos = npos;
+ continue;
+ }
+ QString line = QString::fromLocal8Bit(_buf.constData() + oldnpos + 1, npos - oldnpos - 1);
+ doRedirectInput(line);
+ oldnpos = npos;
+ }
+
+ // All complete lines have been sent. Remove sent bytes from buffer.
+ _buf.remove(0, oldnpos + 1);
+
+ // If line length is too large, send it now.
+ if(_buf.size() >= BUFSIZ)
+ {
+ QString longLine = QString::fromLocal8Bit(_buf.constData(), _buf.size());
+ doRedirectInput(longLine);
+ _buf.clear();
+ }
+ }
+ else if(siz == 0)
+ {
+ // Socket closed on other end.
+ break;
+ }
+ else if(siz < 0)
+ {
+ int err = errno;
+ switch(err)
+ {
+ case EAGAIN:
+#if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN)
+ case EWOULDBLOCK:
+#endif
+ // We read all there is to read.
+ cont = false;
+ break;
+ default:
+ // Something happened.
+ // We can't inform the user via qDebug() and friends as that would loop right back to us again,
+ // so we emit a log line.
+ doRedirectInput(QString("Error in logging code: Could not read log input: %s").arg(strerror(err)));
+ cont = false;
+ }
+ }
+ }
+ while(cont);
+}
+
+PVSSyslogRedirector::PVSSyslogRedirector()
+{
+ openlog("pvsprivinputd", LOG_PID, LOG_USER);
+}
+
+PVSSyslogRedirector::~PVSSyslogRedirector()
+{
+ closelog();
+}
+
+void PVSSyslogRedirector::doRedirectInput(QString const& line)
+{
+ QByteArray bytes = line.toLocal8Bit();
+ syslog(LOG_NOTICE, "%s", bytes.data());
+}
+
+PVSLogfileRedirector::PVSLogfileRedirector(QString const& filename)
+{
+ _file = new QFile(filename, this);
+ _file->open(QIODevice::Append);
+ _stream = new QTextStream(_file);
+}
+
+PVSLogfileRedirector::~PVSLogfileRedirector()
+{
+ _stream->flush();
+ _file->close();
+ delete _stream;
+ delete _file;
+}
+
+void PVSLogfileRedirector::doRedirectInput(QString const& line)
+{
+ *_stream << line << flush;
+}
diff --git a/src/input/pvsSyslog.h b/src/input/pvsSyslog.h
new file mode 100644
index 0000000..8c9591a
--- /dev/null
+++ b/src/input/pvsSyslog.h
@@ -0,0 +1,63 @@
+/*
+ # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # --------------------------------------------------------------------------
+ # pvsSyslog.h:
+ # - Send output to syslog or to a file - interface
+ # --------------------------------------------------------------------------
+ */
+
+#ifndef PVSSYSLOG_H_
+#define PVSSYSLOG_H_
+
+#include <QObject>
+#include <QString>
+
+class PVSLogRedirector : public QObject
+{
+ Q_OBJECT
+public slots:
+ void inputAvailableOn(int fd);
+
+protected:
+ virtual void doRedirectInput(QString const& line) = 0;
+
+private:
+ QByteArray _buf;
+};
+
+class PVSSyslogRedirector : public PVSLogRedirector
+{
+public:
+ PVSSyslogRedirector();
+ virtual ~PVSSyslogRedirector();
+
+protected:
+ void doRedirectInput(QString const& line);
+};
+
+class QFile;
+class QTextStream;
+
+class PVSLogfileRedirector : public PVSLogRedirector
+{
+public:
+ PVSLogfileRedirector(QString const& filename);
+ virtual ~PVSLogfileRedirector();
+
+protected:
+ void doRedirectInput(QString const& line);
+
+private:
+ QFile* _file;
+ QTextStream* _stream;
+};
+
+#endif /* PVSSYSLOG_H_ */
diff --git a/src/input/pvsprivinputd.conf b/src/input/pvsprivinputd.conf
new file mode 100644
index 0000000..a62a922
--- /dev/null
+++ b/src/input/pvsprivinputd.conf
@@ -0,0 +1,28 @@
+;; Lines starting with `;' are comments.
+;;
+;; socketpath:
+;; Where pvsprivinputd should put its listening socket.
+socketpath = /tmp/pvsprivinputd.sock
+
+;; privileged-users:
+;; Comma-separated list of users that are allowed to run privileged actions
+privileged-users = root
+
+;; privileged-groups:
+;; Comma-separated list of user groups that are allowed to run privileged actions
+; privileged-groups = wheel
+
+;; assume-session-local:
+;; Assume that a session is local if it can not be looked up in ConsoleKit,
+;; for example, if you are not running ConsoleKit.
+;;
+;; WARNING: Setting this to true may be a security risk. Running ConsoleKit is
+;; really recommended.
+; assume-session-local = false
+
+;; assume-user-privileged:
+;; Assume that a user is privileged if he/she can not be looked up in
+;; the user database or PolicyKit fails to deliver an answer.
+;;
+;; WARNING: Setting this to true is most definitely a security risk.
+; assume-user-privileged = false \ No newline at end of file
diff --git a/src/input/pvsprivinputd.cpp b/src/input/pvsprivinputd.cpp
new file mode 100644
index 0000000..e6ae155
--- /dev/null
+++ b/src/input/pvsprivinputd.cpp
@@ -0,0 +1,262 @@
+/*
+ # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # --------------------------------------------------------------------------
+ # pvsprivinputd.cpp:
+ # - Handle privileged input events - daemon
+ # --------------------------------------------------------------------------
+ */
+
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <signal.h>
+#include <unistd.h>
+#include <cerrno>
+#include <cstdlib>
+#include <cstdio>
+#include <cstring>
+#include <QtDebug>
+#include <QFileInfo>
+#include <QSettings>
+#include <QCoreApplication>
+#include <QStringList>
+#include <QSocketNotifier>
+#include "pvsCheckPrivileges.h"
+#include "pvsPrivInputSocket.h"
+#include "pvsPrivInputHandler.h"
+#include "pvsPrivInputSignalHandler.h"
+#include "pvsSyslog.h"
+
+using namespace std;
+
+#ifndef UNIX_PATH_MAX
+# define UNIX_PATH_MAX 108 /* according to unix(7) */
+#endif
+
+QByteArray socketPath;
+int signalFds[2];
+
+QTextStream qout(stdout, QIODevice::WriteOnly);
+QTextStream qerr(stderr, QIODevice::WriteOnly);
+
+static void unlinkSocket()
+{
+ if(!socketPath.isNull())
+ {
+ unlink(socketPath.constData());
+ }
+}
+
+static void onCaughtSignal(int signum)
+{
+ pvsPrivInputSendMessage(signalFds[0], &signum, sizeof(signum), 0);
+}
+
+static void usage()
+{
+ qout << QObject::tr(
+ "Usage: %1 [--help|-h] [--daemon|-d] [--log=<Logger>|-l<Logger>]\n"
+ "\n"
+ "Options:\n"
+ " --help, -h Show this message\n"
+ " --daemon, -d Run in background\n"
+ " --log=<Logger>,\n"
+ " -l<Logger> Redirect all output to <Logger>\n"
+ " valid values are:\n"
+ " - any file name\n"
+ " - `syslog' to redirect output to the system log\n"
+ " - `null' to discard output (default)\n"
+ " (without quotes)\n"
+ "\n"
+ "Signals:\n"
+ " SIGINT, SIGTERM (or press Ctrl+C when run in foreground)\n"
+ " Quit\n"
+ " SIGHUP Reload configuration and cached user data\n").arg(qApp->arguments().at(0)) << flush;
+}
+
+int main(int argc, char** argv)
+{
+ QCoreApplication app(argc, argv);
+ app.setApplicationName("pvsprivinputd");
+ app.setOrganizationName("openslx");
+
+ bool no_fork = true;
+ QString logTarget = "null";
+
+ foreach(QString arg, app.arguments().mid(1))
+ {
+ if(arg == "--daemon" || arg == "-d")
+ {
+ no_fork = false;
+ }
+ else if(arg.startsWith("-l"))
+ {
+ logTarget = arg.mid(2);
+ }
+ else if(arg.startsWith("--log="))
+ {
+ logTarget = arg.mid(6);
+ }
+ else if(arg == "--help" || arg == "-h")
+ {
+ usage();
+ return EXIT_SUCCESS;
+ }
+ else
+ {
+ qout << "Unexpected argument: " << arg << endl;
+ usage();
+ return EXIT_FAILURE;
+ }
+ }
+
+ if(logTarget != "null" && logTarget != "syslog")
+ {
+ logTarget = QFileInfo(logTarget).absoluteFilePath();
+ qout << "Writing log to " << logTarget << endl;
+ }
+
+ if(!no_fork)
+ {
+ pid_t pid;
+ pid_t sid;
+
+ pid = fork();
+ if(pid < 0)
+ {
+ qCritical("Could not fork: %s", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ if(pid > 0)
+ {
+ exit(EXIT_SUCCESS);
+ }
+
+ // We are now inside a child process.
+ // Detach from parent:
+ sid = setsid();
+ if(sid < 0)
+ {
+ qCritical("Could not detach from parent process: %s", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ // Change to some benign directory:
+ if(chdir("/") < 0)
+ {
+ qCritical("Could not change directory: %s", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ // Okay, we are running as a daemon. Defer reopening standard file descriptors
+ // so we can report errors.
+
+ // Set the file creation mask to allow all processes to connect to
+ // the socket (access control is handled differently):
+ umask(0);
+
+ // Store the socket path before the signal handler is installed so it does not risk segfaulting
+ // due to bad timing.
+ socketPath = pvsPrivInputGetSocketAddress().toLocal8Bit();
+
+ // Ignore SIGPIPE. Connection errors are handled internally.
+ // According to signal(2), the only error is that the signal number
+ // is invalid. This cannot happen.
+ signal(SIGPIPE, SIG_IGN);
+
+ // Create our socket:
+ int sock = pvsPrivInputMakeServerSocket();
+ if(sock < 0)
+ {
+ exit(EXIT_FAILURE);
+ }
+
+ // Install our main object
+ PVSPrivInputHandler handler(sock);
+
+ // set up signal handling:
+ if(socketpair(AF_UNIX, SOCK_DGRAM, 0, signalFds) < 0)
+ {
+ qCritical("Could not set up signal handling. Giving up.");
+ exit(EXIT_FAILURE);
+ }
+ PVSPrivInputSignalHandler sigHandler;
+ QSocketNotifier sigintWatcher(signalFds[1], QSocketNotifier::Read);
+ sigHandler.allowUnauthenticatedKilling(!pvsPrivInputEnableReceiveCredentials(signalFds[1]));
+ QObject::connect(&sigintWatcher, SIGNAL(activated(int)), &sigHandler, SLOT(signalReceived(int)));
+ QObject::connect(&sigHandler, SIGNAL(terminate()), &app, SLOT(quit()));
+ QObject::connect(&sigHandler, SIGNAL(reloadConfiguration()), PVSCheckPrivileges::instance(), SLOT(updateCachedUserDatabase()));
+ QObject::connect(&sigHandler, SIGNAL(reloadConfiguration()), PVSCheckPrivileges::instance(), SLOT(rereadConfiguration()));
+ signal(SIGINT, onCaughtSignal);
+ signal(SIGTERM, onCaughtSignal);
+ signal(SIGHUP, onCaughtSignal);
+
+ PVSLogRedirector* logRedir = 0;
+ QSocketNotifier* logNotif = 0;
+ // Our setup is complete.
+ if(!no_fork)
+ {
+ // Reopen standard file descriptors:
+ freopen("/dev/null", "r", stdin);
+
+ if(logTarget == "null")
+ {
+ freopen("/dev/null", "w", stdout);
+ freopen("/dev/null", "w", stderr);
+ }
+ else if(logTarget == "syslog")
+ {
+ logRedir = new PVSSyslogRedirector();
+ }
+ else
+ {
+ logRedir = new PVSLogfileRedirector(logTarget);
+ }
+
+ if(logRedir)
+ {
+ int logFds[2];
+ if(socketpair(AF_UNIX, SOCK_STREAM, 0, logFds) < 0)
+ {
+ qWarning("Could not open a socket pair: %s", strerror(errno));
+ return EXIT_FAILURE;
+ }
+
+ logNotif = new QSocketNotifier(logFds[1], QSocketNotifier::Read);
+ QObject::connect(logNotif, SIGNAL(activated(int)), logRedir, SLOT(inputAvailableOn(int)));
+
+ // redirect stdout and stderr:
+ dup2(logFds[0], 1);
+ dup2(logFds[0], 2);
+ }
+ }
+
+ atexit(unlinkSocket);
+
+ qout << "PVS Privileged Input Daemon initialization complete. Entering main loop." << endl;
+
+ // and run the main loop.
+ int ret = app.exec();
+
+ if(logRedir)
+ {
+ delete logNotif;
+ delete logRedir;
+ }
+
+ PVSCheckPrivileges::deleteInstance();
+
+ qout << "PVS Privileged Input Daemon deinitialization complete. Exiting." << endl;
+
+ return ret;
+}
diff --git a/src/input/rebootSystemHandler.cpp b/src/input/rebootSystemHandler.cpp
new file mode 100644
index 0000000..b5b8f8a
--- /dev/null
+++ b/src/input/rebootSystemHandler.cpp
@@ -0,0 +1,32 @@
+/*
+ # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # --------------------------------------------------------------------------
+ # rebootSystemHandler.cpp
+ # - Handle system reboot requests - implementation
+ # --------------------------------------------------------------------------
+ */
+
+#include <sys/types.h>
+#include <signal.h>
+#include <cerrno>
+#include <QtDebug>
+#include "rebootSystemHandler.h"
+
+using namespace std;
+
+void RebootLinuxSystemHandler::handle(InputEvent const& evt, InputEventContext const* ctx)
+{
+ // Rebooting a linux system is particulary easy:
+ if(kill(1, SIGINT) < 0)
+ {
+ qWarning("Could not kill /sbin/init: %s", strerror(errno));
+ }
+}
diff --git a/src/input/rebootSystemHandler.h b/src/input/rebootSystemHandler.h
new file mode 100644
index 0000000..34fa8ae
--- /dev/null
+++ b/src/input/rebootSystemHandler.h
@@ -0,0 +1,34 @@
+/*
+ # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # --------------------------------------------------------------------------
+ # rebootSystemHandler.h
+ # - Handle system reboot requests - interface
+ # --------------------------------------------------------------------------
+ */
+
+#ifndef REBOOTSYSTEMHANDLER_H_
+#define REBOOTSYSTEMHANDLER_H_
+
+#include <QCoreApplication>
+#include "inputEventHandler.h"
+
+class RebootLinuxSystemHandler : public DefaultInputEventHandler<InputEvent::ET_SPECIAL, InputEvent::EC_REBOOT>
+{
+public:
+ void handle(InputEvent const&, InputEventContext const*);
+
+ static void describeInto(QList<SpecialInputEventDescription>& list)
+ {
+ list << SpecialInputEventDescription(tr(QT_TRANSLATE_NOOP("InputEventHandler", "Reboot")), InputEvent::ET_SPECIAL, InputEvent::EC_REBOOT);
+ }
+};
+
+#endif /* REBOOTSYSTEMHANDLER_H_ */
diff --git a/src/input/sayHelloHandler.cpp b/src/input/sayHelloHandler.cpp
new file mode 100644
index 0000000..fc6f668
--- /dev/null
+++ b/src/input/sayHelloHandler.cpp
@@ -0,0 +1,25 @@
+/*
+ # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # --------------------------------------------------------------------------
+ # rebootSystemHandler.h
+ # - Handle "say hello" requests - interface
+ # --------------------------------------------------------------------------
+ */
+
+#include "sayHelloHandler.h"
+#include <iostream>
+
+using namespace std;
+
+void SayHelloHandler::handle(InputEvent const&, InputEventContext const* ctx)
+{
+ cerr << "I'm right here! You sent this message from pid " << ctx->getSenderPid() << " as user " << ctx->getSenderUid() << " with gid " << ctx->getSenderGid() << endl;
+}
diff --git a/src/input/sayHelloHandler.h b/src/input/sayHelloHandler.h
new file mode 100644
index 0000000..b0d4c7e
--- /dev/null
+++ b/src/input/sayHelloHandler.h
@@ -0,0 +1,34 @@
+/*
+ # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # --------------------------------------------------------------------------
+ # rebootSystemHandler.h
+ # - Handle "say hello" requests - interface
+ # --------------------------------------------------------------------------
+ */
+
+#ifndef SAYHELLOHANDLER_CPP_
+#define SAYHELLOHANDLER_CPP_
+
+#include <QCoreApplication>
+#include "inputEventHandler.h"
+
+class SayHelloHandler : public DefaultInputEventHandler<InputEvent::ET_SPECIAL, InputEvent::EC_SAY_HELLO>
+{
+public:
+ void handle(InputEvent const&, InputEventContext const*);
+
+ static void describeInto(QList<SpecialInputEventDescription>& list)
+ {
+ list << SpecialInputEventDescription(tr(QT_TRANSLATE_NOOP("InputEventHandler", "Say Hello")), InputEvent::ET_SPECIAL, InputEvent::EC_SAY_HELLO);
+ }
+};
+
+#endif /* SAYHELLOHANDLER_CPP_ */
diff --git a/src/input/x11FakeKeyboardHandler.cpp b/src/input/x11FakeKeyboardHandler.cpp
new file mode 100644
index 0000000..0c32b66
--- /dev/null
+++ b/src/input/x11FakeKeyboardHandler.cpp
@@ -0,0 +1,906 @@
+/*
+ # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # --------------------------------------------------------------------------
+ # x11FakeKeyboardHandler.h:
+ # - Handle keyboard events on X11 - interface
+ # --------------------------------------------------------------------------
+ */
+
+#include <map>
+#include <set>
+#include <cassert>
+// Qt headers need to be included before X11 headers
+#include <QApplication>
+#include <QtCore>
+#include "x11FakeKeyboardHandler.h"
+// #include <multimap>
+#include <X11/X.h>
+#include <X11/Xlib.h>
+#include <X11/keysym.h>
+#include <X11/keysymdef.h>
+#include <X11/extensions/XTest.h>
+#ifdef HAVE_XINPUT2_H
+# include <X11/extensions/XInput2.h>
+#else
+# include <X11/extensions/XInput.h>
+#endif
+#include <X11/XKBlib.h>
+#include <src/util/consoleLogger.h>
+#include "x11InputUtils.h"
+
+//////////////////////// INPUT EVENT TRANSLATION /////////////////////////////////
+
+typedef unsigned char xmodifier_type;
+
+char modifiernames[][8] = {
+ "SHIFT",
+ "LOCK",
+ "CONTROL",
+ "MOD1",
+ "MOD2",
+ "MOD3",
+ "MOD4",
+ "MOD5"
+};
+
+QString modifiers_to_string(xmodifier_type mods)
+{
+ QString s;
+ for(int i = 0; i < 8; i++)
+ {
+ if(mods & (1<<i))
+ {
+ s += modifiernames[i];
+ s += ", ";
+ }
+ }
+ s.chop(2);
+ return s;
+}
+
+
+typedef std::map<quint32, KeySym> lookup_table_type;
+typedef lookup_table_type::const_iterator lookup_table_const_iterator;
+typedef lookup_table_type::iterator lookup_table_iterator;
+lookup_table_type keysyms;
+void initialize_keysyms() {
+ keysyms[Qt::Key_Escape] = XK_Escape;
+ keysyms[Qt::Key_Tab] = XK_Tab;
+// keysyms[Qt::Key_Backtab] = XK_Backtab;
+ keysyms[Qt::Key_Backspace] = XK_BackSpace;
+ keysyms[Qt::Key_Return] = XK_Return;
+ keysyms[Qt::Key_Enter] = XK_KP_Enter;
+ keysyms[Qt::Key_Insert] = XK_Insert;
+ keysyms[Qt::Key_Delete] = XK_Delete;
+ keysyms[Qt::Key_Pause] = XK_Pause;
+ keysyms[Qt::Key_Print] = XK_Print;
+ keysyms[Qt::Key_SysReq] = XK_Sys_Req;
+ keysyms[Qt::Key_Clear] = XK_Clear;
+ keysyms[Qt::Key_Home] = XK_Home;
+ keysyms[Qt::Key_End] = XK_End;
+ keysyms[Qt::Key_Left] = XK_Left;
+ keysyms[Qt::Key_Up] = XK_Up;
+ keysyms[Qt::Key_Right] = XK_Right;
+ keysyms[Qt::Key_Down] = XK_Down;
+ keysyms[Qt::Key_PageUp] = XK_Page_Up;
+ keysyms[Qt::Key_PageDown] = XK_Page_Down;
+ keysyms[Qt::Key_Shift] = XK_Shift_L;
+ keysyms[Qt::Key_Control] = XK_Control_L;
+ keysyms[Qt::Key_Meta] = XK_Meta_L;
+ keysyms[Qt::Key_Alt] = XK_Alt_L;
+ keysyms[Qt::Key_CapsLock] = XK_Caps_Lock;
+ keysyms[Qt::Key_NumLock] = XK_Num_Lock;
+ keysyms[Qt::Key_ScrollLock] = XK_Scroll_Lock;
+ keysyms[Qt::Key_F1] = XK_F1;
+ keysyms[Qt::Key_F2] = XK_F2;
+ keysyms[Qt::Key_F3] = XK_F3;
+ keysyms[Qt::Key_F4] = XK_F4;
+ keysyms[Qt::Key_F5] = XK_F5;
+ keysyms[Qt::Key_F6] = XK_F6;
+ keysyms[Qt::Key_F7] = XK_F7;
+ keysyms[Qt::Key_F8] = XK_F8;
+ keysyms[Qt::Key_F9] = XK_F9;
+ keysyms[Qt::Key_F10] = XK_F10;
+ keysyms[Qt::Key_F11] = XK_F11;
+ keysyms[Qt::Key_F12] = XK_F12;
+ keysyms[Qt::Key_F13] = XK_F13;
+ keysyms[Qt::Key_F14] = XK_F14;
+ keysyms[Qt::Key_F15] = XK_F15;
+ keysyms[Qt::Key_F16] = XK_F16;
+ keysyms[Qt::Key_F17] = XK_F17;
+ keysyms[Qt::Key_F18] = XK_F18;
+ keysyms[Qt::Key_F19] = XK_F19;
+ keysyms[Qt::Key_F20] = XK_F20;
+ keysyms[Qt::Key_F21] = XK_F21;
+ keysyms[Qt::Key_F22] = XK_F22;
+ keysyms[Qt::Key_F23] = XK_F23;
+ keysyms[Qt::Key_F24] = XK_F24;
+ keysyms[Qt::Key_F25] = XK_F25;
+ keysyms[Qt::Key_F26] = XK_F26;
+ keysyms[Qt::Key_F27] = XK_F27;
+ keysyms[Qt::Key_F28] = XK_F28;
+ keysyms[Qt::Key_F29] = XK_F29;
+ keysyms[Qt::Key_F30] = XK_F30;
+ keysyms[Qt::Key_F31] = XK_F31;
+ keysyms[Qt::Key_F32] = XK_F32;
+ keysyms[Qt::Key_F33] = XK_F33;
+ keysyms[Qt::Key_F34] = XK_F34;
+ keysyms[Qt::Key_F35] = XK_F35;
+ keysyms[Qt::Key_Super_L] = XK_Super_L;
+ keysyms[Qt::Key_Super_R] = XK_Super_R;
+ keysyms[Qt::Key_Menu] = XK_Menu;
+ keysyms[Qt::Key_Hyper_L] = XK_Hyper_L;
+ keysyms[Qt::Key_Hyper_R] = XK_Hyper_R;
+ keysyms[Qt::Key_Help] = XK_Help;
+// keysyms[Qt::Key_Direction_L] = XK_Direction_L;
+// keysyms[Qt::Key_Direction_R] = XK_Direction_R;
+ keysyms[Qt::Key_Space] = XK_space;
+ keysyms[Qt::Key_Exclam] = XK_exclam;
+ keysyms[Qt::Key_QuoteDbl] = XK_quotedbl;
+ keysyms[Qt::Key_NumberSign] = XK_numbersign;
+ keysyms[Qt::Key_Dollar] = XK_dollar;
+ keysyms[Qt::Key_Percent] = XK_percent;
+ keysyms[Qt::Key_Ampersand] = XK_ampersand;
+ keysyms[Qt::Key_Apostrophe] = XK_apostrophe;
+ keysyms[Qt::Key_ParenLeft] = XK_parenleft;
+ keysyms[Qt::Key_ParenRight] = XK_parenright;
+ keysyms[Qt::Key_Asterisk] = XK_asterisk;
+ keysyms[Qt::Key_Plus] = XK_plus;
+ keysyms[Qt::Key_Comma] = XK_comma;
+ keysyms[Qt::Key_Minus] = XK_minus;
+ keysyms[Qt::Key_Period] = XK_period;
+ keysyms[Qt::Key_Slash] = XK_slash;
+ keysyms[Qt::Key_0] = XK_0;
+ keysyms[Qt::Key_1] = XK_1;
+ keysyms[Qt::Key_2] = XK_2;
+ keysyms[Qt::Key_3] = XK_3;
+ keysyms[Qt::Key_4] = XK_4;
+ keysyms[Qt::Key_5] = XK_5;
+ keysyms[Qt::Key_6] = XK_6;
+ keysyms[Qt::Key_7] = XK_7;
+ keysyms[Qt::Key_8] = XK_8;
+ keysyms[Qt::Key_9] = XK_9;
+ keysyms[Qt::Key_Colon] = XK_colon;
+ keysyms[Qt::Key_Semicolon] = XK_semicolon;
+ keysyms[Qt::Key_Less] = XK_less;
+ keysyms[Qt::Key_Equal] = XK_equal;
+ keysyms[Qt::Key_Greater] = XK_greater;
+ keysyms[Qt::Key_Question] = XK_question;
+ keysyms[Qt::Key_At] = XK_at;
+ keysyms[Qt::Key_A] = XK_A;
+ keysyms[Qt::Key_B] = XK_B;
+ keysyms[Qt::Key_C] = XK_C;
+ keysyms[Qt::Key_D] = XK_D;
+ keysyms[Qt::Key_E] = XK_E;
+ keysyms[Qt::Key_F] = XK_F;
+ keysyms[Qt::Key_G] = XK_G;
+ keysyms[Qt::Key_H] = XK_H;
+ keysyms[Qt::Key_I] = XK_I;
+ keysyms[Qt::Key_J] = XK_J;
+ keysyms[Qt::Key_K] = XK_K;
+ keysyms[Qt::Key_L] = XK_L;
+ keysyms[Qt::Key_M] = XK_M;
+ keysyms[Qt::Key_N] = XK_N;
+ keysyms[Qt::Key_O] = XK_O;
+ keysyms[Qt::Key_P] = XK_P;
+ keysyms[Qt::Key_Q] = XK_Q;
+ keysyms[Qt::Key_R] = XK_R;
+ keysyms[Qt::Key_S] = XK_S;
+ keysyms[Qt::Key_T] = XK_T;
+ keysyms[Qt::Key_U] = XK_U;
+ keysyms[Qt::Key_V] = XK_V;
+ keysyms[Qt::Key_W] = XK_W;
+ keysyms[Qt::Key_X] = XK_X;
+ keysyms[Qt::Key_Y] = XK_Y;
+ keysyms[Qt::Key_Z] = XK_Z;
+ keysyms[Qt::Key_BracketLeft] = XK_bracketleft;
+ keysyms[Qt::Key_Backslash] = XK_backslash;
+ keysyms[Qt::Key_BracketRight] = XK_bracketright;
+ keysyms[Qt::Key_AsciiCircum] = XK_asciicircum;
+ keysyms[Qt::Key_Underscore] = XK_underscore;
+ keysyms[Qt::Key_QuoteLeft] = XK_quoteleft;
+ keysyms[Qt::Key_BraceLeft] = XK_braceleft;
+ keysyms[Qt::Key_Bar] = XK_bar;
+ keysyms[Qt::Key_BraceRight] = XK_braceright;
+ keysyms[Qt::Key_AsciiTilde] = XK_asciitilde;
+ keysyms[Qt::Key_nobreakspace] = XK_nobreakspace;
+ keysyms[Qt::Key_exclamdown] = XK_exclamdown;
+ keysyms[Qt::Key_cent] = XK_cent;
+ keysyms[Qt::Key_sterling] = XK_sterling;
+ keysyms[Qt::Key_currency] = XK_currency;
+ keysyms[Qt::Key_yen] = XK_yen;
+ keysyms[Qt::Key_brokenbar] = XK_brokenbar;
+ keysyms[Qt::Key_section] = XK_section;
+ keysyms[Qt::Key_diaeresis] = XK_diaeresis;
+ keysyms[Qt::Key_copyright] = XK_copyright;
+ keysyms[Qt::Key_ordfeminine] = XK_ordfeminine;
+ keysyms[Qt::Key_guillemotleft] = XK_guillemotleft;
+ keysyms[Qt::Key_notsign] = XK_notsign;
+ keysyms[Qt::Key_hyphen] = XK_hyphen;
+ keysyms[Qt::Key_registered] = XK_registered;
+ keysyms[Qt::Key_macron] = XK_macron;
+ keysyms[Qt::Key_degree] = XK_degree;
+ keysyms[Qt::Key_plusminus] = XK_plusminus;
+ keysyms[Qt::Key_twosuperior] = XK_twosuperior;
+ keysyms[Qt::Key_threesuperior] = XK_threesuperior;
+ keysyms[Qt::Key_acute] = XK_acute;
+ keysyms[Qt::Key_mu] = XK_mu;
+ keysyms[Qt::Key_paragraph] = XK_paragraph;
+ keysyms[Qt::Key_periodcentered] = XK_periodcentered;
+ keysyms[Qt::Key_cedilla] = XK_cedilla;
+ keysyms[Qt::Key_onesuperior] = XK_onesuperior;
+ keysyms[Qt::Key_masculine] = XK_masculine;
+ keysyms[Qt::Key_guillemotright] = XK_guillemotright;
+ keysyms[Qt::Key_onequarter] = XK_onequarter;
+ keysyms[Qt::Key_onehalf] = XK_onehalf;
+ keysyms[Qt::Key_threequarters] = XK_threequarters;
+ keysyms[Qt::Key_questiondown] = XK_questiondown;
+ keysyms[Qt::Key_Agrave] = XK_Agrave;
+ keysyms[Qt::Key_Aacute] = XK_Aacute;
+ keysyms[Qt::Key_Acircumflex] = XK_Acircumflex;
+ keysyms[Qt::Key_Atilde] = XK_Atilde;
+ keysyms[Qt::Key_Adiaeresis] = XK_Adiaeresis;
+ keysyms[Qt::Key_Aring] = XK_Aring;
+ keysyms[Qt::Key_AE] = XK_AE;
+ keysyms[Qt::Key_Ccedilla] = XK_Ccedilla;
+ keysyms[Qt::Key_Egrave] = XK_Egrave;
+ keysyms[Qt::Key_Eacute] = XK_Eacute;
+ keysyms[Qt::Key_Ecircumflex] = XK_Ecircumflex;
+ keysyms[Qt::Key_Ediaeresis] = XK_Ediaeresis;
+ keysyms[Qt::Key_Igrave] = XK_Igrave;
+ keysyms[Qt::Key_Iacute] = XK_Iacute;
+ keysyms[Qt::Key_Icircumflex] = XK_Icircumflex;
+ keysyms[Qt::Key_Idiaeresis] = XK_Idiaeresis;
+ keysyms[Qt::Key_ETH] = XK_ETH;
+ keysyms[Qt::Key_Ntilde] = XK_Ntilde;
+ keysyms[Qt::Key_Ograve] = XK_Ograve;
+ keysyms[Qt::Key_Oacute] = XK_Oacute;
+ keysyms[Qt::Key_Ocircumflex] = XK_Ocircumflex;
+ keysyms[Qt::Key_Otilde] = XK_Otilde;
+ keysyms[Qt::Key_Odiaeresis] = XK_Odiaeresis;
+ keysyms[Qt::Key_multiply] = XK_multiply;
+ keysyms[Qt::Key_Ooblique] = XK_Ooblique;
+ keysyms[Qt::Key_Ugrave] = XK_Ugrave;
+ keysyms[Qt::Key_Uacute] = XK_Uacute;
+ keysyms[Qt::Key_Ucircumflex] = XK_Ucircumflex;
+ keysyms[Qt::Key_Udiaeresis] = XK_Udiaeresis;
+ keysyms[Qt::Key_Yacute] = XK_Yacute;
+ keysyms[Qt::Key_THORN] = XK_THORN;
+ keysyms[Qt::Key_ssharp] = XK_ssharp;
+ keysyms[Qt::Key_Agrave] = XK_Agrave;
+ keysyms[Qt::Key_Aacute] = XK_Aacute;
+ keysyms[Qt::Key_Acircumflex] = XK_Acircumflex;
+ keysyms[Qt::Key_Atilde] = XK_Atilde;
+ keysyms[Qt::Key_Adiaeresis] = XK_Adiaeresis;
+ keysyms[Qt::Key_Aring] = XK_Aring;
+ keysyms[Qt::Key_AE] = XK_AE;
+ keysyms[Qt::Key_Ccedilla] = XK_Ccedilla;
+ keysyms[Qt::Key_Egrave] = XK_Egrave;
+ keysyms[Qt::Key_Eacute] = XK_Eacute;
+ keysyms[Qt::Key_Ecircumflex] = XK_Ecircumflex;
+ keysyms[Qt::Key_Ediaeresis] = XK_Ediaeresis;
+ keysyms[Qt::Key_Igrave] = XK_Igrave;
+ keysyms[Qt::Key_Iacute] = XK_Iacute;
+ keysyms[Qt::Key_Icircumflex] = XK_Icircumflex;
+ keysyms[Qt::Key_Idiaeresis] = XK_Idiaeresis;
+ keysyms[Qt::Key_ETH] = XK_ETH;
+ keysyms[Qt::Key_Ntilde] = XK_Ntilde;
+ keysyms[Qt::Key_Ograve] = XK_Ograve;
+ keysyms[Qt::Key_Oacute] = XK_Oacute;
+ keysyms[Qt::Key_Ocircumflex] = XK_Ocircumflex;
+ keysyms[Qt::Key_Otilde] = XK_Otilde;
+ keysyms[Qt::Key_Odiaeresis] = XK_Odiaeresis;
+ keysyms[Qt::Key_division] = XK_division;
+ keysyms[Qt::Key_Ooblique] = XK_Ooblique;
+ keysyms[Qt::Key_Ugrave] = XK_Ugrave;
+ keysyms[Qt::Key_Uacute] = XK_Uacute;
+ keysyms[Qt::Key_Ucircumflex] = XK_Ucircumflex;
+ keysyms[Qt::Key_Udiaeresis] = XK_Udiaeresis;
+ keysyms[Qt::Key_Yacute] = XK_Yacute;
+ keysyms[Qt::Key_THORN] = XK_THORN;
+ keysyms[Qt::Key_ydiaeresis] = XK_ydiaeresis;
+ keysyms[Qt::Key_AltGr] = XK_ISO_Level3_Shift;
+ keysyms[Qt::Key_Multi_key] = XK_Multi_key;
+ keysyms[Qt::Key_Codeinput] = XK_Codeinput;
+ keysyms[Qt::Key_SingleCandidate] = XK_SingleCandidate;
+ keysyms[Qt::Key_MultipleCandidate] = XK_MultipleCandidate;
+ keysyms[Qt::Key_PreviousCandidate] = XK_PreviousCandidate;
+ keysyms[Qt::Key_Mode_switch] = XK_Mode_switch;
+// keysyms[Qt::Key_script_switch] = XK_script_switch;
+ keysyms[Qt::Key_Kanji] = XK_Kanji;
+ keysyms[Qt::Key_Muhenkan] = XK_Muhenkan;
+// keysyms[Qt::Key_Henkan_Mode] = XK_Henkan_Mode;
+ keysyms[Qt::Key_Henkan] = XK_Henkan;
+ keysyms[Qt::Key_Romaji] = XK_Romaji;
+ keysyms[Qt::Key_Hiragana] = XK_Hiragana;
+ keysyms[Qt::Key_Katakana] = XK_Katakana;
+ keysyms[Qt::Key_Hiragana_Katakana] = XK_Hiragana_Katakana;
+ keysyms[Qt::Key_Zenkaku] = XK_Zenkaku;
+ keysyms[Qt::Key_Hankaku] = XK_Hankaku;
+ keysyms[Qt::Key_Zenkaku_Hankaku] = XK_Zenkaku_Hankaku;
+ keysyms[Qt::Key_Touroku] = XK_Touroku;
+ keysyms[Qt::Key_Massyo] = XK_Massyo;
+ keysyms[Qt::Key_Kana_Lock] = XK_Kana_Lock;
+ keysyms[Qt::Key_Kana_Shift] = XK_Kana_Shift;
+ keysyms[Qt::Key_Eisu_Shift] = XK_Eisu_Shift;
+ keysyms[Qt::Key_Eisu_toggle] = XK_Eisu_toggle;
+// keysyms[Qt::Key_Kanji_Bangou] = XK_Kanji_Bangou;
+// keysyms[Qt::Key_Zen_Koho] = XK_Zen_Koho;
+// keysyms[Qt::Key_Mae_Koho] = XK_Mae_Koho;
+ keysyms[Qt::Key_Hangul] = XK_Hangul;
+ keysyms[Qt::Key_Hangul_Hanja] = XK_Hangul_Hanja;
+ keysyms[Qt::Key_Hangul] = XK_Hangul;
+ keysyms[Qt::Key_Hangul_Start] = XK_Hangul_Start;
+ keysyms[Qt::Key_Hangul_End] = XK_Hangul_End;
+ keysyms[Qt::Key_Hangul_Hanja] = XK_Hangul_Hanja;
+ keysyms[Qt::Key_Hangul_Jamo] = XK_Hangul_Jamo;
+ keysyms[Qt::Key_Hangul_Romaja] = XK_Hangul_Romaja;
+// keysyms[Qt::Key_Hangul_Codeinput] = XK_Hangul_Codeinput;
+ keysyms[Qt::Key_Hangul_Jeonja] = XK_Hangul_Jeonja;
+ keysyms[Qt::Key_Hangul_Banja] = XK_Hangul_Banja;
+ keysyms[Qt::Key_Hangul_PreHanja] = XK_Hangul_PreHanja;
+ keysyms[Qt::Key_Hangul_PostHanja] = XK_Hangul_PostHanja;
+// keysyms[Qt::Key_Hangul_SingleCandidate] = XK_Hangul_SingleCandidate;
+// keysyms[Qt::Key_Hangul_MultipleCandidate] = XK_Hangul_MultipleCandidate;
+// keysyms[Qt::Key_Hangul_PreviousCandidate] = XK_Hangul_PreviousCandidate;
+ keysyms[Qt::Key_Hangul_Special] = XK_Hangul_Special;
+// keysyms[Qt::Key_Hangul_switch] = XK_Hangul_switch;
+ keysyms[Qt::Key_Dead_Grave] = XK_dead_grave;
+ keysyms[Qt::Key_Dead_Acute] = XK_dead_acute;
+ keysyms[Qt::Key_Dead_Circumflex] = XK_dead_circumflex;
+ keysyms[Qt::Key_Dead_Tilde] = XK_dead_tilde;
+ keysyms[Qt::Key_Dead_Macron] = XK_dead_macron;
+ keysyms[Qt::Key_Dead_Breve] = XK_dead_breve;
+ keysyms[Qt::Key_Dead_Abovedot] = XK_dead_abovedot;
+ keysyms[Qt::Key_Dead_Diaeresis] = XK_dead_diaeresis;
+ keysyms[Qt::Key_Dead_Abovering] = XK_dead_abovering;
+ keysyms[Qt::Key_Dead_Doubleacute] = XK_dead_doubleacute;
+ keysyms[Qt::Key_Dead_Caron] = XK_dead_caron;
+ keysyms[Qt::Key_Dead_Cedilla] = XK_dead_cedilla;
+ keysyms[Qt::Key_Dead_Ogonek] = XK_dead_ogonek;
+ keysyms[Qt::Key_Dead_Iota] = XK_dead_iota;
+ keysyms[Qt::Key_Dead_Voiced_Sound] = XK_dead_voiced_sound;
+ keysyms[Qt::Key_Dead_Semivoiced_Sound] = XK_dead_semivoiced_sound;
+ keysyms[Qt::Key_Dead_Belowdot] = XK_dead_belowdot;
+ keysyms[Qt::Key_Dead_Hook] = XK_dead_hook;
+ keysyms[Qt::Key_Dead_Horn] = XK_dead_horn;
+// keysyms[Qt::Key_Back] = XK_Back;
+// keysyms[Qt::Key_Forward] = XK_Forward;
+// keysyms[Qt::Key_Stop] = XK_Stop;
+// keysyms[Qt::Key_Refresh] = XK_Refresh;
+// keysyms[Qt::Key_VolumeDown] = XK_VolumeDown;
+// keysyms[Qt::Key_VolumeMute] = XK_VolumeMute;
+// keysyms[Qt::Key_VolumeUp] = XK_VolumeUp;
+// keysyms[Qt::Key_BassBoost] = XK_BassBoost;
+// keysyms[Qt::Key_BassUp] = XK_BassUp;
+// keysyms[Qt::Key_BassDown] = XK_BassDown;
+// keysyms[Qt::Key_TrebleUp] = XK_TrebleUp;
+// keysyms[Qt::Key_TrebleDown] = XK_TrebleDown;
+// keysyms[Qt::Key_MediaPlay] = XK_MediaPlay;
+// keysyms[Qt::Key_MediaStop] = XK_MediaStop;
+// keysyms[Qt::Key_MediaPrevious] = XK_MediaPrevious;
+// keysyms[Qt::Key_MediaNext] = XK_MediaNext;
+// keysyms[Qt::Key_MediaRecord] = XK_MediaRecord;
+// keysyms[Qt::Key_HomePage] = XK_HomePage;
+// keysyms[Qt::Key_Favorites] = XK_Favorites;
+// keysyms[Qt::Key_Search] = XK_Search;
+// keysyms[Qt::Key_Standby] = XK_Standby;
+// keysyms[Qt::Key_OpenUrl] = XK_OpenUrl;
+// keysyms[Qt::Key_LaunchMail] = XK_LaunchMail;
+// keysyms[Qt::Key_LaunchMedia] = XK_LaunchMedia;
+// keysyms[Qt::Key_Launch0] = XK_Launch0;
+// keysyms[Qt::Key_Launch1] = XK_Launch1;
+// keysyms[Qt::Key_Launch2] = XK_Launch2;
+// keysyms[Qt::Key_Launch3] = XK_Launch3;
+// keysyms[Qt::Key_Launch4] = XK_Launch4;
+// keysyms[Qt::Key_Launch5] = XK_Launch5;
+// keysyms[Qt::Key_Launch6] = XK_Launch6;
+// keysyms[Qt::Key_Launch7] = XK_Launch7;
+// keysyms[Qt::Key_Launch8] = XK_Launch8;
+// keysyms[Qt::Key_Launch9] = XK_Launch9;
+// keysyms[Qt::Key_LaunchA] = XK_LaunchA;
+// keysyms[Qt::Key_LaunchB] = XK_LaunchB;
+// keysyms[Qt::Key_LaunchC] = XK_LaunchC;
+// keysyms[Qt::Key_LaunchD] = XK_LaunchD;
+// keysyms[Qt::Key_LaunchE] = XK_LaunchE;
+// keysyms[Qt::Key_LaunchF] = XK_LaunchF;
+// keysyms[Qt::Key_Display] = XK_Display;
+// keysyms[Qt::Key_MediaLast] = XK_MediaLast;
+ keysyms[Qt::Key_Select] = XK_Select;
+// keysyms[Qt::Key_Yes] = XK_Yes;
+// keysyms[Qt::Key_No] = XK_No;
+ keysyms[Qt::Key_Cancel] = XK_Cancel;
+// keysyms[Qt::Key_Printer] = XK_Printer;
+ keysyms[Qt::Key_Execute] = XK_Execute;
+// keysyms[Qt::Key_Sleep] = XK_Sleep;
+// keysyms[Qt::Key_MediaPlay] = XK_MediaPlay;
+// keysyms[Qt::Key_Zoom] = XK_Zoom;
+// keysyms[Qt::Key_Jisho] = XK_Jisho;
+// keysyms[Qt::Key_Oyayubi_Left] = XK_Oyayubi_Left;
+// keysyms[Qt::Key_Oyayubi_Right] = XK_Oyayubi_Right;
+// keysyms[Qt::Key_Context1] = XK_Context1;
+// keysyms[Qt::Key_Context2] = XK_Context2;
+// keysyms[Qt::Key_Context3] = XK_Context3;
+// keysyms[Qt::Key_Context4] = XK_Context4;
+// keysyms[Qt::Key_Call] = XK_Call;
+// keysyms[Qt::Key_Hangup] = XK_Hangup;
+// keysyms[Qt::Key_Flip] = XK_Flip;
+ keysyms[Qt::Key_unknown] = XK_VoidSymbol;
+}
+
+/* Store Keycodes for "normal" Latin1 keys: */
+int basic_keycodes[0x100];
+int basic_modifiers[0x100];
+
+int modifier_keycodes[8];
+Qt::KeyboardModifier modifier_meaning[8];
+
+int xmodifier_from_qtmodifier(Qt::KeyboardModifier m) {
+ for(int i = 0; i < 8; i++) {
+ if(m == modifier_meaning[i])
+ return i;
+ }
+ return -1;
+}
+
+typedef std::map<Qt::KeyboardModifier, xmodifier_type> qt_to_xmodifier_type;
+typedef std::map<xmodifier_type, Qt::KeyboardModifier> x_to_qtmodifier_type;
+qt_to_xmodifier_type qt_to_xmodifier;
+x_to_qtmodifier_type x_to_qtmodifier;
+
+typedef std::multimap<xmodifier_type, int> modifier_to_keycode_map;
+modifier_to_keycode_map modifier_to_keycode;
+typedef std::map<int, xmodifier_type> keycode_to_modifier_map;
+keycode_to_modifier_map keycode_to_modifier;
+
+// We need to query the input devices, preferrable through XInput2, but
+// if that is not available we will contend ourselves with XInput1:
+#ifndef HAVE_XINPUT2_H
+# define XIAllDevices 1 /* does not matter */
+# define XIDeviceInfo XDeviceInfo
+# define XIQueryDevice(dpy, which, ninfos) XListInputDevices(dpy, ninfos)
+# define XIFreeDeviceInfo(infos) XFreeDeviceList(infos)
+# define XIMasterKeyboard IsXKeyboard
+# define XISlaveKeyboard IsXExtensionKeyboard
+ static inline Atom* getDeviceProperties(Display* dpy, XIDeviceInfo* devinfo, int* nprops)
+ {
+ if(devinfo->use == IsXKeyboard)
+ {
+ // According to XOpenDevice(3X11) you cannot open the Core Keyboard.
+ *nprops = 0;
+ return 0;
+ }
+
+ XDevice* dev = XOpenDevice(dpy, devinfo->id);
+ Atom* props = XListDeviceProperties(dpy, dev, nprops);
+ XCloseDevice(dpy, dev);
+ return props;
+ }
+#else
+ static inline Atom* getDeviceProperties(Display* dpy, XIDeviceInfo* devinfo, int* nprops)
+ {
+ return XIListProperties(dpy, devinfo->deviceid, nprops);
+ }
+#endif
+
+
+void initialize_basic_keycodes()
+{
+ for(int i = 0; i < 8; i++) {
+ modifier_keycodes[i] = -1;
+ }
+ for(int i = 0; i < 0x100; i++) {
+ basic_keycodes[i] = -1;
+ }
+
+ Display* dpy = X11InputUtils::display();
+ int min_keycode, max_keycode;
+ XDisplayKeycodes(dpy, &min_keycode, &max_keycode);
+
+ int xkb_opcode, xkb_event, xkb_error, xkb_major, xkb_minor;
+ bool xkb_present = XkbQueryExtension(dpy, &xkb_opcode, &xkb_event, &xkb_error, &xkb_major, &xkb_minor);
+ if(!xkb_present) {
+ int keysyms_per_code;
+ const int count = max_keycode - min_keycode + 1;
+ KeySym* mapping = XGetKeyboardMapping(dpy, min_keycode, count, &keysyms_per_code);
+ for(int i = 0; i < count; i++)
+ {
+ for(int j = 0; j < keysyms_per_code; j++)
+ {
+ const int idx = i * keysyms_per_code + j;
+ const KeySym ks = mapping[idx];
+ const int keycode = min_keycode + i;
+
+ if(ks >= ' ' && ks < 0x100)
+ {
+ if(ks == XK_at) {
+ ConsoleLog writeLine(QString("Keycode %1 (%2) gives `@' with modifiers `%3', but it is really `%4'").arg(keycode).arg(XKeysymToString(XKeycodeToKeysym(dpy, keycode, 0))).arg(modifiers_to_string(j)).arg(XKeysymToString(XKeycodeToKeysym(dpy, keycode, j))));
+
+ }
+
+ if(basic_keycodes[ks] != -1)
+ continue; // already found
+
+ basic_keycodes[ks] = keycode;
+ basic_modifiers[ks] = j;
+ }
+ }
+ }
+ XFree(mapping);
+ }
+ else
+ {
+ // Try to find out what device XTest will send events on:
+ unsigned int xkbDeviceId = XkbUseCoreKbd;
+ Atom xtestDeviceProp = XInternAtom(dpy, "XTEST Device", false);
+ int ndevinfos;
+ XIDeviceInfo* devinfos = XIQueryDevice(dpy, XIAllDevices, &ndevinfos);
+ if(devinfos)
+ {
+#ifndef HAVE_XINPUT2_H
+# define deviceid id
+#endif
+ for(int i = 0; i < ndevinfos && xkbDeviceId == XkbUseCoreKbd; i++)
+ {
+ XIDeviceInfo* devinfo = devinfos + i;
+ qDebug("Found device %lu of type %d with name %s", (unsigned long)devinfo->deviceid, devinfo->use, devinfo->name);
+
+ // We want it to be a keyboard.
+ if(devinfo->use != XIMasterKeyboard && devinfo->use != XISlaveKeyboard)
+ continue;
+
+ int nprops;
+ Atom* props = getDeviceProperties(dpy, devinfo, &nprops);
+ if(props)
+ {
+ for(int j = 0; j < nprops && xkbDeviceId == XkbUseCoreKbd; j++)
+ {
+ Atom prop = props[j];
+ if(prop == xtestDeviceProp)
+ {
+ // The device is the XTest Keyboard:
+ xkbDeviceId = devinfo->deviceid;
+ }
+ }
+ XFree(props);
+ }
+ }
+ XIFreeDeviceInfo(devinfos);
+#ifdef deviceid /* XInput1 */
+# undef deviceid
+#endif
+ }
+
+ // Okay, we know which device to query. Now get its keymap:
+ XkbDescPtr keybDesc = XkbGetKeyboard(dpy, XkbAllComponentsMask, xkbDeviceId);
+ if(!keybDesc)
+ {
+ qWarning("Unable to retrieve keyboard description for device %d. Falling back to unreliable global mapping", xkbDeviceId);
+ }
+
+ for(int i = min_keycode; i <= max_keycode; i++)
+ {
+ for(int j = 0; j <= 0xff; j++)
+ {
+ KeySym ks = 0;
+ unsigned int unconsumed;
+ if(keybDesc)
+ {
+ if(!XkbTranslateKeyCode(keybDesc, i, j, &unconsumed, &ks) || ks == NoSymbol)
+ continue;
+ }
+ else
+ {
+ if(!XkbLookupKeySym(dpy, i, j, &unconsumed, &ks) || ks == NoSymbol)
+ continue;
+ }
+
+ if(ks && (ks < 0x100))
+ {
+// ConsoleLog writeLine(QString("Keycode %1 (%2) seems to give `%3' with modifiers `%4', of which `%5' are unconsumed")
+// .arg(i)
+// .arg(XKeysymToString(XKeycodeToKeysym(dpy, i, 0)))
+// .arg(XKeysymToString(ks))
+// .arg(modifiers_to_string(j))
+// .arg(modifiers_to_string(unconsumed)));
+ if(basic_keycodes[ks] != -1)
+ continue;
+
+ basic_keycodes[ks] = i;
+ basic_modifiers[ks] = unconsumed & j;
+ }
+ }
+ }
+
+ // Free the keyboard description:
+ if(keybDesc)
+ {
+ XkbFreeKeyboard(keybDesc, XkbAllComponentsMask, true);
+ }
+ }
+
+ // find out which keycodes cause the modifier state bits:
+ XModifierKeymap* modkm;
+ modkm = XGetModifierMapping(dpy);
+
+ const int shift_kc = XKeysymToKeycode(dpy, XK_Shift_L);
+ const int control_kc = XKeysymToKeycode(dpy, XK_Control_L);
+ const int alt_kc = XKeysymToKeycode(dpy, XK_Alt_L);
+ const int meta_kc = XKeysymToKeycode(dpy, XK_Meta_L);
+ const int switch_kc = XKeysymToKeycode(dpy, XK_Mode_switch);
+
+ for(int i = 0; i < 8; i++) {
+ for(int j = 0; j < modkm->max_keypermod; j++) {
+ const int idx = i * modkm->max_keypermod + j;
+ const int kc = modkm->modifiermap[idx];
+
+ modifier_to_keycode.insert(std::make_pair((1<<i), kc));
+ keycode_to_modifier[kc] = (1<<i);
+#define remember_modifier(qmod) do { \
+ qt_to_xmodifier[Qt::qmod##Modifier] = 1<<i; \
+ x_to_qtmodifier[1<<i] = Qt::qmod##Modifier; \
+} while(false)
+ if(kc == shift_kc) {
+ remember_modifier(Shift);
+ } else if(kc == control_kc) {
+ remember_modifier(Control);
+ } else if(kc == alt_kc) {
+ remember_modifier(Alt);
+ } else if(kc == meta_kc) {
+ remember_modifier(Meta);
+ } else if(kc == switch_kc) {
+ remember_modifier(GroupSwitch);
+ }
+#undef remember_modifier
+
+ if(modifier_keycodes[i] != -1) {
+ continue; // already found
+ }
+
+ // select one arbitrarily
+ modifier_keycodes[i] = kc;
+ }
+ }
+
+ XFreeModifiermap(modkm);
+}
+
+xmodifier_type translate_modifiers(quint32 mods)
+{
+ xmodifier_type ret = 0;
+
+ for(int j = 1; j < 8; j++) {
+ xmodifier_type i = 1<<j;
+
+ if(mods & x_to_qtmodifier[i])
+ {
+ ret |= i;
+ }
+ }
+
+ return ret;
+}
+
+typedef std::set<int> int_set_type;
+int_set_type pressed_modifier_keys;
+xmodifier_type current_modifiers;
+
+void trackModifiers(int keycode, bool down)
+{
+ // is this a modifier key?
+ const bool is_modifier = keycode_to_modifier.find(keycode) != keycode_to_modifier.end();
+
+ if(!is_modifier)
+ {
+ return;
+ }
+
+ if(down) {
+ pressed_modifier_keys.insert(keycode);
+ } else {
+ pressed_modifier_keys.erase(keycode);
+ }
+
+ int_set_type::iterator i, end = pressed_modifier_keys.end();
+ xmodifier_type modifs = 0;
+ for(i = pressed_modifier_keys.begin(); i != end; i++)
+ {
+ keycode_to_modifier_map::iterator found_kc = keycode_to_modifier.find(*i);
+ if(found_kc != keycode_to_modifier.end())
+ {
+ modifs |= (*found_kc).second;
+ }
+ }
+ current_modifiers = modifs;
+
+ ConsoleLog writeLine(QString("[trackModifiers] current modifiers: %1").arg(modifiers_to_string(modifs)));
+}
+
+typedef std::pair<int, bool> modifier_tweak_type;
+typedef std::vector<modifier_tweak_type> tweak_sequence_type;
+
+void tweakModifiers(Display* dpy, xmodifier_type neededState, xmodifier_type actualState, tweak_sequence_type& tracker)
+{
+ ConsoleLog writeLine(QString("tweakModifiers: Trying to get to `%1' from `%2'").arg(modifiers_to_string(neededState)).arg(modifiers_to_string(actualState)));
+ for(int j = 0; j < 8; j++)
+ {
+ xmodifier_type i = 1<<j;
+
+ ConsoleLog writeLine(QString("Checking for %1...").arg(modifiers_to_string(i)));
+
+ if((i & neededState) && !(i & actualState))
+ {
+ ConsoleLog writeLine(QString("tweakModifiers: Modifier %1 needs to be pressed").arg(modifiernames[j]));
+
+ // needs to be pressed
+
+ //find the keycode:
+ int kc;
+ modifier_to_keycode_map::iterator iter = modifier_to_keycode.find(i);
+
+ if((iter == modifier_to_keycode.end()) || ((*iter).first != i))
+ {
+ continue; // we don't know a key that triggers this modifier
+ }
+
+ ConsoleLog writeLine(QString(" pressing key %1").arg((*iter).second));
+ XTestFakeKeyEvent(dpy, (*iter).second, 1, CurrentTime);
+
+ tracker.push_back(std::make_pair((*iter).second, true));
+ }
+ else if((!(i & neededState)) && (i & actualState))
+ {
+ ConsoleLog writeLine(QString("tweakModifiers: Modifier %1 needs to be released").arg(modifiernames[j]));
+
+ // needs to be released
+
+ int kc = -1;
+ // first, check whether any of the currently pressed keys has triggered this modifier:
+
+ int_set_type::iterator iter, end = pressed_modifier_keys.end();
+ for(iter = pressed_modifier_keys.begin(); iter != end; iter++)
+ {
+ keycode_to_modifier_map::iterator modmap_iter = keycode_to_modifier.find(*iter);
+ if(modmap_iter != keycode_to_modifier.end()) {
+ if(modmap_iter->second == i) {
+ kc = *iter;
+
+ // release this key:
+ ConsoleLog writeLine(QString(" releasing key %1").arg(kc));
+ XTestFakeKeyEvent(dpy, kc, 0, CurrentTime);
+ tracker.push_back(std::make_pair(kc, false));
+ }
+ }
+ }
+
+ if(kc == -1) {
+ // strange, but we need to release some other key:
+ // we don't know which one, so we abort this and hope for the best
+ continue;
+ }
+ }
+ }
+}
+
+void untweakModifiers(Display* dpy, tweak_sequence_type& tracker)
+{
+ tweak_sequence_type::iterator i, end = tracker.end();
+ for(i = tracker.begin(); i != end; i++) {
+ modifier_tweak_type& t = *i;
+ XTestFakeKeyEvent(dpy, t.first, !t.second, CurrentTime);
+ }
+}
+
+void X11FakeKeyboardHandler::initialize()
+{
+ initialize_keysyms();
+ initialize_basic_keycodes();
+ pressed_modifier_keys.clear();
+ current_modifiers = 0;
+}
+
+void X11FakeKeyboardHandler::handle(InputEvent const& evt, InputEventContext const*)
+{
+ Display* dpy = X11InputUtils::display();
+
+ // find out which keysym caused this event:
+ lookup_table_const_iterator i = keysyms.find(evt.qt_keysym());
+ if(i == keysyms.end()) {
+ // Special cases. We don't know how to directly translate those, so we will try to emulate them.
+ switch(evt.qt_keysym())
+ {
+ case Qt::Key_Backtab:
+ handle(InputEvent(evt.type(), evt.code(), evt.qt_modifiers() | Qt::ShiftModifier | Qt::Key_Tab));
+ break;
+ default:
+ ConsoleLog writeLine(QString("Unknown keysym received: %1").arg(evt.qt_keysym(), 8, 16));
+ }
+ } else {
+ KeySym ks = (*i).second;
+
+ // is it a "normal" key?
+ if(ks >= ' ' && ks < 0x100)
+ {
+ if(basic_keycodes[ks] != -1)
+ {
+ if(evt.isPress())
+ {
+ // Try the simple way first:
+ // Does the keycode with current modifiers yield the requested symbol?
+ KeySym unmod_ks = XKeycodeToKeysym(dpy, basic_keycodes[ks], current_modifiers);
+ ConsoleLog writeLine(QString("Pressing the key for %1 would yield %2 right now.").arg(XKeysymToString(ks)).arg(XKeysymToString(unmod_ks)));
+
+ if(ks == XKeycodeToKeysym(dpy, basic_keycodes[ks], current_modifiers))
+ {
+ XTestFakeKeyEvent(dpy, basic_keycodes[ks], 1, CurrentTime);
+ }
+ else
+ {
+ // what modifier keys do we need to press?
+ xmodifier_type mods = translate_modifiers(evt.qt_modifiers());
+
+ // we may need to press additional modifiers to generate this keysym:
+ if(QChar(ks, 0).isLetter())
+ {
+ // but, since we do not differentiate upper and lower case,
+ // and instead let the sender handle those modifiers,
+ // we need to AND ShiftModifier and LockModifier out.
+ mods |= basic_modifiers[ks] & ~(1<<ShiftMapIndex) & ~(1<<LockMapIndex);
+ }
+ else
+ {
+ // Not an alpha character. We do need to respect Shift and Lock for those:
+ mods |= basic_modifiers[ks];
+ }
+
+ // now, tweak the modifiers
+ tweak_sequence_type tweaks;
+ tweakModifiers(dpy, mods, current_modifiers, tweaks);
+
+ // press the key:
+ XTestFakeKeyEvent(dpy, basic_keycodes[ks], 1, CurrentTime);
+
+ // and release the modifiers:
+ untweakModifiers(dpy, tweaks);
+ }
+ }
+ else
+ {
+ // just release the key.
+ XTestFakeKeyEvent(dpy, basic_keycodes[ks], 0, CurrentTime);
+ }
+ }
+ else
+ {
+ ConsoleLog writeLine(QString("No keycode is mapped to `%1'").arg(XKeysymToString(ks)));
+ }
+ }
+ else
+ {
+ // It is some kind of "special" key, so we just fake that, then:
+ KeyCode kc = XKeysymToKeycode(dpy, ks);
+ if(kc != 0)
+ {
+ ConsoleLog writeLine(QString("%1 special keycode %2, hopefully giving keysym %3").arg(evt.isPress() ? "Pressed" : "Released").arg(kc).arg(XKeysymToString(ks)));
+ XTestFakeKeyEvent(dpy, kc, evt.isPress(), CurrentTime);
+ // and track it if it is a modifier key:
+ trackModifiers(kc, evt.isPress());
+ }
+ else
+ {
+ ConsoleLog writeLine(QString("No keycode is mapped to %1").arg(XKeysymToString(ks)));
+ }
+ }
+ }
+
+ // Since there may not be a mainloop running, we need to manually flush the event queue
+ XFlush(dpy);
+}
diff --git a/src/input/x11FakeKeyboardHandler.h b/src/input/x11FakeKeyboardHandler.h
new file mode 100644
index 0000000..3dde7cc
--- /dev/null
+++ b/src/input/x11FakeKeyboardHandler.h
@@ -0,0 +1,29 @@
+/*
+ # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # --------------------------------------------------------------------------
+ # x11FakeKeyboardHandler.h:
+ # - Handle keyboard events on X11 - interface
+ # --------------------------------------------------------------------------
+ */
+
+#ifndef X11FAKEKEYBOARDHANDLER_H_
+#define X11FAKEKEYBOARDHANDLER_H_
+
+#include "inputEventHandler.h"
+
+class X11FakeKeyboardHandler : public DefaultInputEventHandler<InputEvent::ET_KEY>
+{
+public:
+ void handle(InputEvent const&, InputEventContext const* = 0);
+ void initialize();
+};
+
+#endif /* X11FAKEKEYBOARDHANDLER_H_ */
diff --git a/src/input/x11FakeMouseHandler.cpp b/src/input/x11FakeMouseHandler.cpp
new file mode 100644
index 0000000..58415d5
--- /dev/null
+++ b/src/input/x11FakeMouseHandler.cpp
@@ -0,0 +1,59 @@
+/*
+ # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # --------------------------------------------------------------------------
+ # x11FakeMouseHandler.h:
+ # - Handle mouse events on X11 - implementation
+ # --------------------------------------------------------------------------
+ */
+
+#include "x11FakeMouseHandler.h" // need to include before X headers
+#include <src/util/consoleLogger.h>
+#include <X11/extensions/XTest.h>
+#include "x11InputUtils.h"
+
+void X11FakeMouseButtonHandler::handle(InputEvent const& evt, InputEventContext const*)
+{
+ quint16 pressedButton = evt.pressedButton();
+
+ Display* dpy = X11InputUtils::display();
+
+ XTestGrabControl(dpy, 1);
+ for(int i = 0; i < 16; i++) {
+ if((1<<i) == pressedButton)
+ {
+ ConsoleLog writeLine(QString("Got mouse button event: button %1 %2").arg(i + 1).arg(evt.isPress() ? "pressed" : "released"));
+ if(!XTestFakeButtonEvent(dpy, i + 1, evt.isPress(), CurrentTime))
+ {
+ ConsoleLog writeLine("[ERROR] XTestFakeButtonEvent failed");
+ }
+ }
+ }
+ XTestGrabControl(dpy, 0);
+
+ // Since there may not be a mainloop running, we need to manually flush the event queue
+ XFlush(dpy);
+}
+
+void X11FakeMouseMovementHandler::handle(InputEvent const& evt, InputEventContext const*)
+{
+ ConsoleLog writeLine(QString("Received mouse motion event (%1,%2)").arg(evt.xCoord()).arg(evt.yCoord()));
+ Display* dpy = X11InputUtils::display();
+ int screen = 0 /* DefaultScreen(dpy) */;
+ XTestGrabControl(dpy, 1);
+ if(!XTestFakeMotionEvent(dpy, screen, evt.xCoord(), evt.yCoord(), CurrentTime))
+ {
+ ConsoleLog writeLine("[ERROR] XTestFakeMotionEvent failed");
+ }
+ XTestGrabControl(dpy, 0);
+
+ // Since there may not be a mainloop running, we need to manually flush the event queue
+ XFlush(dpy);
+}
diff --git a/src/input/x11FakeMouseHandler.h b/src/input/x11FakeMouseHandler.h
new file mode 100644
index 0000000..0e32256
--- /dev/null
+++ b/src/input/x11FakeMouseHandler.h
@@ -0,0 +1,34 @@
+/*
+ # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # --------------------------------------------------------------------------
+ # x11FakeMouseHandler.h:
+ # - Handle mouse events on X11 - interface
+ # --------------------------------------------------------------------------
+ */
+
+#ifndef X11FAKEMOUSEHANDLER_H_
+#define X11FAKEMOUSEHANDLER_H_
+
+#include "inputEventHandler.h"
+
+class X11FakeMouseButtonHandler : public DefaultInputEventHandler<InputEvent::ET_BUTTON>
+{
+public:
+ void handle(InputEvent const&, InputEventContext const* = 0);
+};
+
+class X11FakeMouseMovementHandler : public DefaultInputEventHandler<InputEvent::ET_POINTER>
+{
+public:
+ void handle(InputEvent const&, InputEventContext const* = 0);
+};
+
+#endif /* X11FAKEMOUSEHANDLER_H_ */
diff --git a/src/input/x11InputUtils.cpp b/src/input/x11InputUtils.cpp
new file mode 100644
index 0000000..b589bd6
--- /dev/null
+++ b/src/input/x11InputUtils.cpp
@@ -0,0 +1,29 @@
+/*
+ # Copyright (c) 2009, 2010 - OpenSLX Project, Computer Center University of
+ # Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # ------------------------------------------------------------------------
+ # src/input/x11InputUtils.h:
+ # Utilities for input handling under X11 - implementation
+ */
+
+#include "x11InputUtils.h"
+
+static Display* _dpy = 0;
+
+void X11InputUtils::setDisplay(Display* dpy)
+{
+ _dpy = dpy;
+}
+
+Display* X11InputUtils::display()
+{
+ return _dpy;
+}
diff --git a/src/input/x11InputUtils.h b/src/input/x11InputUtils.h
new file mode 100644
index 0000000..9c85d09
--- /dev/null
+++ b/src/input/x11InputUtils.h
@@ -0,0 +1,27 @@
+/*
+ # Copyright (c) 2009, 2010 - OpenSLX Project, Computer Center University of
+ # Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # ------------------------------------------------------------------------
+ # src/input/x11InputUtils.h:
+ # Utilities for input handling under X11 - interface
+ */
+
+#ifndef X11INPUTUTILS_H_
+#define X11INPUTUTILS_H_
+
+#include <X11/Xlib.h>
+
+struct X11InputUtils {
+ static void setDisplay(Display*);
+ static Display* display();
+};
+
+#endif /* X11INPUTUTILS_H_ */