/* # 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 #include #include #include #include #include #include "detail/policyChain.h" #include "detail/systemTraits.h" /** * For handling access control, this specifies who sent the input event. * This only really makes sense in the privileged input handler chain. */ struct InputEventContext { InputEventContext() { hasBeenDenied = false; } virtual pid_t senderPid() const = 0; /**< PID of the sending process */ virtual uid_t senderUid() const = 0; /**< UID of the sending process */ virtual gid_t senderGid() const = 0; /**< GID of the sending process */ /** Support the generation of meaningful log messages. */ mutable bool hasBeenDenied; }; namespace input_policy { ///////////////////////////////////////////////////////////////////////// // Policy: // Modifies the behaviour of an input handler class. // // There are several kinds of policy: // - Security Policy (When to allow a certain action) // - System Requirements (When to enable a certain handler) // - Applicability (When to consider a certain handler) // // Policies are tied together using the detail::PolicyChain class. ///////////////////////////////////////////////////////////////////////// /** * Security Policy. * At the moment there are two security policies: * -* If the user is on a local seat, allow. If the user is privileged, * allow. Else deny. * -* Allow everybody. * * Additional security policies can be added by following the example * set by \ref AllowLocalOrPrivileged * * \param PolicyImpl The implementation class that security decisions * should be delegated to. * \see {AllowLocalOrPrivileged} * \see {AllowEverybody} */ template BEGIN_POLICY_CLASS(Security) { bool allow(InputEventContext const* context) { return PolicyImpl::allow(context); } } END_POLICY_CLASS /** * Check the default security model. */ struct AllowLocalOrPrivileged { static bool allow(InputEventContext const*); }; /** * Do not restrict execution. */ struct AllowEverybody { static bool allow(InputEventContext const*); }; /** * Shorthand for unrestricted execution. */ typedef Security Unprivileged; /** * System Requirements. * * At the moment, this is trivial, as PVS only runs on Linux. But, * as porting efforts are already underway, we include the necessary * machinery anyway. * * \param Trait... a list of system traits that need to be * present in order for the handler that this policy is applied to * to run. */ #ifdef DOXYGEN_RUNNING template #else template #endif BEGIN_POLICY_CLASS(Require) { static const bool areSystemRequirementsFulfilled = (NextPolicy::areSystemRequirementsFulfilled && !NextPolicy::areSystemRequirementsVacuouslyFulfilled) || detail::Matches, detail::SystemTraits>::value; static const bool areSystemRequirementsVacuouslyFulfilled = false; } END_POLICY_CLASS #ifndef DOXYGEN_RUNNING enum { HANDLER_CODE_DONT_CARE = 0xffff, HANDLER_VALUE_DONT_CARE = 0xffffffff }; #endif /** * Event selection. * * This policy makes the handler it is applied to applicable if the * template parameters \c EventType, \c EventCode and \c EventValue * match the corresponding fields of the incoming \ref InputEvent. * * Can be applied multiple times, and will be combined by logical * OR. * * \param EventType Match the \ref InputEvent::type() field. * \param EventCode (optional:) Match the \ref InputEvent::code() field. * \param EventValue (optional:) Match the \ref InputEvent::value() field. */ template BEGIN_POLICY_CLASS(Match) { bool isApplicable(InputEvent const& evt) { if(evt.type() != EventType) return NextPolicy::isApplicable(evt); if(EventCode != HANDLER_CODE_DONT_CARE && evt.code() != EventCode) return NextPolicy::isApplicable(evt); if(EventValue != HANDLER_VALUE_DONT_CARE && evt.value() != EventValue) return NextPolicy::isApplicable(evt); return true; } }; END_POLICY_CLASS #ifndef DOXYGEN_RUNNING namespace detail { ///////////////////////////////////////////////////////////////////////// // Base case: If no policies are given: ///////////////////////////////////////////////////////////////////////// struct InputEventHandlerPolicyBase { // The default security policy applies typedef AllowLocalOrPrivileged DefaultSecurityPolicyImpl; bool allow(InputEventContext const* context) { return DefaultSecurityPolicyImpl::allow(context); } // A handler that does not specify requirements works // everywhere static const bool areSystemRequirementsFulfilled = true; // We need this to implement proper logical OR static const bool areSystemRequirementsVacuouslyFulfilled = true; // Generate an error when no match policy is given. bool isApplicable(InputEvent const&) { return false; } // If any policy implementation needs an initialization hook: // Don't forget to call NextPolicy::initialize() in your // implementation! void initialize() { } }; } // namespace detail #endif // DOXYGEN_RUNNING } /** * Base class without template parameters to enable making a list of * polymorphic event handlers. * * The actual handler class needs to provide \ref doHandle and can * override \ref allow and \ref isApplicable. * * \note Do not derive from InputEventHandlerBase to implement * new event handlers! Derive from InputEventHandler instead. */ class InputEventHandlerBase { public: enum HandlerStatus { HANDLER_MATCHED, /**< The handler matched the input event and was executed. */ HANDLER_NOT_ALLOWED, /**< Execution of the handler was prevented by security policy. */ HANDLER_NOT_APPLICABLE /**< The handler did not match the input event. */ }; virtual void initialize() = 0; HandlerStatus handle(InputEvent const& evt, InputEventContext const* context = 0); protected: /** * Check security preconditions for the execution of this handler. * This is normally handled by the \ref input_policy::Security policy. */ virtual bool allow(InputEvent const& event, InputEventContext const* context = 0) = 0; /** * Check if this handler can handle the incoming input event. * This is normally handled by the \ref input_policy::Match policy. */ virtual bool isApplicable(InputEvent const& event, InputEventContext const* context = 0) = 0; /** * Actually handle the incoming event. * It is assumed that all preconditions have been checked and the handler * has been initialized at the point where this method is called. */ virtual void doHandle(InputEvent const& event, InputEventContext const* context = 0) = 0; }; /** * Base class for input event handlers. */ #ifdef DOXYGEN_RUNNING template #else template #endif class InputEventHandler : public InputEventHandlerBase { #ifndef DOXYGEN_RUNNING protected: // instantiate our policy: typedef USE_POLICY(input_policy::detail::InputEventHandlerPolicyBase) policy_type; policy_type policy; #endif public: void initialize() { policy.initialize(); } /** Export this so the handler chain can decide whether to include this handler */ static const bool areSystemRequirementsFulfilled = policy_type::areSystemRequirementsFulfilled; protected: bool allow(InputEvent const&, InputEventContext const* context = 0) { return policy.allow(context); } bool isApplicable(InputEvent const& event, InputEventContext const* = 0) { return policy.isApplicable(event); } }; /** * Chain \ref InputEventHandler instances together. * The chain is also responsible for the creation of instances. * * \see privilegedInputEventHandlerChain.cpp * \see unprivilegedInputEventHandlerChain.cpp */ class InputEventHandlerChain { private: QList handlers; #ifndef DOXYGEN_RUNNING // Implementation detail ///////////////////////////////////////////////////////////////////////// // We need to statically dispatch on a static member of HandlerType. // Unfortunately, we cannot specialize member functions of a template. // So, we need to make this a class with a static non-template member. ///////////////////////////////////////////////////////////////////////// template struct ConditionallyAppend { ///////////////////////////////////////////////////////////////////////// // This method will never be instantiated for handlers that are // not Compatible, thus generating no reference to HandlerType // and permitting compilation to proceed without // tedious nested preprocessor conditionals. ///////////////////////////////////////////////////////////////////////// static void doIt(InputEventHandlerChain* chain) { chain->handlers.append(new HandlerType); } }; template struct ConditionallyAppend { static void doIt(InputEventHandlerChain*) { } }; #endif // DOXYGEN_RUNNING public: /** * Add an event handler to the chain. * * \param fake_parameter The parameter is only for * compilers which cannot handle template member functions * correctly. * \return A reference to the receiver, for invocation chaining. * * \note Do not pass a value to this function. The function * handles creation of an instance itself. */ template InputEventHandlerChain& add(HandlerType const* fake_parameter = 0) { ConditionallyAppend::doIt(this); return *this; } /** * Call \ref InputEventHandlerBase::initialize() on all * handlers in the chain. */ void initialize() { QListIterator i(handlers); while(i.hasNext()) { i.next()->initialize(); } } /** * Handle an input event. All handlers in the chain are tried * in the order they were added, until one handler's * implementation of InputEventHandlerBase::handle() returns * InputEventHandlerBase::HANDLER_MATCHED. */ void handle(InputEvent const& event, InputEventContext const* context = 0) { QListIterator i(handlers); while(i.hasNext()) { switch(i.next()->handle(event, context)) { case InputEventHandlerBase::HANDLER_MATCHED: return; case InputEventHandlerBase::HANDLER_NOT_ALLOWED: context->hasBeenDenied = true; case InputEventHandlerBase::HANDLER_NOT_APPLICABLE: continue; } } } }; #endif /* INPUTEVENTHANDLER_H_ */