summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJose Bollo <jose.bollo@iot.bzh>2018-09-10 12:00:18 +0200
committerJose Bollo <jose.bollo@iot.bzh>2018-09-10 12:00:18 +0200
commit11654afcb5753a54a033db12e1ed4a19b3f7c86e (patch)
tree0d493c80584392eec2c5dc0f1c1c68c9057cf043
Initial commit
Signed-off-by: Jose Bollo <jose.bollo@iot.bzh>
-rw-r--r--.gitignore2
-rw-r--r--CMakeLists.txt82
-rw-r--r--LICENSE202
-rw-r--r--include/CMakeLists.txt19
-rw-r--r--include/cynara/cynara-admin-types.h91
-rw-r--r--include/cynara/cynara-admin.h460
-rw-r--r--include/cynara/cynara-agent.h256
-rw-r--r--include/cynara/cynara-client-async.h645
-rw-r--r--include/cynara/cynara-client-plugin.h75
-rw-r--r--include/cynara/cynara-client.h305
-rw-r--r--include/cynara/cynara-creds-commons.h132
-rw-r--r--include/cynara/cynara-creds-dbus.h171
-rw-r--r--include/cynara/cynara-creds-gdbus.h167
-rw-r--r--include/cynara/cynara-creds-self.h121
-rw-r--r--include/cynara/cynara-creds-socket.h161
-rw-r--r--include/cynara/cynara-error.h148
-rw-r--r--include/cynara/cynara-limits.h35
-rw-r--r--include/cynara/cynara-monitor.h421
-rw-r--r--include/cynara/cynara-plugin.h106
-rw-r--r--include/cynara/cynara-policy-types.h48
-rw-r--r--include/cynara/cynara-session.h73
-rw-r--r--pkgconfig/CMakeLists.txt41
-rw-r--r--pkgconfig/cynara.pc.in5
l---------pkgconfig/link.pc1
-rw-r--r--sample80
-rw-r--r--src/.gitignore5
-rw-r--r--src/CMakeLists.txt95
-rw-r--r--src/Makefile26
-rw-r--r--src/cache.c163
-rw-r--r--src/cache.h48
-rw-r--r--src/cyn.c267
-rw-r--r--src/cyn.h113
-rw-r--r--src/db.c661
-rw-r--r--src/db.h102
-rw-r--r--src/export.map9
-rw-r--r--src/fbuf.c279
-rw-r--r--src/fbuf.h111
-rw-r--r--src/lib-compat.c651
-rw-r--r--src/prot.c440
-rw-r--r--src/prot.h85
-rw-r--r--src/queue.c192
-rw-r--r--src/queue.h31
-rw-r--r--src/rcyn-client.c722
-rw-r--r--src/rcyn-client.h149
-rw-r--r--src/rcyn-protocol.c33
-rw-r--r--src/rcyn-protocol.h28
-rw-r--r--src/rcyn-protocol.txt79
-rw-r--r--src/rcyn-server.c669
-rw-r--r--src/socket.c320
-rw-r--r--src/socket.h21
-rw-r--r--src/test-lib-compat.c256
-rw-r--r--systemd/CMakeLists.txt42
-rw-r--r--systemd/cynara-admin.socket.in15
-rw-r--r--systemd/cynara-check.socket.in15
-rw-r--r--systemd/cynara.service29
-rw-r--r--systemd/cynara.target4
l---------systemd/sockets.target.wants/cynara-admin.socket1
l---------systemd/sockets.target.wants/cynara-check.socket1
58 files changed, 9509 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ac33634
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+build/
+.*.sw*
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..8a3c9f1
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,82 @@
+###########################################################################
+# Copyright (C) 2018 "IoT.bzh"
+#
+# author: José Bollo <jose.bollo@iot.bzh>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###########################################################################
+
+CMAKE_MINIMUM_REQUIRED(VERSION 3.0)
+
+PROJECT(cynara C)
+
+SET(PROJECT_NAME "Cynara")
+SET(PROJECT_PRETTY_NAME "Permission database")
+SET(PROJECT_DESCRIPTION "Secured permission database for applications")
+SET(PROJECT_VERSION "1.99.RC1")
+set(PROJECT_URL "https://gerrit.automotivelinux.org/gerrit/gitweb?p=src/cynara.git;a=summary")
+
+INCLUDE(FindPkgConfig)
+INCLUDE(CheckIncludeFiles)
+INCLUDE(CheckLibraryExists)
+INCLUDE(GNUInstallDirs)
+INCLUDE(CTest)
+
+###########################################################################
+# possible settings
+
+set(CYNARA_VERSION ${PROJECT_VERSION})
+set(CYNARA_SOVERSION 1.99)
+
+add_definitions(-DCYNARA_VERSION="${CYNARA_VERSION}")
+
+set(SYSTEMD ON CACHE BOOL "should use systemd")
+
+###########################################################################
+
+include_directories(include)
+link_libraries(-Wl,--as-needed -Wl,--gc-sections -Wl,--no-undefined)
+
+add_compile_options(-Wall -Wextra -Wconversion)
+add_compile_options(-Wno-unused-parameter) # frankly not using a parameter does it care?
+add_compile_options(-Wno-sign-compare -Wno-sign-conversion)
+add_compile_options(-Werror=maybe-uninitialized)
+add_compile_options(-Werror=implicit-function-declaration)
+add_compile_options(-ffunction-sections -fdata-sections)
+add_compile_options(-fPIC)
+add_compile_options(-g)
+
+set(CMAKE_C_FLAGS_PROFILING "-g -O2 -pg -Wp,-U_FORTIFY_SOURCE")
+set(CMAKE_C_FLAGS_DEBUG "-g -ggdb -Wp,-U_FORTIFY_SOURCE")
+set(CMAKE_C_FLAGS_RELEASE "-g -O2")
+set(CMAKE_C_FLAGS_CCOV "-g -O2 --coverage")
+
+###########################################################################
+
+if(SYSTEMD)
+ PKG_CHECK_MODULES(libsystemd REQUIRED libsystemd>=222)
+endif()
+
+if(SYSTEMD)
+ set(SOCKET_DIR "/run/platform"
+ CACHE PATH "path of the socket system directories")
+ set(SYSTEMD_UNIT_DIR "${CMAKE_INSTALL_FULL_LIBDIR}/systemd/system"
+ CACHE PATH "Path to systemd system unit files")
+ set(CHECK_SOCKET_SPEC "unix:${SOCKET_DIR}/cynara.check")
+ set(ADMIN_SOCKET_SPEC "unix:${SOCKET_DIR}/cynara.admin")
+ add_subdirectory(systemd)
+endif()
+add_subdirectory(include)
+add_subdirectory(src)
+add_subdirectory(pkgconfig)
+
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt
new file mode 100644
index 0000000..6553f89
--- /dev/null
+++ b/include/CMakeLists.txt
@@ -0,0 +1,19 @@
+###########################################################################
+# Copyright (C) 2018 "IoT.bzh"
+#
+# author: José Bollo <jose.bollo@iot.bzh>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###########################################################################
+
+INSTALL(DIRECTORY cynara DESTINATION ${CMAKE_INSTALL_FULL_INCLUDEDIR})
diff --git a/include/cynara/cynara-admin-types.h b/include/cynara/cynara-admin-types.h
new file mode 100644
index 0000000..95d27b9
--- /dev/null
+++ b/include/cynara/cynara-admin-types.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2014-2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+/**
+ * \file src/include/cynara-admin-types.h
+ * \author Lukasz Wojciechowski <l.wojciechow@partner.samsung.com>
+ * \author Aleksander Zdyb <a.zdyb@samsung.com>
+ * \author Zofia Abramowska <z.abramowska@samsung.com>
+ * \author Oskar Switalski <o.switalski@samsung.com>
+ * \version 1.0
+ * \brief This file contains structs and consts for cynara admin.
+ */
+
+#ifndef CYNARA_ADMIN_TYPES_H
+#define CYNARA_ADMIN_TYPES_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \struct cynara_admin_policy
+ * \brief Defines single policy
+ */
+struct cynara_admin_policy {
+ char *bucket; /**< Name of bucket, in which policy is placed */
+
+ char *client; /**< Identifier of client (application) */
+ char *user; /**< Identifier of user */
+ char *privilege; /**< Privilege name */
+
+ int result; /**< Result of policy */
+ char *result_extra; /**< Not always used, may contain some additional result data
+ like e.g. name of bucket in case result == CYNARA_ADMIN_BUCKET */
+};
+
+/**
+ * \struct cynara_admin_policy_descr
+ * \brief Describes policy of type given with result
+ */
+
+struct cynara_admin_policy_descr {
+ int result; /**< Result of policy to describe */
+ char *name; /**< Descriptive name of given policy result */
+};
+
+/**
+ * \name Wildcard
+ * Can replace client, user or privilege name.
+ * WILDCARD matches any string during check procedure from libcynara-client.
+ */
+#define CYNARA_ADMIN_WILDCARD "*"
+
+/**
+ * \name Name of Default Bucket
+ * Default bucket - the one that check starts in.
+ * Default bucket cannot be removed, although its default policy
+ * (which originally is set to DENY) can be changed.
+ */
+#define CYNARA_ADMIN_DEFAULT_BUCKET ""
+
+/**
+ * \name Any
+ * Can replace client, user or privilege name.
+ * ANY matches any string (including WILDCARD) during:
+ *
+ * * policy removal with cynara_admin_erase() function
+ * * listing policies from a single bucket.
+ *
+ * Using ANY as default policy for bucket or as policy type of inserted policy record
+ * is forbidden and will cause CYNARA_API_INVALID_PARAM error.
+ */
+#define CYNARA_ADMIN_ANY "#"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CYNARA_ADMIN_TYPES_H */
diff --git a/include/cynara/cynara-admin.h b/include/cynara/cynara-admin.h
new file mode 100644
index 0000000..a0ab0c9
--- /dev/null
+++ b/include/cynara/cynara-admin.h
@@ -0,0 +1,460 @@
+/*
+ * Copyright (c) 2014-2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+/**
+ * \file src/include/cynara-admin.h
+ * \author Lukasz Wojciechowski <l.wojciechow@partner.samsung.com>
+ * \author Zofia Abramowska <z.abramowska@samsung.com>
+ * \author Oskar Switalski <o.switalski@samsung.com>
+ * \version 1.0
+ * \brief This file contains administration APIs of cynara available with libcynara-admin.
+ * \example cynara-admin.example
+ */
+
+#ifndef CYNARA_ADMIN_H
+#define CYNARA_ADMIN_H
+
+#include <cynara/cynara-admin-types.h>
+#include <cynara/cynara-error.h>
+#include <cynara/cynara-limits.h>
+#include <cynara/cynara-policy-types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \struct cynara_admin
+ * Forward declaration of structure allowing initialization of library
+ * and usage of all libcynara-admin API functions
+ */
+struct cynara_admin;
+
+/**
+ * \brief Initialize cynara-admin structure.
+ *
+ * \par Description:
+ * Initialize cynara-admin library.
+ * Creates structure used in following API calls.
+ *
+ * \par Purpose:
+ * This function must be invoked prior to other admin API calls. It creates structure needed by
+ * other cynara-admin library API functions.
+ *
+ * \par Typical use case:
+ * Once before a service can call other cynara-admin library functions.
+ *
+ * \par Method of function operation:
+ * This API initializes inner library structures and in case of success creates cynara_admin
+ * structure and stores pointer to this structure at memory address passed in pp_cynara_admin
+ * parameter.
+ *
+ * \par Sync (or) async:
+ * This is a synchronous API.
+ *
+ * \par Important notes:
+ * Structure cynara_admin created by cynara_admin_initialize() call should be released with
+ * cynara_admin_finish().
+ *
+ * \param[out] pp_cynara_admin address of pointer for created cynara_admin structure.
+ *
+ * \return CYNARA_API_SUCCESS on success, or error code otherwise.
+ */
+int cynara_admin_initialize(struct cynara_admin **pp_cynara_admin);
+
+/**
+ * \brief Release cynara-admin structure.
+ *
+ * \par Description:
+ * Destroys structure created with cynara_admin_initialize() function.
+ *
+ * \par Purpose:
+ * This API should be used to clean up after usage of cynara-admin library.
+ *
+ * \par Typical use case:
+ * Function should be called once, when done with cynara-admin library API usage.
+ *
+ * \par Method of function operation:
+ * This API releases inner library structures and destroys cynara_admin structure.
+ *
+ * \par Sync (or) Async:
+ * This is a synchronous API.
+ *
+ * \par Important notes:
+ * No invocations of cynara-admin library API functions are allowed after call to
+ * cynara_admin_finish().
+ *
+ * \param[in] p_cynara_admin cynara_admin structure created in cynara_admin_initialize().
+ *
+ * \return CYNARA_API_SUCCESS on success, or error code otherwise.
+ */
+int cynara_admin_finish(struct cynara_admin *p_cynara_admin);
+
+/**
+ * \brief Insert, update or delete policies in cynara database.
+ *
+ * \par Description:
+ * Manages policies in cynara.
+ *
+ * \par Purpose:
+ * This API should be used to insert, update or delete policies in cynara.
+ *
+ * \par Typical use case:
+ * Enables privileged services to alter policies by adding, updating or removing records.
+ *
+ * \par Method of function operation:
+ * \parblock
+ * Policies are arranged into buckets. Every policy is defined in context of some bucket identified
+ * with bucket field (string). A bucket consists of policies identified with tripple: (client, user,
+ * privilege), which is a (unique) key within considered bucket.
+ *
+ * Every policy can be one of two types: simple or bucket-pointing policy.
+ *
+ * * Simple policies have result field with value of CYNARA_ADMIN_DENY or CYNARA_ADMIN_ALLOW.
+ * result_extra field should be NULL in this case.
+ * * Bucket-pointing policies have result field with value of CYNARA_ADMIN_BUCKET and name of
+ * bucket they point to in result_extra field.
+ *
+ *
+ * Type of operation, which is run for every record (single policy) is defined by result field in
+ * cynara_admin_policy structure.
+ *
+ * * In case of CYNARA_ADMIN_DENY or CYNARA_ADMIN_ALLOW a simple policy is updated or inserted into
+ * cynara database.
+ * * In case of CYNARA_ADMIN_BUCKET, a bucket-pointing policy is updated or inserted into cynara
+ * database.
+ * * In case of CYNARA_ADMIN_DELETE, a policy is removed from cynara database.
+ *
+ * One call of cynara_admin_set_policies() can manage many different policies in different buckets.
+ * However, considered buckets must exist before referring to them in policies.
+ * \endparblock
+ *
+ * \par Sync (or) Async:
+ * This is a synchronous API.
+ *
+ * \par Important notes:
+ * \parblock
+ * When plugin API will be specified, there will be more valid types to pass as result.
+ * Numerical values of defines CYNARA_ADMIN_... may change, so usage of defines names is strongly
+ * recommended.
+ *
+ * Policies size cannot exceed CYNARA_MAX_VECTOR_SIZE excluding last null element and string members
+ * length cannot exceed CYNARA_MAX_ID_LENGTH, otherwise CYNARA_API_INVALID_PARAM will be returned.
+ * \endparblock
+ *
+ * \param[in] p_cynara_admin cynara admin structure.
+ * \param[in] policies NULL terminated array of pointers to policy structures.
+ *
+ * \return CYNARA_API_SUCCESS on success, or error code otherwise.
+ */
+int cynara_admin_set_policies(struct cynara_admin *p_cynara_admin,
+ const struct cynara_admin_policy *const *policies);
+
+/**
+ * \brief Add, remove or update buckets in cynara database.
+ *
+ * \par Description:
+ * Adds new, updates or removes existing bucket for policies in cynara.
+ *
+ * \par Purpose:
+ * This API should be used to add, remove or update buckets.
+ *
+ * \par Typical use case:
+ * Enables privileged services to alter policies database by adding, updating or removing buckets.
+ *
+ * \par Method of function operation:
+ * \parblock
+ * Every bucket has a default policy. During search, if no policy matches the searched key (client,
+ * user, privilege), default policy is returned.
+
+ * Operation run on a single bucket defined with bucket parameter.
+
+ * Operation parameter defines what should happen with bucket. In case of:
+ * * CYNARA_ADMIN_DENY, a bucket is inserted or updated with CYNARA_ADMIN_DENY default policy;
+ * * CYNARA_ADMIN_ALLOW, a bucket is inserted or updated with CYNARA_ADMIN_ALLOW default policy;
+ * * CYNARA_ADMIN_DELETE, a bucket is removed with all policies that were kept in it.
+ * \endparblock
+ *
+ * \par Sync (or) Async:
+ * This is a synchronous API.
+ *
+ * \par Important notes:
+ * \parblock
+ * When plugin API will be specified, there will be more valid types to pass as operation / default
+ * policy. Numerical values of defines CYNARA_ADMIN_... may change, so usages of provided consts is
+ * strongly recommended.
+ *
+ * String length cannot exceed CYNARA_MAX_ID_LENGTH, otherwise CYNARA_API_INVALID_PARAM will be
+ * returned.
+ *
+ * Default bucket identified with CYNARA_ADMIN_DEFAULT_BUCKET exists always. Its default policy
+ * is preset to DENY (can be altered, however). Default bucket cannot be removed.
+ *
+ * Extra parameter will be used to pass additional data to cynara extensions to build more complex
+ * policies, such as ALLOW but for 5 minutes only, or ALLOW if user confirms.
+ * \endparblock
+ *
+ * \param[in] p_cynara_admin cynara admin structure.
+ * \param[in] bucket bucket name
+ * \param[in] operation type of operation (default policy or CYNARA_ADMIN_DELETE)
+ * \param[in] extra additional data for default policy (will be available with cynara extensions)
+ *
+ * \return CYNARA_API_SUCCESS on success, or error code otherwise.
+ */
+int cynara_admin_set_bucket(struct cynara_admin *p_cynara_admin, const char *bucket, int operation,
+ const char *extra);
+
+/**
+ * \brief Raw check client and user access for given privilege without using plugins extensions.
+ *
+ * \par Description:
+ * Raw check client and user access for given privilege without using plugins extensions.
+ *
+ * \par Purpose:
+ * This API should be used to check type of matching policy for check request
+ *
+ * \par Typical use case:
+ * Administrator of cynara want to know, what would cynara return to client, if asked about given
+ * access.
+ *
+ * \par Method of function operation:
+ * \parblock
+ * Function works almost the same way as cynara_check() client function.
+ * The differences are:
+ * * user must specify bucket, from which search would be started (in case of cynara_check()
+ * it is always the default bucket)
+ * * user can specify if search should be recursive: disabling recursive check will constrain search
+ * to single bucket only, ignoring all policies leading to other buckets (in case of
+ * cynara_check() search is always recursive)
+ * * when matching policy in cynara is found, its result is returned without being interpreted by
+ * plugin extensions.
+ * \endparblock
+ *
+ * \par Sync (or) Async:
+ * This is a synchronous API.
+ *
+ * \par Important notes:
+ * \parblock
+ * (*result_extra) may be set to NULL, if extra data are not used in matched policy
+ * If (*result_extra) is not NULL, it contains a string allocated by cynara admin library
+ * with malloc(3) function and must be released with free(3) function.
+ *
+ * String length cannot exceed CYNARA_MAX_ID_LENGTH, otherwise CYNARA_API_INVALID_PARAM will be
+ * returned.
+ * \endparblock
+ *
+ * \param[in] p_cynara_admin cynara admin structure.
+ * \param[in] start_bucket name of bucket where search would start.
+ * \param[in] recursive FALSE (== 0) : single bucket search;
+ * TRUE (!= 0) : search does not ignore policies leading to another buckets.
+ * \param[in] client application or process identifier.
+ * \param[in] user user running client.
+ * \param[in] privilege privilege that is a subject of a check.
+ * \param[out] result placeholder for matched policy type.
+ * \param[out] result_extra placeholder for matched policy additional data (see Important Notes!).
+ *
+ * \return CYNARA_API_SUCCESS on success, or error code otherwise.
+ */
+int cynara_admin_check(struct cynara_admin *p_cynara_admin,
+ const char *start_bucket, const int recursive,
+ const char *client, const char *user, const char *privilege,
+ int *result, char **result_extra);
+
+/**
+ * \brief Lists policies from single bucket in cynara database.
+ *
+ * \par Description:
+ * Lists filtered cynara policies from single bucket.
+ *
+ * \par Purpose:
+ * This API should be used to list policies from single bucket.
+ *
+ * \par Typical use case:
+ * List all policies matching defined filter.
+ *
+ * \par Method of function operation:
+ * \parblock
+ * Policies are arranged into buckets. Every bucket contains set of policies. Each of policies are
+ * identified with triple {client, user, privilege}. Function lists all policies from single bucket
+ * with matching client, user and privilege names.
+ *
+ * CYNARA_ADMIN_ANY can be used to match any client, user or privilege, e.g.
+ *
+ * List with parameters: {client = CYNARA_ADMIN_ANY, user = "alice", privilege = CYNARA_ADMIN_ANY}
+ * will match all policies related to "alice", so will match {"app1", "alice", "gps"} and
+ * {CYNARA_ADMIN_WILDCARD, "alice", "sms"}, but won't match {"app3", CYNARA_ADMIN_WILDCARD, "call"}.
+ *
+ * List with parameters: {client = "calculator", user = CYNARA_ADMIN_WILDCARD,
+ * privilege = CYNARA_ADMIN_ANY} will match {"calculator", CYNARA_ADMIN_WILDCARD, "sms"} but won't
+ * match {CYNARA_ADMIN_WILDCARD, CYNARA_ADMIN_WILDCARD, "sms"} nor {"calculator", "bob", "sms"}
+ *
+ * Matching policies are returned as NULL terminated array of pointers to cynara_admin_policy
+ * structures.
+ *
+ * If any of: bucket, client, user, privilege, policies is NULL then CYNARA_API_INVALID_PARAM
+ * is returned.
+ *
+ * If there is no bucket with given name CYNARA_API_BUCKET_NOT_FOUND is returned.
+ *
+ * In case of successful call CYNARA_API_SUCCESS is returned and *policies points to newly created
+ * array of pointers to struct cynara_admin_policy. It is responsibility of caller to release:
+ *
+ * * all non-NULL const char* pointers in all cynara_admin_policy structures;
+ * * all pointers to cynara_admin_policy structures kept in *policies array;
+ * * *policies array itself.
+ *
+ * All allocation made by cynara admin library are done with malloc(3) function and must be released
+ * with free(3) function.
+ * \endparblock
+ *
+ * \par Sync (or) Async:
+ * This is a synchronous API.
+ *
+ * \par Important notes:
+ * String length cannot exceed CYNARA_MAX_ID_LENGTH, otherwise CYNARA_API_INVALID_PARAM will be
+ * returned.
+ *
+ * \param[in] p_cynara_admin cynara admin structure.
+ * \param[in] bucket name.
+ * \param[in] client filter for client name.
+ * \param[in] user filter for user name.
+ * \param[in] privilege filter for privilege.
+ * \param[out] policies placeholder for NULL terminated array of pointers to policy structures.
+ *
+ * \return CYNARA_API_SUCCESS on success, or error code otherwise.
+ */
+int cynara_admin_list_policies(struct cynara_admin *p_cynara_admin, const char *bucket,
+ const char *client, const char *user, const char *privilege,
+ struct cynara_admin_policy ***policies);
+
+/**
+ * \brief Erase policies matching filter from cynara database.
+ *
+ * \par Description:
+ * Erase policies matching filter from cynara database.
+ *
+ * \par Purpose:
+ * This API should be used to erase multiple policies with some common key part,
+ * e.g. all policies related to given user.
+ *
+ * \par Typical use case:
+ * Erase all policies matching defined filter.
+ *
+ * \par Method of function operation:
+ * \parblock
+ * Policies are arranged into buckets. Every bucket contains set of policies. Each of policies are
+ * identified with triple {client, user, privilege}. Function erases all policies with matching
+ * client, user and privilege names.
+ *
+ * There are two modes:
+ * * non-recursive (recursive parameter set to 0) - when policies are erased only from single bucket
+ * * recursive (recursive parameter set to 1) when policies are removed from given start_bucket and
+ * all nested buckets.
+ *
+ * CYNARA_ADMIN_ANY can be used to match any client, user or privilege, e.g.
+ *
+ * Erase with parameters: {client = CYNARA_ADMIN_ANY, user = "alice", privilege = CYNARA_ADMIN_ANY}
+ * will match all policies related to "alice", so will match {"app1", "alice", "gps"} and
+ * {CYNARA_ADMIN_WILDCARD, "alice", "sms"}, but won't match {"app3", CYNARA_ADMIN_WILDCARD, "call"}.
+ *
+ * Erase with parameters: {client = "calculator", user = CYNARA_ADMIN_WILDCARD,
+ * privilege = CYNARA_ADMIN_ANY} will match {"calculator", CYNARA_ADMIN_WILDCARD, "sms"} but won't
+ * match {CYNARA_ADMIN_WILDCARD, CYNARA_ADMIN_WILDCARD, "sms"} nor {"calculator", "bob", "sms"}
+ *
+ * If any of: start_bucket, client, user, privilege, policies is NULL then CYNARA_API_INVALID_PARAM
+ * is returned.
+ *
+ * If there is no bucket with given name CYNARA_API_BUCKET_NOT_FOUND is returned.
+ *
+ * In case of successful call CYNARA_API_SUCCESS is returned.
+ * \endparblock
+ *
+ * \par Sync (or) Async:
+ * This is a synchronous API.
+ *
+ * \par Important notes:
+ * String length cannot exceed CYNARA_MAX_ID_LENGTH, otherwise CYNARA_API_INVALID_PARAM will be
+ * returned.
+ *
+ * \param[in] p_cynara_admin cynara admin structure.
+ * \param[in] start_bucket name of bucket where erase would start.
+ * \param[in] recursive FALSE (== 0) : erase is not recursive (single bucket erase); \n
+ * TRUE (!= 0) : erase follows all policies leading to nested buckets
+ * \param[in] client filter for client name.
+ * \param[in] user filter for user name.
+ * \param[in] privilege filter for privilege.
+ *
+ * \return CYNARA_API_SUCCESS on success, or error code otherwise.
+ */
+int cynara_admin_erase(struct cynara_admin *p_cynara_admin,
+ const char *start_bucket, int recursive,
+ const char *client, const char *user, const char *privilege);
+
+/**
+ * \brief Lists available policies with their name description.
+ *
+ * \par Description:
+ *
+ * Lists available cynara policy results with name description.
+ *
+ * \par Purpose:
+ * This API should be used to list all available policy results
+ * (also from cynara extension plugins).
+ *
+ * \par Typical use case:
+ * Gathering information about possible policy results and presenting them to user (using name
+ * attribute of description). Result can be passed to cynara_admin_set_policies().
+ *
+ * \par Method of function operation:
+ * \parblock
+ * Policies are based on policy result number. Policies can be built in (like primitives: ALLOW,
+ * DENY...) or can be loaded from cynara plugin extensions. This API gives possibility of checking,
+ * which of these result exist in current cynara server and can be presented to user in a readable
+ * way (of course additional translation may be needed).
+ *
+ * Descriptions of existing policy results are returned as NULL terminated array of pointers of
+ * cynara_admin_policy_descr structures.
+ *
+ * Example output could be {{0, "Deny"}, {11, "AskUser"}, {65535, "Allow"}, NULL}
+ *
+ * In case of successful call CYNARA_API_SUCCESS is returned and *descriptions points
+ * to newly created array of pointers to struct cynara_admin_policy_descr. It is responsibility
+ * of caller to release:
+ *
+ * * all non-NULL char* pointers in all cynara_admin_policy_descr structures;
+ * * all pointers to cynara_admin_policy_descr structures kept in *descriptions array;
+ * * *descriptions array itself.
+ *
+ * All allocation made by cynara admin library are done with malloc(3) function and must be released
+ * with free(3) function.
+ * \endparblock
+ *
+ * \par Sync (or) Async:
+ * This is a synchronous API.
+ *
+ * \param[in] p_cynara_admin cynara admin structure.
+ * \param[out] descriptions placeholder for NULL terminated array of pointers of
+ * description structures.
+ *
+ * \return CYNARA_API_SUCCESS on success, or error code otherwise.
+ */
+int cynara_admin_list_policies_descriptions(struct cynara_admin *p_cynara_admin,
+ struct cynara_admin_policy_descr ***descriptions);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CYNARA_ADMIN_H */
diff --git a/include/cynara/cynara-agent.h b/include/cynara/cynara-agent.h
new file mode 100644
index 0000000..3e6d4f0
--- /dev/null
+++ b/include/cynara/cynara-agent.h
@@ -0,0 +1,256 @@
+/*
+ * Copyright (c) 2014-2017 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+/**
+ * @file src/include/cynara-agent.h
+ * @author Adam Malinowski <a.malinowsk2@partner.samsung.com>
+ * @author Oskar Switalski <o.switalski@samsung.com>
+ * @version 1.0
+ * @brief This file contains agent APIs available with libcynara-agent.
+ */
+
+#ifndef CYNARA_AGENT_H
+#define CYNARA_AGENT_H
+
+#include <stdint.h>
+
+#include <cynara/cynara-error.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef uint16_t cynara_agent_req_id;
+typedef struct cynara_agent cynara_agent;
+
+/**
+ * \enum cynara_agent_msg_type
+ * Values specifying type of message.
+ *
+ * \var cynara_agent_msg_type::CYNARA_MSG_TYPE_ACTION
+ * Message of this type indicates its content is a request for performing an action or response to
+ * such request.
+ *
+ * \var cynara_agent_msg_type::CYNARA_MSG_TYPE_CANCEL
+ * Message of this type indicates its content is a request for canceling action or response to such
+ * request.
+ */
+typedef enum {
+ CYNARA_MSG_TYPE_ACTION,
+ CYNARA_MSG_TYPE_CANCEL
+} cynara_agent_msg_type;
+
+/**
+ * \par Description:
+ * Initialize cynara-agent structure.
+ * Create structure used in following API calls.
+ *
+ * \par Purpose:
+ * This API must be used prior to calling other agent API functions.
+ *
+ * \par Typical use case:
+ * Once before other agent API functions are called.
+ *
+ * \par Method of function operation:
+ * This API initializes inner library structures and in case of success returns cynara_agent
+ * structure.
+ *
+ * \par Sync (or) Async:
+ * This is a synchronous API.
+ *
+ * \par Thread safety:
+ * This function is NOT thread safe. If functions from described API are called by multithreaded
+ * application from different threads, they must be put into protected critical section.
+ *
+ * \par Important notes:
+ * Structure cynara_agent created by this function should be released with cynara_agent_finish().
+ *
+ * \param[out] pp_cynara_agent Place holder for created cynara_agent structure.
+ * \param[in] p_agent_type Type (name) of agent used by cynara for communication agent<->plugin.
+ *
+ * \return CYNARA_API_SUCCESS on success, or error code on error.
+ */
+int cynara_agent_initialize(cynara_agent **pp_cynara_agent, const char *p_agent_type);
+
+/**
+ * \par Description:
+ * Destroy structure created with cynara_agent_initialize().
+ *
+ * \par Purpose:
+ * This API should be used to clean up after usage of cynara-agent library.
+ *
+ * \par Typical use case:
+ * Once after connection to cynara is not needed.
+ *
+ * \par Method of function operation:
+ * This API releases inner library structure and destroys cynara_agent structure. Connection to
+ * cynara service is closed.
+ *
+ * \par Sync (or) Async:
+ * This is a synchronous API.
+ *
+ * \par Thread safety:
+ * This function is NOT thread-safe. If functions from described API are called by multithreaded
+ * application from different threads, they must be put into protected critical section.
+ *
+ * \par Important notes:
+ * No other call to cynara-agent library should be made after call to cynara_agent_finish() except
+ * cynara_agent_initialize().
+ *
+ * \param[in] p_cynara_agent cynara_agent structure. If NULL, then the call has no effect.
+ */
+int cynara_agent_finish(cynara_agent *p_cynara_agent);
+
+/**
+ * \par Description:
+ * Get request from cynara service.
+ *
+ * \par Purpose:
+ * This API should be used to get request from cynara service. Request is generated by corresponding
+ * plugin loaded into cynara service.
+ *
+ * \par Typical use case:
+ * Agent waits for request from cynara service. Request may be either ask for performing agent
+ * specific action or ask for canceling such action. Agent calls this function when is ready to
+ * perform or cancel an action.
+ *
+ * \par Method of function operation:
+ * \parblock
+ * Function reads data incoming from cynara service and if at least one complete request is ready
+ * then returns with CYNARA_API_SUCCESS code. Request type, request id and specific
+ * plugin data are stored in given arguments. Function returns exactly one request. If there are
+ * more then one requests ready to get then one must call this function multiple times.
+ *
+ * This function is blocking which means that if there is no request from cynara service it will not
+ * return. On success, buffer for plugin specific data is allocated and size is set. Developer is
+ * responsible for freeing this memory using free() function.
+ * \endparblock
+ *
+ * \par Sync (or) Async:
+ * This is a synchronous API.
+ *
+ * \par Thread safety:
+ * This function is NOT thread safe. If functions from described API are called by multithreaded
+ * application from different threads, they must be put into protected critical section.
+ *
+ * \par Important notes:
+ * \parblock
+ * Call to cynara_agent_get_request() needs cynara_agent structure to be created first.
+ * Use cynara_agent_initialize() before calling this function.
+ *
+ * After CYNARA_API_ACCESS_DENIED error is returned agent should be terminated or at least should
+ * not invoke neither cynara_agent_get_request() nor cynara_agent_put_response() functions.
+ * \endparblock
+ *
+ * \param[in] p_cynara_agent cynara_agent structure.
+ * \param[out] req_type Request type, demand an action or cancel this action.
+ * \param[out] req_id Request identifier used to pair with answer #cynara_agent_put_response() and
+ * cancel request.
+ * \param[out] data Plugin specific data. Buffer is allocated in this function and developer is
+ * responsible for freeing it using free() function. Buffer is filled with data
+ * from corresponding plugin. If there is no enough memory for data
+ * CYNARA_API_OUT_OF_MEMORY is returned and all arguments remain untouched.
+ * \param[out] data_size Size of plugin data (bytes count). In case of out of memory this value
+ * stays untouched.
+ *
+ * \return CYNARA_API_SUCCESS on successfully read request,
+ * CYNARA_API_INTERRUPTED when cynara_agent_cancel_waiting() is called during waiting,
+ * or negative error code otherwise.
+ */
+int cynara_agent_get_request(cynara_agent *p_cynara_agent, cynara_agent_msg_type *req_type,
+ cynara_agent_req_id *req_id, void **data, size_t *data_size);
+
+/**
+ * \par Description:
+ * Send response to cynara service.
+ *
+ * \par Purpose:
+ * This API should be used to send response to cynara service.
+ *
+ * \par Typical use case:
+ * Agent calls this function when is ready to answer request for action or cancel request.
+ *
+ * \par Method of function operation:
+ * Function sends data to cynara service. Data contains answer for previously got question.
+ * Answer may be of type CYNARA_MSG_TYPE_ACTION or CYNARA_MSG_TYPE_CANCEL. Type is
+ * CYNARA_MSG_TYPE_ACTION when request for an action was processed and answer is ready, or
+ * CYNARA_MSG_TYPE_CANCEL when processing request for an action was interrupted by cancel request.
+ * Agent must send exactly one response per one request and cancel. If request is processed before
+ * cancel message arrives the agent sends action response. If cancel arrives before action request
+ * is processed then agent sends cancel response and drops processing related action.
+ * Request id in response must be the same as request id in corresponding request.
+ *
+ * \par Sync (or) Async:
+ * This is a synchronous API.
+ *
+ * \par Thread safety:
+ * This function is NOT thread safe. If functions from described API are called by multithreaded
+ * application from different threads, they must be put into protected critical section.
+ *
+ * \par Important notes:
+ * Call to cynara_agent_get_request() needs cynara_agent structure to be created first.
+ * Use cynara_agent_initialize() before calling this function. Also successful call to
+ * cynara_agent_get_request() is needed before calling this function.
+ *
+ * \param[in] p_cynara_agent cynara_agent structure.
+ * \param[in] resp_type Response type - see Method of operation for details.
+ * \param[in] req_id Request identifier obtained from request.
+ * \param[in] data Plugin specific data. If necessary agent should fill this buffer with data
+ * directed to plugin.
+ * \param[in] data_size Size of plugin data (bytes count).
+ *
+ * \return CYNARA_API_SUCCESS on successfully read request, or negative error code otherwise.
+ */
+int cynara_agent_put_response(cynara_agent *p_cynara_agent, const cynara_agent_msg_type resp_type,
+ const cynara_agent_req_id req_id, const void *data,
+ const size_t data_size);
+
+/**
+ * \par Description:
+ * Break from waiting for cynara service request using cynara_agent_get_request().
+ *
+ * \par Purpose:
+ * This API should be used when cynara_agent_get_request() is blocked and before calling
+ * cynara_agent_finish().
+ *
+ * \par Typical use case:
+ * Agent calls this API, when it wants to gracefully quit.
+ *
+ * \par Method of function operation:
+ * Function notifies cynara_agent_get_request() to stop waiting for request from cynara.
+ * Then cynara_agent_get_request() returns CYNARA_API_INTERRUPTED and no request is fetched.
+ *
+ * \par Sync (or) Async:
+ * This is a synchronous API.
+ *
+ * \par Thread safety:
+ * This function can be used only together with cynara_agent_get_request(), otherwise should
+ * be treaded as NOT thread safe.
+ *
+ * \par Important notes:
+ * Call to cynara_agent_cancel_waiting() needs cynara_agent structure to be created first and
+ * cynara_agent_get_request() running.
+ * Use cynara_agent_initialize() before calling this function.
+ *
+ * \param[in] p_cynara_agent cynara_agent structure.
+ * \return CYNARA_API_SUCCESS on successful waiting cancel, or negative error code otherwise.
+ */
+int cynara_agent_cancel_waiting(cynara_agent *p_cynara_agent);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CYNARA_AGENT_H */
diff --git a/include/cynara/cynara-client-async.h b/include/cynara/cynara-client-async.h
new file mode 100644
index 0000000..78a1313
--- /dev/null
+++ b/include/cynara/cynara-client-async.h
@@ -0,0 +1,645 @@
+/*
+ * Copyright (c) 2014-2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+/**
+ * @file src/include/cynara-client-async.h
+ * @author Zofia Abramowska <z.abramowska@samsung.com>
+ * @author Marcin Niesluchowski <m.niesluchow@samsung.com>
+ * @author Oskar Switalski <o.switalski@samsung.com>
+ * @version 1.0
+ * @brief This file contains asynchronous client APIs of Cynara available
+ * with libcynara-client-asynchronous.
+ * @example cynara-client-async.example
+ */
+
+#ifndef CYNARA_CLIENT_ASYNC_H
+#define CYNARA_CLIENT_ASYNC_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <cynara/cynara-error.h>
+#include <cynara/cynara-limits.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef uint16_t cynara_check_id;
+typedef struct cynara_async cynara_async;
+typedef struct cynara_async_configuration cynara_async_configuration;
+
+/**
+ * \enum cynara_async_status
+ * Values indicating the status of connected cynara socket.
+ *
+ * \var cynara_async_status::CYNARA_STATUS_FOR_READ
+ * Wait for read events on socket.
+ *
+ * \var cynara_async_status::CYNARA_STATUS_FOR_RW
+ * Wait for both read and write events on socket.
+ */
+typedef enum {
+ CYNARA_STATUS_FOR_READ,
+ CYNARA_STATUS_FOR_RW
+} cynara_async_status;
+
+/**
+ * \enum cynara_async_call_cause
+ * Values indicating the reason of cynara_response_callback call.
+ *
+ * \var cynara_async_call_cause::CYNARA_CALL_CAUSE_ANSWER
+ * Callback was called due to response to previous cynara_async_create_request() or
+ * cynara_async_create_simple_request() call.
+ *
+ * \var cynara_async_call_cause::CYNARA_CALL_CAUSE_CANCEL
+ * Callback was called due to request cancellation with cynara_async_cancel_request() call.
+ *
+ * \var cynara_async_call_cause::CYNARA_CALL_CAUSE_FINISH
+ * Callback was called due to cynara_async_finish() call.
+ *
+ * \var cynara_async_call_cause::CYNARA_CALL_CAUSE_SERVICE_NOT_AVAILABLE
+ * Callback was called due to service not available.
+ */
+typedef enum {
+ CYNARA_CALL_CAUSE_ANSWER,
+ CYNARA_CALL_CAUSE_CANCEL,
+ CYNARA_CALL_CAUSE_FINISH,
+ CYNARA_CALL_CAUSE_SERVICE_NOT_AVAILABLE
+} cynara_async_call_cause;
+
+/**
+ * \brief Response_callback is registered once in cynara_async_create_request() or
+ * cynara_async_create_simple_request() and will be triggered exactly once in 4 kinds of situations:
+ *
+ * -# after response is received from cynara service (CYNARA_CALL_CAUSE_ANSWER)
+ * -# when request is canceled with cynara_async_cancel_request() (CYNARA_CALL_CAUSE_CANCEL)
+ * -# when request was pending for response, but cynara_async_finish() was called
+ * (CYNARA_CALL_CAUSE_FINISH)
+ * -# when connection to cynara service was broken and cannot be established again - probably cynara
+ * is unoperational (CYNARA_CALL_CAUSE_SERVICE_NOT_AVAILABLE)
+ *
+ * API functions called during this callback with CYNARA_CALL_CAUSE_SERVICE_NOT_AVAILABLE
+ * or CYNARA_CALL_CAUSE_FINISH cause will return CYNARA_API_OPERATION_NOT_ALLOWED.
+ * cynara_async_finish() will be ignored if called from within this callback.
+ *
+ * \param[in] check_id Number identifying check request. Number is generated in
+ * cynara_async_create_request() or cynara_async_create_simple_request()
+ * and returned to user. It can be used to match response with sent request.
+ * Number is valid since cynara_async_create_request() call or
+ * cynara_async_create_simple_request() call till callback call.
+ * After that the number can be reused by cynara to run new request.
+ * \param[in] cause Cause of triggering this callback.
+ * \param[in] response Response for created request. Should be ignored if cause is not
+ * an answer to request (cause != CYNARA_CALL_CAUSE_ANSWER).
+ * \param[in] user_response_data User specific data - passed to cynara library in
+ * cynara_async_cancel_request() is being only remembered by library.
+ * Cynara library does not take any actions on this pointer,
+ * except for giving it back to user in cynara_response_callback.
+ * After that cynara forgets this data.
+ */
+typedef void (*cynara_response_callback) (cynara_check_id check_id, cynara_async_call_cause cause,
+ int response, void *user_response_data);
+
+/**
+ * \brief Callback used by cynara async API when status of cynara socket is changed in
+ * cynara_async_initialize(), cynara_async_check_cache(), cynara_async_create_request(),
+ * cynara_async_create_simple_request(), cynara_async_process(), cynara_async_cancel_request()
+ * or cynara_async_finish().
+ *
+ * File descriptor changes every time cynara library connects or disconnects cynara service.
+ * Status change is triggered when check_request or cancel needs to be send to
+ * cynara service or sending data has finished and there is nothing more to send to cynara
+ * service.
+ *
+ * Note, that provided file descriptor is used internally by libcynara-client-async
+ * so user should not use it in other way than waiting on it in event loop.
+ * In particular user should not write to, read from or close this fd.
+ * CYNARA_API_OPERATION_NOT_ALLOWED will be returned for every api function called in this callback.
+ * cynara_async_finish() will be ignored if called from within this callback.
+ *
+ * \param[in] old_fd Old descriptor which should be unregistered from event loop,
+ * Special value -1 is used when callback is called after first
+ * successful connect.
+ * \param[in] new_fd New descriptor which should be registered in event loop,
+ * Special value -1 is used when cynara_async_finish() is called and
+ * cynara is disconnected. In this case status should be ignored.
+ * \param[in] status Status indicating which events should be awaited on socket
+ * \param[in] user_status_data User specific data - passed to cynara library in
+ * cynara_async_initialize() is being only remembered by library.
+ * Cynara library does not take any actions on this pointer,
+ * except for giving it back to user in cynara_status_callback.
+ * Data should be valid at least until cynara_async_finish() is called.
+ */
+typedef void (*cynara_status_callback) (int old_fd, int new_fd, cynara_async_status status,
+ void *user_status_data);
+
+/**
+ * \par Description:
+ * Initialize cynara_async_configuration. Create structure used in following configuration
+ * API calls.
+ *
+ * \par Purpose:
+ * For configuration parameter to be used in cynara async initialization function, this API must be
+ * called before any other cynara async configuration API function.
+ * It will create cynara_async_configuration structure, an optional parameter of cynara async
+ * initialization.
+ *
+ * \par Typical use case:
+ * Once before setting parameters of cynara async configuration and passing to
+ * cynara_async_initialize().
+ *
+ * \par Method of function operation:
+ * This API initializes inner library structures and in case of success returns pointer
+ * to created cynara_async_configuration structure.
+ *
+ * \par Sync (or) Async:
+ * This as a synchronous API.
+ *
+ * \par Thread-safety:
+ * This function is NOT thread-safe. If functions from described API are called by multithreaded
+ * application from different threads, they must be put into protected critical section.
+ *
+ * \par Important notes:
+ * Structure cynara_async_configuration created by cynara_async_configuration_create() call
+ * should be released with cynara_async_configuration_destroy().
+ * Structure cynara_async_configuration should be destroyed after passing it to
+ * cynara_async_initialize().
+ *
+ * \param[out] pp_conf Placeholder for created cynara_async_configuration structure.
+ *
+ * \return CYNARA_API_SUCCESS on success
+ * \return negative error code on error
+ */
+int cynara_async_configuration_create(cynara_async_configuration **pp_conf);
+
+/**
+ * \par Description:
+ * Release cynara_async_configuration structure created with cynara_async_configuration_create().
+ *
+ * \par Purpose:
+ * This API should be used to clean up after usage of cynara_async_configuration.
+ *
+ * \par Typical use case:
+ * Once cynara_async_configuration is not needed.
+ *
+ * \par Method of function operation:
+ * This API releases inner library structure and destroys cynara_async_configuration structure.
+ *
+ * \par Sync (or) Async:
+ * This is a synchronous API.
+ *
+ * \par Thread-safety:
+ * This function is NOT thread-safe. If functions from described API are called by multithreaded
+ * application from different threads, they must be put into protected critical section.
+ *
+ * \param[in] p_conf cynara_async_configuration structure. If NULL, the call has no effect.
+ */
+void cynara_async_configuration_destroy(cynara_async_configuration *p_conf);
+
+/**
+ * \par Description:
+ * Set client cache size.
+ *
+ * \par Purpose:
+ * This API is used to change default number of cached responses returned from cynara.
+ *
+ * \par Typical use case:
+ * Once before setting parameters of cynara async configuration and passing to
+ * cynara_async_initialize().
+ *
+ * \par Method of function operation:
+ * This API initializes cache with given capacity.
+ *
+ * \par Sync (or) Async:
+ * This as a synchronous API.
+ *
+ * \par Thread-safety:
+ * This function is NOT thread-safe. If functions from described API are called by multithreaded
+ * application from different threads, they must be put into protected critical section.
+ *
+ * \par Important notes:
+ * After passing cynara_async_configuration to cynara_async_initialize() calling this API will have
+ * no effect.
+ *
+ * \param[in] p_conf cynara_async_configuration structure pointer.
+ * \param[in] cache_size Cache size to be set.
+ *
+ * \return CYNARA_API_SUCCESS on success
+ * \return negative error code on error
+ */
+int cynara_async_configuration_set_cache_size(cynara_async_configuration *p_conf,
+ size_t cache_size);
+
+/**
+ * \par Description:
+ * Initialize cynara-async-client library with given configuration. Create structure used in
+ * following API calls and register callback and user_status_data for
+ * further cynara async API calls.
+ *
+ * \par Purpose:
+ * This API must be used prior to calling any other cynara async API function.
+ * It will create cynara_async structure required for any other cynara async API calls.
+ *
+ * \par Typical use case:
+ * Once before entering event loop and before any other cynara async API is called.
+ *
+ * \par Method of function operation:
+ * This API initializes inner library structures and in case of success
+ * returns cynara_async structure.
+ *
+ * \par Sync (or) Async:
+ * This is an synchronous API.
+ *
+ * \par Thread-safety:
+ * This function is NOT thread-safe. If functions from described API are called by multithreaded
+ * application from different threads, they must be put into protected critical section.
+ *
+ * \par Important notes:
+ * Structure cynara_async created by cynara_async_initialize() call should be released
+ * with cynara_async_finish().
+ *
+ * \param[out] pp_cynara Placeholder for created cynara_async structure.
+ * \param[in] p_conf Configuration for cynara-async-client library.
+ * NULL should be used for default configuration.
+ * Configuration management functions will be added later.
+ * Configuration will be able to specify e.g. size of cache used by library
+ * for holding checks results.
+ * \param[in] callback Function called when connection is started.
+ * If NULL, no callback will be called, when status changes.
+ * \param[in] user_status_data User specific data, passed to callback is being only remembered
+ * by library. Cynara library does not take any actions on this pointer,
+ * except for giving it back to user in cynara_status_callback.
+ * Data should be valid at least until cynara_async_finish() is called.
+ * Can be NULL.
+ *
+ * \return CYNARA_API_SUCCESS on success
+ * \return negative error code on error
+ */
+int cynara_async_initialize(cynara_async **pp_cynara, const cynara_async_configuration *p_conf,
+ cynara_status_callback callback, void *user_status_data);
+
+/**
+ * \par Description:
+ * Release cynara-async-client library and destroy structure created with cynara_async_initialize().
+ *
+ * \par Purpose:
+ * This API should be used to clean up after usage of cynara-async-client library.
+ *
+ * \par Typical use case:
+ * Once after connection to cynara is not needed.
+ *
+ * \par Method of function operation:
+ * This API releases inner library structure and destroys cynara_async structure. Connection to
+ * cynara server is closed. Upon disconnecting this will trigger cynara_status_callback callback
+ * with -1 as new_fd param so client can unregister file descriptor connected with cynara. It will
+ * also trigger cynara_response_callback callback for each created request with
+ * cynara_async_call_cause::CYNARA_CALL_CAUSE_FINISH as cause param.
+ *
+ * \par Sync (or) Async:
+ * This is an asynchronous API.
+ *
+ * \par Thread-safety:
+ * This function is NOT thread-safe. If functions from described API are called by multithreaded
+ * application from different threads, they must be put into protected critical section.
+ *
+ * \par Important notes:
+ * No other call to cynara-async-client library should be made after call to cynara_async_finish().
+ * cynara_async_finish() called from within cynara_response_callback or cynara_status_callback will
+ * be ignored.
+ *
+ * \param[in] p_cynara cynara_async structure. If NULL, then the call has no effect.
+ */
+void cynara_async_finish(cynara_async *p_cynara);
+
+/**
+ * \par Description:
+ * Check access to given privilege for specified user, client and client_session in cache.
+ *
+ * \par Purpose:
+ * This API should be used to check if cache holds information about access of user,
+ * running application identified as clients to a privilege.
+ * This API should be used for fast check in cache.
+ *
+ * \par Typical use case:
+ * A service wants to check in cache, if a client requesting access to some privilege
+ * has proper rights.
+ *
+ * \par Method of function operation:
+ * Client (a process / application) requesting access to a privilege is running as some user.
+ * For such triple (client, user, privilege) a cache is checked.
+ * If cache is invalid it is cleared and call returns same as access not found.
+ * Additional parameter client_session
+ * may be used to distinguish between client session (e.g. for allowing access only for this
+ * particular application launch). Empty string "" can be used, when session differentiation
+ * is not needed.
+ *
+ * \par Sync (or) Async:
+ * This is an synchronous API.
+ *
+ * \par Thread-safety:
+ * This function is NOT thread-safe. If functions from described API are called by multithreaded
+ * application from different threads, they must be put into protected critical section.
+ *
+ * \par Important notes:
+ * \parblock
+ * Call to cynara_async_check_cache() needs cynara_async structure to be created first.
+ * Use cynara_async_initialize() before calling this function. cynara_async_check_cache() called
+ * from within cynara_status_callback or cynara_response_callback with
+ * CYNARA_CALL_CAUSE_SERVICE_NOT_AVAILABLE or CYNARA_CALL_CAUSE_FINISH cause will return
+ * CYNARA_API_OPERATION_NOT_ALLOWED.
+ *
+ * Socket status may occasionally be changed to CYNARA_STATUS_FOR_RW, to ensure
+ * that cynara_async_process() is triggered in event loop after socket is ready to send
+ * monitor logs to Cynara service.
+ *
+ * String length cannot exceed CYNARA_MAX_ID_LENGTH, otherwise CYNARA_API_INVALID_PARAM will be
+ * returned.
+ * \endparblock
+ *
+ * \param[in] p_cynara cynara_async structure.
+ * \param[in] client Application or process identifier.
+ * \param[in] client_session Client defined session.
+ * \param[in] user User of running client.
+ * \param[in] privilege Privilege that is a subject of a check.
+ *
+ * \return CYNARA_API_ACCESS_ALLOWED on checked access allowed
+ * \return CYNARA_API_ACCESS_DENIED on checked access denied
+ * \return CYNARA_API_CACHE_MISS on access not in cache
+ * \return other negative error code on error
+ */
+int cynara_async_check_cache(cynara_async *p_cynara, const char *client, const char *client_session,
+ const char *user, const char *privilege);
+
+/**
+ * \par Description:
+ * Creates access check request to cynara service for client, user accessing given privilege.
+ * Set callback and user_response_data to be called and passed when request processing is finished.
+ *
+ * \par Purpose:
+ * This API should be used to create check request for client identified by a triple
+ * (client, user, privilege) in custom defined session.
+ * Response can be received with cynara_async_process().
+ * Check id is returned to pair request with response for canceling purposes.
+ *
+ * \par Typical use case:
+ * When cynara_async_check_cache() returned CYNARA_API_CACHE_MISS, so cynara service
+ * has to be asked, if a client requesting access to some privilege has proper rights.
+ * To receive matching response client sets callback and specifies arbitrary data to be passed
+ * to this callback.
+ *
+ * \par Method of function operation:
+ * \parblock
+ * Client (a process / application) requesting access to a privilege is running as some user.
+ * For such triple (client, user, privilege) a request event is created and added to pending
+ * requests for cynara_async_process() to process.
+ *
+ * Socket status will be changed to CYNARA_STATUS_FOR_RW, to ensure that cynara_async_process()
+ * will be triggered in event loop after socket is ready to send request to cynara service.
+ * After request is sent and there is nothing more to send to cynara service, status will change
+ * back to CYNARA_STATUS_FOR_READ. Status changes are delivered with cynara_status_callback.
+ * When function is successfully called unique check_id is returned. It is used for matching
+ * generated request with response, that will be received by registered callback.
+ *
+ * Because check_id is coded as 16-bit unsigned integer, there can be only 2^16 = 65536 pending
+ * requests. When response callback is called either because of getting answer or because
+ * of cancel check_id used for that request is released and can be reused by cynara library.
+ * When maximum of pending requests is reached cynara_async_create_request() fails with
+ * CYNARA_API_MAX_PENDING_REQUESTS error code.
+ * \endparblock
+ *
+ * \par Sync (or) Async:
+ * This is an asynchronous API.
+ *
+ * \par Thread-safety:
+ * This function is NOT thread-safe. If functions from described API are called by multithreaded
+ * application from different threads, they must be put into protected critical section.
+ *
+ * \par Important notes:
+ * \parblock
+ * Call cynara_async_create_request() needs cynara_async structure to be created first with
+ * cynara_async_initialize().
+ * Call cynara_async_cancel_request() to cancel pending request.
+ * Call cynara_async_process() to receive response.
+ *
+ * It is guaranteed that if cynara_async_create_request() function succeeds (CYNARA_API_SUCCESS)
+ * a callback will be called exactly once and that it will receive user_response_data.
+ *
+ * If function fails (returns negative error code) request won't be generated and won't be pending,
+ * callback function won't be ever called and user_response_data won't be remembered by library.
+ *
+ * Also no check_id will be generated and *p_check_id value should be ignored.
+ * cynara_async_create_request() called from within cynara_status_callback or
+ * cynara_response_callback with CYNARA_CALL_CAUSE_SERVICE_NOT_AVAILABLE or CYNARA_CALL_CAUSE_FINISH
+ * cause will return CYNARA_API_OPERATION_NOT_ALLOWED.
+ *
+ * String length cannot exceed CYNARA_MAX_ID_LENGTH ,otherwise CYNARA_API_INVALID_PARAM will be
+ * returned.
+ * \endparblock
+ *
+ * \param[in] p_cynara cynara_async structure.
+ * \param[in] client Application or process identifier.
+ * \param[in] client_session Client defined session.
+ * \param[in] user User of running client.
+ * \param[in] privilege Privilege that is a subject of a check.
+ * \param[out] p_check_id Placeholder for check id. If NULL, then no check_id is returned.
+ * \param[in] callback Function called when matching response is received.
+ * If NULL then no callback will be called when response, cancel, finish
+ * or service not availble error happens.
+ * \param[in] user_response_data User specific data, passed to callback is being only remembered
+ * by library. Cynara library does not take any actions on this pointer,
+ * except for giving it back to user in cynara_response_callback.
+ * Can be NULL.
+ *
+ * \return CYNARA_API_SUCCESS on success
+ * \return CYNARA_API_MAX_PENDING_REQUESTS on too much pending requests
+ * \return other negative error code on error
+ */
+int cynara_async_create_request(cynara_async *p_cynara, const char *client,
+ const char *client_session, const char *user, const char *privilege,
+ cynara_check_id *p_check_id, cynara_response_callback callback,
+ void *user_response_data);
+
+/**
+ * \par Description:
+ * Creates simple access check request to cynara service for (potential) permission to take some
+ * action or access a resource.
+ * Set callback and user_response_data to be called and passed when request processing is finished.
+ *
+ * \par Purpose:
+ * This API should be used for a quick check if a user running application identified as client
+ * has access to a given privilege.
+ * Response can be received with cynara_async_process().
+ * Check id is returned to pair request with response for canceling purposes.
+ *
+ * \par Typical use case:
+ * An application may use this API to check if it has (potential) permission to take some action
+ * or access resource in future (permissions may rarely change). The typical use would be to disable
+ * or hide some of functionalities if they probably could not be used anyways.
+ *
+ * \par Method of function operation:
+ * \parblock
+ * This function is very similar to cynara_async_create_request() with the difference, that in case
+ * of answer not being one of CYNARA_API_PERMISSION_DENIED or CYNARA_API_PERMISSION_ALLOWED,
+ * no external application will be consulted. Instead, CYNARA_API_ACCESS_NOT_RESOLVED is returned
+ * by a callback, meaning, that only creating full request through cynara_async_create_request() API
+ * would yield eventual answer.
+ *
+ * If access permission cannot be acquired without usage of external agents, callback can be
+ * called with CYNARA_CALL_CAUSE_ANSWER and response value being CYNARA_API_ACCESS_NOT_RESOLVED.
+ * \endparblock
+ *
+ * \par Sync (or) Async:
+ * This is a synchronous API.
+ *
+ * \par Thread-safety:
+ * This function is NOT thread-safe. If functions from described API are called by multithreaded
+ * application from different threads, they must be put into mutex protected critical section.
+ *
+ * \par Important notes:
+ * \parblock
+ * Call cynara_async_create_simple_request() needs cynara_async structure to be created first
+ * with cynara_async_initialize().
+ * Call cynara_async_cancel_request() to cancel pending request.
+ * Call cynara_async_process() to send request and receive response.
+ *
+ * The answer will be taken from cynara's database without consulting any external applications.
+ * If the answer cannot be resolved in one of CYNARA_API_ACCESS_ALLOWED or
+ * CYNARA_API_ACCESS_DENIED without communicating with external application, response returned
+ * through callback will have value CYNARA_API_ACCESS_NOT_RESOLVED.
+ *
+ * String length cannot exceed CYNARA_MAX_ID_LENGTH, otherwise CYNARA_API_INVALID_PARAM will be
+ * returned.
+ * \endparblock
+ *
+ * \param[in] p_cynara cynara_async structure.
+ * \param[in] client Application or process identifier.
+ * \param[in] client_session Client defined session.
+ * \param[in] user User of running client.
+ * \param[in] privilege Privilege that is a subject of a check.
+ * \param[out] p_check_id Placeholder for check id. If NULL, then no check_id is returned.
+ * \param[in] callback Function called when matching response is received.
+ * If NULL then no callback will be called when response, cancel, finish
+ * or service not availble error happens.
+ * \param[in] user_response_data User specific data, passed to callback is being only stored by
+ * library. Cynara library does not take any actions on this pointer, except for giving
+ * it back to user in cynara_response_callback.
+ * Can be NULL.
+ *
+ * \return CYNARA_API_SUCCESS on success
+ * \return CYNARA_API_MAX_PENDING_REQUESTS on too much pending requests
+ * \return other negative error code on error
+ */
+int cynara_async_create_simple_request(cynara_async *p_cynara, const char *client,
+ const char *client_session, const char *user,
+ const char *privilege, cynara_check_id *p_check_id,
+ cynara_response_callback callback, void *user_response_data);
+
+/**
+ * \par Description:
+ * Process events that appeared on cynara socket.
+ *
+ * \par Purpose:
+ * Process events after they appear on cynara socket.
+ *
+ * \par Typical use case:
+ * After request was queued with cynara_async_create_request() or
+ * cynara_async_create_simple_request() this API will return response.
+ * When event loop will return readiness on cynara socket, client should use this API.
+ *
+ * \par Method of function operation:
+ * \parblock
+ * This API sends pending requests, receives all responses and reacts when cynara
+ * has disconnected. If cynara has disconnected all values in cache become invalid. During these
+ * operations status of cynara socket may change, so cynara_status_callback callback will be
+ * triggered to indicate these changes. cynara_response_callback callback will be triggered with
+ * cynara_async_call_cause::CYNARA_CALL_CAUSE_ANSWER as cause param when response is available.
+ *
+ * If cynara has disconnected it will be triggered with
+ * cynara_async_call_cause::CYNARA_CALL_CAUSE_SERVICE_NOT_AVAILABLE as cause param.
+ * \endparblock
+ *
+ * \par Sync (or) Async:
+ * This is an asynchronous API.
+ *
+ * \par Thread-safety:
+ * This function is NOT thread-safe. If functions from described API are called by multithreaded
+ * application from different threads, they must be put into protected critical section.
+ *
+ * \par Important notes:
+ * Call to cynara_async_process() requires initialized cynara_async structure. For this use
+ * cynara_async_initialize(). cynara_async_process() called from within cynara_status_callback or
+ * cynara_response_callback with CYNARA_CALL_CAUSE_SERVICE_NOT_AVAILABLE or CYNARA_CALL_CAUSE_FINISH
+ * cause will return CYNARA_API_OPERATION_NOT_ALLOWED.
+ *
+ * \param[in] p_cynara cynara_async structure.
+ *
+ * \return CYNARA_API_SUCCESS on success
+ * \return negative error code on error
+ */
+int cynara_async_process(cynara_async *p_cynara);
+
+/**
+ * \par Description:
+ * Cancel request created by cynara_async_create_request() or cynara_async_create_simple_request().
+ *
+ * \par Purpose:
+ * This API should be used to cancel pending check request,
+ * created by cynara_async_create_request() or cynara_async_create_simple_request().
+ *
+ * \par Typical use case:
+ * When cynara client is no longer interested in receiving an answer.
+ * Same check_id value should be used to identify proper request as was generated during
+ * request creation with cynara_async_create_request() or cynara_async_create_simple_request().
+ *
+ * \par Method of function operation:
+ * \parblock
+ * Cancels request created by cynara_async_create_request() or cynara_async_create_simple_request()
+ * call.
+ *
+ * cynara_status_callback callback may be triggered to be able to send cancel to cynara.
+ * cynara_response_callback callback will be triggered with with
+ * cynara_async_call_cause::CYNARA_CALL_CAUSE_CANCEL as cause param.
+ *
+ * If given id is not valid (was not requested or response callback was already delivered)
+ * cynara_async_cancel_request() returns CYNARA_API_INVALID_PARAM.
+ * \endparblock
+ *
+ * \par Sync (or) Async:
+ * This is a synchronous API.
+ *
+ * \par Thread-safety:
+ * This function is NOT thread-safe. If functions from described API are called by multithreaded
+ * application from different threads, they must be put into protected critical section.
+ *
+ * \par Important notes:
+ * Call to cynara_async_cancel_request() needs cynara_async structure to be created first. For this
+ * use cynara_async_initialize(). cynara_async_cancel_request() called from within
+ * cynara_status_callback or cynara_response_callback with CYNARA_CALL_CAUSE_SERVICE_NOT_AVAILABLE
+ * or CYNARA_CALL_CAUSE_FINISH cause will return CYNARA_API_OPERATION_NOT_ALLOWED.
+ *
+ * \param[in] p_cynara cynara_async structure.
+ * \param[in] check_id Check id to be cancelled
+ *
+ * \return CYNARA_API_SUCCESS on success
+ * \return negative error code on error
+ */
+int cynara_async_cancel_request(cynara_async *p_cynara, cynara_check_id check_id);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CYNARA_CLIENT_ASYNC_H */
diff --git a/include/cynara/cynara-client-plugin.h b/include/cynara/cynara-client-plugin.h
new file mode 100644
index 0000000..70000e6
--- /dev/null
+++ b/include/cynara/cynara-client-plugin.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2014-2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+/**
+ * @file src/include/cynara-client-plugin.h
+ * @author Zofia Abramowska <z.abramowska@samsung.com>
+ * @author Oskar Switalski <o.switalski@samsung.com>
+ * @version 1.0
+ * @brief This file defines cynara client side of external plugin interface -
+ * ClientPluginInterface.
+ */
+
+#ifndef CYNARACLIENTPLUGIN_H_
+#define CYNARACLIENTPLUGIN_H_
+
+#include <memory>
+
+#include <plugin/ExternalPluginInterface.h>
+#include <types/ClientSession.h>
+#include <types/PolicyResult.h>
+
+namespace Cynara {
+
+class ClientPluginInterface;
+typedef std::shared_ptr<ClientPluginInterface> ClientPluginInterfacePtr;
+
+/**
+ * A class defining external plugins interface.
+ *
+ * These plugins work inside of cynara client library. They interpret
+ * PolicyResult returned by cynara in terms of:
+ * * cacheability - tells, whether value should be cached (for e.g. policyType like
+ * ALLOW_ONCE should not be cached)
+ * * usability - whether cache entry can still be used (for e.g. policy allowing access for
+ * given type)
+ * * value - translates PolicyResult to CYNARA_API_ACCESS_ALLOWED or CYNARA_API_ACCESS_DENIED
+ *
+ * Plugin implementing ClientPluginInterface must implement ExternalPluginInterface.
+ * Creation/destruction functions with signatures compatible to Cynara::create_t and
+ * Cynara::destroy_t must be provided as factories of ClientPluginInterface.
+ */
+class ClientPluginInterface : public ExternalPluginInterface {
+public:
+ /**
+ * Return entry cacheability
+ */
+ virtual bool isCacheable(const ClientSession &session, const PolicyResult &result) = 0;
+ /**
+ * Return entry usability
+ */
+ virtual bool isUsable(const ClientSession &session, const ClientSession &prevSession,
+ bool &updateSession, PolicyResult &result) = 0;
+ /**
+ * Translate PolicyResult to CYNARA_API_ACCESS_ALLOWED or CYNARA_API_ACCESS_DENIED
+ */
+ virtual int toResult(const ClientSession &session, PolicyResult &result) = 0;
+
+ virtual ~ClientPluginInterface() {};
+};
+
+}
+
+#endif // CYNARACLIENTPLUGIN_H_
diff --git a/include/cynara/cynara-client.h b/include/cynara/cynara-client.h
new file mode 100644
index 0000000..d1388ec
--- /dev/null
+++ b/include/cynara/cynara-client.h
@@ -0,0 +1,305 @@
+/*
+ * Copyright (c) 2014-2016 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+/**
+ * @file src/include/cynara-client.h
+ * @author Lukasz Wojciechowski <l.wojciechow@partner.samsung.com>
+ * @author Zofia Abramowska <z.abramowska@samsung.com>
+ * @author Oskar Switalski <o.switalski@samsung.com>
+ * @version 1.0
+ * @brief This file contains client APIs of Cynara available with libcynara-client.
+ * @example cynara-client.example
+ */
+
+#ifndef CYNARA_CLIENT_H
+#define CYNARA_CLIENT_H
+
+#include <stddef.h>
+
+#include <cynara/cynara-error.h>
+#include <cynara/cynara-limits.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct cynara cynara;
+typedef struct cynara_configuration cynara_configuration;
+
+/**
+ * \par Description:
+ * Initialize cynara_configuration. Create structure used in following configuration
+ * API calls.
+ *
+ * \par Purpose:
+ * For configuration parameter to be used in cynara_initialize() function, this API must be
+ * called before any other cynara configuration API function.
+ * It will create cynara_configuration structure, an optional parameter of cynara initialization.
+ *
+ * \par Typical use case:
+ * Once before setting parameters of cynara configuration and passing to
+ * cynara_initialize().
+ *
+ * \par Method of function operation:
+ * This API initializes inner library structures and in case of success returns pointer
+ * to created cynara_configuration structure.
+ *
+ * \par Sync (or) Async:
+ * This as a synchronous API.
+ *
+ * \par Thread-safety:
+ * This function is NOT thread-safe. If functions from described API are called by multithreaded
+ * application from different threads, they must be put into protected critical section.
+ *
+ * \par Important notes:
+ * Structure cynara_configuration created by cynara_configuration_create() call
+ * should be released with cynara_configuration_destroy().
+ * Structure cynara_configuration should be destroyed after passing it to
+ * cynara_initialize().
+ *
+ * \param[out] pp_conf Placeholder for created cynara_configuration structure.
+ *
+ * \return CYNARA_API_SUCCESS on success
+ * or negative error code on error.
+ */
+int cynara_configuration_create(cynara_configuration **pp_conf);
+
+/**
+ * \par Description:
+ * Release cynara_configuration structure created with cynara_configuration_create().
+ *
+ * \par Purpose:
+ * This API should be used to clean up after usage of cynara_configuration.
+ *
+ * \par Typical use case:
+ * Once cynara_configuration is not needed.
+ *
+ * \par Method of function operation:
+ * This API releases inner library structure and destroys cynara_configuration structure.
+ *
+ * \par Sync (or) Async:
+ * This is a synchronous API.
+ *
+ * \par Thread-safety:
+ * This function is NOT thread-safe. If functions from described API are called by multithreaded
+ * application from different threads, they must be put into protected critical section.
+ *
+ * \param[in] p_conf cynara_configuration structure. If NULL, the call has no effect.
+ */
+void cynara_configuration_destroy(cynara_configuration *p_conf);
+
+/**
+ * \par Description:
+ * Set client cache size.
+ *
+ * \par Purpose:
+ * This API is used to change default number of cached responses returned from cynara.
+ *
+ * \par Typical use case:
+ * Once after cynara_configuration is created with cynara_configuration_create()
+ * and before passing configuration to cynara_initialize().
+ *
+ * \par Method of function operation:
+ * This API initializes cache with given capacity.
+ *
+ * \par Sync (or) Async:
+ * This as a synchronous API.
+ *
+ * \par Thread-safety:
+ * This function is NOT thread-safe. If functions from described API are called by multithreaded
+ * application from different threads, they must be put into protected critical section.
+ *
+ * \par Important notes:
+ * After passing cynara_configuration to cynara_initialize() calling this API will have
+ * no effect.
+ *
+ * \param[in] p_conf cynara_configuration structure pointer.
+ * \param[in] cache_size Cache size to be set.
+ *
+ * \return CYNARA_API_SUCCESS on success
+ * or negative error code on error.
+ */
+int cynara_configuration_set_cache_size(cynara_configuration *p_conf, size_t cache_size);
+
+/**
+ * \par Description:
+ * Initialize cynara-client library with given configuration.
+ * Create structure used in following API calls.
+ *
+ * \par Purpose:
+ * This API must be used by prior calling cynara_check() function.
+ *
+ * \par Typical use case:
+ * Once before a service can call cynara_check().
+ *
+ * \par Method of function operation:
+ * This API initializes inner library structures and in case of success
+ * creates and returns cynara structure.
+ *
+ * \par Sync (or) Async:
+ * This is a Synchronous API.
+ *
+ * \par Thread-safeness:
+ * This function is NOT thread-safe. If functions from described API are called by multithreaded
+ * application from different threads, they must be put into mutex protected critical section.
+ *
+ * \par Important notes:
+ * Structure cynara created by cynara_initialize() call should be released with cynara_finish().
+ *
+ * \param[out] pp_cynara Place holder for created cynara structure.
+ * \param[in] p_conf Configuration for cynara-client library. NULL for default parameters.
+ * [TODO define and describe functions for custom parameters].
+ *
+ * \return CYNARA_API_SUCCESS on success, or error code on error.
+ */
+int cynara_initialize(cynara **pp_cynara, const cynara_configuration *p_conf);
+
+/**
+ * \par Description:
+ * Release cynara-client library and destroy structure created with cynara_initialize().
+ *
+ * \par Purpose:
+ * This API should be used to clean up after usage of cynara-client library.
+ *
+ * \par Typical use case:
+ * Once after last call to cynara_check().
+ *
+ * \par Method of function operation:
+ * This API releases inner library structures and destroys cynara structure.
+ *
+ * \par Sync (or) Async:
+ * This is a Synchronous API.
+ *
+ * \par Thread-safeness:
+ * This function is NOT thread-safe. If functions from described API are called by multithreaded
+ * application from different threads, they must be put into mutex protected critical section.
+ *
+ * \par Important notes:
+ * No other call to libcynara-client should be made after call to cynara_finish().
+ *
+ * \param[in] p_cynara Cynara structure.
+ *
+ * \return CYNARA_API_SUCCESS on success, or error code on error.
+ */
+int cynara_finish(cynara *p_cynara);
+
+/**
+ * \par Description:
+ * Check client, user access for given privilege.
+ *
+ * \par Purpose:
+ * This API should be used to check if a user running application identified as client
+ * has access to a privilege.
+ *
+ * \par Typical use case:
+ * A service want to ask trusted process (Cynara), if a client demanding access to some privilege
+ * has proper rights.
+ *
+ * \par Method of function operation:
+ * Client (a process / application) demanding access to a privilege is running as some user.
+ * For such triple an access to a privilege is checked by calling cynara.
+ * Depending on defined policy, an external application may be launched to ask user a question,
+ * e.g. if [s]he wants to allow client to use a privilege. Additional parameter client_session
+ * may be used to distinguish between client session (e.g. for allowing access only for this
+ * particular application launch).
+ *
+ * \par Sync (or) Async:
+ * This is a Synchronous API.
+ *
+ * \par Thread-safeness:
+ * This function is NOT thread-safe. If functions from described API are called by multithreaded
+ * application from different threads, they must be put into mutex protected critical section.
+ *
+ * \par Important notes:
+ * \parblock
+ * An external application may be launched to allow user interaction in granting or denying access.
+ *
+ * Call to cynara_check() needs cynara structure to be created first with call to
+ * cynara_initialize().
+ *
+ * String length cannot exceed CYNARA_MAX_ID_LENGTH, otherwise CYNARA_API_INVALID_PARAM will be
+ * returned.
+ * \endparblock
+ *
+ * \param[in] p_cynara Cynara structure.
+ * \param[in] client Application or process identifier.
+ * \param[in] client_session Session of client (connection, launch).
+ * \param[in] user User running client.
+ * \param[in] privilege Privilege that is a subject of a check..
+ *
+ * \return CYNARA_API_ACCESS_ALLOWED on access allowed, CYNARA_API_ACCESS_DENIED on access denial
+ * or other error code on error.
+ */
+int cynara_check(cynara *p_cynara, const char *client, const char *client_session, const char *user,
+ const char *privilege);
+
+/**
+ * \par Description:
+ * Check for (potential) permission to take some action or access a resource.
+ *
+ * \par Purpose:
+ * This API should be used for a quick check if a user running application identified as client
+ * has access to a given privilege.
+ *
+ * \par Typical use case:
+ * An application may use this API to check, if it has (potential) permission to take some action
+ * or access resource in future (permissions may rarely change). The typical use would be to disable
+ * or hide some of functionalities, if they probably could not be used anyways.
+ *
+ * \par Method of function operation:
+ * This function is very similar to cynara_check() with the difference, that in case of answer not
+ * being one of CYNARA_API_ACCESS_DENIED or CYNARA_API_ACCESS_ALLOWED, no external application will
+ * be consulted. Instead, CYNARA_API_ACCESS_NOT_RESOLVED is returned, meaning, that only running
+ * full cynara_check() API would yield eventual answer.
+ * Similarly, like in cynara_check(), argument client_session can be used to distinguish client
+ * sessions and grant possibility to yield answer from cache.
+ *
+ * \par Sync (or) Async:
+ * This is a synchronous API.
+ *
+ * \par Thread-safety:
+ * This function is NOT thread-safe. If functions from described API are called by multithreaded
+ * application from different threads, they must be put into mutex protected critical section.
+ *
+ * \par Important notes:
+ * \parblock
+ * The answer will be taken from Cynara's database without consulting any external applications.
+ * If the answer cannot be resolved in one of CYNARA_API_PERMISSION_DENIED or
+ * CYNARA_API_PERMISSION_ALLOWED without communicating with external application the API will return
+ * CYNARA_API_ACCESS_NOT_RESOLVED.
+ *
+ * Call to cynara_simple_check() needs cynara structure to be created first with call to
+ * cynara_initialize(). String length cannot exceed CYNARA_MAX_ID_LENGTH, otherwise
+ * CYNARA_API_INVALID_PARAM will be returned.
+ * \endparblock
+ *
+ * \param[in] p_cynara Cynara structure.
+ * \param[in] client Application or process identifier.
+ * \param[in] client_session Session of client (connection, launch).
+ * \param[in] user User running client.
+ * \param[in] privilege Privilege that is a subject of a check.
+ *
+ * \return CYNARA_API_ACCESS_ALLOWED on access allowed, CYNARA_API_ACCESS_DENIED on access denial,
+ * CYNARA_API_ACCESS_NOT_RESOLVED when decision is not known without usage of external plugins or
+ * agents or negative error code on error.
+ */
+int cynara_simple_check(cynara *p_cynara, const char *client, const char *client_session,
+ const char *user, const char *privilege);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CYNARA_CLIENT_H */
diff --git a/include/cynara/cynara-creds-commons.h b/include/cynara/cynara-creds-commons.h
new file mode 100644
index 0000000..81b9784
--- /dev/null
+++ b/include/cynara/cynara-creds-commons.h
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2014-2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+/**
+ * @file src/include/cynara-creds-commons.h
+ * @author Lukasz Wojciechowski <l.wojciechow@partner.samsung.com>
+ * @author Radoslaw Bartosiak <r.bartosiak@samsung.com>
+ * @author Aleksander Zdyb <a.zdyb@samsung.com>
+ * @author Oskar Switalski <o.switalski@samsung.com>
+ * @version 1.0
+ * @brief This file contains common APIs for Cynara credentials helper.
+ */
+
+#ifndef CYNARA_CREDS_COMMONS_H
+#define CYNARA_CREDS_COMMONS_H
+
+#include <cynara/cynara-error.h>
+
+enum cynara_client_creds {
+ CLIENT_METHOD_SMACK,
+ CLIENT_METHOD_PID,
+
+ CLIENT_METHOD_DEFAULT = 0xFFFF,
+};
+
+enum cynara_user_creds {
+ USER_METHOD_UID,
+ USER_METHOD_GID,
+
+ USER_METHOD_DEFAULT = 0xFFFF,
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \par Description:
+ * Gets the system default method value for client feature used in cynara-creds.
+ *
+ * \par Purpose:
+ * Functions cynara_creds_dbus_get_client() and cynara_creds_socket_get_client() take a method
+ * parameter, which determines a kind of process feature (i.e PID, SMACK label) returned by them.
+ * The described function provides implementation for obtaining a system default value
+ * for this parameter.
+ *
+ * \par Typical use case:
+ * The function might be called before cynara_creds_dbus_get_client()
+ * and cynara_creds_socket_get_client(), when functions shall be invoked with system default
+ * value of method parameter.
+ *
+ * \par Method of function operation:
+ * When this function is called for the first time it reads and returns the value of client_default
+ * parameter from /etc/cynara/creds.conf file.
+ * Returned value is cached so subsequent calls will not consult file again but use cached value.
+ * This also means that after the initial call any changes in the file will be ignored for the
+ * remaining lifetime of the process.
+ *
+ * \par Sync (or) Async:
+ * This is a synchronous API.
+ *
+ * \par Thread safety:
+ * This function is thread-safe.
+ *
+ * \param[out] method Placeholder for system default client feature
+ * (like CLIENT_METHOD_SMACK, CLIENT_METHOD_PID)
+ *
+ * \return CYNARA_API_SUCCESS on success
+ * \return CYNARA_API_CONFIGURATION_ERROR if the configuration file can not be opened or
+ * there are errors in configuration file
+ * \return CYNARA_API_OUT_OF_MEMORY if there is error in memory allocation
+ * \return CYNARA_API_UNKNOWN_ERROR if there is other error
+ *
+ */
+int cynara_creds_get_default_client_method(enum cynara_client_creds *method);
+
+/**
+ * \par Description:
+ * Gets the system default method value for user feature used in cynara-creds.
+ *
+ * \par Purpose:
+ * Functions cynara_creds_dbus_get_user() and cynara_creds_socket_get_user() take a method
+ * parameter, which determines a kind of process feature (i.e UID, GID) returned by them.
+ * The described function provides implementation for obtaining a system default value
+ * for this parameter.
+ *
+ * \par Typical use case:
+ * The function might be called before cynara_creds_dbus_get_user()
+ * and cynara_creds_socket_get_user() when functions shall be invoked with system default
+ * value of method parameter.
+ *
+ * \par Method of function operation:
+ * When this function is called for the first time it reads and returns the value of user_default
+ * parameter from /etc/cynara/creds.conf file.
+ * Returned value is cached so subsequent calls will not consult file again but use cached value.
+ * This also means that after the initial call any changes in the file will be ignored for the
+ * remaining lifetime of the process.
+ *
+ * \par Sync (or) Async:
+ * This is a synchronous API.
+ *
+ * \par Thread safety:
+ * This function is thread-safe.
+ *
+ * \param[out] method Placeholder for system default user feature
+ * (like USER_METHOD_UID, USER_METHOD_GID)
+ *
+ * \return CYNARA_API_SUCCESS on success
+ * \return CYNARA_API_CONFIGURATION_ERROR if the configuration file can not be opened or
+ * there are errors in configuration file
+ * \return CYNARA_API_OUT_OF_MEMORY if there is error in memory allocation
+ * \return CYNARA_API_UNKNOWN_ERROR if there is other error
+ */
+int cynara_creds_get_default_user_method(enum cynara_user_creds *method);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CYNARA_CREDS_COMMONS_H */
diff --git a/include/cynara/cynara-creds-dbus.h b/include/cynara/cynara-creds-dbus.h
new file mode 100644
index 0000000..04af96f
--- /dev/null
+++ b/include/cynara/cynara-creds-dbus.h
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 2014-2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+/**
+ * @file src/include/cynara-creds-dbus.h
+ * @author Lukasz Wojciechowski <l.wojciechow@partner.samsung.com>
+ * @author Radoslaw Bartosiak <r.bartosiak@samsung.com>
+ * @author Oskar Switalski <o.switalski@samsung.com>
+ * @version 1.0
+ * @brief This file contains Cynara credentials helper APIs for dbus clients.
+ * @example cynara-creds-dbus.example
+ */
+
+#ifndef CYNARA_CREDS_DBUS_H
+#define CYNARA_CREDS_DBUS_H
+
+#include <dbus/dbus.h>
+#include <sys/types.h>
+
+#include <cynara/cynara-creds-commons.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \par Description:
+ * Creates a client identification string with given method. Client is a process identified by the
+ * unique name at the other side of the dbus connection.
+ *
+ * \par Purpose:
+ * Client identification string is required for cynara_check() and cynara_async_create_request()
+ * functions.
+ *
+ * \par Typical use case:
+ * The function is called before the call of cynara_check() or cynara_async_create_request()
+ * function. Returned string is used as client parameter in cynara_check() or
+ * cynara_async_create_request() function. String is released with free() function when it is no
+ * longer needed.
+ *
+ * \par Method of function operation:
+ * The function generates client string by calling a method from DBus Interface
+ * ("org.freedesktop.DBus") which is placed on system bus ("org.freedesktop.DBus").
+ *
+ * \par Sync (or) Async:
+ * This is a synchronous API.
+ *
+ * \par Thread safety:
+ * This function is NOT thread-safe. If functions from described API are called by multithreaded
+ * application from different threads, they must be put into mutex protected critical section.
+ *
+ * \par Important notes:
+ * Memory for returned client string is obtained with malloc(), and should be freed with free().
+ * Allocated string is returned only, when function succeeds.
+ * If method is CLIENT_METHOD_DEFAULT then it will be chosen based on Cynara configuration file.
+ *
+ * \param[in] connection DBus connection to a bus. It manages incomming and outgoing messages
+ * \param[in] uniqueName DBus identifier of the client
+ * \param[in] method Method of client identifier creation
+ * \param[out] client Placeholder for allocated string containing client id
+ *
+ * \return CYNARA_API_SUCCESS on success
+ * \return CYNARA_API_INVALID_PARAM when client is NULL or uniqueName or client has wrong
+ * value (i.e NULL or non-existing)
+ * \return CYNARA_API_METHOD_NOT_SUPPORTED when requested method is not supported
+ * \return CYNARA_API_CONFIGURATION_ERROR if the configuration file can not be opened or
+ * there are errors in configuration file
+ * \return CYNARA_API_OUT_OF_MEMORY when there was error allocating memory
+ */
+int cynara_creds_dbus_get_client(DBusConnection *connection, const char *uniqueName,
+ enum cynara_client_creds method, char **client);
+
+/**
+ * \par Description:
+ * Creates a user identification string with given method. User is an executor of process
+ * at the other side of socket.
+ *
+ * \par Purpose:
+ * User identification string is required for cynara_check() and cynara_async_create_request()
+ * functions.
+ *
+ * \par Typical use case:
+ * The function is called before the call of cynara_check() or cynara_async_create_request()
+ * function. Returned string is used as user parameter in cynara_check() or
+ * cynara_async_create_request() function. String is released with free() function when it is no
+ * longer needed.
+ *
+ * \par Method of function operation:
+ * The function generates user string by calling a method from DBus Interface
+ * ("org.freedesktop.DBus") which is placed on system bus ("org.freedesktop.DBus").
+ *
+ * \par Sync (or) Async:
+ * This is a synchronous API.
+ *
+ * \par Thread safety:
+ * This function is NOT thread-safe. If functions from described API are called by multithreaded
+ * application from different threads, they must be put into mutex protected critical section.
+ *
+ * \par Important notes:
+ * Memory for returned user string is obtained with malloc(), and should be freed with free().
+ * Allocated string is returned only, when function succeeds.
+ * If method is USER_METHOD_DEFAULT then it will be chosen based on Cynara configuration file.
+ *
+ * \param[in] connection DBus connection to a bus. It manages incomming and outgoing messages
+ * \param[in] uniqueName DBus identifier of the client invoked by the user
+ * \param[in] method Method of client identifier creation
+ * \param[out] user Placeholder for allocated string containing user id
+ *
+ * \return CYNARA_API_SUCCESS on success
+ * \return CYNARA_API_INVALID_PARAM when user is NULL or connection is not valid DBus connection or
+ * uniqueName does not represent a process conected to the DBus
+ * \return CYNARA_API_METHOD_NOT_SUPPORTED when requested method is not supported
+ * \return CYNARA_API_CONFIGURATION_ERROR if the configuration file can not be opened or
+ * there are errors in configuration file
+ * \return CYNARA_API_OUT_OF_MEMORY when there was error allocating memory
+ */
+int cynara_creds_dbus_get_user(DBusConnection *connection, const char *uniqueName,
+ enum cynara_user_creds method, char **user);
+
+/**
+ * \par Description:
+ * Return PID of a proces identified by the unique name at the other side of the dbus connection.
+ *
+ * \par Purpose:
+ * PID may be used for client_session creation with cynara_helper_session_from_pid() function
+ * from libcynara-helper-session library. Client_session is needed for cynara_check()
+ * and cynara_async_create_request() functions.
+ *
+ * \par Typical use case:
+ * The function is called before the call of cynara_helper_session_from_pid() function.
+ *
+ * \par Method of function operation:
+ * The function reads PID of the peer by calling a method from DBus Interface
+ * ("org.freedesktop.DBus") which is placed on system bus ("org.freedesktop.DBus")
+ * with "GetConnectionUnixProcessID" argument.
+ *
+ * \par Sync (or) Async:
+ * This is a synchronous API.
+ *
+ * \par Thread safety:
+ * This function is NOT thread-safe. If functions from described API are called by multithreaded
+ * application from different threads, they must be put into mutex protected critical section.
+ *
+ * \param[in] connection DBus connection to a bus. It manages incomming and outgoing messages
+ * \param[in] uniqueName DBus identifier of the client invoked by the user
+ * \param[out] pid Placeholder for PID returned by function
+ *
+ * \return CYNARA_API_SUCCESS on success
+ * \return CYNARA_API_INVALID_PARAM when socket_fd is not valid connected socket descriptor
+ * \return CYNARA_API_UNKNOWN_ERROR when system function fails in incredible situation
+ * \return CYNARA_API_OUT_OF_MEMORY when there was error allocating memory
+ */
+int cynara_creds_dbus_get_pid(DBusConnection *connection, const char *uniqueName, pid_t *pid);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CYNARA_CREDS_DBUS_H */
diff --git a/include/cynara/cynara-creds-gdbus.h b/include/cynara/cynara-creds-gdbus.h
new file mode 100644
index 0000000..2334e45
--- /dev/null
+++ b/include/cynara/cynara-creds-gdbus.h
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+/**
+ * @file src/include/cynara-creds-gdbus.h
+ * @author Jacek Bukarewicz <j.bukarewicz@samsung.com>
+ * @author Oskar Switalski <o.switalski@samsung.com>
+ * @version 1.0
+ * @brief This file contains Cynara credentials helper APIs for gdbus clients.
+ * @example cynara-creds-gdbus.example
+ */
+
+#ifndef CYNARA_CREDS_GDBUS_H
+#define CYNARA_CREDS_GDBUS_H
+
+#include <gio/gio.h>
+#include <sys/types.h>
+
+#include <cynara/cynara-creds-commons.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \par Description:
+ * Creates a client identification string with given method. Client is a process identified by the
+ * unique name at the other side of the dbus connection.
+ *
+ * \par Purpose:
+ * Client identification string is required for cynara_check() and cynara_async_create_request()
+ * functions.
+ *
+ * \par Typical use case:
+ * The function is called before the call of cynara_check() or cynara_async_create_request()
+ * function. Returned string is used as client parameter in cynara_check() or
+ * cynara_async_create_request() function. String is released with g_free() function when it is no
+ * longer needed.
+ *
+ * \par Method of function operation:
+ * The function generates client string by calling a method from DBus Interface
+ * ("org.freedesktop.DBus") which is placed on system bus ("org.freedesktop.DBus").
+ *
+ * \par Sync (or) Async:
+ * This is a synchronous API.
+ *
+ * \par Thread safety:
+ * This function is NOT thread-safe. If functions from described API are called by multithreaded
+ * application from different threads, they must be put into mutex protected critical section.
+ *
+ * \par Important notes:
+ * Memory for returned user string should be freed with g_free().
+ * Allocated string is returned only, when function succeeds.
+ * If method is CLIENT_METHOD_DEFAULT then it will be chosen based on Cynara configuration file.
+ *
+ * \param[in] connection DBus connection to a bus. It manages incomming and outgoing messages
+ * \param[in] uniqueName DBus identifier of the client
+ * \param[in] method Method of client identifier creation
+ * \param[out] client Placeholder for allocated string containing client id
+ *
+ * \return CYNARA_API_SUCCESS on success
+ * \return CYNARA_API_INVALID_PARAM when client is NULL or uniqueName or client has wrong
+ * value (i.e NULL or non-existing)
+ * \return CYNARA_API_CONFIGURATION_ERROR if the configuration file can not be opened or
+ * there are errors in configuration file
+ * \return CYNARA_API_METHOD_NOT_SUPPORTED when requested method is not supported
+ */
+int cynara_creds_gdbus_get_client(GDBusConnection *connection, const gchar *uniqueName,
+ enum cynara_client_creds method, gchar **client);
+
+/**
+ * \par Description:
+ * Creates a user identification string with given method. User is an executor of process
+ * at the other side of socket.
+ *
+ * \par Purpose:
+ * User identification string is required for cynara_check() and cynara_async_create_request()
+ * functions.
+ *
+ * \par Typical use case:
+ * The function is called before the call of cynara_check() or cynara_async_create_request()
+ * function. Returned string is used as user parameter in cynara_check() or
+ * cynara_async_create_request() function. String is released with g_free() function when it is no
+ * longer needed.
+ *
+ * \par Method of function operation:
+ * The function generates user string by calling a method from DBus Interface
+ * ("org.freedesktop.DBus") which is placed on system bus ("org.freedesktop.DBus").
+ *
+ * \par Sync (or) Async:
+ * This is a synchronous API.
+ *
+ * \par Thread safety:
+ * This function is NOT thread-safe. If functions from described API are called by multithreaded
+ * application from different threads, they must be put into mutex protected critical section.
+ *
+ * \par Important notes:
+ * Memory for returned user string should be freed with g_free().
+ * Allocated string is returned only, when function succeeds.
+ * If method is USER_METHOD_DEFAULT then it will be chosen based on Cynara configuration file.
+ *
+ * \param[in] connection DBus connection to a bus. It manages incomming and outgoing messages
+ * \param[in] uniqueName DBus identifier of the client invoked by the user
+ * \param[in] method Method of client identifier creation
+ * \param[out] user Placeholder for allocated string containing user id
+ *
+ * \return CYNARA_API_SUCCESS on success
+ * \return CYNARA_API_INVALID_PARAM when user is NULL or connection is not valid DBus connection or
+ * uniqueName does not represent a process conected to the DBus
+ * \return CYNARA_API_CONFIGURATION_ERROR if the configuration file can not be opened or
+ * there are errors in configuration file
+ * \return CYNARA_API_METHOD_NOT_SUPPORTED when requested method is not supported
+ */
+int cynara_creds_gdbus_get_user(GDBusConnection *connection, const gchar *uniqueName,
+ enum cynara_user_creds method, gchar **user);
+
+/**
+ * \par Description:
+ * Return PID of a process identified by the unique name at the other side of the dbus connection.
+ *
+ * \par Purpose:
+ * PID may be used for client_session creation with cynara_helper_session_from_pid() function
+ * from libcynara-helper-session library. Client_session is needed for cynara_check()
+ * and cynara_async_create_request() functions.
+ *
+ * \par Typical use case:
+ * The function is called before the call of cynara_helper_session_from_pid() function.
+ *
+ * \par Method of function operation:
+ * The function reads PID of the peer by calling a method from DBus Interface
+ * ("org.freedesktop.DBus") which is placed on system bus ("org.freedesktop.DBus")
+ * with "GetConnectionUnixProcessID" argument.
+ *
+ * \par Sync (or) Async:
+ * This is a synchronous API.
+ *
+ * \par Thread safety:
+ * This function is NOT thread-safe. If functions from described API are called by multithreaded
+ * application from different threads, they must be put into mutex protected critical section.
+ *
+ * \param[in] connection DBus connection to a bus. It manages incomming and outgoing messages
+ * \param[in] uniqueName DBus identifier of the client invoked by the user
+ * \param[out] pid Placeholder for PID returned by function
+ *
+ * \return CYNARA_API_SUCCESS on success
+ * \return CYNARA_API_INVALID_PARAM when one of parameters is not valid
+ * \return CYNARA_API_UNKNOWN_ERROR when function fails because of unknown error
+ */
+int cynara_creds_gdbus_get_pid(GDBusConnection *connection, const gchar *uniqueName, pid_t *pid);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CYNARA_CREDS_GDBUS_H */
diff --git a/include/cynara/cynara-creds-self.h b/include/cynara/cynara-creds-self.h
new file mode 100644
index 0000000..580a19d
--- /dev/null
+++ b/include/cynara/cynara-creds-self.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+/**
+ * @file src/include/cynara-creds-self.h
+ * @author Zofia Abramowska <z.abramowska@samsung.com>
+ * @version 1.0
+ * @brief This file contains Cynara credentials helper APIs for current process
+ * @example cynara-creds-self.example
+ */
+
+#ifndef CYNARA_CREDS_SELF_H
+#define CYNARA_CREDS_SELF_H
+
+#include <sys/types.h>
+
+#include <cynara/cynara-creds-commons.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \par Description:
+ * Creates a client identification string with given method. Client is a current process.
+ *
+ * \par Purpose:
+ * Client identification string is required for cynara_check() and cynara_async_create_request()
+ * functions.
+ *
+ * \par Typical use case:
+ * The function is called before the call of cynara_check() or cynara_async_create_request()
+ * function. Returned string is used as client parameter in cynara_check() or
+ * cynara_async_create_request() function. String is released with free() function when it is no
+ * longer needed.
+ *
+ * \par Method of function operation:
+ * The function generates client string using current process context.
+ *
+ * \par Sync (or) Async:
+ * This is a synchronous API.
+ *
+ * \par Thread safety:
+ * This function is NOT thread-safe. If functions from described API are called by multithreaded
+ * application from different threads, they must be put into mutex protected critical section.
+ *
+ * \par Important notes:
+ * Memory for returned client string is obtained with malloc(), and should be freed with free().
+ * Allocated string is returned only, when function succeeds.
+ * If method is CLIENT_METHOD_DEFAULT then it will be chosen based on Cynara configuration file.
+ *
+ * \param[in] method Method of client identifier creation
+ * \param[out] client Placeholder for allocated string containing client id
+ *
+ * \return CYNARA_API_SUCCESS on success
+ * \return CYNARA_API_INVALID_PARAM when client is NULL
+ * \return CYNARA_API_METHOD_NOT_SUPPORTED when requested method is not supported
+ * \return CYNARA_API_CONFIGURATION_ERROR if the configuration file can not be opened or
+ * there are errors in configuration file
+ * \return CYNARA_API_OUT_OF_MEMORY when there was error allocating memory
+ */
+int cynara_creds_self_get_client(enum cynara_client_creds method, char **client);
+
+/**
+ * \par Description:
+ * Creates a user identification string with given method. User is current process.
+ *
+ * \par Purpose:
+ * User identification string is required for cynara_check() and cynara_async_create_request()
+ * functions.
+ *
+ * \par Typical use case:
+ * The function is called before the call of cynara_check() or cynara_async_create_request()
+ * function. Returned string is used as user parameter in cynara_check() or
+ * cynara_async_create_request() function. String is released with free() function when it is no
+ * longer needed.
+ *
+ * \par Method of function operation:
+ * The function generates user string using current process context.
+ *
+ * \par Sync (or) Async:
+ * This is a synchronous API.
+ *
+ * \par Thread safety:
+ * This function is NOT thread-safe. If functions from described API are called by multithreaded
+ * application from different threads, they must be put into mutex protected critical section.
+ *
+ * \par Important notes:
+ * Memory for returned user string is obtained with malloc(), and should be freed with free().
+ * Allocated string is returned only, when function succeeds.
+ * If method is USER_METHOD_DEFAULT then it will be chosen based on Cynara configuration file.
+ *
+ * \param[in] method Method of user identifier creation
+ * \param[out] user Placeholder for allocated string containing user id
+ *
+ * \return CYNARA_API_SUCCESS on success
+ * \return CYNARA_API_INVALID_PARAM when user is NULL
+ * \return CYNARA_API_METHOD_NOT_SUPPORTED when requested method is not supported
+ * \return CYNARA_API_CONFIGURATION_ERROR if the configuration file can not be opened or
+ * there are errors in configuration file
+ * \return CYNARA_API_OUT_OF_MEMORY when there was error allocating memory
+ */
+int cynara_creds_self_get_user(enum cynara_user_creds method, char **user);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CYNARA_CREDS_SELF_H */
diff --git a/include/cynara/cynara-creds-socket.h b/include/cynara/cynara-creds-socket.h
new file mode 100644
index 0000000..fe5cbdb
--- /dev/null
+++ b/include/cynara/cynara-creds-socket.h
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2014-2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+/**
+ * @file src/include/cynara-creds-socket.h
+ * @author Radoslaw Bartosiak <r.bartosiak@samsung.com>
+ * @author Aleksander Zdyb <a.zdyb@samsung.com>
+ * @author Lukasz Wojciechowski <l.wojciechow@partner.samsung.com>
+ * @author Oskar Switalski <o.switalski@samsung.com>
+ * @version 1.0
+ * @brief This file contains Cynara credentials helper APIs for socket clients.
+ * @example cynara-creds-socket.example
+ */
+
+#ifndef CYNARA_CREDS_SOCKET_H
+#define CYNARA_CREDS_SOCKET_H
+
+#include <sys/types.h>
+
+#include <cynara/cynara-creds-commons.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \par Description:
+ * Creates a client identification string with given method. Client is a process at the other
+ * side of socket.
+ *
+ * \par Purpose:
+ * Client identification string is required for cynara_check() and cynara_async_create_request()
+ * functions.
+ *
+ * \par Typical use case:
+ * The function is called before the call of cynara_check() or cynara_async_create_request()
+ * function. Returned string is used as client parameter in cynara_check() or
+ * cynara_async_create_request() function. String is released with free() function when it is no
+ * longer needed.
+ *
+ * \par Method of function operation:
+ * The function generates client string using SO_PEERCRED on socket.
+ *
+ * \par Sync (or) Async:
+ * This is a synchronous API.
+ *
+ * \par Thread safety:
+ * This function is NOT thread-safe. If functions from described API are called by multithreaded
+ * application from different threads, they must be put into mutex protected critical section.
+ *
+ * \par Important notes:
+ * Memory for returned client string is obtained with malloc(), and should be freed with free().
+ * Allocated string is returned only, when function succeeds.
+ * If method is CLIENT_METHOD_DEFAULT then it will be chosen based on Cynara configuration file.
+ *
+ * \param[in] socket_fd Descriptor of open connected UNIX socket
+ * \param[in] method Method of client identifier creation
+ * \param[out] client Placeholder for allocated string containing client id
+ *
+ * \return CYNARA_API_SUCCESS on success
+ * \return CYNARA_API_INVALID_PARAM when client is NULL or socket_fd is not valid connected socket
+ * descriptor
+ * \return CYNARA_API_METHOD_NOT_SUPPORTED when requested method is not supported
+ * \return CYNARA_API_CONFIGURATION_ERROR if the configuration file can not be opened or
+ * there are errors in configuration file
+ * \return CYNARA_API_OUT_OF_MEMORY when there was error allocating memory
+ */
+int cynara_creds_socket_get_client(int socket_fd, enum cynara_client_creds method, char **client);
+
+/**
+ * \par Description:
+ * Creates a user identification string with given method. User is an executor of process
+ * at the other side of socket.
+ *
+ * \par Purpose:
+ * User identification string is required for cynara_check() and cynara_async_create_request()
+ * functions.
+ *
+ * \par Typical use case:
+ * The function is called before the call of cynara_check() or cynara_async_create_request()
+ * function. Returned string is used as user parameter in cynara_check() or
+ * cynara_async_create_request() function. String is released with free() function when it is no
+ * longer needed.
+ *
+ * \par Method of function operation:
+ * The function generates user string using SO_PEERCRED on socket.
+ *
+ * \par Sync (or) Async:
+ * This is a synchronous API.
+ *
+ * \par Thread safety:
+ * This function is NOT thread-safe. If functions from described API are called by multithreaded
+ * application from different threads, they must be put into mutex protected critical section.
+ *
+ * \par Important notes:
+ * Memory for returned user string is obtained with malloc(), and should be freed with free().
+ * Allocated string is returned only, when function succeeds.
+ * If method is USER_METHOD_DEFAULT then it will be chosen based on Cynara configuration file.
+ *
+ * \param[in] socket_fd Descriptor of open connected UNIX socket
+ * \param[in] method Method of user identifier creation
+ * \param[out] user Placeholder for allocated string containing user id
+ *
+ * \return CYNARA_API_SUCCESS on success
+ * \return CYNARA_API_INVALID_PARAM when user is NULL or socket_fd is not valid connected socket
+ * descriptor
+ * \return CYNARA_API_METHOD_NOT_SUPPORTED when requested method is not supported
+ * \return CYNARA_API_CONFIGURATION_ERROR if the configuration file can not be opened or
+ * there are errors in configuration file
+ * \return CYNARA_API_OUT_OF_MEMORY when there was error allocating memory
+ */
+int cynara_creds_socket_get_user(int socket_fd, enum cynara_user_creds method, char **user);
+
+/**
+ * \par Description:
+ * Return PID of process at the other side of socket.
+ *
+ * \par Purpose:
+ * PID may be used for client_session creation with cynara_session_from_pid() function
+ * from libcynara-session library. Client_session is needed for cynara_check()
+ * and cynara_async_create_request() functions.
+ *
+ * \par Typical use case:
+ * The function is called before the call of cynara_session_from_pid() function.
+ *
+ * \par Method of function operation:
+ * The function reads PID of peer using SO_PEERCRED on socket.
+ *
+ * \par Sync (or) Async:
+ * This is a synchronous API.
+ *
+ * \par Thread safety:
+ * This function is NOT thread-safe. If functions from described API are called by multithreaded
+ * application from different threads, they must be put into mutex protected critical section.
+ *
+ * \param[in] socket_fd Descriptor of open connected UNIX socket
+ * \param[out] pid Placeholder for pid
+ *
+ * \return CYNARA_API_SUCCESS on success
+ * \return CYNARA_API_INVALID_PARAM when socket_fd is not valid connected socket descriptor
+ * \return CYNARA_API_UNKNOWN_ERROR when system function fails in incredible situation
+ */
+int cynara_creds_socket_get_pid(int socket_fd, pid_t *pid);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CYNARA_CREDS_SOCKET_H */
diff --git a/include/cynara/cynara-error.h b/include/cynara/cynara-error.h
new file mode 100644
index 0000000..8316f1d
--- /dev/null
+++ b/include/cynara/cynara-error.h
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2014-2017 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+/**
+ * @file src/include/cynara-error.h
+ * @author Lukasz Wojciechowski <l.wojciechow@partner.samsung.com>
+ * @author Zofia Abramowska <z.abramowska@samsung.com>
+ * @author Radoslaw Bartosiak <r.bartosiak@samsung.com>
+ * @author Aleksander Zdyb <a.zdyb@samsung.com>
+ * @version 1.0
+ * @brief This file contains error codes returned by APIs of Cynara.
+ */
+
+#ifndef CYNARA_ERROR_H
+#define CYNARA_ERROR_H
+
+#include <stddef.h>
+
+/**
+ * \name Return Codes
+ * Exported by the foundation API. Return codes beginning with negative codes indicate an error.
+ * @{
+*/
+
+/*! \brief indicating that API call was interrupted by user*/
+#define CYNARA_API_INTERRUPTED 4
+
+/*! \brief indicating access that cannot be resolved without further actions*/
+#define CYNARA_API_ACCESS_NOT_RESOLVED 3
+
+/*! \brief indicating access that was checked is allowed */
+#define CYNARA_API_ACCESS_ALLOWED 2
+
+/*! \brief indicating that access that was checked is denied */
+#define CYNARA_API_ACCESS_DENIED 1
+
+/*! \brief indicating the result of the one specific API is successful */
+#define CYNARA_API_SUCCESS 0
+
+/*! \brief indicating that value is not present in cache */
+#define CYNARA_API_CACHE_MISS -1
+
+/*! \brief indicating that pending requests reached maximum */
+#define CYNARA_API_MAX_PENDING_REQUESTS -2
+
+/*! \brief indicating system is running out of memory state */
+#define CYNARA_API_OUT_OF_MEMORY -3
+
+/*! \brief indicating the API's parameter is malformed */
+#define CYNARA_API_INVALID_PARAM -4
+
+/*! \brief indicating that service is not available */
+#define CYNARA_API_SERVICE_NOT_AVAILABLE -5
+
+/*! \brief indicating that provided method is not supported by library */
+#define CYNARA_API_METHOD_NOT_SUPPORTED -6
+
+/*! \brief cynara service does not allow to perform requested operation */
+#define CYNARA_API_OPERATION_NOT_ALLOWED -7
+
+/*! \brief cynara service failed to perform requested operation */
+#define CYNARA_API_OPERATION_FAILED -8
+
+/*! \brief cynara service hasn't found requested bucket */
+#define CYNARA_API_BUCKET_NOT_FOUND -9
+
+/*! \brief indicating an unknown error */
+#define CYNARA_API_UNKNOWN_ERROR -10
+
+/*! \brief indicating configuration error */
+#define CYNARA_API_CONFIGURATION_ERROR -11
+
+/*! \brief indicating invalid parameter in command-line */
+#define CYNARA_API_INVALID_COMMANDLINE_PARAM -12
+
+/*! \brief indicating that provided buffer is too short */
+#define CYNARA_API_BUFFER_TOO_SHORT -13
+
+/*! \brief indicating that database is corrupted */
+#define CYNARA_API_DATABASE_CORRUPTED -14
+
+/*! \brief indicating that user doesn't have enough permission to perform action */
+#define CYNARA_API_PERMISSION_DENIED -15
+/** @}*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \par Description:
+ * Returns the error string in the user-supplied buffer buf of length buflen.
+ *
+ * \par Purpose:
+ * This function populates passed argument buf with a string that describes the error code
+ * passed in the argument errnum, possibly using the LC_MESSAGES part of the current locale
+ * to select the appropriate language.
+ *
+ * \par Typical use case:
+ * Once after any of API functions returns negative value (if the error message is to be presented
+ * to the user).
+ *
+ * \par Method of function operation:
+ * This function copies error string to memory pointed by argument buf along with termination
+ * character ('\0').
+ *
+ * \par Sync (or) async:
+ * This is a synchronous API.
+ *
+ * \par Thread-safety:
+ * This function is thread-safe as long as setlocale() is not invoked concurrently.
+ *
+ * \par Important notes:
+ * This function copies error string to memory pointed by argument buf only if the buffer is
+ * of sufficient size (indicated by argument buflen). User is responsible for allocating sufficient
+ * memory for both error string and termination character ('\0').
+ * Moreover, the user must not invoke setlocale() concurrently with this function, because results
+ * are unspecified.
+ *
+ * \param[in] errnum error number
+ * \param[out] buf buffer for error message
+ * \param[in] buflen buffer size
+ *
+ * \return CYNARA_API_SUCCESS on success, CYNARA_API_BUFFER_TOO_SHORT, if error message couldn't fit
+ * into allocated buffer, or CYNARA_API_INVALID_PARAM if errnum is not a valid error number
+ * or argument buf is NULL.
+ *
+ * \brief Obtain error message from error number.
+ */
+int cynara_strerror(int errnum, char *buf, size_t buflen);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CYNARA_ERROR_H */
diff --git a/include/cynara/cynara-limits.h b/include/cynara/cynara-limits.h
new file mode 100644
index 0000000..f4e98c1
--- /dev/null
+++ b/include/cynara/cynara-limits.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2015-2016 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+/**
+ * @file src/include/cynara-limits.h
+ * @author Rafal Krypa <r.krypa@samsung.com>
+ * @version 1.0
+ * @brief This file contains limits for parameter sizes of Cynara APIs.
+ */
+
+#ifndef CYNARA_LIMITS_H
+#define CYNARA_LIMITS_H
+
+/*! \brief Maximum length of text identifier accepted by Cynara */
+#define CYNARA_MAX_ID_LENGTH 4096
+
+/*! \brief Maximum size of vector accepted by Cynara */
+#define CYNARA_MAX_VECTOR_SIZE 4096
+
+/*! \brief Maximum size of monitor entries buffer accepted by Cynara */
+#define CYNARA_MAX_MONITOR_BUFFER_SIZE 65535
+
+#endif /* CYNARA_LIMITS_H */
diff --git a/include/cynara/cynara-monitor.h b/include/cynara/cynara-monitor.h
new file mode 100644
index 0000000..ce7b8d0
--- /dev/null
+++ b/include/cynara/cynara-monitor.h
@@ -0,0 +1,421 @@
+/*
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+/**
+ * @file src/include/cynara-monitor.h
+ * @author Aleksander Zdyb <a.zdyb@samsung.com>
+ * @version 1.0
+ * @brief This file contains client APIs of Cynara monitoring.
+ */
+
+#ifndef CYNARA_MONITOR_H
+#define CYNARA_MONITOR_H
+
+#include <stddef.h>
+#include <time.h>
+
+#include <cynara/cynara-error.h>
+#include <cynara/cynara-limits.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct cynara_monitor_configuration cynara_monitor_configuration;
+typedef struct cynara_monitor cynara_monitor;
+typedef struct cynara_monitor_entry cynara_monitor_entry;
+
+/**
+ * \par Description:
+ * Initializes cynara_monitor_configuration. Creates structure used in following configuration
+ * API calls.
+ *
+ * \par Purpose:
+ * For configuration parameter to be used in cynara_monitor_initialize() function, this API must be
+ * called before any other Cynara monitor configuration API function.
+ * It will create cynara_monitor_configuration structure, an optional parameter
+ * of Cynara monitor initialization.
+ *
+ * \par Typical use case:
+ * Once before setting parameters of Cynara monitor configuration and passing to
+ * cynara_monitor_initialize().
+ *
+ * \par Method of function operation:
+ * This API initializes inner library structures and in case of success returns pointer
+ * to created cynara_monitor_configuration structure.
+ *
+ * \par Sync (or) Async:
+ * This is a synchronous API.
+ *
+ * \par Thread-safety:
+ * \parblock
+ * This function is NOT thread-safe. If this function is called simultaneously with other functions
+ * from described API in different threads, they must be put into protected critical section.
+ *
+ * \par Important notes:
+ * Structure cynara_monitor_configuration created by cynara_monitor_configuration_create() call
+ * should be released with cynara_monitor_configuration_destroy().
+ * Structure cynara_monitor_configuration should be destroyed after passing it to
+ * cynara_monitor_initialize().
+ * \endparblock
+ *
+ * \param[out] pp_conf Placeholder for created cynara_monitor_configuration structure.
+ *
+ * \return CYNARA_API_SUCCESS on success
+ * or negative error code on error.
+ */
+int cynara_monitor_configuration_create(cynara_monitor_configuration **pp_conf);
+
+/**
+ * \par Description:
+ * Releases cynara_monitor_configuration structure created
+ * with cynara_monitor_configuration_create().
+ *
+ * \par Purpose:
+ * This API should be used to clean up after usage of cynara_monitor_configuration.
+ *
+ * \par Typical use case:
+ * Once cynara_monitor_configuration is not needed.
+ *
+ * \par Method of function operation:
+ * This API releases inner library structure and destroys cynara_monitor_configuration structure.
+ *
+ * \par Sync (or) Async:
+ * This is a synchronous API.
+ *
+ * \par Thread-safety:
+ * This function is NOT thread-safe. If this function is called simultaneously with other functions
+ * from described API in different threads, they must be put into protected critical section.
+ *
+ * \param[in] p_conf cynara_monitor_configuration structure. If NULL, the call has no effect.
+ */
+void cynara_monitor_configuration_destroy(cynara_monitor_configuration *p_conf);
+
+/**
+ * \par Description:
+ * Set monitor entries buffer size.
+ *
+ * \par Purpose:
+ * This API is used to change default number of monitor entries stored on server side
+ * before returning them to client of libcynara-monitor. Buffer size cannot exceed value
+ * of CYNARA_MAX_MONITOR_BUFFER_SIZE, otherwise CYNARA_API_INPUT_PARAM will be returned.
+ *
+ * \par Typical use case:
+ * Once after cynara_configuration is created with cynara_monitor_configuration_create()
+ * and before passing configuration to cynara_monitor_configuration().
+ *
+ * \par Method of function operation:
+ * This API initializes buffer with given capacity.
+ *
+ * \par Sync (or) Async:
+ * This is a synchronous API.
+ *
+ * \par Thread-safety:
+ * This function is NOT thread-safe. If this function is called simultaneously with other functions
+ * from described API in different threads, they must be put into protected critical section.
+ *
+ * \par Important notes:
+ * After passing cynara_configuration to cynara_monitor_initialize() calling this API will have
+ * no effect.
+ *
+ * If buffer size is not set with cynara_monitor_configuration_set_buffer_size(),
+ * a default size will be taken.
+ * \endparblock
+ *
+ * \param[in] p_conf cynara_monitor_configuration structure pointer.
+ * \param[in] buffer_size buffer size to be set.
+ *
+ * \return CYNARA_API_SUCCESS on success
+ * or negative error code on error.
+ */
+int cynara_monitor_configuration_set_buffer_size(cynara_monitor_configuration *p_conf,
+ size_t buffer_size);
+
+/**
+ * \par Description:
+ * Initializes cynara-monitor library with given configuration.
+ * Creates structure used in following API calls.
+ *
+ * \par Purpose:
+ * This API must be used by prior calling cynara_monitor_entries_get() function.
+ *
+ * \par Typical use case:
+ * Once before a service can call cynara_monitor_entries_get().
+ *
+ * \par Method of function operation:
+ * This API initializes inner library structures and in case of success
+ * creates cynara_monitor structure.
+ *
+ * \par Sync (or) Async:
+ * This is a Synchronous API.
+ *
+ * \par Thread-safeness:
+ * This function is NOT thread-safe. If this function is called simultaneously with other functions
+ * from described API in different threads, they must be put into protected critical section.
+ *
+ * \par Important notes:
+ * Structure cynara_monitor created by cynara_monitor_initialize() call should be released
+ * with cynara_monitor_finish().
+ *
+ * \param[out] pp_cynara_monitor Placeholder for created cynara_monitor structure.
+ * \param[in] p_conf Configuration for cynara-monitor library. NULL for default parameters.
+ *
+ * \return CYNARA_API_SUCCESS on success, or error code on error.
+ */
+int cynara_monitor_initialize(cynara_monitor **pp_cynara_monitor,
+ const cynara_monitor_configuration *p_conf);
+
+/**
+ * \par Description:
+ * Releases cynara-monitor library and destroys structure created with cynara_monitor_initialize().
+ *
+ * \par Purpose:
+ * This API should be used to clean up after the usage of cynara-monitor library.
+ *
+ * \par Typical use case:
+ * Once after last call to cynara_monitor_entries_get().
+ *
+ * \par Method of function operation:
+ * This API releases inner library structures and destroys cynara_monitor structure.
+ *
+ * \par Sync (or) Async:
+ * This is a Synchronous API.
+ *
+ * \par Thread-safeness:
+ * This function is NOT thread-safe. If this function is called simultaneously with other functions
+ * from described API in different threads, they must be put into protected critical section.
+ *
+ * \par Important notes:
+ * No other call to libcynara-monitor should be made after call to cynara_monitor_finish().
+ * This function cannot be called, if cynara_monitor_entries_get() haven't returned yet.
+ *
+ * \param[in] p_cynara_monitor Cynara monitor structure.
+ *
+ * \return CYNARA_API_SUCCESS on success, or error code on error.
+ */
+int cynara_monitor_finish(cynara_monitor *p_cynara_monitor);
+
+/**
+ * \brief Returns monitor entries.
+ *
+ * \par Description:
+ *
+ * Returns all available monitor entries right after the size of buffer reaches set limit.
+ *
+ * \par Purpose:
+ * This API should be used to get available monitor entries.
+ *
+ * \par Typical use case:
+ * Gathering information about possible accesses checked in Cynara by individual services.
+ *
+ * \par Method of function operation:
+ * \parblock
+ * Each call to cynara_check() made by individual services are logged. The logs can be obtained
+ * by privileged clients with usage of libcynara-monitor (this API).
+ *
+ * In case of successful call CYNARA_API_SUCCESS is returned and *monitor_entries points
+ * to newly created array of pointers to cynara_monitor_entry. It is responsibility
+ * of the caller to release entries with cynara_monitor_entries_free().
+ *
+ * The function blocks until the size of buffer reaches set limit or cynara_monitor_entries_flush()
+ * is called from another thread.
+ * \endparblock
+ *
+ * \par Sync (or) Async:
+ * This is a synchronous API.
+ *
+ * \par Thread-safeness:
+ * This function is thread-safe.
+ *
+ * \par Important notes:
+ * Although this function is thread-safe, no multiple calls are allowed.
+ *
+ * \param[in] p_cynara_monitor cynara_monitor structure.
+ * \param[out] monitor_entries placeholder for NULL terminated array of pointers to
+ * monitor_entries structures.
+ *
+ * \return CYNARA_API_SUCCESS on success, or error code otherwise.
+ */
+int cynara_monitor_entries_get(cynara_monitor *p_cynara_monitor,
+ cynara_monitor_entry ***monitor_entries);
+
+/**
+ * \brief Makes cynara_monitor_entries_get() return immediately.
+ *
+ * \par Description:
+ *
+ * When called from another thread, makes cynara_monitor_entries_get() return immediately.
+ *
+ * \par Purpose:
+ * This API should be used to make cynara_monitor_entries_get() return immediately.
+ *
+ * \par Typical use case:
+ * User of libcynara-monitor wants to immediately get get monitor entries and/or make
+ * cynara_monitor_entries_get() return without waiting for full batch of entries.
+ *
+ * \par Sync (or) Async:
+ * This is a synchronous API.
+ *
+ * \par Thread-safeness:
+ * This function is thread-safe.
+ *
+ * \param[in] p_cynara_monitor cynara_monitor structure.
+ *
+ * \return CYNARA_API_SUCCESS on success, or error code otherwise.
+ */
+int cynara_monitor_entries_flush(cynara_monitor *p_cynara_monitor);
+
+/**
+ * \brief Releases monitor entries.
+ *
+ * \par Description:
+ *
+ * Releases monitor entries obtained with cynara_monitor_entries_get().
+ *
+ * \par Purpose:
+ * This API should be used to release array of cynara_monitor_entry obtained
+ * with cynara_monitor_entries_get.
+ *
+ * \par Typical use case:
+ * When user obtained all needed data from the entries and won't be accessing the
+ * memory any more.
+ *
+ * \par Sync (or) Async:
+ * This is a synchronous API.
+ *
+ * \par Thread-safeness:
+ * This function is thread-safe.
+ *
+ * \param[in] monitor_entries NULL terminated array of cynara_monitor_entry to be released.
+ */
+void cynara_monitor_entries_free(cynara_monitor_entry **monitor_entries);
+
+/**
+ * \brief Gets client from monitor_entry
+ *
+ * \par Description:
+ * Gives access to client field of monitor_entry given as a parameter.
+ * Please refer to cynara_check() in cynara-client.h for more information on client field.
+ *
+ * \par Sync (or) Async:
+ * This is a synchronous API.
+ *
+ * \par Thread-safeness:
+ * This function is thread-safe.
+ *
+ * \par Important notes:
+ * The returned pointer is valid as long as given monitor_entry is.
+ *
+ * \param[in] monitor_entry cynara_monitor_entry to be accessed.
+ *
+ * \return valid const char * pointer or NULL in case of error.
+ */
+const char *cynara_monitor_entry_get_client(const cynara_monitor_entry *monitor_entry);
+
+/**
+ * \brief Gets user from monitor_entry
+ *
+ * \par Description:
+ * Gives access to user field of monitor_entry given as a parameter.
+ * Please refer to cynara_check() in cynara-client.h for more information on user field.
+ *
+ * \par Sync (or) Async:
+ * This is a synchronous API.
+ *
+ * \par Thread-safeness:
+ * This function is thread-safe.
+ *
+ * \par Important notes:
+ * The returned pointer is valid as long as given monitor_entry is.
+ *
+ * \param[in] monitor_entry cynara_monitor_entry to be accessed.
+ *
+ * \return valid const char * pointer or NULL in case of error.
+ */
+const char *cynara_monitor_entry_get_user(const cynara_monitor_entry *monitor_entry);
+
+/**
+ * \brief Gets privilege from monitor_entry
+ *
+ * \par Description:
+ * Gives access to privilege field of monitor_entry given as a parameter.
+ * Please refer to cynara_check() in cynara-client.h for more information on privilege field.
+ *
+ * \par Sync (or) Async:
+ * This is a synchronous API.
+ *
+ * \par Thread-safeness:
+ * This function is thread-safe.
+ *
+ * \par Important notes:
+ * The returned pointer is valid as long as given monitor_entry is.
+ *
+ * \param[in] monitor_entry cynara_monitor_entry to be accessed.
+ *
+ * \return valid const char * pointer or NULL in case of error.
+ */
+const char *cynara_monitor_entry_get_privilege(const cynara_monitor_entry *monitor_entry);
+
+/**
+ * \brief Gets result from monitor_entry
+ *
+ * \par Description:
+ * Gives access to result field of monitor_entry given as a parameter.
+ * In case of successful get, the result is one of CYNARA_API_ACCESS_ALLOWED
+ * or CYNARA_API_ACCESS_DENIED. In case of error a negative code error is returned.
+ * Please refer to cynara-error.h for more information.
+ *
+ * \par Sync (or) Async:
+ * This is a synchronous API.
+ *
+ * \par Thread-safeness:
+ * This function is thread-safe.
+ *
+ * \param[in] monitor_entry cynara_monitor_entry to be accessed.
+ *
+ * \return CYNARA_API_ACCESS_ALLOWED on access allowed, CYNARA_API_ACCESS_DENIED on access denial
+ * or other error code on error.
+ */
+int cynara_monitor_entry_get_result(const cynara_monitor_entry *monitor_entry);
+
+/**
+ * \brief Gets timestamp from monitor_entry
+ *
+ * \par Description:
+ * Gives access to timestamp field of monitor_entry given as a parameter.
+ * This is the time of cynara_check() being invoked. Please refer to cynara-client.h
+ * for more information about cynara_check().
+ *
+ * \par Sync (or) Async:
+ * This is a synchronous API.
+ *
+ * \par Thread-safeness:
+ * This function is thread-safe.
+ *
+ * \par Important notes:
+ * The returned pointer is valid as long as given monitor_entry is.
+ *
+ * \param[in] monitor_entry cynara_monitor_entry to be accessed.
+ *
+ * \return valid const istruct timspec * pointer or NULL in case of error.
+ */
+const struct timespec *cynara_monitor_entry_get_timestamp(
+ const cynara_monitor_entry *monitor_entry);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CYNARA_MONITOR_H */
diff --git a/include/cynara/cynara-plugin.h b/include/cynara/cynara-plugin.h
new file mode 100644
index 0000000..747966f
--- /dev/null
+++ b/include/cynara/cynara-plugin.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2014-2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @file src/include/cynara-plugin.h
+ * @author Zofia Abramowska <z.abramowska@samsung.com>
+ * @author Oskar Switalski <o.switalski@samsung.com>
+ * @version 1.0
+ * @brief This file defines cynara service side of external plugin interface -
+ * ServicePluginInterface
+ */
+
+#ifndef CYNARA_PLUGIN_H_
+#define CYNARA_PLUGIN_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <log/log.h>
+#include <plugin/ExternalPluginInterface.h>
+#include <types/PolicyResult.h>
+#include <types/PolicyType.h>
+
+namespace Cynara {
+
+//These typedefs will be defined in external headers
+typedef std::string PluginData;
+typedef std::string AgentType;
+
+class ServicePluginInterface;
+typedef std::shared_ptr<ServicePluginInterface> ServicePluginInterfacePtr;
+
+/**
+ * A class defining external plugins interface.
+ * These plugins work inside of cynara and either can produce
+ * response through check instantly or require communication
+ * with given type of agent. Agent must be registered through
+ * cynara-agent API.
+ *
+ * Plugin implementing ServicePluginInterface must implement ExternalPluginInterface.
+ * Creation/destruction functions with signatures compatible to Cynara::create_t and
+ * Cynara::destroy_t must be provided as factories of ServicePluginInterface.
+ */
+
+class ServicePluginInterface : public ExternalPluginInterface {
+public:
+ /**
+ * Enum indicating status of calling plugin method.
+ */
+ enum class PluginStatus {
+ SUCCESS, /**< update() finished successfully */
+ ANSWER_READY, /**< check() returns answer immediately through argument */
+ ANSWER_NOTREADY, /**< check() cannot return answer immediately,
+ communication with agent is required */
+ ERROR /**< either check() or update() fails */
+ };
+
+ /**
+ * Asks plugin, what kind of permission does client, user and privilege has.
+ *
+ * @param[in] client
+ * @param[in] user
+ * @param[in] privilege
+ * @param[out] result Immediate response (if available)
+ * @param[out] requiredAgent When ANSWER_NOTREADY, required AgentType to communicate with
+ * @param[out] pluginData Additional data, that will be passed to agent
+ * @return PluginStatus In case of success - either ANSWER_READY or ANSWER_NOTREADY,
+ * in case of error - ERROR
+ */
+ virtual PluginStatus check(const std::string &client, const std::string &user,
+ const std::string &privilege, PolicyResult &result,
+ AgentType &requiredAgent, PluginData &pluginData) noexcept = 0;
+
+ /**
+ * Updates response returned by agent
+ * @param[in] client
+ * @param[in] user
+ * @param[in] privilege
+ * @param[in] agentData Additional data, passed from agent
+ * @param[out] result Response interpreted from agent
+ * @return PluginStatus In case of success - SUCCESS, in case of error - ERROR
+ */
+ virtual PluginStatus update(const std::string &client, const std::string &user,
+ const std::string &privilege, const PluginData &agentData,
+ PolicyResult &result) noexcept = 0;
+
+ virtual ~ServicePluginInterface() {};
+
+};
+
+} // namespace Cynara
+
+#endif /* CYNARA_PLUGIN_H_ */
diff --git a/include/cynara/cynara-policy-types.h b/include/cynara/cynara-policy-types.h
new file mode 100644
index 0000000..bd74b5a
--- /dev/null
+++ b/include/cynara/cynara-policy-types.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2014-2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+/**
+ * \file src/include/cynara-policy-types.h
+ * \author Lukasz Wojciechowski <l.wojciechow@partner.samsung.com>
+ * \version 1.0
+ * \brief This file contains policy types / operations definitions.
+ */
+
+#ifndef CYNARA_POLICY_TYPES_H
+#define CYNARA_POLICY_TYPES_H
+
+/**
+ * \name Operation Codes
+ * Operation codes that define action type to be taken
+ * @{
+ */
+
+/*! \brief a policy or bucket should be removed */
+#define CYNARA_ADMIN_DELETE -1
+
+/*! \brief set policy result or bucket's default policy to DENY */
+#define CYNARA_ADMIN_DENY 0
+
+/*! \brief set bucket's default policy to NONE */
+#define CYNARA_ADMIN_NONE 1
+
+/*! \brief set policy to point into another bucket */
+#define CYNARA_ADMIN_BUCKET 0xFFFE
+
+/*! \brief set policy result or bucket's default policy to ALLOW */
+#define CYNARA_ADMIN_ALLOW 0xFFFF
+/** @}*/
+
+#endif /* CYNARA_POLICY_TYPES_H */
diff --git a/include/cynara/cynara-session.h b/include/cynara/cynara-session.h
new file mode 100644
index 0000000..1c76bce
--- /dev/null
+++ b/include/cynara/cynara-session.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2014-2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+/**
+ * @file src/include/cynara-session.h
+ * @author Aleksander Zdyb <a.zdyb@samsung.com>
+ * @author Radoslaw Bartosiak <r.bartosiak@samsung.com.pl>
+ * @author Lukasz Wojciechowski <l.wojciechow@partner.samsung.com>
+ * @author Oskar Switalski <o.switalski@samsung.com>
+ * @version 1.0
+ * @brief This file contains Cynara session helper APIs.
+ * @example cynara-session.example
+ */
+
+#ifndef CYNARA_SESSION_H
+#define CYNARA_SESSION_H
+
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \par Description:
+ * Creates a client session string based on pid and time of creation of client process
+ *
+ * \par Purpose:
+ * This function can be used to create session string identifier used in cynara_check()
+ * and cynara_async_create_request() functions defined in client libraries.
+ *
+ * \par Typical use case:
+ * The function is called before the call of cynara_check() or cynara_async_create_request()
+ * function. Returned string is used as client_session param in cynara_check() or
+ * cynara_async_create_request() function. String is released with free() function.
+ *
+ * \par Method of function operation:
+ * The function generates client session based on the pid and start time of the client process.
+ * Time is acquired from /proc/PID directory.
+ *
+ * \par Sync (or) Async:
+ * This is a synchronous API.
+ *
+ * \par Thread safety:
+ * This function is thread-safe.
+ *
+ * \par Important notes:
+ * Memory for returned string is obtained with malloc(), and should be freed with free().
+ *
+ * \param[in] client_pid client application process identifier (PID).
+ *
+ * \return session string on success
+ * \return NULL on error
+ */
+char *cynara_session_from_pid(pid_t client_pid);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CYNARA_SESSION_H */
diff --git a/pkgconfig/CMakeLists.txt b/pkgconfig/CMakeLists.txt
new file mode 100644
index 0000000..f32ad70
--- /dev/null
+++ b/pkgconfig/CMakeLists.txt
@@ -0,0 +1,41 @@
+###########################################################################
+# Copyright (C) 2018 "IoT.bzh"
+#
+# author: José Bollo <jose.bollo@iot.bzh>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###########################################################################
+
+configure_file(cynara.pc.in cynara.pc @ONLY)
+
+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/cynara.pc
+ DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
+
+foreach(item cynara-admin.pc
+ cynara-agent.pc
+ cynara-client-async.pc
+ cynara-client.pc
+ cynara-commons.pc
+ cynara-creds-commons.pc
+ cynara-creds-dbus.pc
+ cynara-creds-gdbus.pc
+ cynara-creds-self.pc
+ cynara-creds-socket.pc
+ cynara-monitor.pc
+ cynara-plugin.pc
+ cynara-session.pc)
+ install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/link.pc
+ DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig
+ RENAME ${item})
+endforeach(item)
+
diff --git a/pkgconfig/cynara.pc.in b/pkgconfig/cynara.pc.in
new file mode 100644
index 0000000..6a39979
--- /dev/null
+++ b/pkgconfig/cynara.pc.in
@@ -0,0 +1,5 @@
+Name: cynara
+Description: cynara package
+Version: @CYNARA_VERSION@
+Libs: -L@CMAKE_INSTALL_FULL_LIBDIR@ -lcynara
+Cflags: -I@CMAKE_INSTALL_FULL_INCLUDEDIR@ -I@CMAKE_INSTALL_FULL_INCLUDEDIR@/cynara
diff --git a/pkgconfig/link.pc b/pkgconfig/link.pc
new file mode 120000
index 0000000..17d4d5d
--- /dev/null
+++ b/pkgconfig/link.pc
@@ -0,0 +1 @@
+cynara.pc \ No newline at end of file
diff --git a/sample b/sample
new file mode 100644
index 0000000..92535a7
--- /dev/null
+++ b/sample
@@ -0,0 +1,80 @@
+set System * * * 1
+set User::App::aftest * * urn:AGL:permission::public:hidden 1
+set User::App::aftest * * urn:AGL:permission::public:no-htdocs 1
+set User::App::agl-identity-service * * urn:AGL:permission::public:hidden 1
+set User::App::agl-identity-service * * urn:AGL:permission::system:run-by-default 1
+set User::App::agl-service-audio-4a * * http://tizen.org/privilege/internal/dbus 1
+set User::App::agl-service-audio-4a * * urn:AGL:permission::public:hidden 1
+set User::App::agl-service-audio-4a * * urn:AGL:permission::system:run-by-default 1
+set User::App::agl-service-audio-4a * * urn:AGL:permission:UNICENS:public:monitor 1
+set User::App::agl-service-bluetooth-pbap * * http://tizen.org/privilege/internal/dbus 1
+set User::App::agl-service-bluetooth-pbap * * urn:AGL:permission::public:hidden 1
+set User::App::agl-service-bluetooth-pbap * * urn:AGL:permission::public:no-htdocs 1
+set User::App::agl-service-bluetooth * * http://tizen.org/privilege/internal/dbus 1
+set User::App::agl-service-bluetooth * * urn:AGL:permission::public:hidden 1
+set User::App::agl-service-bluetooth * * urn:AGL:permission::system:run-by-default 1
+set User::App::agl-service-geoclue * * http://tizen.org/privilege/internal/dbus 1
+set User::App::agl-service-geoclue * * urn:AGL:permission::public:hidden 1
+set User::App::agl-service-geoclue * * urn:AGL:permission::public:no-htdocs 1
+set User::App::agl-service-geoclue * * urn:AGL:permission::system:run-by-default 1
+set User::App::agl-service-geofence * * urn:AGL:permission::public:hidden 1
+set User::App::agl-service-geofence * * urn:AGL:permission::public:no-htdocs 1
+set User::App::agl-service-gps * * urn:AGL:permission::public:hidden 1
+set User::App::agl-service-gps * * urn:AGL:permission::public:no-htdocs 1
+set User::App::agl-service-iiodevices * * urn:AGL:permission::public:hidden 1
+set User::App::agl-service-iiodevices * * urn:AGL:permission::public:no-htdocs 1
+set User::App::agl-service-mediaplayer * * urn:AGL:permission::public:hidden 1
+set User::App::agl-service-mediaplayer * * urn:AGL:permission::public:no-htdocs 1
+set User::App::agl-service-mediascanner * * http://tizen.org/privilege/internal/dbus 1
+set User::App::agl-service-mediascanner * * urn:AGL:permission::public:hidden 1
+set User::App::agl-service-mediascanner * * urn:AGL:permission::public:no-htdocs 1
+set User::App::agl-service-network * * http://tizen.org/privilege/internal/dbus 1
+set User::App::agl-service-network * * urn:AGL:permission::public:hidden 1
+set User::App::agl-service-network * * urn:AGL:permission::system:run-by-default 1
+set User::App::agl-service-nfc * * http://tizen.org/privilege/internal/dbus 1
+set User::App::agl-service-nfc * * urn:AGL:permission::public:hidden 1
+set User::App::agl-service-nfc * * urn:AGL:permission::public:no-htdocs 1
+set User::App::agl-service-nfc * * urn:AGL:permission::system:run-by-default 1
+set User::App::agl-service-radio * * urn:AGL:permission::public:hidden 1
+set User::App::agl-service-radio * * urn:AGL:permission::public:no-htdocs 1
+set User::App::agl-service-radio * * urn:AGL:permission:audio:public:audiostream 1
+set User::App::agl-service-steering-wheel * * urn:AGL:permission::public:hidden 1
+set User::App::agl-service-unicens * * urn:AGL:permission::public:hidden 1
+set User::App::agl-service-unicens * * urn:AGL:permission::system:run-by-default 1
+set User::App::agl-service-weather * * urn:AGL:permission::public:hidden 1
+set User::App::agl-service-weather * * urn:AGL:permission::public:no-htdocs 1
+set User::App::dashboard * * urn:AGL:permission::public:no-htdocs 1
+set User::App::homescreen-service-2017 * * http://tizen.org/privilege/internal/dbus 1
+set User::App::homescreen-service-2017 * * urn:AGL:permission::public:hidden 1
+set User::App::homescreen-service-2017 * * urn:AGL:permission::system:run-by-default 1
+set User::App::homescreen * * http://tizen.org/privilege/internal/dbus 1
+set User::App::homescreen * * urn:AGL:permission::public:no-htdocs 1
+set User::App::homescreen * * urn:AGL:permission::system:run-by-default 1
+set User::App::hvac * * urn:AGL:permission::public:no-htdocs 1
+set User::App::launcher * * http://tizen.org/privilege/internal/dbus 1
+set User::App::launcher * * urn:AGL:permission::public:no-htdocs 1
+set User::App::launcher * * urn:AGL:permission::system:run-by-default 1
+set User::App::low-can-service * * urn:AGL:permission::public:hidden 1
+set User::App::low-can-service * * urn:AGL:permission::public:no-htdocs 1
+set User::App::low-can-service * * urn:AGL:permission::system:run-by-default 1
+set User::App::mediaplayer * * urn:AGL:permission::public:no-htdocs 1
+set User::App::mixer * * urn:AGL:permission::public:4a-audio-mixer 1
+set User::App::mixer * * urn:AGL:permission::public:no-htdocs 1
+set User::App::naviapi-binding-service * * http://tizen.org/privilege/internal/dbus 1
+set User::App::naviapi-binding-service * * urn:AGL:permission::public:hidden 1
+set User::App::navigation * * http://tizen.org/privilege/internal/dbus 1
+set User::App::navigation * * urn:AGL:permission::public:no-htdocs 1
+set User::App::persistence-binding * * urn:AGL:permission::public:hidden 1
+set User::App::persistence-binding * * urn:AGL:permission::system:run-by-default 1
+set User::App::phone * * http://tizen.org/privilege/internal/dbus 1
+set User::App::phone * * urn:AGL:permission::public:no-htdocs 1
+set User::App::poi * * http://tizen.org/privilege/internal/dbus 1
+set User::App::poi * * urn:AGL:permission::public:no-htdocs 1
+set User::App::radio * * urn:AGL:permission::public:no-htdocs 1
+set User::App::settings * * urn:AGL:permission::public:no-htdocs 1
+set User::App::signal-composer * * urn:AGL:permission::public:hidden 1
+set User::App::signal-composer * * urn:AGL:permission::public:no-htdocs 1
+set User::App::windowmanager-service-2017 * * urn:AGL:permission::public:hidden 1
+set User::App::windowmanager-service-2017 * * urn:AGL:permission::system:run-by-default 1
+set User * * * 1
+
diff --git a/src/.gitignore b/src/.gitignore
new file mode 100644
index 0000000..d551f05
--- /dev/null
+++ b/src/.gitignore
@@ -0,0 +1,5 @@
+cynarad
+test-cynara
+cynara.names
+cynara.rules
+.*.sw*
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644
index 0000000..fa3724c
--- /dev/null
+++ b/src/CMakeLists.txt
@@ -0,0 +1,95 @@
+###########################################################################
+# Copyright (C) 2018 "IoT.bzh"
+#
+# author: José Bollo <jose.bollo@iot.bzh>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###########################################################################
+
+set(SERVER_SOURCES
+ cyn.c
+ db.c
+ fbuf.c
+ prot.c
+ queue.c
+ rcyn-protocol.c
+ rcyn-server.c
+ socket.c
+)
+
+set(LIB_SOURCES
+ cache.c
+ lib-compat.c
+ prot.c
+ rcyn-client.c
+ rcyn-protocol.c
+ socket.c
+)
+
+###########################################
+# build and install cynarad
+###########################################
+add_executable(cynarad ${SERVER_SOURCES})
+install(TARGETS cynarad
+ RUNTIME DESTINATION ${CMAKE_INSTALL_FULL_BINDIR})
+
+###########################################
+# build and install libcynara
+###########################################
+ADD_LIBRARY(cynara SHARED ${LIB_SOURCES})
+SET_TARGET_PROPERTIES(cynara PROPERTIES
+ VERSION ${CYNARA_VERSION}
+ SOVERSION ${CYNARA_SOVERSION})
+TARGET_LINK_LIBRARIES(cynara
+ -Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/export.map
+ -Wl,--as-needed
+ -Wl,--gc-sections
+)
+INSTALL(TARGETS cynara LIBRARY DESTINATION ${CMAKE_INSTALL_FULL_LIBDIR})
+INSTALL(FILES rcyn-client.h DESTINATION ${CMAKE_INSTALL_FULL_INCLUDEDIR}/cynara)
+
+###########################################
+# build and install test-cynara
+###########################################
+ADD_EXECUTABLE(test-cynara test-lib-compat.c)
+TARGET_LINK_LIBRARIES(test-cynara cynara)
+INSTALL(TARGETS test-cynara
+ RUNTIME DESTINATION ${CMAKE_INSTALL_FULL_BINDIR})
+
+###########################################
+# alterations for SYSTEMD and socket specs
+###########################################
+
+if(SYSTEMD)
+ target_compile_definitions(cynarad PRIVATE RCYN_DEFAULT_CHECK_SOCKET_SPEC="sd:check")
+ target_compile_definitions(cynarad PRIVATE RCYN_DEFAULT_ADMIN_SOCKET_SPEC="sd:admin")
+ target_compile_definitions(cynarad PRIVATE WITH_SYSTEMD_ACTIVATION)
+ target_link_libraries(cynarad ${libsystemd_LDFLAGS} ${libsystemd_LINK_LIBRARIES})
+ target_include_directories(cynarad PRIVATE ${libsystemd_INCLUDE_DIRS})
+ target_compile_options(cynarad PRIVATE ${libsystemd_CFLAGS})
+else()
+ if(CHECK_SOCKET_SPEC)
+ target_compile_definitions(cynarad PRIVATE RCYN_DEFAULT_CHECK_SOCKET_SPEC="${CHECK_SOCKET_SPEC}")
+ endif()
+ if(ADMIN_SOCKET_SPEC)
+ target_compile_definitions(cynarad PRIVATE RCYN_DEFAULT_ADMIN_SOCKET_SPEC="${ADMIN_SOCKET_SPEC}")
+ endif()
+endif()
+
+if(CHECK_SOCKET_SPEC)
+ target_compile_definitions(cynara PRIVATE RCYN_DEFAULT_CHECK_SOCKET_SPEC="${CHECK_SOCKET_SPEC}")
+endif()
+if(ADMIN_SOCKET_SPEC)
+ target_compile_definitions(cynara PRIVATE RCYN_DEFAULT_ADMIN_SOCKET_SPEC="${ADMIN_SOCKET_SPEC}")
+endif()
+
diff --git a/src/Makefile b/src/Makefile
new file mode 100644
index 0000000..f5969c3
--- /dev/null
+++ b/src/Makefile
@@ -0,0 +1,26 @@
+.PHONY: all clean
+
+srcssrv = db.c fbuf.c queue.c cyn.c prot.c rcyn-protocol.c socket.c
+
+srcscli = prot.c rcyn-client.c rcyn-protocol.c lib-compat.c cache.c socket.c
+
+incssrv = db.h fbuf.h cyn.h prot.h rcyn-protocol.h socket.h
+
+incscli = prot.h rcyn-client.h rcyn-protocol.h cache.h socket.h
+
+defs = -DRCYN_DEFAULT_CHECK_SOCKET_SPEC=\"tcp:localhost:5555\" \
+ -DRCYN_DEFAULT_ADMIN_SOCKET_SPEC=\"tcp:localhost:4444\"
+
+bins = cynarad test-cynara
+
+all: $(bins)
+
+clean:
+ rm cynara.names cynara.rules $(bins) 2>/dev/null || true
+
+cynarad: rcyn-server.c $(srcssrv) $(incssrv)
+ gcc -o cynarad -g -Wall rcyn-server.c $(srcssrv) $(defs)
+
+test-cynara: test-lib-compat.c $(srcscli) $(incscli)
+ gcc -o test-cynara -I../include -g -Wall test-lib-compat.c $(srcscli) $(defs)
+
diff --git a/src/cache.c b/src/cache.c
new file mode 100644
index 0000000..c2db00d
--- /dev/null
+++ b/src/cache.c
@@ -0,0 +1,163 @@
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+
+#include "cache.h"
+
+struct cache
+{
+ uint32_t begin;
+ uint32_t used;
+ uint32_t count;
+ char content[1];
+};
+
+static
+uint32_t
+lenat(
+ cache_t *cache,
+ uint32_t pos
+) {
+ uint32_t p, n;
+ char c;
+
+ p = pos + 1;
+ if (p >= cache->count)
+ p -= cache->count;
+ n = 4;
+ while (n--) {
+ do {
+ c = cache->content[p++];
+ if (p >= cache->count)
+ p -= cache->count;
+ } while(c);
+ }
+ return (p > pos ? p : (p + cache->count)) - pos;
+}
+
+static
+void
+drop_one(
+ cache_t *cache
+) {
+ uint32_t l = lenat(cache, cache->begin);
+ cache->used -= l;
+ cache->begin += l;
+ if (cache->begin > cache->count)
+ cache->begin -= cache->count;
+}
+
+static
+void
+addc(
+ cache_t *cache,
+ char c
+) {
+ uint32_t pos;
+ if (cache->used == cache->count)
+ drop_one(cache);
+ pos = cache->begin + cache->used++;
+ if (pos > cache->count)
+ pos -= cache->count;
+ cache->content[pos < cache->count ? pos : pos - cache->count] = c;
+}
+
+static
+void
+adds(
+ cache_t *cache,
+ const char *s
+) {
+ do { addc(cache, *s); } while(*s++);
+}
+
+int
+cache_put(
+ cache_t *cache,
+ const char *client,
+ const char *session,
+ const char *user,
+ const char *permission,
+ int value
+) {
+ if (cache == NULL
+ || strlen(client) + strlen(session)
+ + strlen(user) + strlen(permission)
+ + 5 > cache->count)
+ return -EINVAL;
+
+ addc(cache, (char)value);
+ adds(cache, client);
+ adds(cache, session);
+ adds(cache, user);
+ adds(cache, permission);
+ return 0;
+}
+
+int
+cache_search(
+ cache_t *cache,
+ const char *client,
+ const char *session,
+ const char *user,
+ const char *permission
+) {
+ return -ENOENT;
+}
+
+void
+cache_clear(
+ cache_t *cache
+) {
+ if (cache) {
+ cache->used = 0;
+ cache->begin = 0;
+ }
+}
+
+int
+cache_resize(
+ cache_t **cache,
+ uint32_t newsize
+) {
+ cache_t *c = *cache, *nc;
+
+ while (c && c->used > newsize)
+ drop_one(c);
+
+ nc = malloc(newsize - 1 + sizeof *c);
+ if (nc == NULL)
+ return -ENOMEM;
+
+ nc->begin = 0;
+ nc->count = newsize;
+ if (!c || c->used == 0)
+ nc->used = 0;
+ else {
+ if (c->begin + c->used <= c->count)
+ memcpy(&nc->content[0], &c->content[c->begin], c->used);
+ else {
+ memcpy(&nc->content[0], &c->content[c->begin], c->count - c->begin);
+ memcpy(&nc->content[c->count - c->begin], &c->content[0], c->used + c->begin - c->count);
+ }
+
+ nc->used = c->used;
+ }
+ *cache = nc;
+ free(c);
+ return 0;
+}
+
+int
+cache_create(
+ cache_t **cache,
+ uint32_t size
+) {
+ *cache = NULL;
+ return cache_resize(cache, size);
+}
+
+
+
diff --git a/src/cache.h b/src/cache.h
new file mode 100644
index 0000000..2f3a9a1
--- /dev/null
+++ b/src/cache.h
@@ -0,0 +1,48 @@
+#pragma once
+
+struct cache;
+typedef struct cache cache_t;
+
+extern
+int
+cache_search(
+ cache_t *cache,
+ const char *client,
+ const char *session,
+ const char *user,
+ const char *permission
+);
+
+extern
+int
+cache_put(
+ cache_t *cache,
+ const char *client,
+ const char *session,
+ const char *user,
+ const char *permission,
+ int value
+);
+
+extern
+void
+cache_clear(
+ cache_t *cache
+);
+
+extern
+int
+cache_resize(
+ cache_t **cache,
+ uint32_t newsize
+);
+
+extern
+int
+cache_create(
+ cache_t **cache,
+ uint32_t size
+);
+
+
+
diff --git a/src/cyn.c b/src/cyn.c
new file mode 100644
index 0000000..4eb6d9e
--- /dev/null
+++ b/src/cyn.c
@@ -0,0 +1,267 @@
+#define _GNU_SOURCE
+
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <errno.h>
+
+
+#include "db.h"
+#include "queue.h"
+#include "cyn.h"
+
+struct callback
+{
+ struct callback *next;
+ void (*callback)(void *closure);
+ void *closure;
+};
+
+/** locking critical section */
+static const void *lock;
+static struct callback *awaiters;
+static struct callback *observers;
+
+static
+int
+delcb(
+ void (*callback)(void *closure),
+ void *closure,
+ struct callback **head
+) {
+ struct callback *c;
+
+ while((c = *head)) {
+ if (c->callback == callback && c->closure == closure) {
+ *head = c->next;
+ free(c);
+ return 1;
+ }
+ head = &c->next;
+ }
+ return 0;
+}
+
+static
+int
+addcb(
+ void (*callback)(void *closure),
+ void *closure,
+ struct callback **head
+) {
+ struct callback *c;
+
+ c = malloc(sizeof *c);
+ if (c == NULL)
+ return -(errno = ENOMEM);
+ c->callback = callback;
+ c->closure = closure;
+ c->next = *head;
+ *head = c;
+ return 0;
+}
+
+static
+int
+changed(
+) {
+ int rc = db_sync();
+ struct callback *c;
+
+ for (c = observers; c ; c = c->next)
+ c->callback(c->closure);
+ return rc;
+}
+
+int
+cyn_init(
+) {
+ /* TODO: paths? */
+ int rc = db_open("/var/lib/cynara/cynara.names", "/var/lib/cynara/cynara.rules");
+ if (rc == 0 && db_is_empty()) {
+ /* TODO: init? */
+ rc = db_set("System", "*", "*", "*", 1);
+ db_sync();
+ }
+ return rc;
+}
+
+/** enter critical recoverable section */
+int
+cyn_enter(
+ const void *magic
+) {
+ if (lock)
+ return -EBUSY;
+ lock = magic;
+ return 0;
+}
+
+int
+cyn_enter_async(
+ void (*enter_cb)(void *closure),
+ void *closure
+) {
+ if (lock)
+ return addcb(enter_cb, closure, &awaiters);
+
+ lock = closure;
+ enter_cb(closure);
+ return 0;
+}
+
+int
+cyn_enter_async_cancel(
+ void (*enter_cb)(void *closure),
+ void *closure
+) {
+ return delcb(enter_cb, closure, &awaiters);
+}
+
+int
+cyn_on_change_add(
+ void (*on_change_cb)(void *closure),
+ void *closure
+) {
+ return addcb(on_change_cb, closure, &observers);
+}
+
+
+int
+cyn_on_change_remove(
+ void (*on_change_cb)(void *closure),
+ void *closure
+) {
+ return delcb(on_change_cb, closure, &observers);
+}
+
+/** leave critical recoverable section */
+int
+cyn_leave(
+ const void *magic,
+ bool commit
+) {
+ int rc;
+ struct callback *e, **p;
+
+ if (!magic)
+ return -EINVAL;
+ if (!lock)
+ return -EALREADY;
+ if (lock != magic)
+ return -EPERM;
+
+ lock = &lock;
+ if (commit)
+ rc = queue_play() ?: changed();
+ else
+ rc = 0;
+ queue_clear();
+
+ e = awaiters;
+ if (!e)
+ lock = 0;
+ else {
+ p = &awaiters;
+ while(e->next) {
+ p = &e->next;
+ e = *p;
+ }
+ *p = NULL;
+ lock = e->closure;
+ e->callback(e->closure);
+ free(e);
+ }
+
+ return rc;
+}
+
+int
+cyn_set(
+ const char *client,
+ const char *session,
+ const char *user,
+ const char *permission,
+ uint32_t value
+) {
+ if (lock && lock != &lock)
+ return queue_set(client, session, user, permission, value);
+
+ return db_set(client, session, user, permission, value) ?: changed();
+}
+
+int
+cyn_drop(
+ const char *client,
+ const char *session,
+ const char *user,
+ const char *permission
+) {
+ if (lock && lock != &lock)
+ return queue_drop(client, session, user, permission);
+
+ return db_drop(client, session, user, permission) ?: changed();
+}
+
+int
+cyn_test(
+ const char *client,
+ const char *session,
+ const char *user,
+ const char *permission,
+ uint32_t *value
+) {
+ int rc;
+
+ rc = db_test(client, session, user, permission, value);
+ if (rc <= 0)
+ *value = DEFAULT;
+ else
+ rc = 0;
+ return rc;
+}
+
+void
+cyn_list(
+ void *closure,
+ void (*callback)(
+ void *closure,
+ const char *client,
+ const char *session,
+ const char *user,
+ const char *permission,
+ uint32_t value),
+ const char *client,
+ const char *session,
+ const char *user,
+ const char *permission
+) {
+ db_for_all(closure, callback, client, session, user, permission);
+}
+
+int
+cyn_check_async(
+ void (*check_cb)(void *closure, uint32_t value),
+ void *closure,
+ const char *client,
+ const char *session,
+ const char *user,
+ const char *permission
+) {
+ uint32_t value;
+
+ cyn_test(client, session, user, permission, &value);
+ if (value == ALLOW || value == DENY) {
+ check_cb(closure, value);
+ return 0;
+ }
+
+ /* TODO: try to resolve AGENT?? */
+
+ check_cb(closure, value);
+ return 0;
+}
+
diff --git a/src/cyn.h b/src/cyn.h
new file mode 100644
index 0000000..0a061c2
--- /dev/null
+++ b/src/cyn.h
@@ -0,0 +1,113 @@
+
+#pragma once
+
+#define DENY 0
+#define ALLOW 1
+#define ASK 2
+#define DEFAULT DENY
+
+extern
+int
+cyn_init(
+);
+
+/** enter critical recoverable section */
+extern
+int
+cyn_enter(
+ const void *magic
+);
+
+/** leave critical recoverable section */
+extern
+int
+cyn_leave(
+ const void *magic,
+ bool commit
+);
+
+extern
+int
+cyn_set(
+ const char *client,
+ const char *session,
+ const char *user,
+ const char *permission,
+ uint32_t value
+);
+
+extern
+int
+cyn_drop(
+ const char *client,
+ const char *session,
+ const char *user,
+ const char *permission
+);
+
+extern
+int
+cyn_test(
+ const char *client,
+ const char *session,
+ const char *user,
+ const char *permission,
+ uint32_t *value
+);
+
+extern
+void
+cyn_list(
+ void *closure,
+ void (*callback)(
+ void *closure,
+ const char *client,
+ const char *session,
+ const char *user,
+ const char *permission,
+ uint32_t value),
+ const char *client,
+ const char *session,
+ const char *user,
+ const char *permission
+);
+
+extern
+int
+cyn_check_async(
+ void (*check_cb)(void *closure, uint32_t value),
+ void *closure,
+ const char *client,
+ const char *session,
+ const char *user,
+ const char *permission
+);
+
+extern
+int
+cyn_enter_async(
+ void (*enter_cb)(void *closure),
+ void *closure
+);
+
+extern
+int
+cyn_enter_async_cancel(
+ void (*enter_cb)(void *closure),
+ void *closure
+);
+
+extern
+int
+cyn_on_change_add(
+ void (*on_change_cb)(void *closure),
+ void *closure
+);
+
+extern
+int
+cyn_on_change_remove(
+ void (*on_change_cb)(void *closure),
+ void *closure
+);
+
diff --git a/src/db.c b/src/db.c
new file mode 100644
index 0000000..c7efdca
--- /dev/null
+++ b/src/db.c
@@ -0,0 +1,661 @@
+
+#define _GNU_SOURCE
+
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdalign.h>
+#include <string.h>
+#include <errno.h>
+#include <syslog.h>
+
+#include "fbuf.h"
+#include "db.h"
+
+#define NOIDX 0
+
+#define ANYIDX 40
+#define ANYSTR "#"
+
+#define WIDEIDX 42
+#define WIDESTR "*"
+
+/**
+ * A rule is a set of 4 integers
+ */
+struct rule
+{
+ uint32_t client, user, permission, value;
+};
+typedef struct rule rule_t;
+
+/**
+ * Sessions
+ */
+struct session
+{
+ struct session *next, *prev;
+ rule_t *rules;
+ const char *name;
+ uint32_t count;
+};
+typedef struct session session_t;
+
+/*
+ * The cynara-agl database is made of 2 memory mapped files:
+ * - names: the zero terminated names
+ * - rules: the rules based on name indexes as 32bits indexes
+ * These files are normally in /var/lib/cynara
+ */
+
+/** the file for the names */
+static fbuf_t fnames;
+
+/** the file for the rules */
+static fbuf_t frules;
+
+/** identification of names version 1 (uuidgen --sha1 -n @url -N urn:AGL:cynara:db:names:1) */
+static const char uuid_names_v1[] = "e9481f9e-b2f4-5716-90cf-c286d98d1868\n--\n";
+
+/** identification of rules version 1 (uuidgen --sha1 -n @url -N urn:AGL:cynara:db:rules:1) */
+static const char uuid_rules_v1[] = "8f7a5b21-48b1-57af-96c9-d5d7192be370\n--\n";
+
+/** length of the identification */
+static const int uuidlen = 40;
+
+/** count of names */
+static uint32_t names_count;
+
+/** the name indexes sorted */
+static uint32_t *names_sorted;
+
+/** the sessions */
+static session_t sessions = {
+ .next = &sessions,
+ .prev = &sessions,
+ .name = WIDESTR
+};
+
+/** return the name of 'index' */
+static
+const char*
+name_at(
+ uint32_t index
+) {
+ return (const char*)(fnames.buffer + index);
+}
+
+/** compare names. used by qsort and bsearch */
+static
+int
+cmpnames(
+ const void *pa,
+ const void *pb
+) {
+ uint32_t a = *(const uint32_t*)pa;
+ uint32_t b = *(const uint32_t*)pb;
+ return strcmp(name_at(a), name_at(b));
+}
+
+/** search the index of 'name' and create it if 'needed' */
+int
+db_get_name_index(
+ uint32_t *index,
+ const char *name,
+ bool needed
+) {
+ uint32_t lo, up, m, i, *p;
+ int c;
+ const char *n;
+ size_t len;
+
+ /* special names */
+ if (!name || !name[0])
+ name = ANYSTR;
+
+ /* dichotomic search */
+ lo = 0;
+ up = names_count;
+ while(lo < up) {
+ m = (lo + up) >> 1;
+ i = names_sorted[m];
+ n = name_at(i);
+ c = strcmp(n, name);
+
+ if (c == 0) {
+ /* found */
+ *index = i;
+ return 0;
+ }
+
+ /* dichotomic iteration */
+ if (c < 0)
+ lo = m + 1;
+ else
+ up = m;
+ }
+
+ /* not found */
+ if (!needed) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ /* check length */
+ len = strnlen(name, MAX_NAME_LENGTH + 1);
+ if (len > MAX_NAME_LENGTH) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* add the name in the file */
+ i = fnames.used;
+ c = fbuf_append(&fnames, name, 1 + (uint32_t)len);
+ if (c < 0)
+ return c;
+
+ /* add the name in sorted array */
+ up = names_count;
+ if (!(up & 1023)) {
+ p = realloc(names_sorted, (up + 1024) * sizeof *names_sorted);
+ if (p == NULL) {
+ syslog(LOG_ERR, "out of memory");
+ return -1;
+ }
+ names_sorted = p;
+ }
+ memmove(&names_sorted[lo + 1], &names_sorted[lo], (up - lo) * sizeof *names_sorted);
+ names_count = up + 1;
+ *index = names_sorted[lo] = i;
+ return 0;
+}
+
+/** initialize names */
+static
+int
+init_names(
+) {
+ int rc;
+ uint32_t pos, len, *ns, *p, all, nc;
+
+ all = 0;
+ nc = 0;
+ ns = NULL;
+
+ /* iterate over names */
+ pos = uuidlen;
+ while (pos < fnames.saved) {
+ /* get name length */
+ len = (uint32_t)strlen(name_at(pos));
+ if (pos + len <= pos || pos + len > fnames.saved) {
+ free(ns);
+ goto bad_file;
+ }
+ /* store the position */
+ if (all <= nc) {
+ all += 1024;
+ p = realloc(ns, all * sizeof *ns);
+ if (p == NULL) {
+ free(ns);
+ syslog(LOG_ERR, "out of memory");
+ goto error;
+ }
+ ns = p;
+ }
+ ns[nc++] = pos;
+ /* next */
+ pos += len + 1;
+ }
+
+ /* sort and record */
+ qsort(ns, nc, sizeof *ns, cmpnames);
+ names_sorted = ns;
+ names_count = nc;
+
+ /* predefined symbols */
+ rc = db_get_name_index(&pos, ANYSTR, true);
+ if (rc < 0)
+ goto error;
+ if (pos != ANYIDX)
+ goto bad_file;
+ rc = db_get_name_index(&pos, WIDESTR, true);
+ if (rc < 0)
+ goto error;
+ if (pos != WIDEIDX)
+ goto bad_file;
+
+ return 0;
+bad_file:
+ syslog(LOG_ERR, "bad file %s", fnames.name);
+ errno = ENOEXEC;
+error:
+ return -1;
+}
+
+/** check whether the 'text' fit ANYSTR, NULL or "" */
+static
+bool
+is_any(
+ const char *text
+) {
+ return text == NULL || text[0] == 0 || 0 == strcmp(text, ANYSTR);
+}
+
+/** check whether the 'text' fit ANYSTR, WIDESTR, NULL or "" */
+static
+bool
+is_any_or_wide(
+ const char *text
+) {
+ return is_any(text) || 0 == strcmp(text, WIDESTR);
+}
+
+/** get in 'session' the session for 'name' and create it if 'needed' */
+static
+int
+get_session(
+ const char *name,
+ bool needed,
+ session_t **session
+) {
+ session_t *s;
+ size_t len;
+
+ /* start on ANY sessions */
+ s = &sessions;
+ if (is_any_or_wide(name))
+ goto found;
+
+ /* look to other sessions */
+ s = s->next;
+ while(s != &sessions) {
+ if (!strcmp(s->name, name))
+ goto found;
+ s = s->next;
+ }
+
+ /* not found */
+ if (!needed) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ /* check length */
+ len = strnlen(name, MAX_NAME_LENGTH + 1);
+ if (len > MAX_NAME_LENGTH) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* create it */
+ s = malloc(sizeof * s + len + 1);
+ if (s == NULL)
+ return -1; /* out of memory */
+
+ /* init new session */
+ s->rules = NULL;
+ s->count = 0;
+ s->name = strcpy((char*)(s + 1), name);
+ s->next = &sessions;
+ s->prev = sessions.prev;
+ sessions.prev = s;
+ s->prev->next = s;
+found:
+ *session = s;
+ return 0;
+}
+
+/** for 'session' set the value the rule at 'index' */
+static
+void
+session_set_at(
+ session_t *session,
+ uint32_t index,
+ uint32_t value
+) {
+ uint32_t pos;
+
+ assert(index < session->count);
+ session->rules[index].value = value;
+ if (session == &sessions) {
+ pos = (uint32_t)(((void*)&session->rules[index]) - frules.buffer);
+ if (pos < frules.saved)
+ frules.saved = pos;
+ }
+}
+
+/** drop of 'session' the rule at 'index' */
+static
+void
+session_drop_at(
+ session_t *session,
+ uint32_t index
+) {
+ uint32_t pos;
+
+ assert(index < session->count);
+ if (index < --session->count)
+ session->rules[index] = session->rules[session->count];
+ if (session == &sessions) {
+ pos = (uint32_t)(((void*)&session->rules[index]) - frules.buffer);
+ if (pos < frules.saved)
+ frules.saved = pos;
+ pos = (uint32_t)(((void*)&session->rules[session->count]) - frules.buffer);
+ frules.used = pos;
+ }
+}
+
+/** add to 'session' the rule 'client' x 'user' x 'permission' x 'value' */
+static
+int
+session_add(
+ session_t *session,
+ uint32_t client,
+ uint32_t user,
+ uint32_t permission,
+ uint32_t value
+) {
+ int rc;
+ uint32_t c;
+ rule_t *rule;
+
+ if (session == &sessions) {
+ c = frules.used + (uint32_t)sizeof *rule;
+ rc = fbuf_ensure_capacity(&frules, c);
+ if (rc)
+ return rc;
+ frules.used = c;
+ session->rules = (rule_t*)(frules.buffer + uuidlen);
+ } else {
+ c = session->count + 32 - (session->count & 31);
+ rule = realloc(session->rules, c * sizeof *rule);
+ if (rule == NULL)
+ return -ENOMEM;
+ session->rules = rule;
+ }
+ rule = &session->rules[session->count++];
+ rule->client = client;
+ rule->user = user;
+ rule->permission = permission;
+ rule->value = value;
+ return 0;
+}
+
+/** init the rules from the file */
+static
+void
+init_rules(
+) {
+ sessions.rules = (rule_t*)(frules.buffer + uuidlen);
+ sessions.count = (frules.used - uuidlen) / sizeof *sessions.rules;
+}
+
+/** open the database for files 'names' and 'rules' (can be NULL) */
+int
+db_open(
+ const char *names,
+ const char *rules
+) {
+ int rc;
+
+ /* open the names */
+ rc = fbuf_open_identify(&fnames, names ?: "cynara.names", uuid_names_v1, uuidlen);
+ if (rc < 0)
+ goto error;
+
+ /* open the rules */
+ rc = fbuf_open_identify(&frules, rules ?: "cynara.rules", uuid_rules_v1, uuidlen);
+ if (rc < 0)
+ goto error;
+
+ /* connect internals */
+ rc = init_names();
+ if (rc < 0)
+ goto error;
+
+ init_rules();
+ return 0;
+error:
+ return -1;
+}
+
+/** close the database */
+void
+db_close(
+) {
+ assert(fnames.name && frules.name);
+ fbuf_close(&fnames);
+ fbuf_close(&frules);
+}
+
+/** is the database empty */
+bool
+db_is_empty(
+) {
+ return !sessions.count;
+}
+
+/** synchronize db on files */
+int
+db_sync(
+) {
+ int rc;
+
+ assert(fnames.name && frules.name);
+ rc = fbuf_sync(&fnames);
+ if (rc == 0)
+ rc = fbuf_sync(&frules);
+ return rc;
+}
+
+/** enumerate */
+void
+db_for_all(
+ void *closure,
+ void (*callback)(
+ void *closure,
+ const char *client,
+ const char *session,
+ const char *user,
+ const char *permission,
+ uint32_t value),
+ const char *client,
+ const char *session,
+ const char *user,
+ const char *permission
+) {
+ uint32_t ucli, uusr, i;
+ bool anyperm, anysession;
+ session_t *ses;
+
+ if (db_get_name_index(&ucli, client, false)
+ || db_get_name_index(&uusr, user, false))
+ return; /* nothing to do! */
+
+ anyperm = is_any(permission);
+ anysession = is_any(session);
+ if (anysession)
+ ses = &sessions;
+ else {
+ if (get_session(session, false, &ses))
+ return; /* ignore if no session */
+ }
+ for(;;) {
+ for (i = 0; i < ses->count; i++) {
+ if ((ucli == ANYIDX || ucli == ses->rules[i].client)
+ && (uusr == ANYIDX || uusr == ses->rules[i].user)
+ && (anyperm || !strcasecmp(permission, name_at(ses->rules[i].permission)))) {
+ callback(closure,
+ name_at(ses->rules[i].client),
+ ses->name,
+ name_at(ses->rules[i].user),
+ name_at(ses->rules[i].permission),
+ ses->rules[i].value);
+ }
+ }
+ if (!anysession)
+ break;
+ ses = ses->next;
+ if (ses == &sessions)
+ break;
+ }
+}
+
+/** drop rules */
+int
+db_drop(
+ const char *client,
+ const char *session,
+ const char *user,
+ const char *permission
+) {
+ uint32_t ucli, uusr, i;
+ bool anyperm, anysession;
+ session_t *ses;
+
+ if (db_get_name_index(&ucli, client, false)
+ || db_get_name_index(&uusr, user, false))
+ return 0; /* nothing to do! */
+
+ anyperm = is_any(permission);
+ anysession = is_any(session);
+ if (anysession)
+ ses = &sessions;
+ else {
+ if (get_session(session, false, &ses))
+ return 0; /* ignore if no session */
+ }
+ for(;;) {
+ i = 0;
+ while (i < ses->count) {
+ if ((ucli == ANYIDX || ucli == ses->rules[i].client)
+ && (uusr == ANYIDX || uusr == ses->rules[i].user)
+ && (anyperm || !strcasecmp(permission, name_at(ses->rules[i].permission))))
+ session_drop_at(ses, i);
+ else
+ i++;
+ }
+ if (!anysession)
+ break;
+ ses = ses->next;
+ if (ses == &sessions)
+ break;
+ }
+ return 0;
+}
+
+/** set rules */
+int
+db_set(
+ const char *client,
+ const char *session,
+ const char *user,
+ const char *permission,
+ uint32_t value
+) {
+ int rc;
+ uint32_t ucli, uusr, uperm, i;
+ session_t *ses;
+
+ /* normalize */
+ client = is_any_or_wide(client) ? WIDESTR : client;
+ session = is_any_or_wide(session) ? WIDESTR : session;
+ user = is_any_or_wide(user) ? WIDESTR : user;
+ permission = is_any_or_wide(permission) ? WIDESTR : permission;
+
+ /* get the session */
+ rc = get_session(session, true, &ses);
+ if (rc)
+ goto error;
+
+ /* get/create strings */
+ rc = db_get_name_index(&ucli, client, true);
+ if (rc)
+ goto error;
+ rc = db_get_name_index(&uusr, user, true);
+ if (rc)
+ goto error;
+
+ /* search the existing rule */
+ for (i = 0 ; i < ses->count ; i++) {
+ if (ucli == ses->rules[i].client
+ && uusr == ses->rules[i].user
+ && !strcasecmp(permission, name_at(ses->rules[i].permission))) {
+ /* found */
+ session_set_at(ses, i, value);
+ return 0;
+ }
+ }
+
+ /* create the rule */
+ rc = db_get_name_index(&uperm, permission, true);
+ if (rc)
+ goto error;
+
+ rc = session_add(ses, ucli, uusr, uperm, value);
+
+ return 0;
+error:
+ return rc;
+}
+
+/** check rules */
+int
+db_test(
+ const char *client,
+ const char *session,
+ const char *user,
+ const char *permission,
+ uint32_t *value
+) {
+ uint32_t ucli, uusr, i, val, score, sc;
+ session_t *ses;
+ rule_t *rule;
+
+ /* check */
+ client = is_any_or_wide(client) ? WIDESTR : client;
+ session = is_any_or_wide(session) ? WIDESTR : session;
+ user = is_any_or_wide(user) ? WIDESTR : user;
+ permission = is_any_or_wide(permission) ? WIDESTR : permission;
+
+ /* search the items */
+ val = score = 0;
+#define NOIDX 0
+ if (db_get_name_index(&ucli, client, false))
+ ucli = NOIDX;
+ if (db_get_name_index(&uusr, user, false))
+ uusr = NOIDX;
+
+ /* get the session */
+ if (get_session(session, false, &ses))
+ ses = &sessions;
+
+retry:
+ /* search the existing rule */
+ for (i = 0 ; i < ses->count ; i++) {
+ rule = &ses->rules[i];
+ if ((ucli == rule->client || WIDEIDX == rule->client)
+ && (uusr == rule->user || WIDEIDX == rule->user)
+ && (WIDEIDX == rule->permission
+ || !strcasecmp(permission, name_at(rule->permission)))) {
+ /* found */
+ sc = 1 + (rule->client != WIDEIDX)
+ + (rule->user != WIDEIDX) + (rule->permission != WIDEIDX);
+ if (sc > score) {
+ score = sc;
+ val = rule->value;
+ }
+ }
+ }
+ if (!score && ses != &sessions) {
+ ses = &sessions;
+ goto retry;
+ }
+
+ if (score)
+ *value = val;
+ return score > 0;
+}
+
diff --git a/src/db.h b/src/db.h
new file mode 100644
index 0000000..ce56e8c
--- /dev/null
+++ b/src/db.h
@@ -0,0 +1,102 @@
+#pragma once
+
+#define MAX_NAME_LENGTH 32767
+
+/** open the database for files 'names' and 'rules' (can be NULL) */
+extern
+int
+db_open(
+ const char *names,
+ const char *rules
+);
+
+/** close the database */
+extern
+void
+db_close(
+);
+
+/** is the database empty */
+extern
+bool
+db_is_empty(
+);
+
+/** sync the database */
+extern
+int
+db_sync(
+);
+
+/** enter critical recoverable section */
+extern
+int
+db_enter(
+);
+
+/** leave critical recoverable section */
+extern
+int
+db_leave(
+ bool commit
+);
+
+/** get an index for a name */
+extern
+int
+db_get_name_index(
+ uint32_t *index,
+ const char *name,
+ bool needed
+);
+
+/** enumerate */
+extern
+void
+db_for_all(
+ void *closure,
+ void (*callback)(
+ void *closure,
+ const char *client,
+ const char *session,
+ const char *user,
+ const char *permission,
+ uint32_t value),
+ const char *client,
+ const char *session,
+ const char *user,
+ const char *permission
+);
+
+/** erase rules */
+extern
+int
+db_drop(
+ const char *client,
+ const char *session,
+ const char *user,
+ const char *permission
+);
+
+/** set rules */
+extern
+int
+db_set(
+ const char *client,
+ const char *session,
+ const char *user,
+ const char *permission,
+ uint32_t value
+);
+
+/** check rules */
+extern
+int
+db_test(
+ const char *client,
+ const char *session,
+ const char *user,
+ const char *permission,
+ uint32_t *value
+);
+
diff --git a/src/export.map b/src/export.map
new file mode 100644
index 0000000..84467af
--- /dev/null
+++ b/src/export.map
@@ -0,0 +1,9 @@
+{
+global:
+ rcyn_*;
+ cynara_*;
+local:
+ *;
+};
+
+
diff --git a/src/fbuf.c b/src/fbuf.c
new file mode 100644
index 0000000..6d4d41c
--- /dev/null
+++ b/src/fbuf.c
@@ -0,0 +1,279 @@
+
+#define _GNU_SOURCE
+
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <limits.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/file.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <syslog.h>
+
+#include "fbuf.h"
+
+/** compute the size to allocate for ensuring 'sz' bytes */
+static
+uint32_t
+get_asz(
+ uint32_t sz
+) {
+ return (sz & 0xfffffc00) + 0x000004cf;
+}
+
+/** open in 'fb' the file of 'name' */
+int
+fbuf_open(
+ fbuf_t *fb,
+ const char *name
+) {
+ struct stat st;
+ int fd, rc;
+ uint32_t sz, asz;
+ void *buffer;
+
+ /* open the file */
+ //fd = open(name, O_RDWR|O_CREAT|O_SYNC|O_DIRECT, 0600);
+ fd = open(name, O_RDWR|O_CREAT, 0600);
+ if (fd < 0)
+ goto error;
+
+ /* lock it */
+ rc = flock(fd, LOCK_EX|LOCK_NB);
+ if (rc < 0)
+ goto error2;
+
+ /* get file stat */
+ rc = fstat(fd, &st);
+ if (rc < 0)
+ goto error3;
+
+ /* check file size */
+ if ((off_t)INT32_MAX < st.st_size) {
+ errno = EFBIG;
+ goto error3;
+ }
+
+ /* compute allocation size */
+ sz = (uint32_t)st.st_size;
+ asz = get_asz(sz);
+ buffer = malloc(asz);
+ if (buffer == NULL)
+ goto error3;
+
+ /* read the file */
+ if (read(fd, buffer, (size_t)sz) != (ssize_t)sz)
+ goto error4;
+
+ /* done */
+ fb->name = name;
+ fb->buffer = buffer;
+ fb->saved = fb->used = fb->size = sz;
+ fb->capacity = asz;
+ fb->fd = fd;
+ return 0;
+
+error4:
+ free(buffer);
+error3:
+ flock(fd, LOCK_UN);
+error2:
+ close(fd);
+error:
+ syslog(LOG_ERR, "can't open file %s: %m", name);
+ memset(fb, 0, sizeof *fb);
+ return -errno;
+}
+
+/** close the file 'fb' */
+void
+fbuf_close(
+ fbuf_t *fb
+) {
+ free(fb->buffer);
+ flock(fb->fd, LOCK_UN);
+ close(fb->fd);
+ memset(fb, 0, sizeof *fb);
+}
+
+/** write to file 'fb' at 'offset' the 'count' bytes pointed by 'buffer' */
+int
+fbuf_write(
+ fbuf_t *fb,
+ const void *buffer,
+ uint32_t count,
+ uint32_t offset
+) {
+ off_t rco;
+ ssize_t rcs;
+
+ /* don't call me for nothing */
+ assert(count);
+
+ /* set write position */
+ rco = lseek(fb->fd, (off_t)offset, SEEK_SET);
+ if (rco != (off_t)offset)
+ goto error;
+
+ /* effective write */
+ rcs = write(fb->fd, buffer, (size_t)count);
+ if (rcs != (ssize_t)count)
+ goto error;
+
+ return 0;
+error:
+ syslog(LOG_ERR, "write of file %s failed: %m", fb->name);
+ return -errno;
+}
+
+/** write to file 'fb' the unsaved bytes and flush the content to the file */
+int
+fbuf_sync(
+ fbuf_t *fb
+) {
+ int rc;
+ bool changed = false;
+
+ /* write unsaved bytes */
+ if (fb->used > fb->saved) {
+ rc = fbuf_write(fb, fb->buffer + fb->saved, fb->used - fb->saved, fb->saved);
+ if (rc < 0)
+ return rc;
+ fb->saved = fb->used;
+ changed = true;
+ }
+
+ /* truncate on needed */
+ if (fb->used < fb->size) {
+ rc = ftruncate(fb->fd, (off_t)fb->used);
+ if (rc < 0)
+ goto error;
+ changed = true;
+ }
+ fb->size = fb->used;
+
+ /* force synchronisation of the file */
+ if (changed) {
+ rc = fsync(fb->fd);
+ if (rc < 0)
+ goto error;
+ }
+
+ return 0;
+error:
+ syslog(LOG_ERR, "sync of file %s failed: %m", fb->name);
+ return -errno;
+}
+
+/** allocate enough memory in 'fb' to store 'count' bytes */
+int
+fbuf_ensure_capacity(
+ fbuf_t *fb,
+ uint32_t count
+) {
+ uint32_t capacity;
+ void *buffer;
+
+ if (count > fb->capacity) {
+ capacity = get_asz(count);
+ buffer = realloc(fb->buffer, capacity);
+ if (buffer == NULL) {
+ syslog(LOG_ERR, "alloc %u for file %s failed: %m", capacity, fb->name);
+ return -ENOMEM;
+ }
+ fb->buffer = buffer;
+ fb->capacity = capacity;
+ }
+ return 0;
+}
+
+/** put at 'offset' in the memory of 'fb' the 'count' bytes pointed by 'buffer' */
+int
+fbuf_put(
+ fbuf_t *fb,
+ const void *buffer,
+ uint32_t count,
+ uint32_t offset
+) {
+ int rc;
+ uint32_t end = offset + count;
+
+ /* don't call me for nothing */
+ assert(count);
+
+ /* grow as necessary */
+ if (end > fb->used) {
+ rc = fbuf_ensure_capacity(fb, end);
+ if (rc < 0)
+ return rc;
+ fb->used = end;
+ }
+
+ /* copy the data */
+ memcpy(fb->buffer + offset, buffer, count);
+
+ /* write the data to the disk */
+ if (offset < fb->saved)
+ fb->saved = offset;
+ return 0;
+}
+
+/** append at end in the memory of 'fb' the 'count' bytes pointed by 'buffer' */
+int
+fbuf_append(
+ fbuf_t *fb,
+ const void *buffer,
+ uint32_t count
+) {
+ /* don't call me for nothing */
+ assert(count);
+
+ return fbuf_put(fb, buffer, count, fb->used);
+}
+
+/** check or make identification of file 'fb' by 'id' of 'len' */
+int
+fbuf_identify(
+ fbuf_t *fb,
+ const char *id,
+ uint32_t idlen
+) {
+ /* init if empty */
+ if (fb->saved == 0 && fb->used == 0)
+ return fbuf_append(fb, id, idlen);
+
+ /* check if not empty */
+ if (fb->saved >= idlen && !memcmp(fb->buffer, id, idlen))
+ return 0;
+
+ /* bad identification */
+ errno = ENOKEY;
+ syslog(LOG_ERR, "identification of file %s failed: %m", fb->name);
+ return -ENOKEY;
+}
+
+/** check or make identification by 'uuid' of file 'fb' */
+int
+fbuf_open_identify(
+ fbuf_t *fb,
+ const char *name,
+ const char *id,
+ uint32_t idlen
+) {
+ int rc;
+
+ rc = fbuf_open(fb, name);
+ if (rc == 0) {
+ rc = fbuf_identify(fb, id, idlen);
+ if (rc < 0)
+ fbuf_close(fb);
+ }
+ return rc;
+}
+
diff --git a/src/fbuf.h b/src/fbuf.h
new file mode 100644
index 0000000..3bbdbde
--- /dev/null
+++ b/src/fbuf.h
@@ -0,0 +1,111 @@
+#pragma once
+
+/**
+ * A fbuf records file data and access
+ */
+struct fbuf
+{
+ /** filename for messages */
+ const char *name;
+
+ /** in memory copy of the file */
+ void *buffer;
+
+ /** size saved to the file */
+ uint32_t saved;
+
+ /** size currently used */
+ uint32_t used;
+
+ /** size currently allocated */
+ uint32_t capacity;
+
+ /** size of the file */
+ uint32_t size;
+
+ /** opened file descriptor for the file */
+ int fd;
+};
+
+/** short type */
+typedef struct fbuf fbuf_t;
+
+
+/** open in 'fb' the file of 'name' */
+extern
+int
+fbuf_open(
+ fbuf_t *fb,
+ const char *name
+);
+
+/** close the file 'fb' */
+extern
+void
+fbuf_close(
+ fbuf_t *fb
+);
+
+/** write to file 'fb' at 'offset' the 'count' bytes pointed by 'buffer' */
+extern
+int
+fbuf_write(
+ fbuf_t *fb,
+ const void *buffer,
+ uint32_t count,
+ uint32_t offset
+);
+
+/** write to file 'fb' the unsaved bytes and flush the content to the file */
+extern
+int
+fbuf_sync(
+ fbuf_t *fb
+);
+
+/** allocate enough memory in 'fb' to store 'count' bytes */
+extern
+int
+fbuf_ensure_capacity(
+ fbuf_t *fb,
+ uint32_t count
+);
+
+/** put at 'offset' in the memory of 'fb' the 'count' bytes pointed by 'buffer' */
+extern
+int
+fbuf_put(
+ fbuf_t *fb,
+ const void *buffer,
+ uint32_t count,
+ uint32_t offset
+);
+
+/** append at end in the memory of 'fb' the 'count' bytes pointed by 'buffer' */
+extern
+int
+fbuf_append(
+ fbuf_t *fb,
+ const void *buffer,
+ uint32_t count
+);
+
+/** check or make identification of file 'fb' by 'id' of 'len' */
+extern
+int
+fbuf_identify(
+ fbuf_t *fb,
+ const char *id,
+ uint32_t idlen
+);
+
+/** check or make identification by 'uuid' of file 'fb' */
+extern
+int
+fbuf_open_identify(
+ fbuf_t *fb,
+ const char *name,
+ const char *id,
+ uint32_t idlen
+);
+
diff --git a/src/lib-compat.c b/src/lib-compat.c
new file mode 100644
index 0000000..5c6c4e2
--- /dev/null
+++ b/src/lib-compat.c
@@ -0,0 +1,651 @@
+/*
+cynara_admin_initialize(&m_CynaraAdmin),
+cynara_admin_finish(m_CynaraAdmin);
+cynara_admin_set_policies(m_CynaraAdmin, pp_policies.data()),
+cynara_admin_list_policies(m_CynaraAdmin, bucketName.c_str(), appId.c_str(),
+cynara_admin_erase(m_CynaraAdmin, bucketName.c_str(), static_cast<int>(recursive),
+cynara_admin_check(m_CynaraAdmin, bucket.c_str(), recursive, label.c_str(),
+
+cynara_initialize(&m_Cynara, nullptr),
+cynara_finish(m_Cynara);
+cynara_check(m_Cynara,
+*/
+#define _GNU_SOURCE
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/epoll.h>
+
+#include <cynara/cynara-admin.h>
+#include <cynara/cynara-client.h>
+#include <cynara/cynara-client-async.h>
+#include <cynara/cynara-creds-commons.h>
+
+#ifndef CYNARA_ADMIN_ASK
+# define CYNARA_ADMIN_ASK 11
+#endif
+
+#include "rcyn-client.h"
+
+/******************** ADMIN ********************************/
+
+static int from_status(int rc)
+{
+ switch (-rc) {
+ case 0: rc = CYNARA_API_SUCCESS; break;
+ case ENOMEM: rc = CYNARA_API_OUT_OF_MEMORY; break;
+ case ENOTSUP: rc = CYNARA_API_METHOD_NOT_SUPPORTED; break;
+ case ENOENT: rc = CYNARA_API_CACHE_MISS; break;
+ default: rc = CYNARA_API_UNKNOWN_ERROR; break;
+ }
+ return rc;
+}
+
+static int from_check_status(int rc)
+{
+ switch (rc) {
+ case 0: rc = CYNARA_API_ACCESS_DENIED; break;
+ case 1: rc = CYNARA_API_ACCESS_ALLOWED; break;
+ case -EEXIST: rc = CYNARA_API_ACCESS_NOT_RESOLVED; break;
+ default: rc = from_status(rc); break;
+ }
+ return rc;
+}
+
+static int from_value(uint32_t value)
+{
+ switch(value) {
+ case 0: return CYNARA_ADMIN_DENY;
+ case 1: return CYNARA_ADMIN_ALLOW;
+ case 2: return CYNARA_ADMIN_ASK;
+ }
+ return (int)value;
+}
+
+static uint32_t to_value(int value)
+{
+ switch(value) {
+ case CYNARA_ADMIN_DENY: return 0;
+ case CYNARA_ADMIN_NONE: return 0;
+ case CYNARA_ADMIN_BUCKET: return 0;
+ case CYNARA_ADMIN_ALLOW: return 1;
+ case CYNARA_ADMIN_ASK: return 2;
+ }
+ return (uint32_t)value;
+}
+
+/************************************ ERROR ****************************************/
+
+static const struct {
+ int num;
+ const char *text;
+} error_descriptions[] = {
+ { CYNARA_API_INTERRUPTED, "API call was interrupted by user" },
+ { CYNARA_API_ACCESS_NOT_RESOLVED, "access cannot be resolved without further actions" },
+ { CYNARA_API_ACCESS_ALLOWED, "access that was checked is allowed" },
+ { CYNARA_API_ACCESS_DENIED, "access that was checked is denied" },
+ { CYNARA_API_SUCCESS, "successful" },
+ { CYNARA_API_CACHE_MISS, "value is not present in cache" },
+ { CYNARA_API_MAX_PENDING_REQUESTS, "pending requests reached maximum" },
+ { CYNARA_API_OUT_OF_MEMORY, "system is running out of memory" },
+ { CYNARA_API_INVALID_PARAM, "parameter is malformed" },
+ { CYNARA_API_SERVICE_NOT_AVAILABLE, "service is not available" },
+ { CYNARA_API_METHOD_NOT_SUPPORTED, "method is not supported by library" },
+ { CYNARA_API_OPERATION_NOT_ALLOWED, "not allowed to perform requested operation" },
+ { CYNARA_API_OPERATION_FAILED, "failed to perform requested operation" },
+ { CYNARA_API_BUCKET_NOT_FOUND, "service hasn't found requested bucket" },
+ { CYNARA_API_UNKNOWN_ERROR, "unknown error" },
+ { CYNARA_API_CONFIGURATION_ERROR, "configuration error" },
+ { CYNARA_API_INVALID_COMMANDLINE_PARAM, "invalid parameter in command-line" },
+ { CYNARA_API_BUFFER_TOO_SHORT, "provided buffer is too short" },
+ { CYNARA_API_DATABASE_CORRUPTED, "database is corrupted" },
+ { CYNARA_API_PERMISSION_DENIED, "user doesn't have enough permission to perform action" },
+};
+
+int cynara_strerror(int errnum, char *buf, size_t buflen)
+{
+ int i = (int)(sizeof error_descriptions / sizeof *error_descriptions);
+ while(i) {
+ if (error_descriptions[--i].num == errnum) {
+ if (strlen(error_descriptions[i].text) >= buflen)
+ return CYNARA_API_BUFFER_TOO_SHORT;
+ if (buf == NULL)
+ break;
+ strcpy(buf, error_descriptions[i].text);
+ return CYNARA_API_SUCCESS;
+ }
+ }
+ return CYNARA_API_INVALID_PARAM;
+}
+
+/******************** ADMIN ********************************/
+
+struct cynara_admin;
+
+int cynara_admin_initialize(struct cynara_admin **pp_cynara_admin)
+{
+ return from_status(rcyn_open((rcyn_t**)pp_cynara_admin, rcyn_Admin, 0));
+}
+
+int cynara_admin_finish(struct cynara_admin *p_cynara_admin)
+{
+ rcyn_close((rcyn_t*)p_cynara_admin);
+ return CYNARA_API_SUCCESS;
+}
+
+int cynara_admin_set_policies(struct cynara_admin *p_cynara_admin,
+ const struct cynara_admin_policy *const *policies)
+{
+ int rc, rc2;
+ const struct cynara_admin_policy *p;
+
+ rc = rcyn_enter((rcyn_t*)p_cynara_admin);
+ if (rc == 0) {
+ p = *policies;
+ while (rc == 0 && p != NULL) {
+ if (p->result == CYNARA_ADMIN_DELETE)
+ rc = rcyn_drop((rcyn_t*)p_cynara_admin,
+ p->client, "*", p->user, p->privilege);
+ else if (p->result != CYNARA_ADMIN_BUCKET && p->result != CYNARA_ADMIN_NONE)
+ rc = rcyn_set((rcyn_t*)p_cynara_admin,
+ p->client, "*", p->user, p->privilege, to_value(p->result));
+ p = *++policies;
+ }
+ rc2 = rcyn_leave((rcyn_t*)p_cynara_admin, rc == 0);
+ if (rc == 0)
+ rc = rc2;
+ }
+ return rc;
+}
+
+static void check_cb(
+ void *closure,
+ const char *client,
+ const char *session,
+ const char *user,
+ const char *permission,
+ uint32_t value
+) {
+ *((int*)closure) = from_value(value);
+}
+
+int cynara_admin_check(struct cynara_admin *p_cynara_admin,
+ const char *start_bucket, const int recursive,
+ const char *client, const char *user, const char *privilege,
+ int *result, char **result_extra)
+{
+ if (result_extra)
+ *result_extra = NULL;
+ *result = CYNARA_ADMIN_DENY;
+ return from_status(rcyn_get((rcyn_t*)p_cynara_admin, client, "*", user, privilege, check_cb, result));
+}
+
+struct list_data
+{
+ struct cynara_admin_policy **policies;
+ const char *bucket;
+ unsigned count;
+ int error;
+};
+
+static void list_cb(
+ void *closure,
+ const char *client,
+ const char *session,
+ const char *user,
+ const char *permission,
+ uint32_t value
+) {
+ struct list_data *data = closure;
+ struct cynara_admin_policy *pol;
+
+ if (data->error)
+ return;
+
+ pol = calloc(1, sizeof *pol);
+ if (pol == NULL)
+ goto error;
+
+ pol->bucket = strdup(data->bucket ?: "");
+ pol->client = strdup(client);
+ pol->user = strdup(user);
+ pol->privilege = strdup(permission);
+ if (pol->bucket == NULL || pol->client == NULL || pol->user == NULL || pol->privilege == NULL)
+ goto error;
+
+ pol->result = from_value(value);
+ pol->result_extra = 0;
+ closure = realloc(data->policies, (data->count + 1) * sizeof *data->policies);
+ if (closure == NULL)
+ goto error;
+
+ (data->policies = closure)[data->count++] = pol;
+ return;
+error:
+ if (pol) {
+ free(pol->bucket);
+ free(pol->client);
+ free(pol->user);
+ free(pol->privilege);
+ free(pol);
+ }
+ data->error = -ENOMEM;
+
+}
+
+int cynara_admin_list_policies(struct cynara_admin *p_cynara_admin, const char *bucket,
+ const char *client, const char *user, const char *privilege,
+ struct cynara_admin_policy ***policies)
+{
+ int rc;
+ struct list_data data;
+
+ data.policies = NULL;
+ data.bucket = bucket && strcmp(bucket, "#") && strcmp(bucket, "*") ? bucket : NULL;
+ data.count = 0;
+ data.error = 0;
+ rc = rcyn_get((rcyn_t*)p_cynara_admin, client, "*", user, privilege, list_cb, &data);
+ if (rc == 0 && data.error != 0)
+ rc = data.error;
+ if (rc == 0 && !data.error) {
+ if ((*policies = realloc(data.policies, (data.count + 1) * sizeof *data.policies)) != NULL)
+ policies[0][data.count] = NULL;
+ else
+ rc = -ENOMEM;
+ }
+ if (rc) {
+ while(data.count)
+ free(data.policies[--data.count]);
+ free(data.policies);
+ *policies = NULL;
+ }
+ return from_status(rc);
+}
+
+int cynara_admin_erase(struct cynara_admin *p_cynara_admin,
+ const char *start_bucket, int recursive,
+ const char *client, const char *user, const char *privilege)
+{
+ int rc, rc2;
+
+ rc = rcyn_enter((rcyn_t*)p_cynara_admin);
+ if (rc == 0) {
+ rc = rcyn_drop((rcyn_t*)p_cynara_admin,
+ client, "*", user, privilege);
+ rc2 = rcyn_leave((rcyn_t*)p_cynara_admin, rc == 0);
+ if (rc == 0)
+ rc = rc2;
+ }
+ return from_status(rc);
+}
+
+
+int cynara_admin_list_policies_descriptions(struct cynara_admin *p_cynara_admin,
+ struct cynara_admin_policy_descr ***descriptions)
+{
+ struct cynara_admin_policy_descr **d = malloc(4 * sizeof *d), *s;
+ if (d) {
+ d[0] = malloc(sizeof *s);
+ d[1] = malloc(sizeof *s);
+ d[2] = malloc(sizeof *s);
+ d[3] = NULL;
+ if (d[0] != NULL && d[1] != NULL && d[2] != NULL) {
+ d[0]->name = strdup("Deny");
+ d[1]->name = strdup("AskUser");
+ d[2]->name = strdup("Allow");
+ if (d[0]->name != NULL && d[1]->name != NULL && d[2]->name != NULL) {
+ d[0]->result = CYNARA_ADMIN_DENY;
+ d[1]->result = CYNARA_ADMIN_ASK;
+ d[2]->result = CYNARA_ADMIN_ALLOW;
+ *descriptions = d;
+ return CYNARA_API_SUCCESS;
+ }
+ free(d[0]->name);
+ free(d[1]->name);
+ free(d[2]->name);
+ }
+ free(d[0]);
+ free(d[1]);
+ free(d[2]);
+ }
+ *descriptions = NULL;
+ return CYNARA_API_OUT_OF_MEMORY;
+}
+
+/************************************* CLIENT-ASYNC **************************************/
+
+int cynara_async_configuration_create(cynara_async_configuration **pp_conf)
+{
+ *pp_conf = (cynara_async_configuration*)pp_conf;
+ return CYNARA_API_SUCCESS;
+}
+
+void cynara_async_configuration_destroy(cynara_async_configuration *p_conf)
+{
+}
+
+int cynara_async_configuration_set_cache_size(cynara_async_configuration *p_conf,
+ size_t cache_size)
+{
+ return CYNARA_API_SUCCESS;
+}
+
+struct reqasync
+{
+ struct reqasync *next;
+ cynara_async *cynasync;
+ cynara_response_callback callback;
+ void *user_response_data;
+ cynara_check_id id;
+ bool canceled;
+};
+
+struct cynara_async
+{
+ rcyn_t *rcyn;
+ cynara_status_callback callback;
+ void *user_status_data;
+ struct reqasync *reqs;
+ cynara_check_id ids;
+};
+
+static int async_control_cb(void *closure, int op, int fd, uint32_t events)
+{
+ cynara_async *p_cynara = closure;
+ cynara_async_status s = (events & EPOLLOUT) ? CYNARA_STATUS_FOR_RW : CYNARA_STATUS_FOR_READ;
+ switch(op) {
+ case EPOLL_CTL_ADD:
+ p_cynara->callback(-1, fd, s, p_cynara->user_status_data);
+ break;
+ case EPOLL_CTL_MOD:
+ p_cynara->callback(fd, fd, s, p_cynara->user_status_data);
+ break;
+ case EPOLL_CTL_DEL:
+ p_cynara->callback(fd, -1, 0, p_cynara->user_status_data);
+ break;
+ }
+ return 0;
+}
+
+int cynara_async_initialize(cynara_async **pp_cynara, const cynara_async_configuration *p_conf,
+ cynara_status_callback callback, void *user_status_data)
+{
+ int ret;
+ cynara_async *p_cynara;
+
+ p_cynara = malloc(sizeof *p_cynara);
+ if (p_cynara == NULL)
+ ret = CYNARA_API_OUT_OF_MEMORY;
+ else {
+ ret = from_status(rcyn_open(&p_cynara->rcyn, rcyn_Check, 0));
+ if (ret != CYNARA_API_SUCCESS)
+ free(p_cynara);
+ else {
+ p_cynara->callback = callback;
+ p_cynara->user_status_data = user_status_data;
+ p_cynara->reqs = NULL;
+ p_cynara->ids = 0;
+ rcyn_async_setup(p_cynara->rcyn, async_control_cb, p_cynara);
+ *pp_cynara = p_cynara;
+ }
+ }
+ return ret;
+}
+
+void cynara_async_finish(cynara_async *p_cynara)
+{
+ struct reqasync *req;
+
+ for(req = p_cynara->reqs ; req ; req = req->next) {
+ if (!req->canceled) {
+ req->callback(req->id, CYNARA_CALL_CAUSE_FINISH, 0, req->user_response_data);
+ req->canceled = true;
+ }
+ }
+
+ rcyn_close(p_cynara->rcyn);
+
+ while((req = p_cynara->reqs)) {
+ p_cynara->reqs = req->next;
+ free(req);
+ }
+ free(p_cynara);
+}
+
+int cynara_async_check_cache(cynara_async *p_cynara, const char *client, const char *client_session,
+ const char *user, const char *privilege)
+{
+ return from_check_status(rcyn_cache_check(p_cynara->rcyn, client, client_session,user, privilege));
+}
+
+static void reqcb(void *closure, int status)
+{
+ struct reqasync *req = closure, **p;
+
+ p = &req->cynasync->reqs;
+ while(*p && *p != req)
+ p = &(*p)->next;
+ if (*p)
+ *p = req->next;
+
+ if (!req->canceled)
+ req->callback(req->id, CYNARA_CALL_CAUSE_ANSWER, status, req->user_response_data);
+
+ free(req);
+}
+
+static int create_reqasync(cynara_async *p_cynara, const char *client,
+ const char *client_session, const char *user, const char *privilege,
+ cynara_check_id *p_check_id, cynara_response_callback callback,
+ void *user_response_data, bool simple)
+{
+ int rc;
+ struct reqasync *req;
+
+ req = malloc(sizeof *req);
+ if (req == NULL)
+ return CYNARA_API_OUT_OF_MEMORY;
+
+ req->next = p_cynara->reqs;
+ req->cynasync = p_cynara;
+ req->callback = callback;
+ req->user_response_data = user_response_data;
+ req->id = ++p_cynara->ids;
+ req->canceled = false;
+
+ rc = rcyn_async_check(p_cynara->rcyn, client, client_session, user, privilege, simple, reqcb, req);
+ if (rc == 0)
+ p_cynara->reqs = req;
+ else
+ free(req);
+ return from_status(rc);
+}
+
+int cynara_async_create_request(cynara_async *p_cynara, const char *client,
+ const char *client_session, const char *user, const char *privilege,
+ cynara_check_id *p_check_id, cynara_response_callback callback,
+ void *user_response_data)
+{
+ return create_reqasync(p_cynara, client, client_session, user, privilege, p_check_id, callback, user_response_data, false);
+}
+
+int cynara_async_create_simple_request(cynara_async *p_cynara, const char *client,
+ const char *client_session, const char *user,
+ const char *privilege, cynara_check_id *p_check_id,
+ cynara_response_callback callback, void *user_response_data)
+{
+ return create_reqasync(p_cynara, client, client_session, user, privilege, p_check_id, callback, user_response_data, true);
+}
+
+
+int cynara_async_process(cynara_async *p_cynara)
+{
+ return rcyn_async_process(p_cynara->rcyn);
+}
+
+int cynara_async_cancel_request(cynara_async *p_cynara, cynara_check_id check_id)
+{
+ struct reqasync *req = p_cynara->reqs;
+
+ while(req && req->id != check_id)
+ req = req->next;
+ if (req && !req->canceled) {
+ req->canceled = true;
+ req->callback(req->id, CYNARA_CALL_CAUSE_CANCEL, 0, req->user_response_data);
+ }
+ return CYNARA_API_SUCCESS;
+}
+
+/************************************* CLIENT **************************************/
+
+int cynara_configuration_create(cynara_configuration **pp_conf)
+{
+ *pp_conf = (cynara_configuration*)pp_conf;
+ return CYNARA_API_SUCCESS;
+}
+
+void cynara_configuration_destroy(cynara_configuration *p_conf)
+{
+}
+
+int cynara_configuration_set_cache_size(cynara_configuration *p_conf, size_t cache_size)
+{
+ return CYNARA_API_SUCCESS;
+}
+
+int cynara_initialize(cynara **pp_cynara, const cynara_configuration *p_conf)
+{
+ return from_status(rcyn_open((rcyn_t**)pp_cynara, rcyn_Check, 0));
+}
+
+int cynara_finish(cynara *p_cynara)
+{
+ rcyn_close((rcyn_t*)p_cynara);
+ return CYNARA_API_SUCCESS;
+}
+
+int cynara_check(cynara *p_cynara, const char *client, const char *client_session, const char *user,
+ const char *privilege)
+{
+ return from_check_status(rcyn_check((rcyn_t*)p_cynara, client, client_session, user, privilege));
+}
+
+int cynara_simple_check(cynara *p_cynara, const char *client, const char *client_session,
+ const char *user, const char *privilege)
+{
+ return from_check_status(rcyn_test((rcyn_t*)p_cynara, client, client_session, user, privilege));
+}
+
+/************************************* CREDS... & SESSION *********************************/
+#define MAX_LABEL_LENGTH 1024
+
+#if !defined(DEFAULT_PEERSEC_LABEL)
+# define DEFAULT_PEERSEC_LABEL "NoLabel"
+#endif
+
+int cynara_creds_get_default_client_method(enum cynara_client_creds *method)
+{
+ *method = CLIENT_METHOD_SMACK;
+ return CYNARA_API_SUCCESS;
+}
+
+int cynara_creds_get_default_user_method(enum cynara_user_creds *method)
+{
+ *method = USER_METHOD_UID;
+ return CYNARA_API_SUCCESS;
+}
+
+int cynara_creds_self_get_client(enum cynara_client_creds method, char **client)
+{
+ char label[MAX_LABEL_LENGTH + 1];
+ int len, fd;
+
+ label[0] = 0;
+ fd = open("/proc/self/current/attr", O_RDONLY);
+ if (fd >= 0) {
+ len = (int)read(fd, label, sizeof label - 1);
+ label[len >= 0 ? len : 0] = 0;
+ close(fd);
+ }
+ return (*client = strdup(label[0] ? label : DEFAULT_PEERSEC_LABEL))
+ ? CYNARA_API_SUCCESS : CYNARA_API_OUT_OF_MEMORY;
+}
+
+int cynara_creds_self_get_user(enum cynara_user_creds method, char **user)
+{
+ return asprintf(user, "%ld", (long)getuid()) > 0
+ ? CYNARA_API_SUCCESS : CYNARA_API_OUT_OF_MEMORY;
+}
+
+int cynara_creds_socket_get_client(int fd, enum cynara_client_creds method, char **client)
+{
+ int rc;
+ socklen_t length;
+ struct ucred ucred;
+ char label[MAX_LABEL_LENGTH + 1];
+
+ /* get the credentials */
+ length = (socklen_t)(sizeof ucred);
+ rc = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &length);
+ if (rc < 0 || length != (socklen_t)(sizeof ucred) || !~ucred.uid)
+ return CYNARA_API_OPERATION_FAILED;
+
+ /* get the security label */
+ length = (socklen_t)(sizeof label);
+ rc = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, label, &length);
+ if (rc < 0 || length > (socklen_t)(sizeof label))
+ return CYNARA_API_OPERATION_FAILED;
+
+ return (*client = strdup(label))
+ ? CYNARA_API_SUCCESS : CYNARA_API_OUT_OF_MEMORY;
+}
+
+
+
+int cynara_creds_socket_get_user(int fd, enum cynara_user_creds method, char **user)
+{
+ int rc;
+ socklen_t length;
+ struct ucred ucred;
+
+ /* get the credentials */
+ length = (socklen_t)(sizeof ucred);
+ rc = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &length);
+ if (rc < 0 || length != (socklen_t)(sizeof ucred) || !~ucred.uid)
+ return CYNARA_API_OPERATION_FAILED;
+ return asprintf(user, "%ld", (long)ucred.uid) > 0
+ ? CYNARA_API_SUCCESS : CYNARA_API_OUT_OF_MEMORY;
+}
+
+
+
+int cynara_creds_socket_get_pid(int fd, pid_t *pid)
+{
+ int rc;
+ socklen_t length;
+ struct ucred ucred;
+
+ /* get the credentials */
+ length = (socklen_t)(sizeof ucred);
+ rc = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &length);
+ if (rc < 0 || length != (socklen_t)(sizeof ucred) || !~ucred.uid)
+ return CYNARA_API_OPERATION_FAILED;
+ *pid = ucred.pid;
+ return CYNARA_API_SUCCESS;
+}
+
+char *cynara_session_from_pid(pid_t client_pid)
+{
+ char *r;
+
+ return asprintf(&r, "%ld", (long)client_pid) < 0 ? NULL : r;
+}
+
diff --git a/src/prot.c b/src/prot.c
new file mode 100644
index 0000000..db0e41e
--- /dev/null
+++ b/src/prot.c
@@ -0,0 +1,440 @@
+#define _GNU_SOURCE
+
+#include <stdlib.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/uio.h>
+
+#include "prot.h"
+
+/** the structure buf is generic the meaning of pos/count is not fixed */
+struct buf
+{
+ /** a position */
+ unsigned pos;
+
+ /** a count */
+ unsigned count;
+
+ /** a fixed size content */
+ char content[MAXBUFLEN];
+};
+typedef struct buf buf_t;
+
+/** structure for recording received fields */
+struct fields
+{
+ /** count of field (negative if invalid) */
+ int count;
+
+ /** the fields as strings */
+ const char *fields[MAXARGS];
+};
+typedef struct fields fields_t;
+
+/** structure for handling the protocol */
+struct prot
+{
+ /** input buf, pos is the scanning position */
+ buf_t inbuf;
+
+ /** output buf, pos is to be written position */
+ buf_t outbuf;
+
+ /** the fields */
+ fields_t fields;
+};
+
+
+/**
+ * Put the 'count' in 'fields' to the 'buf'
+ * returns:
+ * - 0 on success
+ * - -EINVAL if the count of fields is too big
+ * - -ECANCELED if there is not enought space in the buffer
+ */
+static
+int
+buf_put_fields(
+ buf_t *buf,
+ unsigned count,
+ const char **fields
+) {
+ unsigned ifield, pos, remain;
+ const char *t;
+ char c;
+
+ /* check the count of fields */
+ if (count > MAXARGS)
+ return -EINVAL;
+
+ /* get the writing position and the free count */
+ pos = buf->pos + buf->count;
+ if (pos >= MAXBUFLEN)
+ pos -= MAXBUFLEN;
+ remain = MAXBUFLEN - buf->count;
+
+ /* put all fields */
+ for (ifield = 0 ; ifield < count ; ifield++) {
+ /* prepend the field separator if needed */
+ if (ifield) {
+ if (!remain--)
+ goto cancel;
+ buf->content[pos++] = FS;
+ if (pos == MAXBUFLEN)
+ pos = 0;
+ }
+ /* put the field if any (NULL aliases "") */
+ t = fields[ifield];
+ if (t) {
+ /* put all chars of the field */
+ while((c = *t++)) {
+ /* escape special characters */
+ if (c == FS || c == RS || c == ESC) {
+ if (!remain--)
+ goto cancel;
+ buf->content[pos++] = ESC;
+ if (pos == MAXBUFLEN)
+ pos = 0;
+ }
+ /* put the char */
+ if (!remain--)
+ goto cancel;
+ buf->content[pos++] = c;
+ if (pos == MAXBUFLEN)
+ pos = 0;
+ }
+ }
+ }
+
+ /* put the end indicator */
+ if (!remain--)
+ goto cancel;
+ buf->content[pos] = RS;
+
+ /* record the new values */
+ buf->count = MAXBUFLEN - remain;
+ return 0;
+
+cancel:
+ return -ECANCELED;
+}
+
+/**
+ * write the content of 'buf' to 'fd'
+ */
+static
+int
+buf_write(
+ buf_t *buf,
+ int fd
+) {
+ int n;
+ unsigned count;
+ ssize_t rc;
+ struct iovec vec[2];
+
+ /* get the count of byte to write (avoid int overflow) */
+ count = buf->count > INT_MAX ? INT_MAX : buf->count;
+
+ /* calling it with nothing to write is an error */
+ if (count == 0)
+ return -ENODATA;
+
+ /* prepare the iovec */
+ vec[0].iov_base = buf->content + buf->pos;
+ if (buf->pos + count <= MAXBUFLEN) {
+ vec[0].iov_len = count;
+ n = 1;
+ } else {
+ vec[0].iov_len = MAXBUFLEN - buf->pos;
+ vec[1].iov_base = buf->content;
+ vec[1].iov_len = count - vec[0].iov_len;
+ n = 2;
+ }
+
+ /* write the buffers */
+ do {
+ rc = writev(fd, vec, n);
+ } while(rc < 0 && errno == EINTR);
+
+ /* check error */
+ if (rc < 0)
+ rc = -errno;
+ else {
+ /* update the state */
+ buf->count -= (unsigned)rc;
+ buf->pos += (unsigned)rc;
+ if (buf->pos >= MAXBUFLEN)
+ buf->pos -= MAXBUFLEN;
+ }
+
+ return (int)rc;
+}
+
+/* get the 'fields' from 'buf' */
+static
+void
+buf_get_fields(
+ buf_t *buf,
+ fields_t *fields
+) {
+ char c;
+ unsigned read, write;
+
+ /* advance the pos after the end */
+ assert(buf->content[buf->pos] == RS);
+ buf->pos++;
+
+ /* init first field */
+ fields->fields[fields->count = 0] = buf->content;
+ read = write = 0;
+ for (;;) {
+ c = buf->content[read++];
+ switch(c) {
+ case FS: /* field separator */
+ buf->content[write++] = 0;
+ if (fields->count >= MAXARGS)
+ return;
+ fields->fields[++fields->count] = &buf->content[write];
+ break;
+ case RS: /* end of line (record separator) */
+ buf->content[write] = 0;
+ fields->count += (write > 0);
+ return;
+ case ESC: /* escaping */
+ c = buf->content[read++];
+ if (c != FS && c != RS && c != ESC)
+ buf->content[write++] = ESC;
+ buf->content[write++] = c;
+ break;
+ default: /* other characters */
+ buf->content[write++] = c;
+ break;
+ }
+ }
+}
+
+/**
+ * Advance pos of 'buf' until end of record RS found in buffer.
+ * return 1 if found or 0 if not found
+ */
+static
+int
+buf_scan_end_record(
+ buf_t *buf
+) {
+ unsigned nesc;
+
+ /* search the next RS */
+ while(buf->pos < buf->count) {
+ if (buf->content[buf->pos] == RS) {
+ /* check whether RS is escaped */
+ nesc = 0;
+ while (buf->pos > nesc && buf->content[buf->pos - (nesc + 1)] == ESC)
+ nesc++;
+ if ((nesc & 1) == 0)
+ return 1; /* not escaped */
+ }
+ buf->pos++;
+ }
+ return 0;
+}
+
+/** remove chars of 'buf' until pos */
+static
+void
+buf_crop(
+ buf_t *buf
+) {
+ buf->count -= buf->pos;
+ if (buf->count)
+ memmove(buf->content, buf->content + buf->pos, buf->count);
+ buf->pos = 0;
+}
+
+/** read input 'buf' from 'fd' */
+static
+int
+inbuf_read(
+ buf_t *buf,
+ int fd
+) {
+ ssize_t szr;
+ int rc;
+
+ if (buf->count == MAXBUFLEN)
+ return -ENOBUFS;
+
+ do {
+ szr = read(fd, buf->content + buf->count, MAXBUFLEN - buf->count);
+ } while(szr < 0 && errno == EINTR);
+ if (szr >= 0)
+ buf->count += (unsigned)(rc = (int)szr);
+ else if (szr < 0)
+ rc = -(errno == EWOULDBLOCK ? EAGAIN : errno);
+
+ return rc;
+}
+
+/**
+ * create the prot structure in 'prot'
+ * Return 0 in case of success or -ENOMEM in case of error
+ */
+int
+prot_create(
+ prot_t **prot
+) {
+ prot_t *p;
+
+ /* allocation of the structure */
+ *prot = p = malloc(sizeof *p);
+ if (p == NULL)
+ return -ENOMEM;
+
+ /* initialisation of the structure */
+ prot_reset(p);
+
+ /* terminate */
+ return 0;
+}
+
+/**
+ * Destroys the protocol 'prot'
+ */
+void
+prot_destroy(
+ prot_t *prot
+) {
+ free(prot);
+}
+
+/**
+ * reset the protocol 'prot'
+ */
+void
+prot_reset(
+ prot_t *prot
+) {
+ /* initialisation of the structure */
+ prot->inbuf.pos = prot->inbuf.count = 0;
+ prot->outbuf.pos = prot->outbuf.count = 0;
+ prot->fields.count = -1;
+}
+
+/**
+ * Put protocol encoded 'count' 'fields' to the output buffer
+ * returns:
+ * - 0 on success
+ * - -EINVAL if the count of fields is too big
+ * - -ECANCELED if there is not enought space in the buffer
+ */
+int
+prot_put(
+ prot_t *prot,
+ unsigned count,
+ const char **fields
+) {
+ return buf_put_fields(&prot->outbuf, count, fields);
+}
+
+/**
+ * Put protocol encoded fields until NULL found to the output buffer
+ * returns:
+ * - 0 on success
+ * - -EINVAL if the count of fields is too big
+ * - -ECANCELED if there is not enought space in the buffer
+ */
+int
+prot_putx(
+ prot_t *prot,
+ ...
+) {
+ const char *p, *fields[MAXARGS];
+ unsigned n;
+ va_list l;
+
+ va_start(l, prot);
+ n = 0;
+ p = va_arg(l, const char *);
+ while (p) {
+ if (n == MAXARGS)
+ return -EINVAL;
+ fields[n++] = p;
+ p = va_arg(l, const char *);
+ }
+ va_end(l);
+ return prot_put(prot, n, fields);
+}
+
+/**
+ * Check whether write should be done or not
+ * Returns 1 if there is something to write or 0 otherwise
+ */
+int
+prot_should_write(
+ prot_t *prot
+) {
+ return prot->outbuf.count > 0;
+}
+
+/**
+ * Write the content to write and return either the count
+ * of bytes written or an error code (negative). Note that
+ * the returned value tries to be the same as those returned
+ * by "man 2 write". The only exception is -ENODATA that is
+ * returned if there is nothing to be written.
+ */
+int
+prot_write(
+ prot_t *prot,
+ int fdout
+) {
+ return buf_write(&prot->outbuf, fdout);
+}
+
+int
+prot_can_read(
+ prot_t *prot
+) {
+ return prot->inbuf.count < MAXBUFLEN;
+}
+
+int
+prot_read(
+ prot_t *prot,
+ int fdin
+) {
+ return inbuf_read(&prot->inbuf, fdin);
+}
+
+int
+prot_get(
+ prot_t *prot,
+ const char ***fields
+) {
+ if (prot->fields.count < 0) {
+ if (!buf_scan_end_record(&prot->inbuf))
+ return -EAGAIN;
+ buf_get_fields(&prot->inbuf, &prot->fields);
+ }
+ if (fields)
+ *fields = prot->fields.fields;
+ return (int)prot->fields.count;
+}
+
+void
+prot_next(
+ prot_t *prot
+) {
+ if (prot->fields.count >= 0) {
+ buf_crop(&prot->inbuf);
+ prot->fields.count = -1;
+ }
+}
+
+
diff --git a/src/prot.h b/src/prot.h
new file mode 100644
index 0000000..a1ded3b
--- /dev/null
+++ b/src/prot.h
@@ -0,0 +1,85 @@
+#pragma once
+
+struct prot;
+typedef struct prot prot_t;
+
+#define MAXBUFLEN 2000
+#define MAXARGS 20
+#define FS ' '
+#define RS '\n'
+#define ESC '\\'
+
+
+extern
+int
+prot_create(
+ prot_t **prot
+);
+
+extern
+void
+prot_destroy(
+ prot_t *prot
+);
+
+extern
+void
+prot_reset(
+ prot_t *prot
+);
+
+extern
+int
+prot_put(
+ prot_t *prot,
+ unsigned count,
+ const char **fields
+);
+
+extern
+int
+prot_putx(
+ prot_t *prot,
+ ...
+);
+
+extern
+int
+prot_should_write(
+ prot_t *prot
+);
+
+extern
+int
+prot_write(
+ prot_t *prot,
+ int fdout
+);
+
+extern
+int
+prot_can_read(
+ prot_t *prot
+);
+
+extern
+int
+prot_read(
+ prot_t *prot,
+ int fdin
+);
+
+extern
+int
+prot_get(
+ prot_t *prot,
+ const char ***fields
+);
+
+extern
+void
+prot_next(
+ prot_t *prot
+);
+
+
diff --git a/src/queue.c b/src/queue.c
new file mode 100644
index 0000000..16b0a0f
--- /dev/null
+++ b/src/queue.c
@@ -0,0 +1,192 @@
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "db.h"
+
+#define DROP 0
+#define SET 1
+
+/**
+ * Queue
+ */
+struct queue
+{
+ uint32_t read, write, capacity;
+ void *queue;
+};
+typedef struct queue queue_t;
+
+/** the queue */
+static queue_t queue;
+
+static
+bool
+qread(
+ void *data,
+ uint32_t length
+) {
+ if (queue.read + length > queue.write)
+ return false;
+
+ memcpy(data, queue.queue + queue.read, length);
+ queue.read += length;
+ return true;
+}
+
+static
+bool
+qget_char(
+ char *value
+) {
+ return qread(value, sizeof *value);
+}
+
+static
+bool
+qget_uint32(
+ uint32_t *value
+) {
+ return qread(value, sizeof *value);
+}
+
+static
+bool
+qget_string(
+ const char **text
+) {
+ char *p = queue.queue + queue.read;
+ uint32_t length = (uint32_t)strlen(p);
+ if (queue.read + length >= queue.write)
+ return false;
+ *text = p;
+ queue.read += length + 1;
+ return true;
+}
+
+static
+bool
+qwrite(
+ const void *data,
+ uint32_t length
+) {
+ uint32_t c;
+ void *b;
+
+ c = queue.capacity;
+ while (c < queue.write + length)
+ c += 4096;
+ if (c != queue.capacity) {
+ b = realloc(queue.queue, c);
+ if (b == NULL)
+ return false;
+ queue.queue = b;
+ queue.capacity = c;
+ }
+ memcpy(queue.queue + queue.write, data, length);
+ queue.write += length;
+ return true;
+}
+
+static
+bool
+qput_char(
+ char value
+) {
+ return qwrite(&value, sizeof value);
+}
+
+static
+bool
+qput_uint32(
+ uint32_t value
+) {
+ return qwrite(&value, sizeof value);
+}
+
+static
+bool
+qput_string(
+ const char *text
+) {
+ size_t len;
+ text = text ?: "";
+ /* check length */
+ len = strnlen(text, MAX_NAME_LENGTH + 1);
+ if (len > MAX_NAME_LENGTH)
+ return false;
+ return qwrite(text, 1 + (uint32_t)len);
+}
+
+int
+queue_drop(
+ const char *client,
+ const char *session,
+ const char *user,
+ const char *permission
+) {
+ return qput_char(DROP)
+ && qput_string(client)
+ && qput_string(session)
+ && qput_string(user)
+ && qput_string(permission)
+ ? 0 : -(errno = ENOMEM);
+}
+
+int
+queue_set(
+ const char *client,
+ const char *session,
+ const char *user,
+ const char *permission,
+ uint32_t value
+) {
+ return qput_char(SET)
+ && qput_string(client)
+ && qput_string(session)
+ && qput_string(user)
+ && qput_string(permission)
+ && qput_uint32(value)
+ ? 0 : -(errno = ENOMEM);
+}
+
+
+void
+queue_clear(
+) {
+ queue.write = 0;
+}
+
+int
+queue_play(
+) {
+ int rc, rc2;
+ char op;
+ const char *client;
+ const char *session;
+ const char *user;
+ const char *permission;
+ uint32_t value;
+
+ rc = 0;
+ queue.read = 0;
+ while (queue.read < queue.write) {
+ rc2 = -EINVAL;
+ if (qget_char(&op)
+ && qget_string(&client)
+ && qget_string(&session)
+ && qget_string(&user)
+ && qget_string(&permission)) {
+ if (op == DROP)
+ rc2 = db_drop(client, session, user, permission);
+ else if (qget_uint32(&value))
+ rc2 = db_set(client, session, user, permission, value);
+ }
+ if (rc2 != 0 && rc == 0)
+ rc = rc2;
+ }
+ return rc;
+}
+
diff --git a/src/queue.h b/src/queue.h
new file mode 100644
index 0000000..70518c4
--- /dev/null
+++ b/src/queue.h
@@ -0,0 +1,31 @@
+
+extern
+int
+queue_drop(
+ const char *client,
+ const char *session,
+ const char *user,
+ const char *permission
+);
+
+extern
+int
+queue_set(
+ const char *client,
+ const char *session,
+ const char *user,
+ const char *permission,
+ uint32_t value
+);
+
+extern
+void
+queue_clear(
+);
+
+extern
+int
+queue_play(
+);
+
+
diff --git a/src/rcyn-client.c b/src/rcyn-client.c
new file mode 100644
index 0000000..f712ca6
--- /dev/null
+++ b/src/rcyn-client.c
@@ -0,0 +1,722 @@
+#define _GNU_SOURCE
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <assert.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/epoll.h>
+
+#include "prot.h"
+#include "rcyn-protocol.h"
+#include "rcyn-client.h"
+#include "cache.h"
+#include "socket.h"
+
+#define MIN_CACHE_SIZE 400
+
+struct asreq;
+typedef struct asreq asreq_t;
+
+/** recording of asynchronous requests */
+struct asreq
+{
+ /** link to the next pending request */
+ asreq_t *next;
+
+ /** callback function */
+ void (*callback)(
+ void *closure,
+ int status);
+
+ /** closure of the callback */
+ void *closure;
+};
+
+struct rcyn;
+typedef struct rcyn rcyn_t;
+
+/**
+ * structure recording a rcyn client
+ */
+struct rcyn
+{
+ /** file descriptor of the socket */
+ int fd;
+
+ /** count of pending requests */
+ int pending;
+
+ /** type of link */
+ rcyn_type_t type;
+
+ /** protocol manager object */
+ prot_t *prot;
+
+ /** cache object */
+ cache_t *cache;
+
+ /** copy of the reply */
+ struct {
+ /** count of fields of the reply */
+ int count;
+
+ /** fields (or fields) of the reply */
+ const char **fields;
+ } reply;
+
+ /** async */
+ struct {
+ /** control callback */
+ rcyn_async_ctl_t controlcb;
+
+ /** closure */
+ void *closure;
+
+ /** reqests */
+ asreq_t *requests;
+ } async;
+};
+
+static void disconnection(rcyn_t *rcyn);
+
+/**
+ * Flush the write buffer
+ */
+static
+int
+flushw(
+ rcyn_t *rcyn
+) {
+ int rc;
+ struct pollfd pfd;
+
+ rc = prot_should_write(rcyn->prot);
+ while (rc) {
+ rc = prot_write(rcyn->prot, rcyn->fd);
+ if (rc == -EAGAIN) {
+ pfd.fd = rcyn->fd;
+ pfd.events = POLLOUT;
+ do { rc = poll(&pfd, 1, 0); } while (rc < 0 && errno == EINTR);
+ if (rc < 0)
+ rc = -errno;
+ }
+ if (rc < 0) {
+ break;
+ }
+ rc = prot_should_write(rcyn->prot);
+ }
+ return rc;
+}
+
+/**
+ * Put the command made of arguments ...
+ * Increment the count of pending requests.
+ * Return 0 in case of success or a negative number on error.
+ */
+static
+int
+putx(
+ rcyn_t *rcyn,
+ ...
+) {
+ const char *p, *fields[MAXARGS];
+ unsigned n;
+ va_list l;
+ int rc;
+
+ /* reconstruct the array of arguments */
+ va_start(l, rcyn);
+ n = 0;
+ p = va_arg(l, const char *);
+ while (p && n < MAXARGS) {
+ fields[n++] = p;
+ p = va_arg(l, const char *);
+ }
+ va_end(l);
+
+ /* put it to the output buffer */
+ rc = prot_put(rcyn->prot, n, fields);
+ if (rc == -ECANCELED) {
+ /* not enough room in the buffer, flush it */
+ rc = flushw(rcyn);
+ if (rc == 0)
+ rc = prot_put(rcyn->prot, n, fields);
+ }
+ /* client always flushes */
+ if (rc == 0) {
+ rcyn->pending++;
+ rc = flushw(rcyn);
+ }
+ return rc;
+}
+
+static
+int
+wait_input(
+ rcyn_t *rcyn
+) {
+ int rc;
+ struct pollfd pfd;
+
+ pfd.fd = rcyn->fd;
+ pfd.events = POLLIN;
+ do { rc = poll(&pfd, 1, 0); } while (rc < 0 && errno == EINTR);
+ return rc < 0 ? -errno : 0;
+}
+
+static
+int
+get_reply(
+ rcyn_t *rcyn
+) {
+ int rc;
+
+ prot_next(rcyn->prot);
+ rc = rcyn->reply.count = prot_get(rcyn->prot, &rcyn->reply.fields);
+ if (rc <= 0)
+ return rc;
+ if (0 != strcmp(rcyn->reply.fields[0], _clear_)) {
+ if (0 != strcmp(rcyn->reply.fields[0], _item_))
+ rcyn->pending--;
+ return rc;
+ }
+ cache_clear(rcyn->cache);
+ return rcyn->reply.count = 0;
+}
+
+static
+int
+wait_reply(
+ rcyn_t *rcyn,
+ bool block
+) {
+ int rc;
+
+ for(;;) {
+ prot_next(rcyn->prot);
+ rc = get_reply(rcyn);
+ if (rc > 0)
+ return rc;
+ if (rc < 0) {
+ rc = prot_read(rcyn->prot, rcyn->fd);
+ while (rc <= 0) {
+ if (rc == 0)
+ return -(errno = EPIPE);
+ if (rc == -EAGAIN && block)
+ rc = wait_input(rcyn);
+ if (rc < 0)
+ return rc;
+ rc = prot_read(rcyn->prot, rcyn->fd);
+ }
+ }
+ }
+}
+
+static
+int
+flushr(
+ rcyn_t *rcyn
+) {
+ int rc;
+
+ do { rc = wait_reply(rcyn, false); } while(rc > 0);
+ return rc;
+}
+
+static
+int
+status_done(
+ rcyn_t *rcyn
+) {
+ return strcmp(rcyn->reply.fields[0], _done_) ? -ECANCELED : 0;
+}
+
+static
+int
+status_check(
+ rcyn_t *rcyn
+) {
+ return !strcmp(rcyn->reply.fields[0], _yes_) ? 1
+ : !strcmp(rcyn->reply.fields[0], _no_) ? 0
+ : -EEXIST;
+}
+
+static
+int
+wait_pending_reply(
+ rcyn_t *rcyn
+) {
+ int rc;
+ for (;;) {
+ rc = wait_reply(rcyn, true);
+ if (rc < 0)
+ return rc;
+ if (rc > 0 && rcyn->pending == 0)
+ return rc;
+ }
+}
+
+static
+int
+wait_done(
+ rcyn_t *rcyn
+) {
+ int rc = wait_pending_reply(rcyn);
+ if (rc > 0)
+ rc = status_done(rcyn);
+ return rc;
+}
+
+static
+int
+async(
+ rcyn_t *rcyn,
+ int op,
+ uint32_t events
+) {
+ return rcyn->async.controlcb
+ ? rcyn->async.controlcb(rcyn->async.closure, op, rcyn->fd, events)
+ : 0;
+}
+
+static
+void
+disconnection(
+ rcyn_t *rcyn
+) {
+ if (rcyn->fd >= 0) {
+ async(rcyn, EPOLL_CTL_DEL, 0);
+ close(rcyn->fd);
+ rcyn->fd = -1;
+ }
+}
+
+static
+int
+connection(
+ rcyn_t *rcyn
+) {
+ int rc;
+ const char *spec;
+
+ /* socket spec */
+ switch(rcyn->type) {
+ default:
+ case rcyn_Check: spec = rcyn_default_check_socket_spec; break;
+ case rcyn_Admin: spec = rcyn_default_admin_socket_spec; break;
+ }
+
+ /* init the client */
+ rcyn->pending = 0;
+ rcyn->reply.count = -1;
+ cache_clear(rcyn->cache);
+ prot_reset(rcyn->prot);
+ rcyn->fd = socket_open(spec, 0);
+ if (rcyn->fd < 0)
+ return -errno;
+
+ /* negociate the protocol */
+ rc = putx(rcyn, _rcyn_, "1", NULL);
+ if (rc >= 0) {
+ rc = wait_pending_reply(rcyn);
+ if (rc >= 0) {
+ rc = -EPROTO;
+ if (rcyn->reply.count == 2
+ && 0 == strcmp(rcyn->reply.fields[0], _yes_)
+ && 0 == strcmp(rcyn->reply.fields[1], "1")) {
+ rc = async(rcyn, EPOLL_CTL_ADD, EPOLLIN);
+ if (rc >= 0)
+ return 0;
+ }
+ }
+ }
+ disconnection(rcyn);
+ return rc;
+}
+
+
+/************************************************************************************/
+
+int
+rcyn_open(
+ rcyn_t **prcyn,
+ rcyn_type_t type,
+ uint32_t cache_size
+) {
+ rcyn_t *rcyn;
+ int rc;
+
+ /* allocate the structure */
+ *prcyn = rcyn = malloc(sizeof *rcyn);
+ if (rcyn == NULL) {
+ rc = -ENOMEM;
+ goto error;
+ }
+
+ /* create a protocol object */
+ rc = prot_create(&rcyn->prot);
+ if (rc < 0)
+ goto error2;
+
+ /* record type and weakly create cache */
+ cache_create(&rcyn->cache, cache_size < MIN_CACHE_SIZE ? MIN_CACHE_SIZE : cache_size);
+ rcyn->type = type;
+ rcyn->async.controlcb = NULL;
+ rcyn->async.closure = 0;
+ rcyn->async.requests = NULL;
+
+ /* connection */
+ rc = connection(rcyn);
+ if (rc < 0)
+ goto error3;
+
+ /* done */
+ return 0;
+
+error3:
+ free(rcyn->cache);
+ prot_destroy(rcyn->prot);
+error2:
+ free(rcyn);
+error:
+ *prcyn = NULL;
+ return rc;
+}
+
+void
+rcyn_close(
+ rcyn_t *rcyn
+) {
+ rcyn_async_setup(rcyn, NULL, NULL);
+ disconnection(rcyn);
+ prot_destroy(rcyn->prot);
+ free(rcyn->cache);
+ free(rcyn);
+}
+
+int
+rcyn_enter(
+ rcyn_t *rcyn
+) {
+ int rc;
+
+ if (rcyn->type != rcyn_Admin)
+ return -EPERM;
+ if (rcyn->async.requests != NULL)
+ return -EINPROGRESS;
+
+ rc = putx(rcyn, _enter_, NULL);
+ if (rc >= 0)
+ rc = wait_done(rcyn);
+ return rc;
+}
+
+int
+rcyn_leave(
+ rcyn_t *rcyn,
+ bool commit
+) {
+ int rc;
+
+ if (rcyn->type != rcyn_Admin)
+ return -EPERM;
+ if (rcyn->async.requests != NULL)
+ return -EINPROGRESS;
+
+ rc = putx(rcyn, _leave_, commit ? _commit_ : NULL/*default: rollback*/, NULL);
+ if (rc >= 0)
+ rc = wait_done(rcyn);
+ return rc;
+}
+
+static
+int
+check_or_test(
+ rcyn_t *rcyn,
+ const char *client,
+ const char *session,
+ const char *user,
+ const char *permission,
+ const char *action
+) {
+ int rc;
+
+ if (rcyn->async.requests != NULL)
+ return -EINPROGRESS;
+
+ /* ensure there is no clear cache pending */
+ flushr(rcyn);
+
+ /* check cache item */
+ rc = cache_search(rcyn->cache, client, session, user, permission);
+ if (rc >= 0)
+ return rc;
+
+ /* send the request */
+ rc = putx(rcyn, action, client, session, user, permission, NULL);
+ if (rc >= 0) {
+ /* get the response */
+ rc = wait_pending_reply(rcyn);
+ if (rc >= 0) {
+ rc = status_check(rcyn);
+ if (rc >= 0)
+ cache_put(rcyn->cache, client, session, user, permission, rc);
+ }
+ }
+ return rc;
+}
+
+int
+rcyn_check(
+ rcyn_t *rcyn,
+ const char *client,
+ const char *session,
+ const char *user,
+ const char *permission
+) {
+ return check_or_test(rcyn, client, session, user, permission, _check_);
+}
+
+int
+rcyn_test(
+ rcyn_t *rcyn,
+ const char *client,
+ const char *session,
+ const char *user,
+ const char *permission
+) {
+ return check_or_test(rcyn, client, session, user, permission, _check_);
+}
+
+int
+rcyn_set(
+ rcyn_t *rcyn,
+ const char *client,
+ const char *session,
+ const char *user,
+ const char *permission,
+ int value
+) {
+ char val[30];
+ int rc;
+
+ if (rcyn->type != rcyn_Admin)
+ return -EPERM;
+ if (rcyn->async.requests != NULL)
+ return -EINPROGRESS;
+
+ snprintf(val, sizeof val, "%u", (unsigned)value);
+ rc = putx(rcyn, _set_, client, session, user, permission, val, NULL);
+ if (rc >= 0)
+ rc = wait_done(rcyn);
+ return rc;
+}
+
+int
+rcyn_get(
+ rcyn_t *rcyn,
+ const char *client,
+ const char *session,
+ const char *user,
+ const char *permission,
+ void (*callback)(
+ void *closure,
+ const char *client,
+ const char *session,
+ const char *user,
+ const char *permission,
+ uint32_t value
+ ),
+ void *closure
+) {
+ int rc;
+
+ if (rcyn->type != rcyn_Admin)
+ return -EPERM;
+ if (rcyn->async.requests != NULL)
+ return -EINPROGRESS;
+
+ rc = putx(rcyn, _get_, client, session, user, permission, NULL);
+ if (rc >= 0) {
+ rc = wait_reply(rcyn, true);
+ while (rc == 6 && !strcmp(rcyn->reply.fields[0], _item_)) {
+ callback(closure,
+ rcyn->reply.fields[1],
+ rcyn->reply.fields[2],
+ rcyn->reply.fields[3],
+ rcyn->reply.fields[4],
+ (uint32_t)atoi(rcyn->reply.fields[5]));
+ rc = wait_reply(rcyn, true);
+ }
+ rc = status_done(rcyn);
+ }
+ return rc;
+}
+
+int
+rcyn_drop(
+ rcyn_t *rcyn,
+ const char *client,
+ const char *session,
+ const char *user,
+ const char *permission
+) {
+ int rc;
+
+ if (rcyn->type != rcyn_Admin)
+ return -EPERM;
+ if (rcyn->async.requests != NULL)
+ return -EINPROGRESS;
+
+ rc = putx(rcyn, _drop_, client, session, user, permission, NULL);
+ if (rc >= 0)
+ rc = wait_done(rcyn);
+ return rc;
+}
+
+/************************************************************************************/
+
+int
+rcyn_cache_resize(
+ rcyn_t *rcyn,
+ uint32_t size
+) {
+ return cache_resize(&rcyn->cache, size < MIN_CACHE_SIZE ? MIN_CACHE_SIZE : size);
+}
+
+void
+rcyn_cache_clear(
+ rcyn_t *rcyn
+) {
+ cache_clear(rcyn->cache);
+}
+
+int
+rcyn_cache_check(
+ rcyn_t *rcyn,
+ const char *client,
+ const char *session,
+ const char *user,
+ const char *permission
+) {
+ return cache_search(rcyn->cache, client, session, user, permission);
+}
+
+
+/************************************************************************************/
+
+int
+rcyn_async_setup(
+ rcyn_t *rcyn,
+ rcyn_async_ctl_t controlcb,
+ void *closure
+) {
+ asreq_t *ar;
+
+ /* cancel pending requests */
+ while((ar = rcyn->async.requests) != NULL) {
+ rcyn->async.requests = ar->next;
+ ar->callback(ar->closure, -ECANCELED);
+ free(ar);
+ }
+ /* remove existing polling */
+ async(rcyn, EPOLL_CTL_DEL, 0);
+ /* records new data */
+ rcyn->async.controlcb = controlcb;
+ rcyn->async.closure = closure;
+ /* record to polling */
+ return async(rcyn, EPOLL_CTL_ADD, EPOLLIN);
+}
+
+int
+rcyn_async_process(
+ rcyn_t *rcyn
+) {
+ int rc;
+ const char *first;
+ asreq_t *ar;
+
+ for (;;) {
+ /* non blocking wait for a reply */
+ rc = wait_reply(rcyn, false);
+ if (rc < 0)
+ return rc == -EAGAIN ? 0 : rc;
+
+ /* skip empty replies */
+ if (rc == 0)
+ continue;
+
+ /* skip done/error replies */
+ first = rcyn->reply.fields[0];
+ if (!strcmp(first, _done_)
+ || !strcmp(first, _error_))
+ continue;
+
+ /* ignore unexpected answers */
+ ar = rcyn->async.requests;
+ if (ar == NULL)
+ continue;
+
+ /* emit the asynchronous answer */
+ rcyn->async.requests = ar->next;
+ rc = status_check(rcyn);
+ ar->callback(ar->closure, rc);
+ free(ar);
+ }
+}
+
+int
+rcyn_async_check(
+ rcyn_t *rcyn,
+ const char *client,
+ const char *session,
+ const char *user,
+ const char *permission,
+ bool simple,
+ void (*callback)(
+ void *closure,
+ int status),
+ void *closure
+) {
+ int rc;
+ asreq_t **pr, *ar;
+
+ /* allocate */
+ ar = malloc(sizeof *ar);
+ if (ar == NULL)
+ return -ENOMEM;
+
+ /* init */
+ ar->next = NULL;
+ ar->callback = callback;
+ ar->closure = closure;
+
+ /* send the request */
+ rc = putx(rcyn, simple ? _test_ : _check_,
+ client, session, user, permission, NULL);
+ if (rc >= 0)
+ rc = flushw(rcyn);
+ if (rc < 0) {
+ free(ar);
+ return rc;
+ }
+
+ /* record the request */
+ pr = &rcyn->async.requests;
+ while(*pr != NULL)
+ pr = &(*pr)->next;
+ *pr = ar;
+ return 0;
+}
+
+
diff --git a/src/rcyn-client.h b/src/rcyn-client.h
new file mode 100644
index 0000000..6706820
--- /dev/null
+++ b/src/rcyn-client.h
@@ -0,0 +1,149 @@
+
+#pragma once
+
+typedef enum rcyn_type {
+ rcyn_Check,
+ rcyn_Admin
+} rcyn_type_t;
+
+struct rcyn;
+typedef struct rcyn rcyn_t;
+
+extern
+int
+rcyn_open(
+ rcyn_t **rcyn,
+ rcyn_type_t type,
+ uint32_t cache_size
+);
+
+extern
+void
+rcyn_close(
+ rcyn_t *rcyn
+);
+
+extern
+int
+rcyn_enter(
+ rcyn_t *rcyn
+);
+
+extern
+int
+rcyn_leave(
+ rcyn_t *rcyn,
+ bool commit
+);
+
+extern
+int
+rcyn_check(
+ rcyn_t *rcyn,
+ const char *client,
+ const char *session,
+ const char *user,
+ const char *permission
+);
+
+extern
+int
+rcyn_test(
+ rcyn_t *rcyn,
+ const char *client,
+ const char *session,
+ const char *user,
+ const char *permission
+);
+
+extern
+int
+rcyn_set(
+ rcyn_t *rcyn,
+ const char *client,
+ const char *session,
+ const char *user,
+ const char *permission,
+ int value
+);
+
+extern
+int
+rcyn_get(
+ rcyn_t *rcyn,
+ const char *client,
+ const char *session,
+ const char *user,
+ const char *permission,
+ void (*callback)(
+ void *closure,
+ const char *client,
+ const char *session,
+ const char *user,
+ const char *permission,
+ uint32_t value
+ ),
+ void *closure
+);
+
+extern
+int
+rcyn_drop(
+ rcyn_t *rcyn,
+ const char *client,
+ const char *session,
+ const char *user,
+ const char *permission
+);
+
+extern
+void
+rcyn_cache_clear(
+ rcyn_t *rcyn
+);
+
+extern
+int
+rcyn_cache_check(
+ rcyn_t *rcyn,
+ const char *client,
+ const char *session,
+ const char *user,
+ const char *permission
+);
+
+typedef int (*rcyn_async_ctl_t)(
+ void *closure,
+ int op,
+ int fd,
+ uint32_t events);
+
+extern
+int
+rcyn_async_setup(
+ rcyn_t *rcyn,
+ rcyn_async_ctl_t controlcb,
+ void *closure
+);
+
+extern
+int
+rcyn_async_process(
+ rcyn_t *rcyn
+);
+
+extern
+int
+rcyn_async_check(
+ rcyn_t *rcyn,
+ const char *client,
+ const char *session,
+ const char *user,
+ const char *permission,
+ bool test,
+ void (*callback)(
+ void *closure,
+ int status),
+ void *closure
+);
+
diff --git a/src/rcyn-protocol.c b/src/rcyn-protocol.c
new file mode 100644
index 0000000..c656570
--- /dev/null
+++ b/src/rcyn-protocol.c
@@ -0,0 +1,33 @@
+#include "rcyn-protocol.h"
+
+const char
+ _check_[] = "check",
+ _drop_[] = "drop",
+ _enter_[] = "enter",
+ _get_[] = "get",
+ _leave_[] = "leave",
+ _rcyn_[] = "rcyn",
+ _set_[] = "set",
+ _test_[] = "test";
+
+const char
+ _commit_[] = "commit",
+ _rollback_[] = "rollback";
+
+const char
+ _clear_[] = "clear",
+ _done_[] = "done",
+ _error_[] = "error",
+ _item_[] = "item",
+ _no_[] = "no",
+ _yes_[] = "yes";
+
+#if !defined(RCYN_DEFAULT_CHECK_SOCKET_SPEC)
+# define RCYN_DEFAULT_CHECK_SOCKET_SPEC "unix:/run/platform/cynara.check"
+#endif
+#if !defined(RCYN_DEFAULT_ADMIN_SOCKET_SPEC)
+# define RCYN_DEFAULT_ADMIN_SOCKET_SPEC "unix:/run/platform/cynara.admin"
+#endif
+const char
+ rcyn_default_check_socket_spec[] = RCYN_DEFAULT_CHECK_SOCKET_SPEC,
+ rcyn_default_admin_socket_spec[] = RCYN_DEFAULT_ADMIN_SOCKET_SPEC;
diff --git a/src/rcyn-protocol.h b/src/rcyn-protocol.h
new file mode 100644
index 0000000..8906194
--- /dev/null
+++ b/src/rcyn-protocol.h
@@ -0,0 +1,28 @@
+#pragma once
+
+extern const char
+ _check_[],
+ _drop_[],
+ _enter_[],
+ _get_[],
+ _leave_[],
+ _rcyn_[],
+ _set_[],
+ _test_[];
+
+extern const char
+ _commit_[],
+ _rollback_[];
+
+extern const char
+ _clear_[],
+ _done_[],
+ _error_[],
+ _item_[],
+ _no_[],
+ _yes_[];
+
+extern const char
+ rcyn_default_check_socket_spec[],
+ rcyn_default_admin_socket_spec[];
+
diff --git a/src/rcyn-protocol.txt b/src/rcyn-protocol.txt
new file mode 100644
index 0000000..134d05e
--- /dev/null
+++ b/src/rcyn-protocol.txt
@@ -0,0 +1,79 @@
+protocol
+========
+
+hello:
+
+ c->s rcyn 1
+ s->c yes 1
+
+invalidate cache:
+
+ s->c clear
+
+test a permission:
+
+ c->s test CLIENT SESSION USER PERMISSION
+ s->c yes|no|VALUE [CACHING]
+
+check a permission:
+
+ c->s check CLIENT SESSION USER PERMISSION
+ s->c yes|no|VALUE [CACHING]
+
+erase (admin):
+
+ c->s drop CLIENT SESSION USER PERMISSION
+ s->c done|error ...
+
+set (admin):
+
+ c->s set CLIENT SESSION USER PERMISSION VALUE
+ s->c done|error ...
+
+list permissions (admin):
+
+ c->s get CLIENT SESSION USER PERMISSION
+ s->c item CLIENT SESSION USER PERMISSION VALUE
+ s->c ...
+ s->c done
+
+enter critical (admin)
+
+ c->s enter
+ s->c done
+
+leave critical (admin)
+
+ c->s leave [commit|rollback]
+ s->c done|error ...
+
+register agent (agent):
+
+ c->s agent NAME [ARGS...]
+ s->c done|error ...
+
+asking (agent ask CLIENT SESSION USER PERMISSION):
+
+ s->c ask CLIENT SESSION USER PERMISSION
+ c->s done | ([yes|no] [always|session|one-time])
+
+
+----------------------------------------------------------
+ c->s c(heck) CLIENT SESSION USER PERMISSION
+ c->s d(rop) CLIENT SESSION USER PERMISSION
+ c->s e(nter)
+ c->s g(et) CLIENT SESSION USER PERMISSION
+ c->s l(eave) [commit|rollback]
+ c->s r(cyn)
+ c->s s(et) CLIENT SESSION USER PERMISSION VALUE
+ c->s t(est) CLIENT SESSION USER PERMISSION
+
+ s->c clear
+ s->c done
+ s->c done [CLIENT SESSION USER PERMISSION VALUE]
+ s->c done|error ...
+ s->c item CLIENT SESSION USER PERMISSION VALUE
+ s->c yes|no
+ s->c yes|no|VALUE [CACHING]
+
+
diff --git a/src/rcyn-server.c b/src/rcyn-server.c
new file mode 100644
index 0000000..4fc365e
--- /dev/null
+++ b/src/rcyn-server.c
@@ -0,0 +1,669 @@
+#define _GNU_SOURCE
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <poll.h>
+#include <sys/epoll.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#if defined(WITH_SYSTEMD_ACTIVATION)
+#include <systemd/sd-daemon.h>
+#endif
+
+#include "prot.h"
+#include "cyn.h"
+#include "rcyn-protocol.h"
+#include "socket.h"
+
+typedef enum rcyn_type {
+ rcyn_Check,
+ rcyn_Admin
+} rcyn_type_t;
+
+/** structure for using epoll easily */
+typedef struct pollitem pollitem_t;
+struct pollitem
+{
+ /** callback on event */
+ void (*handler)(pollitem_t *pollitem, uint32_t events, int pollfd);
+
+ /** data */
+ union {
+ void *closure;
+ int fd;
+ };
+};
+
+static
+int
+pollitem_do(
+ pollitem_t *pollitem,
+ uint32_t events,
+ int fd,
+ int pollfd,
+ int op
+) {
+ struct epoll_event ev = { .events = events, .data.ptr = pollitem };
+ return epoll_ctl(pollfd, op, fd, &ev);
+}
+
+static
+int
+pollitem_add(
+ pollitem_t *pollitem,
+ uint32_t events,
+ int fd,
+ int pollfd
+) {
+ return pollitem_do(pollitem, events, fd, pollfd, EPOLL_CTL_ADD);
+}
+
+#if 0
+static
+int
+pollitem_mod(
+ pollitem_t *pollitem,
+ uint32_t events,
+ int fd,
+ int pollfd
+) {
+ return pollitem_do(pollitem, events, fd, pollfd, EPOLL_CTL_MOD);
+}
+#endif
+
+static
+int
+pollitem_del(
+ int fd,
+ int pollfd
+) {
+ return pollitem_do(NULL, 0, fd, pollfd, EPOLL_CTL_DEL);
+}
+
+
+/** structure that represents a rcyn client */
+struct client
+{
+ /** a protocol structure */
+ prot_t *prot;
+
+ /** the in/out file descriptor */
+ int fd;
+
+ /** type of client */
+ rcyn_type_t type;
+
+ /** the version of the protocol used */
+ unsigned version: 4;
+
+ /** is relaxed version of the protocol */
+ unsigned relax: 1;
+
+ /** is the actual link invalid or valid */
+ unsigned invalid: 1;
+
+ /** enter/leave status, record if entered */
+ unsigned entered: 1;
+
+ /** enter/leave status, record if entring pending */
+ unsigned entering: 1;
+
+ /** indicate if some check were made */
+ unsigned checked: 1;
+
+ /** polling callback */
+ pollitem_t pollitem;
+};
+typedef struct client client_t;
+
+/**
+ * Check 'arg' against 'value' beginning at offset accepting it if 'arg' prefixes 'value'
+ * Returns 1 if matching or 0 if not.
+ */
+static
+bool
+ckarg(
+ const char *arg,
+ const char *value,
+ unsigned offset
+) {
+ while(arg[offset])
+ if (arg[offset] == value[offset])
+ offset++;
+ else
+ return false;
+ return true;
+}
+
+/**
+ * Flush the write buffer
+ */
+static
+int
+flushw(
+ client_t *cli
+) {
+ int rc;
+ struct pollfd pfd;
+
+ rc = prot_should_write(cli->prot);
+ while (rc) {
+ rc = prot_write(cli->prot, cli->fd);
+ if (rc == -EAGAIN) {
+ pfd.fd = cli->fd;
+ pfd.events = POLLOUT;
+ do { rc = poll(&pfd, 1, 0); } while (rc < 0 && errno == EINTR);
+ }
+ if (rc < 0) {
+ break;
+ }
+ rc = prot_should_write(cli->prot);
+ }
+ return rc;
+}
+
+/**
+ * Send a reply to client
+ */
+static
+int
+putx(
+ client_t *cli,
+ ...
+) {
+ const char *p, *fields[MAXARGS];
+ unsigned n;
+ va_list l;
+ int rc;
+
+ va_start(l, cli);
+ n = 0;
+ p = va_arg(l, const char *);
+ while (p) {
+ if (n == MAXARGS)
+ return -EINVAL;
+ fields[n++] = p;
+ p = va_arg(l, const char *);
+ }
+ va_end(l);
+ rc = prot_put(cli->prot, n, fields);
+ if (rc == -ECANCELED) {
+ rc = flushw(cli);
+ if (rc == 0)
+ rc = prot_put(cli->prot, n, fields);
+ }
+ return rc;
+}
+
+/** emit a simple done reply and flush */
+static
+void
+send_done(
+ client_t *cli
+) {
+ putx(cli, _done_, NULL);
+ flushw(cli);
+}
+
+/** emit a simple error reply and flush */
+static
+void
+send_error(
+ client_t *cli,
+ const char *errorstr
+) {
+ putx(cli, _error_, errorstr, NULL);
+ flushw(cli);
+}
+
+/** emit a simple done/error reply */
+static
+void
+send_done_or_error(
+ client_t *cli,
+ int status
+) {
+ if (status >= 0)
+ send_done(cli);
+ else
+ send_error(cli, NULL);
+}
+
+/** callback of entering */
+static
+void
+entercb(
+ void *closure
+) {
+ client_t *cli = closure;
+
+ cli->entered = true;
+ cli->entering = false;
+ send_done(cli);
+}
+
+/** callback of checking */
+static
+void
+checkcb(
+ void *closure,
+ uint32_t value
+) {
+ client_t *cli = closure;
+ const char *a;
+ char val[30];
+
+ if (value == DENY)
+ a = _no_;
+ else if (value == ALLOW)
+ a = _yes_;
+ else {
+ snprintf(val, sizeof val, "%d", value);
+ a = val;
+ }
+ putx(cli, a, NULL);
+ flushw(cli);
+}
+
+/** callback of getting list of entries */
+static
+void
+getcb(
+ void *closure,
+ const char *client,
+ const char *session,
+ const char *user,
+ const char *permission,
+ uint32_t value
+) {
+ client_t *cli = closure;
+ char val[30];
+ snprintf(val, sizeof val, "%d", value);
+ putx(cli, _item_, client, session, user, permission, val, NULL);
+}
+
+/** handle a request */
+static
+void
+onrequest(
+ client_t *cli,
+ int count,
+ const char *args[]
+) {
+ int rc;
+ uint32_t value;
+
+ /* just ignore empty lines */
+ if (count == 0)
+ return;
+
+ /* version hand-shake */
+ if (!cli->version) {
+ if (!ckarg(args[0], _rcyn_, 0) || count != 2 || !ckarg(args[1], "1", 0))
+ goto invalid;
+ putx(cli, _yes_, "1", NULL);
+ flushw(cli);
+ cli->version = 1;
+ return;
+ }
+
+ switch(args[0][0]) {
+ case 'c': /* check */
+ if (ckarg(args[0], _check_, 1) && count == 5) {
+ cli->checked = 1;
+ cyn_check_async(checkcb, cli, args[1], args[2], args[3], args[4]);
+ return;
+ }
+ break;
+ case 'd': /* drop */
+ if (ckarg(args[0], _drop_, 1) && count == 5) {
+ if (cli->type != rcyn_Admin)
+ break;
+ if (!cli->entered)
+ break;
+ rc = cyn_drop(args[1], args[2], args[3], args[4]);
+ send_done_or_error(cli, rc);
+ return;
+ }
+ break;
+ case 'e': /* enter */
+ if (ckarg(args[0], _enter_, 1) && count == 1) {
+ if (cli->type != rcyn_Admin)
+ break;
+ if (cli->entered || cli->entering)
+ break;
+ cli->entering = true;
+ /* TODO: remove from polling until entered? */
+ cyn_enter_async(entercb, cli);
+ return;
+ }
+ break;
+ case 'g': /* get */
+ if (ckarg(args[0], _get_, 1) && count == 5) {
+ if (cli->type != rcyn_Admin)
+ break;
+ cyn_list(cli, getcb, args[1], args[2], args[3], args[4]);
+ send_done(cli);
+ return;
+ }
+ break;
+ case 'l': /* leave */
+ if (ckarg(args[0], _leave_, 1) && count <= 2) {
+ if (cli->type != rcyn_Admin)
+ break;
+ if (count == 2 && !ckarg(args[1], _commit_, 0) && !ckarg(args[1], _rollback_, 0))
+ break;
+ if (!cli->entered)
+ break;
+ rc = cyn_leave(cli, count == 2 && ckarg(args[1], _commit_, 0));
+ cli->entered = false;
+ send_done_or_error(cli, rc);
+ return;
+ }
+ break;
+ case 's': /* set */
+ if (ckarg(args[0], _set_, 1) && count == 6) {
+ if (cli->type != rcyn_Admin)
+ break;
+ if (!cli->entered)
+ break;
+ value = (uint32_t)atol(args[5]);
+ rc = cyn_set(args[1], args[2], args[3], args[4], value);
+ send_done_or_error(cli, rc);
+ return;
+ }
+ break;
+ case 't': /* test */
+ if (ckarg(args[0], _test_, 1) && count == 5) {
+ cyn_test(args[1], args[2], args[3], args[4], &value);
+ checkcb(cli, value);
+ return;
+ }
+ break;
+ }
+invalid: /* invalid rest detected */
+ send_error(cli, "invalid");
+ if (!cli->relax)
+ cli->invalid = 1;
+}
+
+/** on change callback, emits a clear for caching */
+static
+void
+onchange(
+ void *closure
+) {
+ client_t *cli = closure;
+ if (cli->checked) {
+ cli->checked = false;
+ putx(cli, _clear_, NULL);
+ flushw(cli);
+ }
+}
+
+/** destroy a client */
+static
+void
+destroy_client(
+ client_t *cli,
+ bool closefds
+) {
+ if (closefds)
+ close(cli->fd);
+ if (cli->entering)
+ cyn_enter_async_cancel(entercb, cli);
+ if (cli->entered)
+ cyn_leave(cli, false);
+ cyn_on_change_remove(onchange, cli);
+ prot_destroy(cli->prot);
+ free(cli);
+}
+
+/** handle client requests */
+static
+void
+on_client_event(
+ pollitem_t *pollitem,
+ uint32_t events,
+ int pollfd
+) {
+ int nargs, nr;
+ const char **args;
+ client_t *cli = pollitem->closure;
+
+ /* is it a hangup? */
+ if (events & EPOLLHUP)
+ goto terminate;
+
+ /* possible input */
+ if (events & EPOLLIN) {
+ nr = prot_read(cli->prot, cli->fd);
+ if (nr <= 0)
+ goto terminate;
+ nargs = prot_get(cli->prot, &args);
+ while (nargs >= 0) {
+ onrequest(cli, nargs, args);
+ if (cli->invalid && !cli->relax)
+ goto terminate;
+ prot_next(cli->prot);
+ nargs = prot_get(cli->prot, &args);
+ }
+ }
+ return;
+
+ /* terminate the client session */
+terminate:
+ pollitem_del(cli->fd, pollfd);
+ destroy_client(cli, true);
+}
+
+/** create a client */
+static
+int
+create_client(
+ client_t **pcli,
+ int fd,
+ rcyn_type_t type
+) {
+ client_t *cli;
+ int rc;
+
+ /* allocate the object */
+ *pcli = cli = calloc(1, sizeof *cli);
+ if (cli == NULL) {
+ rc = -ENOMEM;
+ goto error;
+ }
+
+ /* create protocol object */
+ rc = prot_create(&cli->prot);
+ if (rc < 0)
+ goto error2;
+
+ /* monitor change and caching */
+ rc = cyn_on_change_add(onchange, cli);
+ if (rc < 0)
+ goto error3;
+
+ /* records the file descriptor */
+ cli->fd = fd;
+ cli->type = type;
+ cli->version = 0; /* version not set */
+ cli->relax = true; /* relax on error */
+ cli->invalid = false; /* not invalid */
+ cli->entered = false; /* not entered */
+ cli->entering = false; /* not entering */
+ cli->checked = false; /* no check made */
+ cli->pollitem.handler = on_client_event;
+ cli->pollitem.closure = cli;
+ return 0;
+error3:
+ prot_destroy(cli->prot);
+error2:
+ free(cli);
+error:
+ *pcli = NULL;
+ return rc;
+}
+
+/** handle server events */
+static
+void
+on_server_event(
+ pollitem_t *pollitem,
+ uint32_t events,
+ int pollfd,
+ rcyn_type_t type
+) {
+ int servfd = pollitem->fd;
+ int fd, rc;
+ struct sockaddr saddr;
+ socklen_t slen;
+ client_t *cli;
+
+ /* is it a hangup? it shouldn't! */
+ if (events & EPOLLHUP) {
+ fprintf(stderr, "unexpected server socket closing\n");
+ exit(2);
+ }
+
+ /* EPOLLIN is the only expected event but asserting makes fear */
+ if (!(events & EPOLLIN))
+ return;
+
+ /* accept the connection */
+ slen = (socklen_t)sizeof saddr;
+ fd = accept(servfd, &saddr, &slen);
+ if (fd < 0) {
+ fprintf(stderr, "can't accept connection: %m\n");
+ return;
+ }
+ fcntl(fd, F_SETFD, FD_CLOEXEC);
+ fcntl(fd, F_SETFL, O_NONBLOCK);
+
+ /* create a client for the connection */
+ rc = create_client(&cli, fd, type);
+ if (rc < 0) {
+ fprintf(stderr, "can't create client connection: %s\n", strerror(-rc));
+ close(fd);
+ return;
+ }
+
+ /* add the client to the epolling */
+ rc = pollitem_add(&cli->pollitem, EPOLLIN, fd, pollfd);
+ if (rc < 0) {
+ fprintf(stderr, "can't poll client connection: %s\n", strerror(-rc));
+ destroy_client(cli, 1);
+ return;
+ }
+}
+
+/** handle check server events */
+static
+void
+on_check_server_event(
+ pollitem_t *pollitem,
+ uint32_t events,
+ int pollfd
+) {
+ on_server_event(pollitem, events, pollfd, rcyn_Check);
+}
+
+/** handle admin server events */
+static
+void
+on_admin_server_event(
+ pollitem_t *pollitem,
+ uint32_t events,
+ int pollfd
+) {
+ on_server_event(pollitem, events, pollfd, rcyn_Admin);
+}
+
+int main(int ac, char **av)
+{
+ int rc;
+ pollitem_t pi_admin, pi_check, *pi;
+ int pollfd, fd;
+ struct epoll_event ev;
+ const char *check_spec = rcyn_default_check_socket_spec;
+ const char *admin_spec = rcyn_default_admin_socket_spec;
+
+ /*
+ * future possible options:
+ * - strict/relax
+ * - databse name(s)
+ * - socket name
+ * - policy
+ */
+
+ /* connection to the database */
+ rc = cyn_init();
+ if (rc < 0) {
+ fprintf(stderr, "can't initialise database: %m\n");
+ return 1;
+ }
+
+ /* create the polling fd */
+ pollfd = epoll_create1(EPOLL_CLOEXEC);
+ if (pollfd < 0) {
+ fprintf(stderr, "can't create polling: %m\n");
+ return 1;
+ }
+
+ /* create the admin server socket */
+ fd = socket_open(admin_spec, 1);
+ if (fd < 0) {
+ fprintf(stderr, "can't create admin server socket: %m\n");
+ return 1;
+ }
+
+ /* add the server to pollfd */
+ pi_admin.handler = on_admin_server_event;
+ pi_admin.fd = fd;
+ rc = pollitem_add(&pi_admin, EPOLLIN, fd, pollfd);
+ if (rc < 0) {
+ fprintf(stderr, "can't poll admin server: %m\n");
+ return 1;
+ }
+
+ /* create the server socket */
+ fd = socket_open(check_spec, 1);
+ if (fd < 0) {
+ fprintf(stderr, "can't create check server socket: %m\n");
+ return 1;
+ }
+
+ /* add the server to pollfd */
+ pi_check.handler = on_check_server_event;
+ pi_check.fd = fd;
+ rc = pollitem_add(&pi_check, EPOLLIN, fd, pollfd);
+ if (rc < 0) {
+ fprintf(stderr, "can't poll check server: %m\n");
+ return 1;
+ }
+
+ /* ready ! */
+#if defined(WITH_SYSTEMD_ACTIVATION)
+ sd_notify(0, "READY=1");
+#endif
+
+ /* process inputs */
+ for(;;) {
+ rc = epoll_wait(pollfd, &ev, 1, -1);
+ if (rc == 1) {
+ pi = ev.data.ptr;
+ pi->handler(pi, ev.events, pollfd);
+ }
+ }
+}
+
diff --git a/src/socket.c b/src/socket.c
new file mode 100644
index 0000000..3ab0408
--- /dev/null
+++ b/src/socket.c
@@ -0,0 +1,320 @@
+/*
+ * Copyright (C) 2015-2018 "IoT.bzh"
+ * Author José Bollo <jose.bollo@iot.bzh>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define _GNU_SOURCE
+
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <endian.h>
+#include <netdb.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#if defined(WITH_SYSTEMD_ACTIVATION)
+#include <systemd/sd-daemon.h>
+#endif
+
+#include "socket.h"
+
+#define BACKLOG 30
+
+/******************************************************************************/
+
+/**
+ * known types
+ */
+enum type {
+ /** type internet */
+ Type_Inet,
+
+ /** type systemd */
+ Type_Systemd,
+
+ /** type Unix */
+ Type_Unix
+};
+
+/**
+ * Structure for known entries
+ */
+struct entry
+{
+ /** the known prefix */
+ const char *prefix;
+
+ /** the type of the entry */
+ unsigned type: 2;
+
+ /** should not set SO_REUSEADDR for servers */
+ unsigned noreuseaddr: 1;
+
+ /** should not call listen for servers */
+ unsigned nolisten: 1;
+};
+
+/**
+ * The known entries with the default one at the first place
+ */
+static struct entry entries[] = {
+ {
+ .prefix = "tcp:",
+ .type = Type_Inet
+ },
+ {
+ .prefix = "sd:",
+ .type = Type_Systemd,
+ .noreuseaddr = 1,
+ .nolisten = 1
+ },
+ {
+ .prefix = "unix:",
+ .type = Type_Unix
+ }
+};
+
+/******************************************************************************/
+
+/**
+ * open a unix domain socket for client or server
+ *
+ * @param spec the specification of the path (prefix with @ for abstract)
+ * @param server 0 for client, server otherwise
+ *
+ * @return the file descriptor number of the socket or -1 in case of error
+ */
+static int open_unix(const char *spec, int server)
+{
+ int fd, rc, abstract;
+ struct sockaddr_un addr;
+ size_t length;
+
+ abstract = spec[0] == '@';
+
+ /* check the length */
+ length = strlen(spec);
+ if (length >= 108) {
+ errno = ENAMETOOLONG;
+ return -1;
+ }
+
+ /* remove the file on need */
+ if (server && !abstract)
+ unlink(spec);
+
+ /* create a socket */
+ fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (fd < 0)
+ return fd;
+
+ /* prepare address */
+ memset(&addr, 0, sizeof addr);
+ addr.sun_family = AF_UNIX;
+ strcpy(addr.sun_path, spec);
+ if (abstract)
+ addr.sun_path[0] = 0; /* implement abstract sockets */
+
+ if (server) {
+ rc = bind(fd, (struct sockaddr *) &addr, (socklen_t)(sizeof addr));
+ } else {
+ rc = connect(fd, (struct sockaddr *) &addr, (socklen_t)(sizeof addr));
+ }
+ if (rc < 0) {
+ close(fd);
+ return rc;
+ }
+ return fd;
+}
+
+/**
+ * open a tcp socket for client or server
+ *
+ * @param spec the specification of the host:port/...
+ * @param server 0 for client, server otherwise
+ *
+ * @return the file descriptor number of the socket or -1 in case of error
+ */
+static int open_tcp(const char *spec, int server)
+{
+ int rc, fd;
+ const char *service, *host, *tail;
+ struct addrinfo hint, *rai, *iai;
+
+ /* scan the uri */
+ tail = strchrnul(spec, '/');
+ service = strchr(spec, ':');
+ if (service == NULL || tail < service) {
+ errno = EINVAL;
+ return -1;
+ }
+ host = strndupa(spec, service++ - spec);
+ service = strndupa(service, tail - service);
+
+ /* get addr */
+ memset(&hint, 0, sizeof hint);
+ hint.ai_family = AF_INET;
+ hint.ai_socktype = SOCK_STREAM;
+ rc = getaddrinfo(host, service, &hint, &rai);
+ if (rc != 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* get the socket */
+ iai = rai;
+ while (iai != NULL) {
+ fd = socket(iai->ai_family, iai->ai_socktype, iai->ai_protocol);
+ if (fd >= 0) {
+ if (server) {
+ rc = bind(fd, iai->ai_addr, iai->ai_addrlen);
+ } else {
+ rc = connect(fd, iai->ai_addr, iai->ai_addrlen);
+ }
+ if (rc == 0) {
+ freeaddrinfo(rai);
+ return fd;
+ }
+ close(fd);
+ }
+ iai = iai->ai_next;
+ }
+ freeaddrinfo(rai);
+ return -1;
+}
+
+/**
+ * open a systemd socket for server
+ *
+ * @param spec the specification of the systemd name
+ *
+ * @return the file descriptor number of the socket or -1 in case of error
+ */
+static int open_systemd(const char *spec)
+{
+#if defined(WITH_SYSTEMD_ACTIVATION)
+ char **names;
+ int fd = -1;
+ int c = sd_listen_fds_with_names(0, &names);
+ if (c < 0)
+ errno = -c;
+ else if (names) {
+ for (c = 0 ; names[c] ; c++) {
+ if (!strcmp(names[c], spec))
+ fd = SD_LISTEN_FDS_START + c;
+ free(names[c]);
+ }
+ free(names);
+ }
+ if (fd < 0 && '0' <= *spec && *spec <= '9')
+ fd = SD_LISTEN_FDS_START + atoi(spec);
+ return fd;
+#else
+ errno = EAFNOSUPPORT;
+ return -1;
+#endif
+}
+
+/******************************************************************************/
+
+/**
+ * Get the entry of the uri by searching to its prefix
+ *
+ * @param uri the searched uri
+ * @param offset where to store the prefix length
+ *
+ * @return the found entry or the default one
+ */
+static struct entry *get_entry(const char *uri, int *offset)
+{
+ int l, i = (int)(sizeof entries / sizeof * entries);
+
+ for (;;) {
+ if (!i) {
+ l = 0;
+ break;
+ }
+ i--;
+ l = (int)strlen(entries[i].prefix);
+ if (!strncmp(uri, entries[i].prefix, l))
+ break;
+ }
+
+ *offset = l;
+ return &entries[i];
+}
+
+/**
+ * open socket for client or server
+ *
+ * @param uri the specification of the socket
+ * @param server 0 for client, server otherwise
+ *
+ * @return the file descriptor number of the socket or -1 in case of error
+ */
+int socket_open(const char *uri, int server)
+{
+ int fd, rc, offset;
+ struct entry *e;
+
+ /* search for the entry */
+ e = get_entry(uri, &offset);
+
+ /* get the names */
+ uri += offset;
+
+ /* open the socket */
+ switch (e->type) {
+ case Type_Unix:
+ fd = open_unix(uri, server);
+ break;
+ case Type_Inet:
+ fd = open_tcp(uri, server);
+ break;
+ case Type_Systemd:
+ if (server)
+ fd = open_systemd(uri);
+ else {
+ errno = EINVAL;
+ fd = -1;
+ }
+ break;
+ default:
+ errno = EAFNOSUPPORT;
+ fd = -1;
+ break;
+ }
+ if (fd < 0)
+ return -1;
+
+ /* set it up */
+ fcntl(fd, F_SETFD, FD_CLOEXEC);
+ fcntl(fd, F_SETFL, O_NONBLOCK);
+ if (server) {
+ if (!e->noreuseaddr) {
+ rc = 1;
+ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &rc, sizeof rc);
+ }
+ if (!e->nolisten)
+ listen(fd, BACKLOG);
+ }
+ return fd;
+}
+
diff --git a/src/socket.h b/src/socket.h
new file mode 100644
index 0000000..b5989e1
--- /dev/null
+++ b/src/socket.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2018 "IoT.bzh"
+ * Author José Bollo <jose.bollo@iot.bzh>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+extern int socket_open(const char *uri, int server);
+
diff --git a/src/test-lib-compat.c b/src/test-lib-compat.c
new file mode 100644
index 0000000..627c208
--- /dev/null
+++ b/src/test-lib-compat.c
@@ -0,0 +1,256 @@
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/epoll.h>
+
+#include <cynara/cynara-admin.h>
+#include <cynara/cynara-client-async.h>
+#include <cynara/cynara-client.h>
+
+#define STD 0
+#define TEST 1
+#define CACHE 2
+
+#define STRFY(x) #x
+#define CKEX(x) ckex((x),STD,__LINE__,STRFY(x))
+#define TEEX(x) ckex((x),TEST,__LINE__,STRFY(x))
+#define CAEX(x) ckex((x),CACHE,__LINE__,STRFY(x))
+
+struct cynara_admin *admin;
+struct cynara_async_configuration *aconf;
+struct cynara_async *aclient;
+struct cynara_configuration *conf;
+struct cynara *client;
+char buffer[4000];
+char *str[40];
+int nstr;
+int pollfd;
+
+#define BUCKET "BUCK"
+
+void ckex(int rc, int type, int line, const char *x)
+{
+ int err = 1;
+ switch(type) {
+ case STD:
+ err = (rc != CYNARA_API_SUCCESS);
+ break;
+ case TEST:
+ err = (rc != CYNARA_API_ACCESS_DENIED
+ && rc != CYNARA_API_ACCESS_ALLOWED
+ && rc != CYNARA_API_ACCESS_NOT_RESOLVED);
+ break;
+ case CACHE:
+ err = (rc != CYNARA_API_ACCESS_DENIED
+ && rc != CYNARA_API_ACCESS_ALLOWED
+ && rc != CYNARA_API_ACCESS_NOT_RESOLVED
+ && rc != CYNARA_API_CACHE_MISS);
+ break;
+ }
+ if (err) {
+ char buffer[200];
+ cynara_strerror(rc, buffer, 200);
+ printf("ERROR(%d) %s by %s line %d\n", rc, buffer, x, line);
+ exit(1);
+ }
+ printf("SUCCESS %s\n", x);
+}
+
+int is(const char *first, const char *second, int mincount)
+{
+ return nstr >= mincount + 2
+ && !strcmp(first, str[0])
+ && !strcmp(second, str[1]);
+}
+
+void adm_list(char *cli, char *usr, char *perm)
+{
+ int i;
+ struct cynara_admin_policy **policies;
+ CKEX(cynara_admin_list_policies(admin, BUCKET, cli, usr, perm, &policies));
+ i = 0;
+ while(policies[i]) {
+ printf("%s %s %s %s %d %s\n",
+ policies[i]->bucket,
+ policies[i]->client,
+ policies[i]->user,
+ policies[i]->privilege,
+ policies[i]->result,
+ policies[i]->result_extra ?: "");
+ free(policies[i]->bucket);
+ free(policies[i]->client);
+ free(policies[i]->user);
+ free(policies[i]->privilege);
+ free(policies[i]->result_extra);
+ free(policies[i]);
+ i++;
+ }
+ free(policies);
+}
+
+void adm_check(char *cli, char *usr, char *perm)
+{
+ char *rs;
+ int ri;
+
+ CKEX(cynara_admin_check(admin, BUCKET, 1, cli, usr, perm, &ri, &rs));
+ printf("got %d %s \n", ri, rs ?: "NULL");
+}
+
+void adm_set()
+{
+ struct cynara_admin_policy **policies, *p;
+ int n, i;
+
+ n = (nstr - 2) / 4;
+ policies = malloc((1 + n) * sizeof *policies + n * sizeof **policies);
+ policies[n] = NULL;
+ p = (struct cynara_admin_policy*)(&policies[n + 1]);
+ for (i = 0 ; i < n ; i++, p++) {
+ policies[i] = p;
+ p->bucket = BUCKET;
+ p->client = str[2 + i * 4 + 0];
+ p->user = str[2 + i * 4 + 1];
+ p->privilege = str[2 + i * 4 + 2];
+ p->result = atoi(str[2 + i * 4 + 3]);
+ p->result_extra = NULL;
+ }
+ CKEX(cynara_admin_set_policies(admin, (const struct cynara_admin_policy *const *)policies));
+ free(policies);
+}
+
+void adm_erase(char *cli, char *usr, char *perm)
+{
+ CKEX(cynara_admin_erase(admin, BUCKET, 1, cli, usr, perm));
+}
+
+void adm_desc()
+{
+ int i;
+ struct cynara_admin_policy_descr **d;
+ CKEX(cynara_admin_list_policies_descriptions(admin, &d));
+ i = 0;
+ while(d[i]) {
+ printf("desc[%d] %d -> %s\n", i, d[i]->result, d[i]->name);
+ free(d[i]->name);
+ free(d[i]);
+ i++;
+ }
+ free(d);
+}
+
+void asy_cache(char *cli, char *ses, char *usr, char *perm)
+{
+ CAEX(cynara_async_check_cache(aclient, cli, ses, usr, perm));
+}
+
+void asyncb(cynara_check_id check_id, cynara_async_call_cause cause,
+ int response, void *user_response_data)
+{
+ printf("RECEIVE %d %d\n", cause, response);
+}
+
+void asy_check(char *cli, char *ses, char *usr, char *perm, int simple)
+{
+ if (simple)
+ CKEX(cynara_async_create_simple_request(aclient, cli, ses, usr, perm, NULL, asyncb, NULL));
+ else
+ CKEX(cynara_async_create_request(aclient, cli, ses, usr, perm, NULL, asyncb, NULL));
+}
+
+void syn_check(char *cli, char *ses, char *usr, char *perm, int simple)
+{
+ if (simple)
+ TEEX(cynara_simple_check(client, cli, ses, usr, perm));
+ else
+ TEEX(cynara_check(client, cli, ses, usr, perm));
+}
+
+void asyncstscb(int old_fd, int new_fd, cynara_async_status status, void *data)
+{
+ struct epoll_event ev;
+
+ ev.data.fd = new_fd;
+ ev.events = (status == CYNARA_STATUS_FOR_RW ? EPOLLOUT : 0)|EPOLLIN;
+ if (old_fd == new_fd) {
+ if (new_fd != -1)
+ epoll_ctl(pollfd, EPOLL_CTL_MOD, new_fd, &ev);
+ } else {
+ if (old_fd != -1)
+ epoll_ctl(pollfd, EPOLL_CTL_DEL, old_fd, &ev);
+ if (new_fd != -1)
+ epoll_ctl(pollfd, EPOLL_CTL_ADD, new_fd, &ev);
+ }
+}
+
+int main(int ac, char **av)
+{
+ struct epoll_event ev;
+
+ pollfd = epoll_create(10);
+ ev.data.fd = 0;
+ ev.events = EPOLLIN;
+ epoll_ctl(pollfd, EPOLL_CTL_ADD, 0, &ev);
+
+ CKEX(cynara_admin_initialize(&admin));
+
+ CKEX(cynara_async_configuration_create(&aconf));
+ CKEX(cynara_async_configuration_set_cache_size(aconf, 1000));
+ CKEX(cynara_async_initialize(&aclient, aconf, asyncstscb, NULL));
+ cynara_async_configuration_destroy(aconf);
+
+ CKEX(cynara_configuration_create(&conf));
+ CKEX(cynara_configuration_set_cache_size(conf, 1000));
+ CKEX(cynara_initialize(&client, conf));
+ cynara_configuration_destroy(conf);
+
+ for(;;) {
+ epoll_wait(pollfd, &ev, 1, -1);
+
+ if (ev.data.fd) {
+ cynara_async_process(aclient);
+ continue;
+ }
+
+ if (!fgets(buffer, sizeof buffer, stdin))
+ break;
+
+ str[nstr = 0] = strtok(buffer, " \t\n");
+ while(str[nstr])
+ str[++nstr] = strtok(NULL, " \t\n");
+
+ if (is("admin", "listall", 0))
+ adm_list("#", "#", "#");
+ else if (is("admin", "list", 3))
+ adm_list(str[2], str[3], str[4]);
+ else if (is("admin", "check", 3))
+ adm_check(str[2], str[3], str[4]);
+ else if (is("admin", "set", 4))
+ adm_set();
+ else if (is("admin", "erase", 3))
+ adm_erase(str[2], str[3], str[4]);
+ else if (is("admin", "desc", 0))
+ adm_desc();
+ else if (is("async", "cache", 4))
+ asy_cache(str[2], str[3], str[4], str[5]);
+ else if (is("async", "check", 4))
+ asy_check(str[2], str[3], str[4], str[5], 0);
+ else if (is("async", "test", 4))
+ asy_check(str[2], str[3], str[4], str[5], 1);
+ else if (is("sync", "check", 4))
+ syn_check(str[2], str[3], str[4], str[5], 0);
+ else if (is("sync", "test", 4))
+ syn_check(str[2], str[3], str[4], str[5], 1);
+ else if (nstr > 0 && !strcmp(str[0], "exit"))
+ break;
+ else if (nstr > 0 && str[0][0] != '#')
+ printf("ERROR bad input\n");
+ }
+
+ cynara_finish(client);
+ cynara_async_finish(aclient);
+ cynara_admin_finish(admin);
+}
+
diff --git a/systemd/CMakeLists.txt b/systemd/CMakeLists.txt
new file mode 100644
index 0000000..f8116a2
--- /dev/null
+++ b/systemd/CMakeLists.txt
@@ -0,0 +1,42 @@
+# Copyright (c) 2014-2016 Samsung Electronics Co., Ltd All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# @file CMakeLists.txt
+# @author Lukasz Wojciechowski <l.wojciechow@partner.samsung.com>
+#
+
+SET(CYNARA_ADMIN_SOCKET_GROUP
+ "security_fw"
+ CACHE STRING
+ "Group to apply on administrative sockets")
+
+CONFIGURE_FILE(cynara-admin.socket.in cynara-admin.socket @ONLY)
+CONFIGURE_FILE(cynara-check.socket.in cynara-check.socket @ONLY)
+
+INSTALL(FILES
+ ${CMAKE_CURRENT_SOURCE_DIR}/cynara.service
+ ${CMAKE_CURRENT_SOURCE_DIR}/cynara.target
+ ${CMAKE_CURRENT_BINARY_DIR}/cynara-admin.socket
+ ${CMAKE_CURRENT_BINARY_DIR}/cynara-check.socket
+ DESTINATION
+ ${SYSTEMD_UNIT_DIR}
+)
+
+INSTALL(DIRECTORY
+ ${CMAKE_CURRENT_SOURCE_DIR}/sockets.target.wants
+ DESTINATION
+ ${SYSTEMD_UNIT_DIR}
+)
+
+
diff --git a/systemd/cynara-admin.socket.in b/systemd/cynara-admin.socket.in
new file mode 100644
index 0000000..ebc59c6
--- /dev/null
+++ b/systemd/cynara-admin.socket.in
@@ -0,0 +1,15 @@
+[Socket]
+FileDescriptorName=admin
+ListenStream=@SOCKET_DIR@/cynara.admin
+SocketMode=0600
+SmackLabelIPIn=@
+SmackLabelIPOut=@
+
+Service=cynara.service
+
+[Unit]
+Wants=cynara.target
+Before=cynara.target
+
+[Install]
+WantedBy=sockets.target
diff --git a/systemd/cynara-check.socket.in b/systemd/cynara-check.socket.in
new file mode 100644
index 0000000..1139d2f
--- /dev/null
+++ b/systemd/cynara-check.socket.in
@@ -0,0 +1,15 @@
+[Socket]
+FileDescriptorName=check
+ListenStream=@SOCKET_DIR@/cynara.check
+SocketMode=0666
+SmackLabelIPIn=*
+SmackLabelIPOut=@
+
+Service=cynara.service
+
+[Unit]
+Wants=cynara.target
+Before=cynara.target
+
+[Install]
+WantedBy=sockets.target
diff --git a/systemd/cynara.service b/systemd/cynara.service
new file mode 100644
index 0000000..e124b91
--- /dev/null
+++ b/systemd/cynara.service
@@ -0,0 +1,29 @@
+[Unit]
+Description=Cynara service
+Requires=afm-system-setup.service
+After=afm-system-setup.service
+
+[Service]
+ExecStartPre=+-/usr/bin/sh -c 'if test ! -d /var/lib/cynara; then mkdir -p /var/lib/cynara; chown cynara:cynara /var/lib/cynara; chsmack -a System /var/lib/cynara; fi'
+ExecStart=/usr/bin/cynarad
+
+Type=notify
+
+KillMode=process
+TimeoutStopSec=3
+Restart=always
+
+Sockets=cynara-admin.socket
+Sockets=cynara-check.socket
+
+UMask=0000
+User=cynara
+Group=cynara
+SmackProcessLabel=System
+#NoNewPrivileges=true
+
+#Environment="CYNARA_LOG_LEVEL=LOG_DEBUG"
+#Environment="CYNARA_AUDIT_LEVEL=ALL"
+
+[Install]
+WantedBy=multi-user.target
diff --git a/systemd/cynara.target b/systemd/cynara.target
new file mode 100644
index 0000000..9b2dee4
--- /dev/null
+++ b/systemd/cynara.target
@@ -0,0 +1,4 @@
+[Unit]
+Description=cynara sockets
+DefaultDependencies=true
+
diff --git a/systemd/sockets.target.wants/cynara-admin.socket b/systemd/sockets.target.wants/cynara-admin.socket
new file mode 120000
index 0000000..3d0b1ce
--- /dev/null
+++ b/systemd/sockets.target.wants/cynara-admin.socket
@@ -0,0 +1 @@
+../cynara-admin.socket \ No newline at end of file
diff --git a/systemd/sockets.target.wants/cynara-check.socket b/systemd/sockets.target.wants/cynara-check.socket
new file mode 120000
index 0000000..921ca66
--- /dev/null
+++ b/systemd/sockets.target.wants/cynara-check.socket
@@ -0,0 +1 @@
+../cynara-check.socket \ No newline at end of file