summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRafal Krypa <r.krypa@samsung.com>2015-04-17 11:17:02 +0200
committerRafal Krypa <r.krypa@samsung.com>2015-04-17 16:17:28 +0200
commit0a18023646c011eeb05abea4ec12fa1c5229728a (patch)
treeca2908562575c10e18f74d3b83352dd60aeb246f
parent57fd245f1331b42917f2875241b617b4e2515b83 (diff)
downloadsecurity-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.spec2
-rw-r--r--src/common/CMakeLists.txt2
-rw-r--r--src/common/cynara.cpp174
-rw-r--r--src/common/include/cynara.h29
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