diff options
author | Rafal Krypa <r.krypa@samsung.com> | 2015-04-17 11:17:02 +0200 |
---|---|---|
committer | Rafal Krypa <r.krypa@samsung.com> | 2015-04-17 16:17:28 +0200 |
commit | 0a18023646c011eeb05abea4ec12fa1c5229728a (patch) | |
tree | ca2908562575c10e18f74d3b83352dd60aeb246f | |
parent | 57fd245f1331b42917f2875241b617b4e2515b83 (diff) | |
download | security-manager-0a18023646c011eeb05abea4ec12fa1c5229728a.tar.gz security-manager-0a18023646c011eeb05abea4ec12fa1c5229728a.tar.bz2 security-manager-0a18023646c011eeb05abea4ec12fa1c5229728a.zip |
cynara: rewrite class using cynara async API for parallel processing
Cynara class method check() can now be called in parallel by multiple
threads. Each call blocks until it gets a response.
This is a first step toward making security-manager multi-threaded, for
processing multiple requests in parallel.
Cynara class remains a singleton for now, but eventually there will be
single instance constructed (and destructed) from the main thread and
called for checks from separate threads processing user requests.
Change-Id: Ie1f55b9610caf45dc0df06dbd713070d39ccac07
Signed-off-by: Rafal Krypa <r.krypa@samsung.com>
-rw-r--r-- | packaging/security-manager.spec | 2 | ||||
-rw-r--r-- | src/common/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/common/cynara.cpp | 174 | ||||
-rw-r--r-- | src/common/include/cynara.h | 29 |
4 files changed, 195 insertions, 12 deletions
diff --git a/packaging/security-manager.spec b/packaging/security-manager.spec index d6d259bf..8baa231a 100644 --- a/packaging/security-manager.spec +++ b/packaging/security-manager.spec @@ -20,7 +20,7 @@ BuildRequires: pkgconfig(libtzplatform-config) BuildRequires: pkgconfig(sqlite3) BuildRequires: pkgconfig(db-util) BuildRequires: pkgconfig(cynara-admin) -BuildRequires: pkgconfig(cynara-client) +BuildRequires: pkgconfig(cynara-client-async) BuildRequires: boost-devel %{?systemd_requires} diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 6571e642..32b3f77a 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -7,7 +7,7 @@ PKG_CHECK_MODULES(COMMON_DEP libsmack db-util cynara-admin - cynara-client + cynara-client-async ) FIND_PACKAGE(Boost REQUIRED) diff --git a/src/common/cynara.cpp b/src/common/cynara.cpp index 14acb360..30f0777c 100644 --- a/src/common/cynara.cpp +++ b/src/common/cynara.cpp @@ -544,14 +544,39 @@ int CynaraAdmin::GetPrivilegeManagerMaxLevel(const std::string &label, const std Cynara::Cynara() { + int ret; + + ret = eventfd(0, 0); + if (ret == -1) { + LogError("Error while creating eventfd: " << strerror(errno)); + ThrowMsg(CynaraException::UnknownError, "Error while creating eventfd"); + } + + // Poll the eventfd for reading + pollFds[0].fd = ret; + pollFds[0].events = POLLIN; + + // Temporary, will be replaced by cynara fd when available + pollFds[1].fd = pollFds[0].fd; + pollFds[1].events = 0; + checkCynaraError( - cynara_initialize(&m_Cynara, nullptr), + cynara_async_initialize(&cynara, nullptr, &Cynara::statusCallback, &(pollFds[1])), "Cannot connect to Cynara policy interface."); + + thread = std::thread(&Cynara::run, this); } Cynara::~Cynara() { - cynara_finish(m_Cynara); + LogDebug("Sending terminate event to Cynara thread"); + terminate.store(true); + threadNotifyPut(); + thread.join(); + + // Critical section + std::lock_guard<std::mutex> guard(mutex); + cynara_async_finish(cynara); } Cynara &Cynara::getInstance() @@ -560,13 +585,150 @@ Cynara &Cynara::getInstance() return cynara; } +void Cynara::threadNotifyPut() +{ + int ret = eventfd_write(pollFds[0].fd, 1); + if (ret == -1) + LogError("Unexpected error while writing to eventfd: " << strerror(errno)); +} + +void Cynara::threadNotifyGet() +{ + eventfd_t value; + int ret = eventfd_read(pollFds[0].fd, &value); + if (ret == -1) + LogError("Unexpected error while reading from eventfd: " << strerror(errno)); +} + +void Cynara::statusCallback(int oldFd, int newFd, cynara_async_status status, + void *ptr) +{ + auto cynaraFd = static_cast<struct pollfd *>(ptr); + + LogDebug("Cynara status callback. " << + "Status = " << status << ", oldFd = " << oldFd << ", newFd = " << newFd); + + if (newFd == -1) { + cynaraFd->events = 0; + } else { + cynaraFd->fd = newFd; + + switch (status) { + case CYNARA_STATUS_FOR_READ: + cynaraFd->events = POLLIN; + break; + + case CYNARA_STATUS_FOR_RW: + cynaraFd->events = POLLIN | POLLOUT; + break; + } + } +} + +void Cynara::responseCallback(cynara_check_id checkId, + cynara_async_call_cause cause, int response, void *ptr) +{ + LogDebug("Response for received for Cynara check id: " << checkId); + + auto promise = static_cast<std::promise<bool>*>(ptr); + + switch (cause) { + case CYNARA_CALL_CAUSE_ANSWER: + LogDebug("Cynara cause: ANSWER: " << response); + promise->set_value(response); + break; + + case CYNARA_CALL_CAUSE_CANCEL: + LogDebug("Cynara cause: CANCEL"); + promise->set_value(CYNARA_API_ACCESS_DENIED); + break; + + case CYNARA_CALL_CAUSE_FINISH: + LogDebug("Cynara cause: FINISH"); + promise->set_value(CYNARA_API_ACCESS_DENIED); + break; + + case CYNARA_CALL_CAUSE_SERVICE_NOT_AVAILABLE: + LogError("Cynara cause: SERVICE_NOT_AVAILABLE"); + + try { + ThrowMsg(CynaraException::ServiceNotAvailable, + "Cynara service not available"); + } catch (...) { + promise->set_exception(std::current_exception()); + } + break; + } +} + +void Cynara::run() +{ + LogInfo("Cynara thread started"); + while (true) { + int ret = poll(pollFds, 2, -1); + if (ret == -1) { + if (errno != EINTR) + LogError("Unexpected error returned by poll: " << strerror(errno)); + continue; + } + + // Check eventfd for termination signal + if (pollFds[0].revents) { + threadNotifyGet(); + if (terminate.load()) { + LogInfo("Cynara thread terminated"); + return; + } + } + + // Check if Cynara fd is ready for processing + try { + if (pollFds[1].revents) { + // Critical section + std::lock_guard<std::mutex> guard(mutex); + + checkCynaraError(cynara_async_process(cynara), + "Unexpected error returned by cynara_async_process"); + } + } catch (const CynaraException::Base &e) { + LogError("Error while processing Cynara events: " << e.DumpToString()); + } + } +} + bool Cynara::check(const std::string &label, const std::string &privilege, const std::string &user, const std::string &session) { - return checkCynaraError( - cynara_check(m_Cynara, - label.c_str(), session.c_str(), user.c_str(), privilege.c_str()), - "Cannot check permission with Cynara."); + LogDebug("check: client = " << label << ", user = " << user << + ", privilege = " << privilege << ", session = " << session); + + std::promise<bool> promise; + auto future = promise.get_future(); + + // Critical section + { + std::lock_guard<std::mutex> guard(mutex); + + int ret = cynara_async_check_cache(cynara, + label.c_str(), session.c_str(), user.c_str(), privilege.c_str()); + + if (ret != CYNARA_API_CACHE_MISS) + return checkCynaraError(ret, "Error while checking Cynara cache"); + + LogDebug("Cynara cache miss"); + + cynara_check_id check_id; + checkCynaraError( + cynara_async_create_request(cynara, + label.c_str(), session.c_str(), user.c_str(), privilege.c_str(), + &check_id, &Cynara::responseCallback, &promise), + "Cannot check permission with Cynara."); + + threadNotifyPut(); + LogDebug("Waiting for response to Cynara query id " << check_id); + } + + return future.get(); } } // namespace SecurityManager diff --git a/src/common/include/cynara.h b/src/common/include/cynara.h index 3e9a8186..aa0ed601 100644 --- a/src/common/include/cynara.h +++ b/src/common/include/cynara.h @@ -24,12 +24,18 @@ #ifndef _SECURITY_MANAGER_CYNARA_ #define _SECURITY_MANAGER_CYNARA_ -#include <cynara-client.h> +#include <cynara-client-async.h> #include <cynara-admin.h> #include <dpl/exception.h> #include <string> #include <vector> #include <map> +#include <mutex> +#include <thread> +#include <future> + +#include <poll.h> +#include <sys/eventfd.h> #include "security-manager.h" @@ -293,7 +299,7 @@ private: class Cynara { public: - virtual ~Cynara(); + ~Cynara(); static Cynara &getInstance(); @@ -311,9 +317,24 @@ public: private: Cynara(); - struct cynara *m_Cynara; -}; + static void statusCallback(int oldFd, int newFd, + cynara_async_status status, void *ptr); + + static void responseCallback(cynara_check_id checkId, + cynara_async_call_cause cause, int response, void *ptr); + + void run(); + + void threadNotifyPut(); + void threadNotifyGet(); + + cynara_async *cynara; + struct pollfd pollFds[2]; + std::mutex mutex; + std::thread thread; + std::atomic<bool> terminate{false}; +}; } // namespace SecurityManager |