summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSuchang Woo <suchang.woo@samsung.com>2015-07-16 17:12:54 +0900
committerSuchang Woo <suchang.woo@samsung.com>2015-07-16 17:28:30 +0900
commitd966429452d4ff6a3527f0deb8db63be5b87f720 (patch)
treea811fe0a17e1c6b69868cf1176eb99815227238b
parent99b1d99a4f93a6bf0786abc6ae5ad1f3d9415933 (diff)
downloadbuxton2-master.tar.gz
buxton2-master.tar.bz2
buxton2-master.zip
Initial importHEADmaster
Signed-off-by: Suchang Woo <suchang.woo@samsung.com>
-rw-r--r--CMakeLists.txt59
-rw-r--r--LICENSE.Apache-2.0204
-rw-r--r--backend/CMakeLists.txt11
-rw-r--r--backend/gdbm.c323
-rw-r--r--client/CMakeLists.txt21
-rw-r--r--client/c_common.c162
-rw-r--r--client/c_common.h36
-rw-r--r--client/c_direct.c490
-rw-r--r--client/c_direct.h93
-rw-r--r--client/c_log.h36
-rw-r--r--client/c_main.c429
-rw-r--r--client/c_proc.c463
-rw-r--r--client/c_proc.h89
-rw-r--r--common/backend.h74
-rw-r--r--common/backends.c205
-rw-r--r--common/backends.h31
-rw-r--r--common/common.c127
-rw-r--r--common/common.h124
-rw-r--r--common/config.c305
-rw-r--r--common/config.h30
-rw-r--r--common/direct.c548
-rw-r--r--common/direct.h44
-rw-r--r--common/log.h49
-rw-r--r--common/proto.c328
-rw-r--r--common/proto.h33
-rw-r--r--common/serialize.c970
-rw-r--r--common/serialize.h70
-rw-r--r--daemon/CMakeLists.txt30
-rw-r--r--daemon/cynara.c348
-rw-r--r--daemon/cynara.h46
-rw-r--r--daemon/daemon.c877
-rw-r--r--daemon/daemon.h51
-rw-r--r--daemon/main.c133
-rw-r--r--daemon/socks.c232
-rw-r--r--daemon/socks.h29
-rw-r--r--lib/CMakeLists.txt23
-rw-r--r--lib/buxton2.c2024
-rw-r--r--lib/buxton2.pc.in10
-rw-r--r--lib/include/buxton2.h338
-rw-r--r--packaging/buxton2-pre.service10
-rw-r--r--packaging/buxton2.conf21
-rw-r--r--packaging/buxton2.manifest8
-rw-r--r--packaging/buxton2.service11
-rw-r--r--packaging/buxton2.socket11
-rw-r--r--packaging/buxton2.spec169
-rw-r--r--vconf-compat/CMakeLists.txt29
-rw-r--r--vconf-compat/vconf-keys.h24
-rw-r--r--vconf-compat/vconf.c680
-rw-r--r--vconf-compat/vconf.h199
-rw-r--r--vconf-compat/vconf.pc.in10
-rw-r--r--vconf-compat/vconf.sym19
-rwxr-xr-xvconf-compat/vconftool235
52 files changed, 10921 insertions, 0 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..888d33f
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,59 @@
+CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
+PROJECT(buxton2 C)
+
+# check variables
+IF(NOT DEFINED VERSION)
+ MESSAGE(FATAL_ERROR "VERSION is not defined")
+ENDIF()
+STRING(REGEX MATCH "^[0-9]*" MAJVER ${VERSION})
+
+IF(NOT DEFINED LIB_INSTALL_DIR)
+ SET(LIB_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/lib)
+ MESSAGE(WARNING "LIB_INSTALL_DIR is not defined, default value is used")
+ENDIF()
+IF(NOT DEFINED INCLUDE_INSTALL_DIR)
+ SET(INCLUDE_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/include)
+ MESSAGE(WARNING "INCLUDE_INSTALL_DIR is not defined, default value is used")
+ENDIF()
+
+OPTION(NDEBUG "Debugging and assertions disabled" TRUE)
+IF(NDEBUG)
+ ADD_DEFINITIONS(-DNDEBUG)
+ENDIF()
+
+IF(NOT "${CONFPATH}" STREQUAL "")
+ ADD_DEFINITIONS(-DCONFPATH="${CONFPATH}")
+ENDIF()
+IF(NOT "${MODULE_DIR}" STREQUAL "")
+ ADD_DEFINITIONS(-DMODULE_DIR="${MODULE_DIR}")
+ENDIF()
+IF(NOT "${DB_DIR}" STREQUAL "")
+ ADD_DEFINITIONS(-DDB_DIR="${DB_DIR}")
+ENDIF()
+IF(NOT "${TMPFS_DIR}" STREQUAL "")
+ ADD_DEFINITIONS(-DTMPFS_DIR="${TMPFS_DIR}")
+ENDIF()
+IF(NOT "${SOCKPATH}" STREQUAL "")
+ ADD_DEFINITIONS(-DSOCKPATH="${SOCKPATH}")
+ENDIF()
+
+# pkg-config
+INCLUDE(FindPkgConfig)
+PKG_CHECK_MODULES(PKGS REQUIRED glib-2.0>=2.36)
+
+FOREACH(flag ${PKGS_CFLAGS})
+ SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}")
+ENDFOREACH()
+
+SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -Wall")
+MESSAGE("CFLAGS: ${CMAKE_C_FLAGS}")
+
+# build subdirectories
+INCLUDE_DIRECTORIES(common)
+
+ADD_SUBDIRECTORY(daemon)
+ADD_SUBDIRECTORY(backend)
+ADD_SUBDIRECTORY(lib)
+ADD_SUBDIRECTORY(client)
+ADD_SUBDIRECTORY(vconf-compat)
+
diff --git a/LICENSE.Apache-2.0 b/LICENSE.Apache-2.0
new file mode 100644
index 0000000..8f17f50
--- /dev/null
+++ b/LICENSE.Apache-2.0
@@ -0,0 +1,204 @@
+Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved.
+
+ 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/backend/CMakeLists.txt b/backend/CMakeLists.txt
new file mode 100644
index 0000000..380d87b
--- /dev/null
+++ b/backend/CMakeLists.txt
@@ -0,0 +1,11 @@
+# gdbm.so build
+SET(TARGET gdbm)
+SET(SRC gdbm.c)
+ADD_LIBRARY(${TARGET} SHARED ${SRC})
+SET_TARGET_PROPERTIES(${TARGET} PROPERTIES
+ PREFIX ""
+ COMPILE_FLAGS "-fvisibility=hidden"
+)
+TARGET_LINK_LIBRARIES(${TARGET} ${PKGS_LDFLAGS} -lgdbm)
+INSTALL(TARGETS ${TARGET} DESTINATION ${MODULE_DIR} COMPONENT RuntimeLibraries)
+
diff --git a/backend/gdbm.c b/backend/gdbm.c
new file mode 100644
index 0000000..1525166
--- /dev/null
+++ b/backend/gdbm.c
@@ -0,0 +1,323 @@
+/*
+ * Buxton
+ *
+ * Copyright (C) 2015 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <assert.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <fcntl.h>
+
+#include <glib.h>
+#include <gdbm.h>
+
+#include "backend.h"
+#include "log.h"
+
+static GHashTable *dbs;
+
+static void free_db(GDBM_FILE db)
+{
+ if (!db)
+ return;
+
+ gdbm_close(db);
+}
+
+static GDBM_FILE open_gdbm(const char *dbpath)
+{
+ GDBM_FILE db;
+ char *nm;
+
+ assert(dbpath);
+
+ if (!dbs) {
+ errno = ENODEV;
+ return NULL;
+ }
+
+ db = g_hash_table_lookup(dbs, dbpath);
+ if (db)
+ return db;
+
+ db = gdbm_open(dbpath, 0, GDBM_WRCREAT, S_IRUSR | S_IWUSR, NULL);
+ if (!db) {
+ bxt_err("Open '%s' failed: %s", dbpath,
+ gdbm_strerror(gdbm_errno));
+ errno = EIO;
+ return NULL;
+ }
+
+ nm = strdup(dbpath);
+ if (!nm) {
+ gdbm_close(db);
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ g_hash_table_insert(dbs, nm, db);
+
+ bxt_dbg("Open '%s'", dbpath);
+
+ return db;
+}
+
+static int open_db(const char *dbpath)
+{
+ GDBM_FILE db;
+
+ if (!dbpath || !*dbpath) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ db = open_gdbm(dbpath);
+ if (!db)
+ return -1;
+
+ return 0;
+}
+
+static int remove_db(const char *dbpath)
+{
+ GDBM_FILE db;
+ int r;
+
+ if (!dbpath || !*dbpath) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (!dbs) {
+ errno = ENODEV;
+ return -1;
+ }
+
+ db = g_hash_table_lookup(dbs, dbpath);
+ if (db)
+ g_hash_table_remove(dbs, dbpath);
+
+ r = unlink(dbpath);
+ if (r == -1) {
+ bxt_err("Remove '%s' failed: %d", dbpath, errno);
+ return -1;
+ }
+
+ bxt_dbg("Remove '%s'", dbpath);
+
+ return 0;
+}
+
+static int set_value(const char *dbpath, const char *key, const void *data,
+ int dlen)
+{
+ GDBM_FILE db;
+ int r;
+ datum d_key;
+ datum d_data;
+
+ if (!dbpath || !*dbpath || !key || !*key || !data || dlen <= 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ db = open_gdbm(dbpath);
+ if (!db)
+ return -1;
+
+ d_key.dptr = (char *)key;
+ d_key.dsize = strlen(key) + 1;
+
+ d_data.dptr = (char *)data;
+ d_data.dsize = dlen;
+
+ r = gdbm_store(db, d_key, d_data, GDBM_REPLACE);
+ if (r) {
+ if (gdbm_errno == GDBM_READER_CANT_STORE)
+ errno = EROFS;
+
+ bxt_err("Set '%s' failed: %s", key,
+ gdbm_strerror(gdbm_errno));
+
+ return -1;
+ }
+
+ bxt_dbg("Set '%s' Key '%s'", dbpath, key);
+
+ return 0;
+}
+
+static int get_value(const char *dbpath, const char *key, void **data,
+ int *dlen)
+{
+ GDBM_FILE db;
+ datum d_key;
+ datum d_data;
+
+ if (!dbpath || !*dbpath || !key || !*key || !data || !dlen) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ db = open_gdbm(dbpath);
+ if (!db)
+ return -1;
+
+ d_key.dptr = (char *)key;
+ d_key.dsize = strlen(key) + 1;
+
+ d_data = gdbm_fetch(db, d_key);
+ if (d_data.dptr == NULL) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ *data = d_data.dptr;
+ *dlen = d_data.dsize;
+
+ bxt_dbg("Get '%s' Key '%s'", dbpath, key);
+
+ return 0;
+}
+
+static int unset_value(const char *dbpath, const char *key)
+{
+ GDBM_FILE db;
+ int r;
+ datum d_key;
+
+ if (!dbpath || !*dbpath || !key || !*key) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ db = open_gdbm(dbpath);
+ if (!db)
+ return -1;
+
+ d_key.dptr = (char *)key;
+ d_key.dsize = strlen(key) + 1;
+
+ r = gdbm_delete(db, d_key);
+ if (r) {
+ switch (gdbm_errno) {
+ case GDBM_READER_CANT_DELETE:
+ errno = EROFS;
+ break;
+ case GDBM_ITEM_NOT_FOUND:
+ errno = ENOENT;
+ break;
+ default:
+ errno = EIO;
+ break;
+ }
+
+ return -1;
+ }
+
+ bxt_dbg("Unset '%s' Key '%s'", dbpath, key);
+
+ return 0;
+}
+
+static int list_keys(const char *dbpath, char ***keys, unsigned int *klen)
+{
+ GDBM_FILE db;
+ GList *list;
+ GList *l;
+ datum d_key;
+ int i;
+ unsigned int _klen;
+ char **_keys;
+
+ if (!dbpath || !*dbpath || !keys) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ db = open_gdbm(dbpath);
+ if (!db)
+ return -1;
+
+ _klen = 0;
+ list = NULL;
+ d_key = gdbm_firstkey(db);
+ while (d_key.dptr) {
+ list = g_list_append(list, d_key.dptr);
+ _klen++;
+ d_key = gdbm_nextkey(db, d_key);
+ }
+
+ /* +1 for NULL terminated */
+ _keys = malloc(sizeof(void *) * (_klen + 1));
+ if (!_keys) {
+ g_list_free_full(list, (GDestroyNotify)free);
+ errno = ENOMEM;
+ return -1;
+ }
+
+ for (i = 0, l = list; l && i < _klen; l = g_list_next(l), i++)
+ _keys[i] = l->data;
+
+ /* NULL terminated */
+ _keys[i] = NULL;
+
+ g_list_free(list);
+
+ *keys = _keys;
+
+ if (klen)
+ *klen = _klen;
+
+ bxt_dbg("List '%s'", dbpath);
+
+ return 0;
+}
+
+static void module_exit(void)
+{
+ g_hash_table_destroy(dbs);
+ dbs = NULL;
+}
+
+static int module_init(void)
+{
+ dbs = g_hash_table_new_full(g_str_hash, g_str_equal,
+ (GDestroyNotify)free, (GDestroyNotify)free_db);
+ if (!dbs) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ return 0;
+}
+
+DEFINE_BUXTON_BACKEND = {
+ .name = "gdbm",
+
+ .module_init = module_init,
+ .module_exit = module_exit,
+
+ .open_db = open_db,
+ .remove_db = remove_db,
+ .set_value = set_value,
+ .get_value = get_value,
+ .unset_value = unset_value,
+ .list_keys = list_keys,
+};
+
diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt
new file mode 100644
index 0000000..6cc6335
--- /dev/null
+++ b/client/CMakeLists.txt
@@ -0,0 +1,21 @@
+# buxtonctl build
+INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/lib/include)
+
+SET(TARGET "buxton2ctl")
+SET(SRC c_main.c
+ c_common.c
+ c_proc.c
+ c_direct.c
+ ../common/common.c
+ ../common/direct.c
+ ../common/backends.c
+ ../common/config.c
+ ../common/serialize.c
+)
+ADD_EXECUTABLE(${TARGET} ${SRC})
+SET_TARGET_PROPERTIES(${TARGET} PROPERTIES
+ LINK_FLAGS "-fPIE"
+)
+TARGET_LINK_LIBRARIES(${TARGET} ${PKG_LDFLAGS} buxton2 -ldl)
+INSTALL(TARGETS ${TARGET} DESTINATION bin)
+
diff --git a/client/c_common.c b/client/c_common.c
new file mode 100644
index 0000000..04b61ce
--- /dev/null
+++ b/client/c_common.c
@@ -0,0 +1,162 @@
+/*
+ * Buxton
+ *
+ * Copyright (C) 2015 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#include "common.h"
+
+#include "c_log.h"
+#include "c_common.h"
+
+static const char const *type_names[BUXTON_TYPE_MAX] = {
+ [BUXTON_TYPE_UNKNOWN] = "Unknown",
+ [BUXTON_TYPE_STRING] = "String",
+ [BUXTON_TYPE_INT32] = "Int32",
+ [BUXTON_TYPE_UINT32] = "UInt32",
+ [BUXTON_TYPE_INT64] = "Int64",
+ [BUXTON_TYPE_UINT64] = "UInt64",
+ [BUXTON_TYPE_DOUBLE] = "Double",
+ [BUXTON_TYPE_BOOLEAN] = "Boolean",
+};
+
+void c_print_value(const struct buxton_layer *layer,
+ const char *key, const struct buxton_value *val)
+{
+ const char *lnm;
+
+ lnm = layer ? layer->name : NULL;
+
+ printf("[%s] %s = ", lnm ? lnm : "''", key ? key : "''");
+
+ if (!val) {
+ printf("NULL\n");
+ return;
+ }
+
+ switch (val->type) {
+ case BUXTON_TYPE_STRING:
+ printf("%s: %s\n", type_names[val->type], val->value.s);
+ break;
+ case BUXTON_TYPE_INT32:
+ printf("%s: %d\n", type_names[val->type], val->value.i);
+ break;
+ case BUXTON_TYPE_UINT32:
+ printf("%s: %u\n", type_names[val->type], val->value.u);
+ break;
+ case BUXTON_TYPE_INT64:
+ printf("%s: %" PRId64 "\n", type_names[val->type],
+ val->value.i64);
+ break;
+ case BUXTON_TYPE_UINT64:
+ printf("%s: %" PRIu64 "\n", type_names[val->type],
+ val->value.u64);
+ break;
+ case BUXTON_TYPE_DOUBLE:
+ printf("%s: %lf\n", type_names[val->type], val->value.d);
+ break;
+ case BUXTON_TYPE_BOOLEAN:
+ printf("%s: %s\n", type_names[val->type],
+ val->value.b ? "True" : "False");
+ break;
+ default:
+ printf("Unknown type\n");
+ break;
+ }
+}
+
+void c_print_priv(const struct buxton_layer *layer, const char *key,
+ enum buxton_priv_type type, const char *priv)
+{
+ const char *tnm;
+ const char *lnm;
+
+ lnm = layer ? layer->name : NULL;
+
+ switch (type) {
+ case BUXTON_PRIV_READ:
+ tnm = "Read";
+ break;
+ case BUXTON_PRIV_WRITE:
+ tnm = "Write";
+ break;
+ default:
+ tnm = "Unknown";
+ break;
+ }
+
+ printf("[%s] %s - %s: '%s'\n", lnm ? lnm : "''", key ? key : "''",
+ tnm, priv ? priv : "");
+}
+
+int c_set_value(enum buxton_key_type type, const char *value,
+ struct buxton_value *val)
+{
+ char *end;
+ struct buxton_value _val;
+
+ if (!value || !val) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ memset(&_val, 0, sizeof(_val));
+ _val.type = type;
+
+ errno = 0;
+ end = NULL;
+ switch (_val.type) {
+ case BUXTON_TYPE_STRING:
+ _val.value.s = (char *)value;
+ break;
+ case BUXTON_TYPE_INT32:
+ _val.value.i = (int32_t)strtol(value, &end, 0);
+ break;
+ case BUXTON_TYPE_UINT32:
+ _val.value.u = (uint32_t)strtoul(value, &end, 0);
+ break;
+ case BUXTON_TYPE_INT64:
+ _val.value.i64 = strtoll(value, &end, 0);
+ break;
+ case BUXTON_TYPE_UINT64:
+ _val.value.u64 = strtoull(value, &end, 0);
+ break;
+ case BUXTON_TYPE_DOUBLE:
+ _val.value.d = strtod(value, &end);
+ break;
+ case BUXTON_TYPE_BOOLEAN:
+ _val.value.b = !!strtol(value, &end, 0);
+ break;
+ default:
+ bxt_err("Set: Unknown type: %d", type);
+ return -1;
+ }
+
+ if (errno || ((end && *end != '\0'))) {
+ bxt_err("Set: '%s': Invalid number", value);
+ return -1;
+ }
+
+ *val = _val;
+
+ return 0;
+}
+
diff --git a/client/c_common.h b/client/c_common.h
new file mode 100644
index 0000000..a90dbb1
--- /dev/null
+++ b/client/c_common.h
@@ -0,0 +1,36 @@
+/*
+ * Buxton
+ *
+ * Copyright (C) 2015 Samsung Electronics Co., Ltd.
+ *
+ * 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
+
+#include <errno.h>
+#include <string.h>
+
+#include "buxton2.h"
+
+#include "c_log.h"
+
+void c_print_value(const struct buxton_layer *layer,
+ const char *key, const struct buxton_value *val);
+
+void c_print_priv(const struct buxton_layer *layer,
+ const char *key, enum buxton_priv_type type, const char *priv);
+
+int c_set_value(enum buxton_key_type type, const char *value,
+ struct buxton_value *val);
+
diff --git a/client/c_direct.c b/client/c_direct.c
new file mode 100644
index 0000000..f54ab4a
--- /dev/null
+++ b/client/c_direct.c
@@ -0,0 +1,490 @@
+/*
+ * Buxton
+ *
+ * Copyright (C) 2015 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <pwd.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "common.h"
+#include "direct.h"
+
+#include "c_log.h"
+#include "c_common.h"
+#include "c_direct.h"
+
+static const char *confpath;
+
+void c_direct_set_conf(const char *conf)
+{
+ if (!conf || !*conf) {
+ bxt_err("Invalid config path. Default path is used.");
+ return;
+ }
+
+ confpath = conf;
+}
+
+static void change_user(const char *name)
+{
+ struct passwd pwd;
+ struct passwd *result;
+ char *buf;
+ size_t bufsize;
+ int r;
+
+ bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
+ if (bufsize == -1) {
+ bxt_err("sysconf: _SC_GETPW_R_SIZE_MAX errno %d", errno);
+ return;
+ }
+
+ buf = malloc(bufsize);
+ if (buf == NULL)
+ return;
+
+ r = getpwnam_r(name, &pwd, buf, bufsize, &result);
+
+ free(buf);
+
+ if (r != 0) {
+ bxt_err("getpwnam_r: '%s' errno %d", name, errno);
+ return;
+ }
+
+ if (result == NULL) {
+ bxt_err("getpwnam_r: '%s' not exist", name);
+ return;
+ }
+
+ r = setuid(pwd.pw_uid);
+ if (r == -1)
+ bxt_err("setuid: errno %d", errno);
+}
+
+static void c_exit(void)
+{
+ direct_exit();
+}
+
+static int c_init(void)
+{
+ int r;
+
+ /* TODO: configurable */
+ change_user("buxton");
+
+ r = direct_init(MODULE_DIR, confpath ? confpath : CONFPATH);
+ if (r == -1) {
+ bxt_err("Init: %s", strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+int c_direct_get(const struct buxton_layer *layer,
+ const char *key, UNUSED const char *value,
+ UNUSED const char *rpriv, UNUSED const char *wpriv)
+{
+ int r;
+ struct buxton_value val;
+
+ if (!layer || !key || !*key) {
+ errno = EINVAL;
+ bxt_err("Get: Layer '%s' Key '%s': %s",
+ layer ? buxton_layer_get_name(layer) : "",
+ key ? key : "", strerror(errno));
+ return -1;
+ }
+
+ r = c_init();
+ if (r == -1)
+ return -1;
+
+ r = direct_get(layer, key, &val);
+
+ c_exit();
+
+ if (r == -1) {
+ bxt_err("Get: Layer '%s' Key '%s': %s",
+ buxton_layer_get_name(layer), key,
+ strerror(errno));
+ return -1;
+ }
+
+ c_print_value(layer, key, &val);
+
+ value_free(&val);
+
+ return 0;
+}
+
+static int c_direct_set(const struct buxton_layer *layer,
+ const char *key, const char *value, enum buxton_key_type type)
+{
+ int r;
+ struct buxton_value val;
+
+ if (!layer || !key || !*key || !value) {
+ errno = EINVAL;
+ bxt_err("Set: Layer '%s' Key '%s' Value '%s': %s",
+ layer ? buxton_layer_get_name(layer) : "",
+ key ? key : "", value ? value : "",
+ strerror(errno));
+ return -1;
+ }
+
+ r = c_set_value(type, value, &val);
+ if (r == -1)
+ return -1;
+
+ r = c_init();
+ if (r == -1)
+ return -1;
+
+ r = direct_set(layer, key, &val);
+
+ c_exit();
+
+ if (r == -1) {
+ bxt_err("Set: Layer '%s' Key '%s' Value '%s': %s",
+ buxton_layer_get_name(layer), key, value,
+ strerror(errno));
+ }
+
+ return r;
+}
+
+int c_direct_set_str(const struct buxton_layer *layer,
+ const char *key, const char *value,
+ UNUSED const char *rpriv, UNUSED const char *wpriv)
+{
+ return c_direct_set(layer, key, value, BUXTON_TYPE_STRING);
+}
+
+int c_direct_set_int32(const struct buxton_layer *layer,
+ const char *key, const char *value,
+ UNUSED const char *rpriv, UNUSED const char *wpriv)
+{
+ return c_direct_set(layer, key, value, BUXTON_TYPE_INT32);
+}
+
+int c_direct_set_uint32(const struct buxton_layer *layer,
+ const char *key, const char *value,
+ UNUSED const char *rpriv, UNUSED const char *wpriv)
+{
+ return c_direct_set(layer, key, value, BUXTON_TYPE_UINT32);
+}
+
+int c_direct_set_int64(const struct buxton_layer *layer,
+ const char *key, const char *value,
+ UNUSED const char *rpriv, UNUSED const char *wpriv)
+{
+ return c_direct_set(layer, key, value, BUXTON_TYPE_INT64);
+}
+
+int c_direct_set_uint64(const struct buxton_layer *layer,
+ const char *key, const char *value,
+ UNUSED const char *rpriv, UNUSED const char *wpriv)
+{
+ return c_direct_set(layer, key, value, BUXTON_TYPE_UINT64);
+}
+
+int c_direct_set_double(const struct buxton_layer *layer,
+ const char *key, const char *value,
+ UNUSED const char *rpriv, UNUSED const char *wpriv)
+{
+ return c_direct_set(layer, key, value, BUXTON_TYPE_DOUBLE);
+}
+
+int c_direct_set_bool(const struct buxton_layer *layer,
+ const char *key, const char *value,
+ UNUSED const char *rpriv, UNUSED const char *wpriv)
+{
+ return c_direct_set(layer, key, value, BUXTON_TYPE_BOOLEAN);
+}
+
+
+static int c_direct_create(const struct buxton_layer *layer,
+ const char *key, const char *value, enum buxton_key_type type,
+ const char *rpriv, const char *wpriv)
+{
+ int r;
+ struct buxton_value val;
+
+ if (!layer || !key || !*key || !value || !rpriv || !wpriv) {
+ errno = EINVAL;
+ bxt_err("Create: '%s' '%s' '%s' Priv '%s' '%s': %s",
+ layer ? buxton_layer_get_name(layer) : "",
+ key ? key : "", value ? value : "",
+ rpriv ? rpriv : "", wpriv ? wpriv : "",
+ strerror(errno));
+ return -1;
+ }
+
+ r = c_set_value(type, value, &val);
+ if (r == -1)
+ return -1;
+
+ r = c_init();
+ if (r == -1)
+ return -1;
+
+ r = direct_create(layer, key, rpriv, wpriv, &val);
+
+ c_exit();
+
+ if (r == -1) {
+ bxt_err("Create: '%s' '%s' '%s' Priv '%s' '%s': %s",
+ buxton_layer_get_name(layer), key, value,
+ rpriv, wpriv, strerror(errno));
+ }
+
+ return r;
+}
+
+int c_direct_create_str(const struct buxton_layer *layer,
+ const char *key, const char *value,
+ const char *rpriv, const char *wpriv)
+{
+ return c_direct_create(layer, key, value, BUXTON_TYPE_STRING,
+ rpriv, wpriv);
+}
+
+int c_direct_create_int32(const struct buxton_layer *layer,
+ const char *key, const char *value,
+ const char *rpriv, const char *wpriv)
+{
+ return c_direct_create(layer, key, value, BUXTON_TYPE_INT32,
+ rpriv, wpriv);
+}
+
+int c_direct_create_uint32(const struct buxton_layer *layer,
+ const char *key, const char *value,
+ const char *rpriv, const char *wpriv)
+{
+ return c_direct_create(layer, key, value, BUXTON_TYPE_UINT32,
+ rpriv, wpriv);
+}
+
+int c_direct_create_int64(const struct buxton_layer *layer,
+ const char *key, const char *value,
+ const char *rpriv, const char *wpriv)
+{
+ return c_direct_create(layer, key, value, BUXTON_TYPE_INT64,
+ rpriv, wpriv);
+}
+
+int c_direct_create_uint64(const struct buxton_layer *layer,
+ const char *key, const char *value,
+ const char *rpriv, const char *wpriv)
+{
+ return c_direct_create(layer, key, value, BUXTON_TYPE_UINT64,
+ rpriv, wpriv);
+}
+
+int c_direct_create_double(const struct buxton_layer *layer,
+ const char *key, const char *value,
+ const char *rpriv, const char *wpriv)
+{
+ return c_direct_create(layer, key, value, BUXTON_TYPE_DOUBLE,
+ rpriv, wpriv);
+}
+
+int c_direct_create_bool(const struct buxton_layer *layer,
+ const char *key, const char *value,
+ const char *rpriv, const char *wpriv)
+{
+ return c_direct_create(layer, key, value, BUXTON_TYPE_BOOLEAN,
+ rpriv, wpriv);
+}
+
+static int c_direct_get_priv(const struct buxton_layer *layer,
+ const char *key, enum buxton_priv_type type)
+{
+ int r;
+ char *priv;
+
+ if (!layer || !key || !*key) {
+ errno = EINVAL;
+ bxt_err("Get-priv: Layer '%s' Key '%s': %s",
+ layer ? buxton_layer_get_name(layer) : "",
+ key ? key : "",
+ strerror(errno));
+ return -1;
+ }
+
+ r = c_init();
+ if (r == -1)
+ return r;
+
+ r = direct_get_priv(layer, key, type, &priv);
+
+ c_exit();
+
+ if (r == -1) {
+ bxt_err("Get-priv: Layer '%s' Key '%s': %s",
+ buxton_layer_get_name(layer), key,
+ strerror(errno));
+ return -1;
+ }
+
+ c_print_priv(layer, key, type, priv);
+ free(priv);
+
+ return r;
+}
+
+int c_direct_get_rpriv(const struct buxton_layer *layer,
+ const char *key, UNUSED const char *value,
+ UNUSED const char *rpriv, UNUSED const char *wpriv)
+{
+ return c_direct_get_priv(layer, key, BUXTON_PRIV_READ);
+}
+
+int c_direct_get_wpriv(const struct buxton_layer *layer,
+ const char *key, UNUSED const char *value,
+ UNUSED const char *rpriv, UNUSED const char *wpriv)
+{
+ return c_direct_get_priv(layer, key, BUXTON_PRIV_WRITE);
+}
+
+static int c_direct_set_priv(const struct buxton_layer *layer,
+ const char *key, const char *priv, enum buxton_priv_type type)
+{
+ int r;
+
+ if (!layer || !key || !*key || !priv) {
+ errno = EINVAL;
+ bxt_err("Set-priv: Layer '%s' Key '%s' Priv. '%s': %s",
+ layer ? buxton_layer_get_name(layer) : "",
+ key ? key : "", priv ? priv : "",
+ strerror(errno));
+ return -1;
+ }
+
+ r = c_init();
+ if (r == -1)
+ return -1;
+
+ r = direct_set_priv(layer, key, type, priv);
+
+ c_exit();
+
+ if (r == -1) {
+ bxt_err("Set-priv: Layer '%s' Key '%s' Priv. '%s': %s",
+ buxton_layer_get_name(layer), key, priv,
+ strerror(errno));
+ }
+
+ return r;
+}
+
+int c_direct_set_rpriv(const struct buxton_layer *layer,
+ const char *key, const char *value,
+ UNUSED const char *rpriv, UNUSED const char *wpriv)
+{
+ return c_direct_set_priv(layer, key, value, BUXTON_PRIV_READ);
+}
+
+int c_direct_set_wpriv(const struct buxton_layer *layer,
+ const char *key, const char *value,
+ UNUSED const char *rpriv, UNUSED const char *wpriv)
+{
+ return c_direct_set_priv(layer, key, value, BUXTON_PRIV_WRITE);
+}
+
+int c_direct_unset(const struct buxton_layer *layer,
+ const char *key, UNUSED const char *value,
+ UNUSED const char *rpriv, UNUSED const char *wpriv)
+{
+ int r;
+
+ if (!layer || !key || !*key) {
+ errno = EINVAL;
+ bxt_err("Unset: Layer '%s' Key '%s': %s",
+ layer ? buxton_layer_get_name(layer) : "",
+ key ? key : "", strerror(errno));
+ return -1;
+ }
+
+ r = c_init();
+ if (r == -1)
+ return -1;
+
+ r = direct_unset(layer, key);
+
+ c_exit();
+
+ if (r == -1) {
+ bxt_err("Unset: Layer '%s' Key '%s': %s",
+ buxton_layer_get_name(layer), key,
+ strerror(errno));
+ }
+
+ return r;
+}
+
+int c_direct_list(const struct buxton_layer *layer,
+ UNUSED const char *key, UNUSED const char *value,
+ UNUSED const char *rpriv, UNUSED const char *wpriv)
+{
+ int r;
+ char **keys;
+ char **k;
+
+ if (!layer) {
+ errno = EINVAL;
+ bxt_err("List: Layer '%s': %s",
+ layer ? buxton_layer_get_name(layer) : "",
+ strerror(errno));
+ return -1;
+ }
+
+ r = c_init();
+ if (r == -1)
+ return -1;
+
+ r = direct_list(layer, &keys, NULL);
+
+ c_exit();
+
+ if (r == -1) {
+ bxt_err("List: Layer '%s': %s", buxton_layer_get_name(layer),
+ strerror(errno));
+ return -1;
+ }
+
+ k = keys;
+ while (k && *k) {
+ printf("%s\n", *k);
+ k++;
+ }
+
+ buxton_free_keys(keys);
+
+ return 0;
+}
+
diff --git a/client/c_direct.h b/client/c_direct.h
new file mode 100644
index 0000000..0fdc7f8
--- /dev/null
+++ b/client/c_direct.h
@@ -0,0 +1,93 @@
+/*
+ * Buxton
+ *
+ * Copyright (C) 2015 Samsung Electronics Co., Ltd.
+ *
+ * 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
+
+#ifndef UNUSED
+# define UNUSED __attribute__((unused))
+#endif
+
+#include "buxton2.h"
+
+void c_direct_set_conf(const char *conf);
+
+int c_direct_get(const struct buxton_layer *layer,
+ const char *key, UNUSED const char *value,
+ UNUSED const char *rpriv, UNUSED const char *wpriv);
+int c_direct_set_str(const struct buxton_layer *layer,
+ const char *key, const char *value,
+ UNUSED const char *rpriv, UNUSED const char *wpriv);
+int c_direct_set_int32(const struct buxton_layer *layer,
+ const char *key, const char *value,
+ UNUSED const char *rpriv, UNUSED const char *wpriv);
+int c_direct_set_uint32(const struct buxton_layer *layer,
+ const char *key, const char *value,
+ UNUSED const char *rpriv, UNUSED const char *wpriv);
+int c_direct_set_int64(const struct buxton_layer *layer,
+ const char *key, const char *value,
+ UNUSED const char *rpriv, UNUSED const char *wpriv);
+int c_direct_set_uint64(const struct buxton_layer *layer,
+ const char *key, const char *value,
+ UNUSED const char *rpriv, UNUSED const char *wpriv);
+int c_direct_set_double(const struct buxton_layer *layer,
+ const char *key, const char *value,
+ UNUSED const char *rpriv, UNUSED const char *wpriv);
+int c_direct_set_bool(const struct buxton_layer *layer,
+ const char *key, const char *value,
+ UNUSED const char *rpriv, UNUSED const char *wpriv);
+int c_direct_get_rpriv(const struct buxton_layer *layer,
+ const char *key, UNUSED const char *value,
+ UNUSED const char *rpriv, UNUSED const char *wpriv);
+int c_direct_set_rpriv(const struct buxton_layer *layer,
+ const char *key, const char *value,
+ UNUSED const char *rpriv, UNUSED const char *wpriv);
+int c_direct_get_wpriv(const struct buxton_layer *layer,
+ const char *key, UNUSED const char *value,
+ UNUSED const char *rpriv, UNUSED const char *wpriv);
+int c_direct_set_wpriv(const struct buxton_layer *layer,
+ const char *key, const char *value,
+ UNUSED const char *rpriv, UNUSED const char *wpriv);
+int c_direct_unset(const struct buxton_layer *layer,
+ const char *key, UNUSED const char *value,
+ UNUSED const char *rpriv, UNUSED const char *wpriv);
+int c_direct_list(const struct buxton_layer *layer,
+ UNUSED const char *key, UNUSED const char *value,
+ UNUSED const char *rpriv, UNUSED const char *wpriv);
+
+int c_direct_create_str(const struct buxton_layer *layer,
+ const char *key, const char *value,
+ const char *rpriv, const char *wpriv);
+int c_direct_create_int32(const struct buxton_layer *layer,
+ const char *key, const char *value,
+ const char *rpriv, const char *wpriv);
+int c_direct_create_uint32(const struct buxton_layer *layer,
+ const char *key, const char *value,
+ const char *rpriv, const char *wpriv);
+int c_direct_create_int64(const struct buxton_layer *layer,
+ const char *key, const char *value,
+ const char *rpriv, const char *wpriv);
+int c_direct_create_uint64(const struct buxton_layer *layer,
+ const char *key, const char *value,
+ const char *rpriv, const char *wpriv);
+int c_direct_create_double(const struct buxton_layer *layer,
+ const char *key, const char *value,
+ const char *rpriv, const char *wpriv);
+int c_direct_create_bool(const struct buxton_layer *layer,
+ const char *key, const char *value,
+ const char *rpriv, const char *wpriv);
+
diff --git a/client/c_log.h b/client/c_log.h
new file mode 100644
index 0000000..c33443e
--- /dev/null
+++ b/client/c_log.h
@@ -0,0 +1,36 @@
+/*
+ * Buxton
+ *
+ * Copyright (C) 2015 Samsung Electronics Co., Ltd.
+ *
+ * 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
+
+#if defined(NDEBUG)
+
+# define bxt_err(fmt, ...) fprintf(stderr, fmt "\n", ##__VA_ARGS__)
+# define bxt_dbg(fmt, ...) do { } while (0)
+
+#else
+
+# define bxt_err(fmt, ...) \
+ fprintf(stderr, "Err:%s:%d " fmt "\n", __func__, __LINE__, \
+ ##__VA_ARGS__)
+# define bxt_dbg(fmt, ...) \
+ printf("Debug:%s:%d " fmt "\n", __func__, __LINE__, \
+ ##__VA_ARGS__)
+
+#endif
+
diff --git a/client/c_main.c b/client/c_main.c
new file mode 100644
index 0000000..e6c046e
--- /dev/null
+++ b/client/c_main.c
@@ -0,0 +1,429 @@
+/*
+ * Buxton
+ *
+ * Copyright (C) 2015 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <string.h>
+#include <getopt.h>
+#include <assert.h>
+
+#include <glib.h>
+
+#include "buxton2.h"
+
+#include "c_log.h"
+#include "c_proc.h"
+#include "c_direct.h"
+
+struct options {
+ char *confpath;
+ gboolean direct;
+ gboolean install;
+ gint uid;
+ gboolean help;
+};
+
+typedef int (*comm_func)(const struct buxton_layer *layer,
+ const char *key, const char *value,
+ const char *rpriv, const char *wpriv);
+
+struct command {
+ const char *name;
+ const char *summary;
+ int nargs;
+ const char *usage;
+ comm_func func;
+ comm_func dfunc;
+};
+
+static const struct command const commands[] = {
+ {
+ .name = "check",
+ .summary = "Check the availability of Buxton",
+ .nargs = 0,
+ .usage = "",
+ .func = c_check,
+ .dfunc = c_check,
+ },
+ {
+ .name = "get",
+ .summary = "Get a value by key",
+ .nargs = 2,
+ .usage = "LAYER KEY",
+ .func = c_get,
+ .dfunc = c_direct_get,
+ },
+ {
+ .name = "create-string",
+ .summary = "Create a key with a string value",
+ .nargs = 5,
+ .usage = "LAYER KEY VALUE READ_PRIV WRITE_PRIV",
+ .func = c_create_str,
+ .dfunc = c_direct_create_str,
+ },
+ {
+ .name = "create-int32",
+ .summary = "Create a key with an int32_t value",
+ .nargs = 5,
+ .usage = "LAYER KEY VALUE READ_PRIV WRITE_PRIV",
+ .func = c_create_int32,
+ .dfunc = c_direct_create_int32,
+ },
+ {
+ .name = "create-uint32",
+ .summary = "Create a key with an uint32_t value",
+ .nargs = 5,
+ .usage = "LAYER KEY VALUE READ_PRIV WRITE_PRIV",
+ .func = c_create_uint32,
+ .dfunc = c_direct_create_uint32,
+ },
+ {
+ .name = "create-int64",
+ .summary = "Create a key with an int64_t value",
+ .nargs = 5,
+ .usage = "LAYER KEY VALUE READ_PRIV WRITE_PRIV",
+ .func = c_create_int64,
+ .dfunc = c_direct_create_int64,
+ },
+ {
+ .name = "create-uint64",
+ .summary = "Create a key with an uint64_t value",
+ .nargs = 5,
+ .usage = "LAYER KEY VALUE READ_PRIV WRITE_PRIV",
+ .func = c_create_uint64,
+ .dfunc = c_direct_create_uint64,
+ },
+ {
+ .name = "create-double",
+ .summary = "Create a key with a double precision value",
+ .nargs = 5,
+ .usage = "LAYER KEY VALUE READ_PRIV WRITE_PRIV",
+ .func = c_create_double,
+ .dfunc = c_direct_create_double,
+ },
+ {
+ .name = "create-bool",
+ .summary = "Create a key with a boolean value",
+ .nargs = 5,
+ .usage = "LAYER KEY VALUE READ_PRIV WRITE_PRIV",
+ .func = c_create_bool,
+ .dfunc = c_direct_create_bool,
+ },
+ {
+ .name = "set-string",
+ .summary = "Set a key with a string value",
+ .nargs = 3,
+ .usage = "LAYER KEY VALUE",
+ .func = c_set_str,
+ .dfunc = c_direct_set_str,
+ },
+ {
+ .name = "set-int32",
+ .summary = "Set a key with an int32_t value",
+ .nargs = 3,
+ .usage = "LAYER KEY VALUE",
+ .func = c_set_int32,
+ .dfunc = c_direct_set_int32,
+ },
+ {
+ .name = "set-uint32",
+ .summary = "Set a key with an uint32_t value",
+ .nargs = 3,
+ .usage = "LAYER KEY VALUE",
+ .func = c_set_uint32,
+ .dfunc = c_direct_set_uint32,
+ },
+ {
+ .name = "set-int64",
+ .summary = "Set a key with an int64_t value",
+ .nargs = 3,
+ .usage = "LAYER KEY VALUE",
+ .func = c_set_int64,
+ .dfunc = c_direct_set_int64,
+ },
+ {
+ .name = "set-uint64",
+ .summary = "Set a key with an uint64_t value",
+ .nargs = 3,
+ .usage = "LAYER KEY VALUE",
+ .func = c_set_uint64,
+ .dfunc = c_direct_set_uint64,
+ },
+ {
+ .name = "set-double",
+ .summary = "Set a key with a double precision value",
+ .nargs = 3,
+ .usage = "LAYER KEY VALUE",
+ .func = c_set_double,
+ .dfunc = c_direct_set_double,
+ },
+ {
+ .name = "set-bool",
+ .summary = "Set a key with a boolean value",
+ .nargs = 3,
+ .usage = "LAYER KEY VALUE",
+ .func = c_set_bool,
+ .dfunc = c_direct_set_bool,
+ },
+ {
+ .name = "get-read-priv",
+ .summary = "Get a value's read privilege",
+ .nargs = 2,
+ .usage = "LAYER KEY",
+ .func = c_get_rpriv,
+ .dfunc = c_direct_get_rpriv,
+ },
+ {
+ .name = "set-read-priv",
+ .summary = "Set a value's read privilege",
+ .nargs = 3,
+ .usage = "LAYER KEY PRIVILEGE",
+ .func = c_set_rpriv,
+ .dfunc = c_direct_set_rpriv,
+ },
+ {
+ .name = "get-write-priv",
+ .summary = "Get a value's write privilege",
+ .nargs = 2,
+ .usage = "LAYER KEY",
+ .func = c_get_wpriv,
+ .dfunc = c_direct_get_wpriv,
+ },
+ {
+ .name = "set-write-priv",
+ .summary = "Set a value's write privilege",
+ .nargs = 3,
+ .usage = "LAYER KEY PRIVILEGE",
+ .func = c_set_wpriv,
+ .dfunc = c_direct_set_wpriv,
+ },
+ {
+ .name = "unset",
+ .summary = "Unset a value by key",
+ .nargs = 2,
+ .usage = "LAYER KEY",
+ .func = c_unset,
+ .dfunc = c_direct_unset,
+ },
+ {
+ .name = "list-keys",
+ .summary = "List the keys for a layer",
+ .nargs = 1,
+ .usage = "LAYER",
+ .func = c_list,
+ .dfunc = c_direct_list,
+ },
+};
+
+static const struct command *find_comm(const char *name)
+{
+ int i;
+
+ if (!name)
+ return NULL;
+
+ for (i = 0; i < sizeof(commands) / sizeof(commands[0]); i++) {
+ if (commands[i].name && !strcmp(name, commands[i].name))
+ return &commands[i];
+ }
+
+ return NULL;
+}
+
+static int get_layer(const char *lnm, uid_t uid, enum buxton_layer_type type,
+ struct buxton_layer **layer)
+{
+ struct buxton_layer *_layer;
+
+ if (lnm) {
+ _layer = buxton_create_layer(lnm);
+ if (!_layer) {
+ bxt_err("create layer '%s' error", lnm);
+ return -1;
+ }
+ } else {
+ _layer = NULL;
+ }
+
+ if (!_layer) {
+ *layer = NULL;
+ return 0;
+ }
+
+ if (uid == 0)
+ uid = getuid();
+
+ buxton_layer_set_uid(_layer, uid);
+ buxton_layer_set_type(_layer, type);
+
+ *layer = _layer;
+
+ return 0;
+}
+
+static void print_usage(const char *name, const struct command *comm)
+{
+ assert(name);
+ assert(comm);
+ printf(" Usage: %s [option] %s %s\n\n", name, comm->name, comm->usage);
+}
+
+static void usage(const char *name)
+{
+ int i;
+
+ printf(" Usage: %s [option] [command] ...\n\n", name);
+ printf(" Option:\n");
+ printf(" -c, --config-file=[FILE] Path to configuration file\n");
+ printf(" (direct access only)\n");
+ printf(" -d, --direct Directly access the database\n");
+ printf(" -i, --install Execute on BASE db used for\n");
+ printf(" the default value\n");
+ printf(" -u, --uid=[UID] Specify the UID\n");
+ printf(" -h, --help Display help and exit\n\n");
+ printf(" Command:\n");
+
+ for (i = 0; i < sizeof(commands) / sizeof(commands[0]); i++)
+ printf("%16s - %s\n", commands[i].name, commands[i].summary);
+
+ printf("\n");
+ printf(" Example:\n");
+ printf(" - Get value\n");
+ printf(" $ %s get system bluetooth/status\n", name);
+ printf(" - Set an empty string\n");
+ printf(" $ %s set-string system home/language \"\"\n", name);
+ printf(" - Set a negative value\n");
+ printf(" $ %s set-int32 system wifi/status -1\n", name);
+ printf("\n");
+
+ exit(EXIT_FAILURE);
+}
+
+static int parse_args(gint *argc, gchar ***argv, struct options *opt)
+{
+ GError *err;
+ gboolean b;
+ GOptionContext *optctx;
+ GOptionEntry entries[] = {
+ { "config-file", 'c', 0,
+ G_OPTION_ARG_STRING, &opt->confpath, NULL, NULL },
+ { "direct", 'd', 0,
+ G_OPTION_ARG_NONE, &opt->direct, NULL, NULL },
+ { "install", 'i', 0,
+ G_OPTION_ARG_NONE, &opt->install, NULL, NULL },
+ { "uid", 'u', 0,
+ G_OPTION_ARG_INT, &opt->uid, NULL, NULL },
+ { "help", 'h', 0,
+ G_OPTION_ARG_NONE, &opt->help, NULL, NULL },
+ { NULL }
+ };
+
+ assert(argc);
+ assert(argv);
+ assert(*argv);
+ assert(**argv);
+
+ optctx = g_option_context_new(NULL);
+ if (!optctx) {
+ bxt_err("option new error");
+ return -1;
+ }
+
+ g_option_context_add_main_entries(optctx, entries, NULL);
+ g_option_context_set_help_enabled(optctx, FALSE);
+ g_option_context_set_ignore_unknown_options(optctx, TRUE);
+
+ err = NULL;
+ b = g_option_context_parse(optctx, argc, argv, &err);
+ g_option_context_free(optctx);
+
+ if (!b) {
+ bxt_err("option parse error: %s", err->message);
+ usage(**argv);
+ g_clear_error(&err);
+ return -1;
+ }
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ int r;
+ const struct command *comm;
+ comm_func func;
+ struct buxton_layer *layer;
+ struct options opt = {
+ .confpath = NULL,
+ .direct = FALSE,
+ .install = FALSE,
+ .uid = 0,
+ .help = FALSE,
+ };
+
+ r = parse_args(&argc, &argv, &opt);
+ if (r == -1)
+ return EXIT_FAILURE;
+
+ if (opt.help || argc < 2) {
+ usage(argv[0]);
+ return EXIT_FAILURE;
+ }
+
+ comm = find_comm(argv[1]);
+ if (!comm) {
+ bxt_err("Unknown command '%s'", argv[1]);
+ return EXIT_FAILURE;
+ }
+
+ func = opt.direct ? comm->dfunc : comm->func;
+ if (!func) {
+ bxt_err("Command '%s' not supported", comm->name);
+ return EXIT_FAILURE;
+ }
+
+ if (argc - 2 < comm->nargs) {
+ print_usage(argv[0], comm);
+ return EXIT_FAILURE;
+ }
+
+ if (opt.direct && opt.confpath)
+ c_direct_set_conf(opt.confpath);
+
+ r = get_layer(argc > 2 ? argv[2] : NULL, (uid_t)opt.uid,
+ opt.install ? BUXTON_LAYER_BASE : BUXTON_LAYER_NORMAL,
+ &layer);
+ if (r == -1)
+ return EXIT_FAILURE;
+
+ assert(func);
+ r = func(layer, argc > 3 ? argv[3] : NULL,
+ argc > 4 ? argv[4] : NULL,
+ argc > 5 ? argv[5] : NULL,
+ argc > 6 ? argv[6] : NULL);
+
+ buxton_free_layer(layer);
+
+ if (r == -1)
+ return EXIT_FAILURE;
+
+ return EXIT_SUCCESS;
+}
+
diff --git a/client/c_proc.c b/client/c_proc.c
new file mode 100644
index 0000000..cb82d10
--- /dev/null
+++ b/client/c_proc.c
@@ -0,0 +1,463 @@
+/*
+ * Buxton
+ *
+ * Copyright (C) 2015 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+
+#include "buxton2.h"
+
+#include "common.h"
+
+#include "c_log.h"
+#include "c_common.h"
+#include "c_proc.h"
+
+static struct buxton_client *client;
+
+static void status_cb(enum buxton_status status, void *data)
+{
+ bxt_dbg("Status: %d", status);
+}
+
+static int _close(void)
+{
+ int r;
+
+ if (!client)
+ return 0;
+
+ r = buxton_close(client);
+ if (r == -1)
+ bxt_err("close: %s", strerror(errno));
+
+ client = NULL;
+
+ return r;
+}
+
+static int _open(void)
+{
+ int r;
+
+ if (client)
+ return 0;
+
+ r = buxton_open(&client, status_cb, NULL);
+ if (r == -1)
+ bxt_err("open: %s", strerror(errno));
+
+ return r;
+}
+
+int c_open(void)
+{
+ return _open();
+}
+
+int c_check(UNUSED const struct buxton_layer *layer,
+ UNUSED const char *key, UNUSED const char *value,
+ UNUSED const char *rpriv, UNUSED const char *wpriv)
+{
+ int r;
+ struct buxton_client *cli;
+
+ r = buxton_open(&cli, NULL, NULL);
+ if (r == -1) {
+ printf("Failed to connect the Buxton service\n");
+ return -1;
+ }
+
+ printf("Buxton service is available\n");
+
+ buxton_close(cli);
+
+ return 0;
+}
+
+int c_get(const struct buxton_layer *layer,
+ const char *key, UNUSED const char *value,
+ UNUSED const char *rpriv, UNUSED const char *wpriv)
+{
+ int r;
+ struct buxton_value *val;
+
+ if (!layer || !key || !*key) {
+ errno = EINVAL;
+ bxt_err("Get: Layer '%s' Key '%s': %s",
+ layer ? buxton_layer_get_name(layer) : "",
+ key ? key : "", strerror(errno));
+ return -1;
+ }
+
+ r = _open();
+ if (r == -1)
+ return -1;
+
+ r = buxton_get_value_sync(client, layer, key, &val);
+
+ _close();
+
+ if (r == -1) {
+ bxt_err("Get: Layer '%s' Key '%s': %s",
+ buxton_layer_get_name(layer), key,
+ strerror(errno));
+ return -1;
+ }
+
+ c_print_value(layer, key, val);
+
+ buxton_value_free(val);
+
+ return 0;
+}
+
+static int c_set(const struct buxton_layer *layer,
+ const char *key, const char *value, enum buxton_key_type type)
+{
+ int r;
+ struct buxton_value val;
+
+ if (!layer || !key || !*key || !value) {
+ errno = EINVAL;
+ bxt_err("Set: Layer '%s' Key '%s' Value '%s': %s",
+ layer ? buxton_layer_get_name(layer) : "",
+ key ? key : "", value ? value : "",
+ strerror(errno));
+ return -1;
+ }
+
+ r = c_set_value(type, value, &val);
+ if (r == -1)
+ return -1;
+
+ r = _open();
+ if (r == -1)
+ return -1;
+
+ r = buxton_set_value_sync(client, layer, key, &val);
+
+ _close();
+
+ if (r == -1) {
+ bxt_err("Set: Layer '%s' Key '%s' Value '%s': %s",
+ buxton_layer_get_name(layer), key, value,
+ strerror(errno));
+ }
+
+ return r;
+}
+
+int c_set_str(const struct buxton_layer *layer,
+ const char *key, const char *value,
+ UNUSED const char *rpriv, UNUSED const char *wpriv)
+{
+ return c_set(layer, key, value, BUXTON_TYPE_STRING);
+}
+
+int c_set_int32(const struct buxton_layer *layer,
+ const char *key, const char *value,
+ UNUSED const char *rpriv, UNUSED const char *wpriv)
+{
+ return c_set(layer, key, value, BUXTON_TYPE_INT32);
+}
+
+int c_set_uint32(const struct buxton_layer *layer,
+ const char *key, const char *value,
+ UNUSED const char *rpriv, UNUSED const char *wpriv)
+{
+ return c_set(layer, key, value, BUXTON_TYPE_UINT32);
+}
+
+int c_set_int64(const struct buxton_layer *layer,
+ const char *key, const char *value,
+ UNUSED const char *rpriv, UNUSED const char *wpriv)
+{
+ return c_set(layer, key, value, BUXTON_TYPE_INT64);
+}
+
+int c_set_uint64(const struct buxton_layer *layer,
+ const char *key, const char *value,
+ UNUSED const char *rpriv, UNUSED const char *wpriv)
+{
+ return c_set(layer, key, value, BUXTON_TYPE_UINT64);
+}
+
+int c_set_double(const struct buxton_layer *layer,
+ const char *key, const char *value,
+ UNUSED const char *rpriv, UNUSED const char *wpriv)
+{
+ return c_set(layer, key, value, BUXTON_TYPE_DOUBLE);
+}
+
+int c_set_bool(const struct buxton_layer *layer,
+ const char *key, const char *value,
+ UNUSED const char *rpriv, UNUSED const char *wpriv)
+{
+ return c_set(layer, key, value, BUXTON_TYPE_BOOLEAN);
+}
+
+static int c_create(const struct buxton_layer *layer, const char *key,
+ const char *value, enum buxton_key_type type,
+ const char *rpriv, const char *wpriv)
+{
+ int r;
+ struct buxton_value val;
+
+ if (!layer || !key || !*key || !value || !rpriv || !wpriv) {
+ errno = EINVAL;
+ bxt_err("Create: '%s' '%s' '%s' Priv '%s' '%s': %s",
+ layer ? buxton_layer_get_name(layer) : "",
+ key ? key : "", value ? value : "",
+ rpriv ? rpriv : "", wpriv ? wpriv : "",
+ strerror(errno));
+ return -1;
+ }
+
+ r = c_set_value(type, value, &val);
+ if (r == -1)
+ return -1;
+
+ r = _open();
+ if (r == -1)
+ return -1;
+
+ r = buxton_create_value_sync(client, layer, key, rpriv, wpriv, &val);
+
+ _close();
+
+ if (r == -1) {
+ bxt_err("Create: '%s' '%s' '%s' Priv '%s' '%s': %s",
+ buxton_layer_get_name(layer), key, value,
+ rpriv, wpriv, strerror(errno));
+ }
+
+ return r;
+}
+
+int c_create_str(const struct buxton_layer *layer, const char *key,
+ const char *value, const char *rpriv, const char *wpriv)
+{
+ return c_create(layer, key, value, BUXTON_TYPE_STRING, rpriv, wpriv);
+}
+
+int c_create_int32(const struct buxton_layer *layer, const char *key,
+ const char *value, const char *rpriv, const char *wpriv)
+{
+ return c_create(layer, key, value, BUXTON_TYPE_INT32, rpriv, wpriv);
+}
+
+int c_create_uint32(const struct buxton_layer *layer, const char *key,
+ const char *value, const char *rpriv, const char *wpriv)
+{
+ return c_create(layer, key, value, BUXTON_TYPE_UINT32, rpriv, wpriv);
+}
+
+int c_create_int64(const struct buxton_layer *layer, const char *key,
+ const char *value, const char *rpriv, const char *wpriv)
+{
+ return c_create(layer, key, value, BUXTON_TYPE_INT64, rpriv, wpriv);
+}
+
+int c_create_uint64(const struct buxton_layer *layer, const char *key,
+ const char *value, const char *rpriv, const char *wpriv)
+{
+ return c_create(layer, key, value, BUXTON_TYPE_UINT64, rpriv, wpriv);
+}
+
+int c_create_double(const struct buxton_layer *layer, const char *key,
+ const char *value, const char *rpriv, const char *wpriv)
+{
+ return c_create(layer, key, value, BUXTON_TYPE_DOUBLE, rpriv, wpriv);
+}
+
+int c_create_bool(const struct buxton_layer *layer, const char *key,
+ const char *value, const char *rpriv, const char *wpriv)
+{
+ return c_create(layer, key, value, BUXTON_TYPE_BOOLEAN, rpriv, wpriv);
+}
+
+static int c_get_priv(const struct buxton_layer *layer,
+ const char *key, enum buxton_priv_type type)
+{
+ int r;
+ char *priv;
+
+ if (!layer || !key || !*key) {
+ errno = EINVAL;
+ bxt_err("Get-priv: Layer '%s' Key '%s': %s",
+ layer ? buxton_layer_get_name(layer) : "",
+ key ? key : "", strerror(errno));
+ return -1;
+ }
+
+ r = _open();
+ if (r == -1)
+ return -1;
+
+ r = buxton_get_privilege_sync(client, layer, key, type, &priv);
+
+ _close();
+
+ if (r == -1) {
+ bxt_err("Get-priv: Layer '%s' Key '%s': %s",
+ buxton_layer_get_name(layer), key,
+ strerror(errno));
+ return -1;
+ }
+
+ c_print_priv(layer, key, type, priv);
+ free(priv);
+
+ return r;
+}
+
+int c_get_rpriv(const struct buxton_layer *layer,
+ const char *key, UNUSED const char *value,
+ UNUSED const char *rpriv, UNUSED const char *wpriv)
+{
+ return c_get_priv(layer, key, BUXTON_PRIV_READ);
+}
+
+int c_get_wpriv(const struct buxton_layer *layer,
+ const char *key, UNUSED const char *value,
+ UNUSED const char *rpriv, UNUSED const char *wpriv)
+{
+ return c_get_priv(layer, key, BUXTON_PRIV_WRITE);
+}
+
+int c_set_priv(const struct buxton_layer *layer,
+ const char *key, const char *priv, enum buxton_priv_type type)
+{
+ int r;
+
+ if (!layer || !key || !*key || !priv) {
+ errno = EINVAL;
+ bxt_err("Set-priv: Layer '%s' Key '%s' Priv '%s': %s",
+ layer ? buxton_layer_get_name(layer) : "",
+ key ? key : "", priv ? priv : "",
+ strerror(errno));
+ return -1;
+ }
+
+ r = _open();
+ if (r == -1)
+ return -1;
+
+ r = buxton_set_privilege_sync(client, layer, key, type, priv);
+
+ _close();
+
+ if (r == -1) {
+ bxt_err("Set-priv: Layer '%s' Key '%s' Priv '%s': %s",
+ buxton_layer_get_name(layer), key, priv,
+ strerror(errno));
+ }
+
+ return r;
+}
+
+int c_set_rpriv(const struct buxton_layer *layer,
+ const char *key, const char *value,
+ UNUSED const char *rpriv, UNUSED const char *wpriv)
+{
+ return c_set_priv(layer, key, value, BUXTON_PRIV_READ);
+}
+
+int c_set_wpriv(const struct buxton_layer *layer,
+ const char *key, const char *value,
+ UNUSED const char *rpriv, UNUSED const char *wpriv)
+{
+ return c_set_priv(layer, key, value, BUXTON_PRIV_WRITE);
+}
+
+int c_unset(const struct buxton_layer *layer,
+ const char *key, UNUSED const char *value,
+ UNUSED const char *rpriv, UNUSED const char *wpriv)
+{
+ int r;
+
+ if (!layer || !key || !*key) {
+ errno = EINVAL;
+ bxt_err("Unset: Layer '%s' Key '%s': %s",
+ layer ? buxton_layer_get_name(layer) : "",
+ key ? key : "", strerror(errno));
+ return -1;
+ }
+
+ r = _open();
+ if (r == -1)
+ return -1;
+
+ r = buxton_unset_value_sync(client, layer, key);
+
+ _close();
+
+ if (r == -1) {
+ bxt_err("Unset: Layer '%s' Key '%s': %s",
+ buxton_layer_get_name(layer), key,
+ strerror(errno));
+ }
+
+ return r;
+}
+
+int c_list(const struct buxton_layer *layer,
+ UNUSED const char *key, UNUSED const char *value,
+ UNUSED const char *rpriv, UNUSED const char *wpriv)
+{
+ int r;
+ char **keys;
+ char **k;
+
+ if (!layer) {
+ errno = EINVAL;
+ bxt_err("List: Layer '%s': %s",
+ layer ? buxton_layer_get_name(layer) : "",
+ strerror(errno));
+ return -1;
+ }
+
+ r = _open();
+ if (r == -1)
+ return -1;
+
+ r = buxton_list_keys_sync(client, layer, &keys, NULL);
+
+ _close();
+
+ if (r == -1) {
+ bxt_err("List: Layer '%s': %s", buxton_layer_get_name(layer),
+ strerror(errno));
+ return -1;
+ }
+
+ k = keys;
+ while (k && *k) {
+ printf("%s\n", *k);
+ k++;
+ }
+
+ buxton_free_keys(keys);
+
+ return 0;
+}
+
diff --git a/client/c_proc.h b/client/c_proc.h
new file mode 100644
index 0000000..53371c4
--- /dev/null
+++ b/client/c_proc.h
@@ -0,0 +1,89 @@
+/*
+ * Buxton
+ *
+ * Copyright (C) 2015 Samsung Electronics Co., Ltd.
+ *
+ * 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
+
+#ifndef UNUSED
+# define UNUSED __attribute__((unused))
+#endif
+
+#include "buxton2.h"
+
+int c_open(void);
+
+int c_check(UNUSED const struct buxton_layer *layer,
+ UNUSED const char *key, UNUSED const char *value,
+ UNUSED const char *rpriv, UNUSED const char *wpriv);
+int c_get(const struct buxton_layer *layer,
+ const char *key, UNUSED const char *value,
+ UNUSED const char *rpriv, UNUSED const char *wpriv);
+int c_set_str(const struct buxton_layer *layer,
+ const char *key, const char *value,
+ UNUSED const char *rpriv, UNUSED const char *wpriv);
+int c_set_int32(const struct buxton_layer *layer,
+ const char *key, const char *value,
+ UNUSED const char *rpriv, UNUSED const char *wpriv);
+int c_set_uint32(const struct buxton_layer *layer,
+ const char *key, const char *value,
+ UNUSED const char *rpriv, UNUSED const char *wpriv);
+int c_set_int64(const struct buxton_layer *layer,
+ const char *key, const char *value,
+ UNUSED const char *rpriv, UNUSED const char *wpriv);
+int c_set_uint64(const struct buxton_layer *layer,
+ const char *key, const char *value,
+ UNUSED const char *rpriv, UNUSED const char *wpriv);
+int c_set_double(const struct buxton_layer *layer,
+ const char *key, const char *value,
+ UNUSED const char *rpriv, UNUSED const char *wpriv);
+int c_set_bool(const struct buxton_layer *layer,
+ const char *key, const char *value,
+ UNUSED const char *rpriv, UNUSED const char *wpriv);
+int c_get_rpriv(const struct buxton_layer *layer,
+ const char *key, UNUSED const char *value,
+ UNUSED const char *rpriv, UNUSED const char *wpriv);
+int c_get_wpriv(const struct buxton_layer *layer,
+ const char *key, UNUSED const char *value,
+ UNUSED const char *rpriv, UNUSED const char *wpriv);
+int c_set_rpriv(const struct buxton_layer *layer,
+ const char *key, const char *value,
+ UNUSED const char *rpriv, UNUSED const char *wpriv);
+int c_set_wpriv(const struct buxton_layer *layer,
+ const char *key, const char *value,
+ UNUSED const char *rpriv, UNUSED const char *wpriv);
+int c_unset(const struct buxton_layer *layer,
+ const char *key, UNUSED const char *value,
+ UNUSED const char *rpriv, UNUSED const char *wpriv);
+int c_list(const struct buxton_layer *layer,
+ UNUSED const char *key, UNUSED const char *value,
+ UNUSED const char *rpriv, UNUSED const char *wpriv);
+
+int c_create_str(const struct buxton_layer *layer, const char *key,
+ const char *value, const char *rpriv, const char *wpriv);
+int c_create_int32(const struct buxton_layer *layer, const char *key,
+ const char *value, const char *rpriv, const char *wpriv);
+int c_create_uint32(const struct buxton_layer *layer, const char *key,
+ const char *value, const char *rpriv, const char *wpriv);
+int c_create_int64(const struct buxton_layer *layer, const char *key,
+ const char *value, const char *rpriv, const char *wpriv);
+int c_create_uint64(const struct buxton_layer *layer, const char *key,
+ const char *value, const char *rpriv, const char *wpriv);
+int c_create_double(const struct buxton_layer *layer, const char *key,
+ const char *value, const char *rpriv, const char *wpriv);
+int c_create_bool(const struct buxton_layer *layer, const char *key,
+ const char *value, const char *rpriv, const char *wpriv);
+
diff --git a/common/backend.h b/common/backend.h
new file mode 100644
index 0000000..7e04922
--- /dev/null
+++ b/common/backend.h
@@ -0,0 +1,74 @@
+/*
+ * Buxton
+ *
+ * Copyright (C) 2015 Samsung Electronics Co., Ltd.
+ *
+ * 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
+
+#include <stdlib.h>
+
+#ifndef EXPORT
+# define EXPORT __attribute__((visibility("default")))
+#endif
+
+typedef int (*backend_module_init)(void);
+typedef void (*backend_module_exit)(void);
+
+typedef int (*backend_open_db)(const char *dbpath);
+typedef int (*backend_remove_db)(const char *dbpath);
+typedef int (*backend_set_value)(const char *dbpath,
+ const char *key, const void *data, int dlen);
+typedef int (*backend_get_value)(const char *dbpath,
+ const char *key, void **data, int *dlen);
+typedef int (*backend_unset_value)(const char *dbpath, const char *key);
+typedef int (*backend_list_keys)(const char *dbpath,
+ char ***keys, unsigned int *klen);
+
+struct backend {
+ const char *name;
+
+ backend_module_init module_init;
+ backend_module_exit module_exit;
+
+ backend_open_db open_db;
+ backend_remove_db remove_db;
+ backend_set_value set_value;
+ backend_get_value get_value;
+ backend_unset_value unset_value;
+ backend_list_keys list_keys;
+
+ void *reserved[7];
+};
+
+static inline void backend_list_free(char **keys)
+{
+ char **k;
+
+ if (!keys)
+ return;
+
+ k = keys;
+ while (*k) {
+ free(*k);
+ k++;
+ }
+
+ free(keys);
+}
+
+#define BUXTON_BACKEND_SYMBOL "buxton_backend"
+#define DEFINE_BUXTON_BACKEND EXPORT const struct backend buxton_backend
+
diff --git a/common/backends.c b/common/backends.c
new file mode 100644
index 0000000..fbeaf53
--- /dev/null
+++ b/common/backends.c
@@ -0,0 +1,205 @@
+/*
+ * Buxton
+ *
+ * Copyright (C) 2015 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ */
+
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <dlfcn.h>
+
+#include <glib.h>
+
+#include "backend.h"
+#include "log.h"
+
+static GHashTable *backends;
+
+struct module {
+ void *handle;
+ const struct backend *backend;
+};
+
+static void free_backend(struct module *mod)
+{
+ if (!mod)
+ return;
+
+ if (mod->backend && mod->backend->module_exit)
+ mod->backend->module_exit();
+
+ if (mod->handle)
+ dlclose(mod->handle);
+
+ free(mod);
+}
+
+const struct backend *backend_get(const char *name)
+{
+ struct module *mod;
+
+ if (!name || !*name) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (!backends) {
+ errno = ENOENT;
+ return NULL;
+ }
+
+ mod = g_hash_table_lookup(backends, name);
+ if (!mod)
+ errno = ENOENT;
+
+ return mod->backend;
+}
+
+static struct module *load_module(const char *moddir, const char *modname)
+{
+ struct module *mod;
+ const struct backend *backend;
+ void *handle;
+ int r;
+ char modpath[FILENAME_MAX];
+
+ assert(moddir);
+ assert(modname);
+
+ snprintf(modpath, sizeof(modpath), "%s/%s", moddir, modname);
+
+ handle = dlopen(modpath, RTLD_NOW);
+ if (!handle) {
+ bxt_err("load '%s' error: %s", modpath, dlerror());
+ return NULL;
+ }
+
+ mod = calloc(1, sizeof(*mod));
+ if (!mod) {
+ bxt_err("load '%s' error: Not enough space", modpath);
+ goto err;
+ }
+
+ dlerror();
+ backend = dlsym(handle, BUXTON_BACKEND_SYMBOL);
+ if (!backend) {
+ bxt_err("load '%s' error: %s", modpath, dlerror());
+ goto err;
+ }
+
+ if (!backend->name || !*backend->name) {
+ bxt_err("load '%s' error: no name", modpath);
+ goto err;
+ }
+
+ if (!backend->module_init) {
+ bxt_err("load '%s' error: no init", modpath);
+ goto err;
+ }
+
+ r = backend->module_init();
+ if (r) {
+ bxt_err("load '%s' error: init: %d", modpath, errno);
+ goto err;
+ }
+
+ mod->handle = handle;
+ mod->backend = backend;
+
+ return mod;
+
+err:
+ dlclose(handle);
+ free(mod);
+ return NULL;
+}
+
+static int load_modules(const char *moddir)
+{
+ DIR *dir;
+ struct dirent *de;
+ char *ext;
+ struct module *mod;
+
+ assert(moddir);
+ assert(backends);
+
+ dir = opendir(moddir);
+ if (!dir) {
+ bxt_err("opendir error: %d", errno);
+ return -1;
+ }
+
+ while ((de = readdir(dir)) != NULL) {
+ ext = strrchr(de->d_name, '.');
+ if (!ext)
+ continue;
+
+ if (strncmp(ext, ".so", sizeof(".so")))
+ continue;
+
+ mod = load_module(moddir, de->d_name);
+ if (mod) {
+ g_hash_table_insert(backends,
+ (gpointer)mod->backend->name, mod);
+ }
+ }
+
+ closedir(dir);
+
+ return 0;
+}
+
+GList *backend_list(void)
+{
+ GList *list;
+
+ if (!backends)
+ return NULL;
+
+ list = g_hash_table_get_keys(backends);
+
+ return list;
+}
+
+void backend_exit(void)
+{
+ g_hash_table_destroy(backends);
+ backends = NULL;
+}
+
+int backend_init(const char *moddir)
+{
+ if (!moddir || !*moddir) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (backends)
+ return 0;
+
+ backends = g_hash_table_new_full(g_str_hash, g_str_equal,
+ NULL, (GDestroyNotify)free_backend);
+ if (!backends) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ return load_modules(moddir);
+}
+
diff --git a/common/backends.h b/common/backends.h
new file mode 100644
index 0000000..46cb1c6
--- /dev/null
+++ b/common/backends.h
@@ -0,0 +1,31 @@
+/*
+ * Buxton
+ *
+ * Copyright (C) 2015 Samsung Electronics Co., Ltd.
+ *
+ * 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
+
+#include <glib.h>
+
+#include "backend.h"
+
+int backend_init(const char *moddir);
+void backend_exit(void);
+const struct backend *backend_get(const char *name);
+
+/* Do not free the content of list. use g_list_free() */
+GList *backend_list(void);
+
diff --git a/common/common.c b/common/common.c
new file mode 100644
index 0000000..daf90fd
--- /dev/null
+++ b/common/common.c
@@ -0,0 +1,127 @@
+/*
+ * Buxton
+ *
+ * Copyright (C) 2015 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <errno.h>
+
+#include "common.h"
+
+struct buxton_layer *layer_create(const char *layer_name)
+{
+ struct buxton_layer *layer;
+
+ if (!layer_name || !*layer_name) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ layer = calloc(1, sizeof(*layer));
+ if (!layer)
+ return NULL;
+
+ layer->name = strdup(layer_name);
+ if (!layer->name) {
+ free(layer);
+ return NULL;
+ }
+
+ layer->refcnt = 1;
+ layer->uid = getuid();
+ layer->type = BUXTON_LAYER_NORMAL;
+
+ return layer;
+}
+
+/* ignore ref. count */
+void layer_free(struct buxton_layer *layer)
+{
+ if (!layer)
+ return;
+
+ free(layer->name);
+ free(layer);
+}
+
+struct buxton_layer *layer_ref(struct buxton_layer *layer)
+{
+ if (!layer)
+ return NULL;
+
+ layer->refcnt++;
+
+ return layer;
+}
+
+struct buxton_layer *layer_unref(struct buxton_layer *layer)
+{
+ if (!layer)
+ return NULL;
+
+ layer->refcnt--;
+ if (layer->refcnt == 0) {
+ layer_free(layer);
+ return NULL;
+ }
+
+ return layer;
+}
+
+void value_free(struct buxton_value *val)
+{
+ if (!val)
+ return;
+
+ switch (val->type) {
+ case BUXTON_TYPE_STRING:
+ free(val->value.s);
+ break;
+ default:
+ break;
+ }
+}
+
+char *get_search_key(const struct buxton_layer *layer, const char *key,
+ const char *uid)
+{
+ char *lykey;
+ int keylen;
+
+ if (!layer || !key || !*key) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ keylen = strlen(layer->name) + strlen(key) + 2;
+
+ if (uid)
+ keylen += strlen(uid) + 1;
+
+ lykey = malloc(keylen);
+ if (!lykey)
+ return NULL;
+
+ snprintf(lykey, keylen, "%s\t%s%s%s", layer->name, key,
+ uid ? "\t" : "", uid ? uid : "");
+
+ return lykey;
+}
+
diff --git a/common/common.h b/common/common.h
new file mode 100644
index 0000000..7d2838e
--- /dev/null
+++ b/common/common.h
@@ -0,0 +1,124 @@
+/*
+ * Buxton
+ *
+ * Copyright (C) 2015 Samsung Electronics Co., Ltd.
+ *
+ * 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
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <glib.h>
+
+#include "buxton2.h"
+
+#ifndef CONFPATH
+# warning "CONFPATH is not set. default value is used"
+# define CONFPATH "/etc/buxton.conf"
+#endif
+
+#ifndef MODULE_DIR
+# warning "MODULE_DIR is not set. default value is used"
+# define MODULE_DIR "/usr/lib/buxton"
+#endif
+
+#ifndef DB_DIR
+# warning "DB_DIR is not set. default value is used"
+# define DB_DIR "/var/lib/buxton"
+#endif
+
+#ifndef TMPFS_DIR
+# warning "TMPFS_DIR is not set. default value is used"
+# define TMPFS_DIR "/run/buxton"
+#endif
+
+#ifndef SOCKPATH
+# warning "SOCKPATH is not set. default value is used"
+# define SOCKPATH "/run/buxton-0"
+#endif
+
+enum layer_type {
+ LAYER_UNKNWON = 0,
+ LAYER_SYSTEM,
+ LAYER_USER,
+ LAYER_MAX, /* sentinel value */
+};
+
+enum storage_type {
+ STORAGE_UNKNOWN = 0,
+ STORAGE_PERSISTENT,
+ STORAGE_VOLATILE,
+ STORAGE_MAX, /* sentinel value */
+};
+
+struct layer {
+ gchar *name;
+ enum layer_type type;
+ gchar *backend;
+ enum storage_type storage;
+ gchar *description;
+};
+
+enum message_type {
+ MSG_UNKNOWN = 0,
+ /* basic request */
+ MSG_SET,
+ MSG_GET,
+ MSG_CREAT,
+ MSG_UNSET,
+ MSG_LIST,
+ MSG_NOTIFY,
+ MSG_UNNOTIFY,
+ MSG_NOTI,
+ /* privilege request */
+ MSG_SET_WP,
+ MSG_SET_RP,
+ MSG_GET_WP,
+ MSG_GET_RP,
+ MSG_MAX, /* sentinel value */
+};
+
+struct buxton_layer {
+ int refcnt;
+ char *name;
+ uid_t uid;
+ enum buxton_layer_type type;
+};
+
+struct buxton_layer *layer_create(const char *layer_name);
+void layer_free(struct buxton_layer *layer);
+
+struct buxton_layer *layer_ref(struct buxton_layer *layer);
+struct buxton_layer *layer_unref(struct buxton_layer *layer);
+
+struct buxton_value {
+ enum buxton_key_type type;
+ union {
+ char *s;
+ int32_t i;
+ uint32_t u;
+ int64_t i64;
+ uint64_t u64;
+ double d;
+ int32_t b;
+ } value;
+};
+
+void value_free(struct buxton_value *val);
+
+char *get_search_key(const struct buxton_layer *layer, const char *key,
+ const char *uid);
+
diff --git a/common/config.c b/common/config.c
new file mode 100644
index 0000000..db144d7
--- /dev/null
+++ b/common/config.c
@@ -0,0 +1,305 @@
+/*
+ * Buxton
+ *
+ * Copyright (C) 2015 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <errno.h>
+#include <assert.h>
+
+#include <glib.h>
+
+#include "log.h"
+#include "config.h"
+
+#define K_TYPE "Type"
+#define K_BACKEND "Backend"
+#define K_STORAGE "Storage"
+#define K_DESC "Description"
+
+#define K_T_SYSTEM "System"
+#define K_T_USER "User"
+
+#define K_B_DB "persistent"
+#define K_B_MEM "volatile"
+
+static GHashTable *layers;
+
+static void free_layer(struct layer *layer)
+{
+ if (!layer)
+ return;
+
+ g_free(layer->name);
+ g_free(layer->backend);
+ g_free(layer->description);
+ free(layer);
+}
+
+static GKeyFile *load_conf(const char *confpath)
+{
+ GKeyFile *kf;
+ gboolean b;
+ GError *err;
+
+ assert(confpath);
+
+ kf = g_key_file_new();
+ if (!kf) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ err = NULL;
+ b = g_key_file_load_from_file(kf, confpath, G_KEY_FILE_NONE, &err);
+ if (!b) {
+ bxt_err("Load '%s' error: %s", confpath,
+ err ? err->message : "");
+ g_clear_error(&err);
+ g_key_file_free(kf);
+ return NULL;
+ }
+
+ return kf;
+}
+
+static struct layer *create_layer(GKeyFile *kf, gchar *name)
+{
+ GError *err;
+ struct layer *layer;
+ gchar *s;
+
+ assert(name && *name);
+
+ layer = calloc(1, sizeof(*layer));
+ if (!layer)
+ goto err;
+
+ /* 'Type' */
+ err = NULL;
+ s = g_key_file_get_string(kf, name, K_TYPE, &err);
+ if (!s) {
+ bxt_err("Layer '%s' : %s",
+ name, err ? err->message : "");
+ g_clear_error(&err);
+ goto err;
+ }
+
+ if (!strncmp(s, K_T_SYSTEM, sizeof(K_T_SYSTEM)))
+ layer->type = LAYER_SYSTEM;
+ else if (!strncmp(s, K_T_USER, sizeof(K_T_USER)))
+ layer->type = LAYER_USER;
+
+ g_free(s);
+
+ /* 'Backend' */
+ s = g_key_file_get_string(kf, name, K_BACKEND, &err);
+ if (!s) {
+ bxt_err("Layer '%s' : %s",
+ name, err ? err->message : "");
+ g_clear_error(&err);
+ goto err;
+ }
+
+ layer->backend = s;
+
+ /* 'Storage' */
+ s = g_key_file_get_string(kf, name, K_STORAGE, &err);
+ if (!s) {
+ bxt_err("Layer '%s' : %s",
+ name, err ? err->message : "");
+ g_clear_error(&err);
+ goto err;
+ }
+
+ if (!strncasecmp(s, K_B_DB, sizeof(K_B_DB)))
+ layer->storage = STORAGE_PERSISTENT;
+ else if (!strncasecmp(s, K_B_MEM, sizeof(K_B_MEM)))
+ layer->storage = STORAGE_VOLATILE;
+
+ g_free(s);
+
+ /* 'Description' */
+ s = g_key_file_get_string(kf, name, K_DESC, &err);
+ if (!s) {
+ bxt_err("Layer '%s' : %s",
+ name, err ? err->message : "");
+ g_clear_error(&err);
+ goto err;
+ }
+
+ layer->description = s;
+
+ /* Layer name */
+ layer->name = name;
+
+ return layer;
+
+err:
+ g_free(name);
+ free_layer(layer);
+
+ return NULL;
+}
+
+static gboolean has_backend(GList *backends, const char *name)
+{
+ GList *l;
+
+ if (!name || !*name)
+ return FALSE;
+
+ for (l = backends; l; l = g_list_next(l)) {
+ const char *nm = l->data;
+
+ if (nm && !strcmp(name, nm))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void add_layers(GKeyFile *kf, GList *backends)
+{
+ gchar **lays;
+ int i;
+ struct layer *layer;
+ struct layer *f;
+ gboolean b;
+
+ assert(kf);
+ assert(layers);
+
+ lays = g_key_file_get_groups(kf, NULL);
+ if (!lays) {
+ bxt_err("No specified layers");
+ return;
+ }
+
+ i = 0;
+ while (lays[i]) {
+ layer = create_layer(kf, lays[i++]);
+ if (!layer)
+ continue;
+
+ b = has_backend(backends, layer->backend);
+ if (!b) {
+ bxt_err("Layer '%s' : invalid backend", layer->name);
+ free_layer(layer);
+ continue;
+ }
+
+ f = g_hash_table_lookup(layers, layer->name);
+ if (f) {
+ bxt_err("Layer '%s' : already exists", layer->name);
+ free_layer(layer);
+ continue;
+ }
+
+ if (layer->type == LAYER_UNKNWON) {
+ bxt_err("Layer '%s' : unknwon type", layer->name);
+ free_layer(layer);
+ continue;
+ }
+
+ if (layer->storage == STORAGE_UNKNOWN) {
+ bxt_err("Layer '%s' : unknwon storage type",
+ layer->name);
+ free_layer(layer);
+ continue;
+ }
+
+ g_hash_table_insert(layers, layer->name, layer);
+ }
+
+ g_free(lays);
+}
+
+const struct layer *conf_get_layer(const char *name)
+{
+ const struct layer *layer;
+
+ if (!name || !*name) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (!layers) {
+ errno = ENOENT;
+ return NULL;
+ }
+
+ layer = g_hash_table_lookup(layers, name);
+ if (!layer) {
+ bxt_dbg("Layer '%s' not exist", name);
+ errno = ENOENT;
+ }
+
+ return layer;
+}
+
+int conf_remove(const char *name)
+{
+ const struct layer *layer;
+ gboolean b;
+
+ layer = conf_get_layer(name);
+ if (!layer)
+ return -1;
+
+ b = g_hash_table_remove(layers, name);
+
+ return b ? 0 : -1;
+}
+
+void conf_exit(void)
+{
+ g_hash_table_destroy(layers);
+ layers = NULL;
+}
+
+int conf_init(const char *confpath, GList *backends)
+{
+ GKeyFile *kf;
+
+ if (!confpath || !*confpath) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (layers)
+ return 0;
+
+ kf = load_conf(confpath);
+ if (!kf)
+ return -1;
+
+ layers = g_hash_table_new_full(g_str_hash, g_str_equal,
+ NULL, (GDestroyNotify)free_layer);
+ if (!layers) {
+ errno = ENOMEM;
+ g_key_file_free(kf);
+ return -1;
+ }
+
+ add_layers(kf, backends);
+
+ g_key_file_free(kf);
+
+ return 0;
+}
diff --git a/common/config.h b/common/config.h
new file mode 100644
index 0000000..52bce85
--- /dev/null
+++ b/common/config.h
@@ -0,0 +1,30 @@
+/*
+ * Buxton
+ *
+ * Copyright (C) 2015 Samsung Electronics Co., Ltd.
+ *
+ * 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
+
+#include <glib.h>
+
+#include "common.h"
+
+int conf_init(const char *confpath, GList *backends);
+void conf_exit(void);
+
+const struct layer *conf_get_layer(const char *name);
+int conf_remove(const char *name);
+
diff --git a/common/direct.c b/common/direct.c
new file mode 100644
index 0000000..1725863
--- /dev/null
+++ b/common/direct.c
@@ -0,0 +1,548 @@
+/*
+ * Buxton
+ *
+ * Copyright (C) 2015 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib.h>
+
+#include "buxton2.h"
+
+#include "common.h"
+#include "log.h"
+#include "direct.h"
+#include "config.h"
+#include "backends.h"
+#include "serialize.h"
+
+static int get_path(uid_t uid, enum buxton_layer_type type,
+ const struct layer *ly, char *path, int sz)
+{
+ const char *prefix;
+ char suffix[16];
+
+ if (!ly || !path || sz <= 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (type == BUXTON_LAYER_NORMAL && ly->storage == STORAGE_VOLATILE)
+ prefix = TMPFS_DIR;
+ else
+ prefix = DB_DIR;
+
+ if (type == BUXTON_LAYER_NORMAL && ly->type == LAYER_USER)
+ snprintf(suffix, sizeof(suffix), "-%u", uid);
+ else
+ suffix[0] = '\0';
+
+ snprintf(path, sz, "%s/%s%s.db", prefix, ly->name, suffix);
+
+ return 0;
+}
+
+static int get_raw(const struct layer *ly, uid_t uid,
+ enum buxton_layer_type type, const char *key,
+ uint8_t **data, int *len)
+{
+ int r;
+ const struct backend *backend;
+ char path[FILENAME_MAX];
+
+ assert(ly);
+ assert(key);
+ assert(data);
+ assert(len);
+
+ backend = backend_get(ly->backend);
+ assert(backend);
+
+ if (!backend->get_value) {
+ bxt_err("Get: backend '%s' has no get func", backend->name);
+ return -1;
+ }
+
+ r = get_path(uid, type, ly, path, sizeof(path));
+ if (r == -1)
+ return -1;
+
+ r = backend->get_value(path, key, (void **)data, len);
+ if (r == -1) {
+ if (errno != ENOENT)
+ bxt_err("Get: get_value: %d", errno);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int get_val(const struct layer *ly, uid_t uid,
+ enum buxton_layer_type type, const char *key,
+ char **rpriv, char **wpriv, struct buxton_value *val)
+{
+ int r;
+ uint8_t *data;
+ int len;
+
+ assert(ly);
+ assert(key);
+
+ r = get_raw(ly, uid, type, key, &data, &len);
+ if (r == -1)
+ return -1;
+
+ r = deserialz_data(data, len, rpriv, wpriv, val);
+
+ free(data);
+
+ if (r == -1)
+ return -1;
+
+ return 0;
+}
+
+int direct_get(const struct buxton_layer *layer,
+ const char *key, struct buxton_value *val)
+{
+ int r;
+ const struct layer *ly;
+ struct buxton_value base_val;
+ struct buxton_value db_val;
+
+ if (!layer || !key || !*key || !val) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ ly = conf_get_layer(layer->name);
+ if (!ly)
+ return -1;
+
+ /* First, refer to base db */
+ r = get_val(ly, layer->uid, BUXTON_LAYER_BASE, key, NULL, NULL,
+ &base_val);
+ if (r == -1)
+ return -1;
+
+ if (layer->type == BUXTON_LAYER_BASE) {
+ *val = base_val;
+ return 0;
+ }
+
+ /* DB backend System layer has no normal db */
+ if (ly->type == LAYER_SYSTEM && ly->storage == STORAGE_PERSISTENT) {
+ *val = base_val;
+ return 0;
+ }
+
+ r = get_val(ly, layer->uid, BUXTON_LAYER_NORMAL, key, NULL, NULL,
+ &db_val);
+ if (r == -1 && errno != ENOENT) {
+ value_free(&base_val);
+ return -1;
+ }
+
+ if (errno == ENOENT) {
+ *val = base_val;
+ return 0;
+ }
+
+ value_free(&base_val);
+ *val = db_val;
+
+ return 0;
+}
+
+int direct_check(const struct buxton_layer *layer, const char *key)
+{
+ int r;
+ const struct layer *ly;
+ struct buxton_value val;
+
+ if (!layer || !key || !*key) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ ly = conf_get_layer(layer->name);
+ if (!ly)
+ return -1;
+
+ r = get_val(ly, layer->uid, BUXTON_LAYER_BASE, key, NULL, NULL, &val);
+ if (r == -1)
+ return -1;
+
+ value_free(&val);
+
+ return 0;
+}
+
+static int set_raw(const struct layer *ly, uid_t uid,
+ enum buxton_layer_type type, const char *key,
+ uint8_t *data, int len)
+{
+ int r;
+ const struct backend *backend;
+ char path[FILENAME_MAX];
+
+ assert(ly);
+ assert(key);
+ assert(data);
+ assert(len > 0);
+
+ backend = backend_get(ly->backend);
+ assert(backend);
+
+ if (!backend->set_value) {
+ bxt_err("Set: backend '%s' has no set func", backend->name);
+ return -1;
+ }
+
+ r = get_path(uid, type, ly, path, sizeof(path));
+ if (r == -1)
+ return -1;
+
+ r = backend->set_value(path, key, data, len);
+ if (r == -1)
+ return -1;
+
+ return 0;
+}
+
+static int set_val(const struct layer *ly, uid_t uid,
+ enum buxton_layer_type type, const char *key,
+ const char *rpriv, const char *wpriv,
+ const struct buxton_value *val)
+{
+ int r;
+ uint8_t *data;
+ int len;
+
+ assert(val);
+
+ r = serialz_data(rpriv ? rpriv : "", wpriv ? wpriv : "", val,
+ &data, &len);
+ if (r == -1)
+ return -1;
+
+ r = set_raw(ly, uid, type, key, data, len);
+
+ free(data);
+
+ if (r == -1)
+ return -1;
+
+ return 0;
+}
+
+int direct_set(const struct buxton_layer *layer,
+ const char *key, const struct buxton_value *val)
+{
+ int r;
+ const struct layer *ly;
+ char *rp;
+ char *wp;
+
+ if (!layer || !key || !*key || !val) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ ly = conf_get_layer(layer->name);
+ if (!ly)
+ return -1;
+
+ r = get_val(ly, layer->uid, BUXTON_LAYER_BASE, key, &rp, &wp, NULL);
+ if (r == -1)
+ return -1;
+
+ r = set_val(ly, layer->uid, layer->type, key, rp, wp, val);
+
+ free(rp);
+ free(wp);
+
+ if (r == -1)
+ return -1;
+
+ return 0;
+}
+
+int direct_create(const struct buxton_layer *layer, const char *key,
+ const char *rpriv, const char *wpriv,
+ const struct buxton_value *val)
+{
+ int r;
+ const struct layer *ly;
+
+ if (!layer || !key || !*key || !val) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ r = check_key_name(key);
+ if (r == -1)
+ return -1;
+
+ ly = conf_get_layer(layer->name);
+ if (!ly)
+ return -1;
+
+ r = get_val(ly, layer->uid, BUXTON_LAYER_BASE, key, NULL, NULL, NULL);
+ if (r == -1 && errno != ENOENT)
+ return -1;
+
+ if (r == 0) {
+ errno = EEXIST;
+ return -1;
+ }
+
+ r = set_val(ly, layer->uid, BUXTON_LAYER_BASE, key, rpriv, wpriv, val);
+ if (r == -1)
+ return -1;
+
+ return 0;
+}
+
+int direct_unset(const struct buxton_layer *layer, const char *key)
+{
+ int r;
+ const struct layer *ly;
+ const struct backend *backend;
+ char path[FILENAME_MAX];
+
+ if (!layer || !key || !*key) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ ly = conf_get_layer(layer->name);
+ if (!ly)
+ return -1;
+
+ backend = backend_get(ly->backend);
+ assert(backend);
+
+ if (!backend->unset_value) {
+ bxt_err("Unset: backend '%s' has no unset func",
+ backend->name);
+ return -1;
+ }
+
+ r = get_path(layer->uid, layer->type, ly, path, sizeof(path));
+ if (r == -1)
+ return -1;
+
+ r = backend->unset_value(path, key);
+ if (r == -1) {
+ bxt_err("Unset: unset_value: %d", errno);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int comp_str(const void *pa, const void *pb)
+{
+ const char *sa = pa ? *(char * const *)pa : "";
+ const char *sb = pb ? *(char * const *)pb : "";
+
+ return strcmp(sa, sb);
+}
+
+int direct_list(const struct buxton_layer *layer,
+ char ***names, unsigned int *len)
+{
+ int r;
+ const struct layer *ly;
+ const struct backend *backend;
+ char path[FILENAME_MAX];
+ unsigned int _len;
+
+ if (!layer || !names) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ ly = conf_get_layer(layer->name);
+ if (!ly)
+ return -1;
+
+ backend = backend_get(ly->backend);
+ assert(backend);
+
+ if (!backend->list_keys) {
+ bxt_err("List: backend '%s' has no list func",
+ backend->name);
+ return -1;
+ }
+
+ r = get_path(layer->uid, BUXTON_LAYER_BASE, ly, path, sizeof(path));
+ if (r == -1)
+ return -1;
+
+ r = backend->list_keys(path, names, &_len);
+ if (r == -1) {
+ bxt_err("List: list_keys: %d", errno);
+ return -1;
+ }
+
+ if (_len > 1)
+ qsort(*names, _len, sizeof(char *), comp_str);
+
+ if (len)
+ *len = _len;
+
+ return 0;
+}
+
+int direct_get_priv(const struct buxton_layer *layer,
+ const char *key, enum buxton_priv_type type, char **priv)
+{
+ int r;
+ const struct layer *ly;
+ char **rp;
+ char **wp;
+
+ if (!layer || !key || !*key || !priv) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ switch (type) {
+ case BUXTON_PRIV_READ:
+ rp = priv;
+ wp = NULL;
+ break;
+ case BUXTON_PRIV_WRITE:
+ rp = NULL;
+ wp = priv;
+ break;
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+
+ ly = conf_get_layer(layer->name);
+ if (!ly)
+ return -1;
+
+ r = get_val(ly, layer->uid, BUXTON_LAYER_BASE, key, rp, wp, NULL);
+ if (r == -1)
+ return -1;
+
+ return 0;
+}
+
+int direct_set_priv(const struct buxton_layer *layer,
+ const char *key, enum buxton_priv_type type, const char *priv)
+{
+ int r;
+ const struct layer *ly;
+ char *rp;
+ char *wp;
+ const char *t_rp;
+ const char *t_wp;
+ struct buxton_value val;
+
+ if (!layer || !key || !*key || !priv) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ switch (type) {
+ case BUXTON_PRIV_READ:
+ case BUXTON_PRIV_WRITE:
+ break;
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+
+ ly = conf_get_layer(layer->name);
+ if (!ly)
+ return -1;
+
+ r = get_val(ly, layer->uid, BUXTON_LAYER_BASE, key, &rp, &wp, &val);
+ if (r == -1)
+ return -1;
+
+ switch (type) {
+ case BUXTON_PRIV_READ:
+ t_rp = priv;
+ t_wp = wp;
+ break;
+ case BUXTON_PRIV_WRITE:
+ t_rp = rp;
+ t_wp = priv;
+ break;
+ default: /* Never reach */
+ t_rp = rp;
+ t_wp = wp;
+ break;
+ }
+
+ r = set_val(ly, layer->uid, BUXTON_LAYER_BASE, key, t_rp, t_wp, &val);
+
+ value_free(&val);
+ free(rp);
+ free(wp);
+
+ if (r == -1)
+ return -1;
+
+ return 0;
+}
+
+void direct_exit(void)
+{
+ conf_exit();
+ backend_exit();
+}
+
+int direct_init(const char *moddir, const char *confpath)
+{
+ int r;
+ GList *backends;
+
+ if (!moddir || !*moddir || !confpath || !*confpath) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ r = backend_init(moddir);
+ if (r == -1)
+ return -1;
+
+ backends = backend_list();
+ if (!backends)
+ return -1;
+
+ r = conf_init(confpath, backends);
+
+ g_list_free(backends);
+
+ if (r == -1)
+ return -1;
+
+ return 0;
+}
+
diff --git a/common/direct.h b/common/direct.h
new file mode 100644
index 0000000..722f62d
--- /dev/null
+++ b/common/direct.h
@@ -0,0 +1,44 @@
+/*
+ * Buxton
+ *
+ * Copyright (C) 2015 Samsung Electronics Co., Ltd.
+ *
+ * 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
+
+#include "buxton2.h"
+
+int direct_init(const char *moddir, const char *confpath);
+void direct_exit(void);
+
+int direct_get(const struct buxton_layer *layer,
+ const char *key, struct buxton_value *val);
+int direct_set(const struct buxton_layer *layer,
+ const char *key, const struct buxton_value *val);
+int direct_check(const struct buxton_layer *layer, const char *key);
+
+int direct_create(const struct buxton_layer *layer, const char *key,
+ const char *rprive, const char *wpriv,
+ const struct buxton_value *val);
+int direct_unset(const struct buxton_layer *layer, const char *key);
+
+int direct_list(const struct buxton_layer *layer,
+ char ***names, unsigned int *len);
+
+int direct_get_priv(const struct buxton_layer *layer,
+ const char *key, enum buxton_priv_type type, char **priv);
+int direct_set_priv(const struct buxton_layer *layer,
+ const char *key, enum buxton_priv_type type, const char *priv);
+
diff --git a/common/log.h b/common/log.h
new file mode 100644
index 0000000..0de5696
--- /dev/null
+++ b/common/log.h
@@ -0,0 +1,49 @@
+/*
+ * Buxton
+ *
+ * Copyright (C) 2015 Samsung Electronics Co., Ltd.
+ *
+ * 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
+
+#if defined(_DLOG_H_)
+
+# define bxt_info(fmt, ...) LOGI(fmt, ##__VA_ARGS__)
+# define bxt_err(fmt, ...) LOGE(fmt, ##__VA_ARGS__)
+# define bxt_dbg(fmt, ...) LOGD(fmt, ##__VA_ARGS__)
+
+#else /* _DLOG_H_ */
+
+# include <stdio.h>
+
+# if defined(NDEBUG)
+
+# define bxt_info(fmt, ...) printf("Buxton: " fmt "\n", ##__VA_ARGS__)
+# define bxt_err(fmt, ...) \
+ fprintf(stderr, "Buxton: " fmt "\n", ##__VA_ARGS__)
+# define bxt_dbg(fmt, ...) do { } while (0)
+
+# else /* NDEBUG */
+
+# define bxt_info(fmt, ...) printf("Buxton: " fmt "\n", ##__VA_ARGS__)
+# define bxt_err(fmt, ...) \
+ fprintf(stderr, "Buxton:Err: " fmt "\n", ##__VA_ARGS__)
+# define bxt_dbg(fmt, ...) \
+ printf("Buxton:D:%s:%d: " fmt "\n", __func__, __LINE__, \
+ ##__VA_ARGS__)
+
+# endif
+
+#endif /* _DLOG_H_ */
diff --git a/common/proto.c b/common/proto.c
new file mode 100644
index 0000000..b87e396
--- /dev/null
+++ b/common/proto.c
@@ -0,0 +1,328 @@
+/*
+ * Buxton
+ *
+ * Copyright (C) 2015 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ */
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <errno.h>
+#include <assert.h>
+#include <stdint.h>
+#include <poll.h>
+
+#include "common.h"
+#include "log.h"
+#include "proto.h"
+
+#define SEND_TIMEOUT_MSEC 1000
+#define SEND_PACKET_MAX 8192
+
+struct recv_info {
+ int fd;
+
+ recv_callback callback;
+ void *user_data;
+
+ enum message_type type;
+ uint8_t *data;
+ int32_t len;
+
+ int32_t recved;
+};
+
+static GList *recv_list;
+
+static struct recv_info *find_rif(int fd)
+{
+ GList *l;
+
+ for (l = recv_list; l; l = g_list_next(l)) {
+ if (((struct recv_info *)l->data)->fd == fd)
+ return l->data;
+ }
+
+ return NULL;
+}
+
+static int recv_first(int fd, recv_callback callback, void *user_data)
+{
+ int r;
+ struct recv_info *rif;
+ uint32_t hdr;
+
+ rif = calloc(1, sizeof(*rif));
+ if (!rif)
+ return -1;
+
+ rif->fd = fd;
+ rif->callback = callback;
+ rif->user_data = user_data;
+
+ r = recv(fd, &hdr, sizeof(uint32_t), 0);
+ if (r <= 0) {
+ free(rif);
+ if (r == 0)
+ bxt_dbg("recv: fd %d closed", fd);
+ else
+ bxt_err("recv: fd %d errno %d", fd, errno);
+
+ return -1;
+ }
+
+ rif->type = hdr >> 24;
+ rif->len = hdr & 0xffffff;
+ if (rif->len == 0) {
+ free(rif);
+ bxt_err("recv: fd %d invalid header %x", fd, hdr);
+ return -1;
+ }
+
+ rif->data = malloc(rif->len);
+ if (!rif->data) {
+ free(rif);
+ return -1;
+ }
+
+ recv_list = g_list_append(recv_list, rif);
+
+ bxt_dbg("rif %p type %d len %d added", rif, rif->type, rif->len);
+
+ return 0;
+
+}
+
+static void remove_rif(struct recv_info *rif)
+{
+ if (!rif)
+ return;
+
+ recv_list = g_list_remove(recv_list, rif);
+ free(rif->data);
+ free(rif);
+}
+
+static int recv_cont(struct recv_info *rif)
+{
+ int r;
+
+ assert(rif);
+
+ r = recv(rif->fd, &rif->data[rif->recved], rif->len - rif->recved, 0);
+ if (r <= 0) {
+ if (r == 0)
+ bxt_dbg("recv: fd %d closed", rif->fd);
+ else
+ bxt_err("recv: fd %d errno %d", rif->fd, errno);
+
+ remove_rif(rif);
+ return -1;
+ }
+ rif->recved += r;
+
+ if (rif->recved > rif->len) {
+ bxt_err("recv: fd %d expected %d > received %d", rif->fd,
+ rif->len, rif->recved);
+ remove_rif(rif);
+ return -1;
+ }
+
+ if (rif->recved == rif->len) {
+ bxt_dbg("rif %p received %d / %d", rif, rif->recved, rif->len);
+
+ assert(rif->callback);
+ rif->callback(rif->user_data, rif->type, rif->data, rif->len);
+ remove_rif(rif);
+ }
+
+ return 0;
+}
+
+int proto_recv_frag(int fd, recv_callback callback, void *user_data)
+{
+ int r;
+ struct recv_info *rif;
+
+ if (fd < 0 || !callback) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ rif = find_rif(fd);
+ if (!rif)
+ r = recv_first(fd, callback, user_data);
+ else
+ r = recv_cont(rif);
+
+ return r;
+}
+
+
+int proto_send_block(int fd, enum message_type type, uint8_t *data, int32_t len)
+{
+ int r;
+ uint32_t hdr;
+ int sent;
+ struct pollfd fds[1];
+ int s;
+
+ if (fd < 0 || !data || len <= 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ bxt_dbg("send: fd %d type %d len %d start", fd, type, len);
+ hdr = (type << 24) | (len & 0xffffff);
+
+ r = send(fd, &hdr, sizeof(uint32_t), 0);
+ if (r == -1) {
+ bxt_err("send: fd %d errno %d", fd, errno);
+ return -1;
+ }
+
+ sent = 0;
+ while (len > sent) {
+ fds[0].fd = fd;
+ fds[0].events = POLLOUT;
+ fds[0].revents = 0;
+
+ /* CAN BE BLOCKED ! */
+ r = poll(fds, 1, SEND_TIMEOUT_MSEC);
+ if (r == -1) {
+ bxt_err("send: fd %d poll errno %d", fd, errno);
+ return -1;
+ }
+ if (r == 0) {
+ bxt_err("send: fd %d timeout", fd);
+ return -1;
+ }
+
+ s = len - sent;
+ if (s > SEND_TIMEOUT_MSEC)
+ s = SEND_TIMEOUT_MSEC;
+
+ r = send(fd, &data[sent], s, 0);
+ if (r == -1) {
+ bxt_err("send: fd %d errno %d", fd, errno);
+ return -1;
+ }
+
+ sent += r;
+ }
+ bxt_dbg("send: fd %d sent %d", fd, sent);
+
+ return 0;
+}
+
+int proto_send(int fd, enum message_type type, uint8_t *data, int32_t len)
+{
+ int r;
+ uint32_t hdr;
+ uint8_t *buf;
+
+ assert(fd >= 0);
+ assert(data);
+ assert(len > 0);
+
+ buf = malloc(len + sizeof(uint32_t));
+ if (!buf) {
+ bxt_err("send: send buffer alloc error");
+ return -1;
+ }
+
+ hdr = (type << 24) | (len & 0xffffff);
+
+ memcpy(buf, &hdr, sizeof(uint32_t));
+ memcpy(buf + sizeof(uint32_t), data, len);
+
+ r = send(fd, buf, len + sizeof(uint32_t), 0);
+
+ free(buf);
+
+ if (r == -1) {
+ bxt_err("send: fd %d errno %d", fd, errno);
+ return -1;
+ }
+
+ if (r != len + sizeof(uint32_t))
+ bxt_err("send: %d / %d byte", r,
+ (int32_t)(len + sizeof(uint32_t)));
+
+ return 0;
+}
+
+int proto_recv(int fd, enum message_type *type, uint8_t **data, int32_t *len)
+{
+ int r;
+ uint32_t hdr;
+ uint8_t *_data;
+ int32_t _len;
+ enum message_type _type;
+
+ assert(fd >= 0);
+ assert(type);
+ assert(data);
+ assert(len);
+
+ r = recv(fd, &hdr, sizeof(uint32_t), 0);
+ if (r <= 0) {
+ if (r == 0)
+ bxt_dbg("recv: fd %d closed", fd);
+ else
+ bxt_err("recv: fd %d errno %d", fd, errno);
+
+ return -1;
+ }
+
+ _type = hdr >> 24;
+ _len = hdr & 0xffffff;
+
+ if (_len == 0) {
+ bxt_err("recv: fd %d Invalid message", fd);
+ return -1;
+ }
+
+ _data = malloc(_len);
+ if (!_data) {
+ /* flush ? */
+ return -1;
+ }
+
+ r = recv(fd, _data, _len, 0);
+ if (r <= 0) {
+ if (r == 0)
+ bxt_dbg("recv: fd %d closed", fd);
+ else
+ bxt_err("recv: fd %d errno %d", fd, errno);
+
+ free(_data);
+
+ return -1;
+ }
+
+ if (r != _len) {
+ bxt_err("recv: fd %d expect size %d > received %d",
+ fd, _len, r);
+ free(_data);
+ return -1;
+ }
+
+ *type = _type;
+ *data = _data;
+ *len = _len;
+
+ return 0;
+}
diff --git a/common/proto.h b/common/proto.h
new file mode 100644
index 0000000..39e923a
--- /dev/null
+++ b/common/proto.h
@@ -0,0 +1,33 @@
+/*
+ * Buxton
+ *
+ * Copyright (C) 2015 Samsung Electronics Co., Ltd.
+ *
+ * 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
+
+#include <stdint.h>
+
+#include "common.h"
+
+int proto_send(int fd, enum message_type type, uint8_t *data, int32_t len);
+int proto_recv(int fd, enum message_type *type, uint8_t **data, int32_t *len);
+
+typedef void (*recv_callback)(void *user_data,
+ enum message_type type, uint8_t *data, int32_t len);
+int proto_recv_frag(int fd, recv_callback callback, void *user_data);
+int proto_send_block(int fd, enum message_type type, uint8_t *data,
+ int32_t len);
+
diff --git a/common/serialize.c b/common/serialize.c
new file mode 100644
index 0000000..2cda9c1
--- /dev/null
+++ b/common/serialize.c
@@ -0,0 +1,970 @@
+/*
+ * Buxton
+ *
+ * Copyright (C) 2015 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+#include <glib.h>
+
+#include "buxton2.h"
+
+#include "serialize.h"
+#include "log.h"
+
+#define KEY_NAME_MAX 4096
+#define VALUE_MAX 4096
+
+int check_key_name(const char *key)
+{
+ const char *p;
+ int len;
+
+ len = 0;
+ p = key;
+ while (*p) {
+ /* from 0x21 '!' to 0x7e '~' */
+ if (*p < 0x21 || *p > 0x7e) {
+ errno = EINVAL;
+ bxt_err("Key name has invalid character '%x'", *p);
+ return -1;
+ }
+ p++;
+ len++;
+ if (len > KEY_NAME_MAX) {
+ errno = ENAMETOOLONG;
+ bxt_err("Key name is too long");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static inline int check_val(const char *s)
+{
+ if (!s)
+ return 0;
+
+ if (strlen(s) > VALUE_MAX) {
+ errno = EMSGSIZE;
+ return -1;
+ }
+
+ return 0;
+}
+
+static int check_values(const char *rpriv, const char *wpriv,
+ const struct buxton_value *val)
+{
+ int r;
+
+ r = check_val(rpriv);
+ if (r == -1) {
+ bxt_err("Read priv. string length is too long");
+ return -1;
+ }
+
+ r = check_val(wpriv);
+ if (r == -1) {
+ bxt_err("Write priv. string length is too long");
+ return -1;
+ }
+
+ if (val && val->type == BUXTON_TYPE_STRING) {
+ r = check_val(val->value.s);
+ if (r == -1) {
+ bxt_err("Value string length is too long");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static GVariant *val_to_gv(const struct buxton_value *val)
+{
+ GVariant *gv;
+
+ if (!val)
+ return g_variant_new_tuple(NULL, 0);
+
+ switch (val->type) {
+ case BUXTON_TYPE_STRING:
+ if (!val->value.s) {
+ bxt_err("Serialize: value has NULL string");
+ return NULL;
+ }
+
+ gv = g_variant_new_string(val->value.s);
+ break;
+ case BUXTON_TYPE_INT32:
+ gv = g_variant_new_int32(val->value.i);
+ break;
+ case BUXTON_TYPE_UINT32:
+ gv = g_variant_new_uint32(val->value.u);
+ break;
+ case BUXTON_TYPE_INT64:
+ gv = g_variant_new_int64(val->value.i64);
+ break;
+ case BUXTON_TYPE_UINT64:
+ gv = g_variant_new_uint64(val->value.u64);
+ break;
+ case BUXTON_TYPE_DOUBLE:
+ gv = g_variant_new_double(val->value.d);
+ break;
+ case BUXTON_TYPE_BOOLEAN:
+ gv = g_variant_new_boolean(val->value.b);
+ break;
+ default:
+ bxt_err("Serialize: Invalid value type: %d", val->type);
+ gv = NULL;
+ break;
+ }
+
+ return gv;
+}
+
+static uint8_t *gv_to_data(GVariant *gv, int *len)
+{
+ uint8_t *data;
+ int _len;
+
+ assert(gv);
+ assert(len);
+
+ _len = g_variant_get_size(gv);
+ assert(_len > 0);
+
+ data = malloc(_len);
+ if (!data)
+ return NULL;
+
+ g_variant_store(gv, data);
+
+ *len = _len;
+
+ return data;
+}
+
+/*
+ * Data format = v Variant v
+ *
+ * In an initial version,
+ * Variant v = (ssv) read privilege s, write priv. w, value v
+ *
+ */
+int serialz_data(const char *rpriv, const char *wpriv,
+ const struct buxton_value *val,
+ uint8_t **data, int *len)
+{
+ GVariant *gv;
+ GVariant *vv;
+ GVariant *v;
+ int _len;
+ uint8_t *_data;
+ int r;
+
+ if (!rpriv || !wpriv || !val || !data || !len) {
+ errno = EINVAL;
+ bxt_err("serialize data: invalid argument:%s%s%s%s%s",
+ rpriv ? "" : " read priv",
+ wpriv ? "" : " write priv",
+ val ? "" : " value",
+ data ? "" : " data",
+ len ? "" : " len");
+
+ return -1;
+ }
+
+ r = check_values(rpriv, wpriv, val);
+ if (r == -1)
+ return -1;
+
+ v = val_to_gv(val);
+ if (!v) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ vv = g_variant_new("(ssv)", rpriv, wpriv, v);
+ assert(vv);
+
+ gv = g_variant_new_variant(vv);
+ assert(gv);
+
+ _data = gv_to_data(gv, &_len);
+
+ g_variant_unref(gv);
+
+ if (!_data)
+ return -1;
+
+ *data = _data;
+ *len = _len;
+
+ return 0;
+}
+
+static int gv_to_val(GVariant *v, struct buxton_value *val)
+{
+ const char *t;
+ const char *s;
+
+ assert(v);
+ assert(val);
+
+ t = g_variant_get_type_string(v);
+ assert(t);
+
+ if (!strncmp(t, "()", sizeof("()"))) {
+ val->type = BUXTON_TYPE_UNKNOWN;
+ return 0;
+ }
+
+ switch (*t) {
+ case 's':
+ val->type = BUXTON_TYPE_STRING;
+
+ s = g_variant_get_string(v, NULL);
+ assert(s);
+
+ val->value.s = strdup(s);
+ if (!val->value.s)
+ return -1;
+
+ break;
+ case 'i':
+ val->type = BUXTON_TYPE_INT32;
+ val->value.i = g_variant_get_int32(v);
+ break;
+ case 'u':
+ val->type = BUXTON_TYPE_UINT32;
+ val->value.u = g_variant_get_uint32(v);
+ break;
+ case 'x':
+ val->type = BUXTON_TYPE_INT64;
+ val->value.i64 = g_variant_get_int64(v);
+ break;
+ case 't':
+ val->type = BUXTON_TYPE_UINT64;
+ val->value.u64 = g_variant_get_uint64(v);
+ break;
+ case 'd':
+ val->type = BUXTON_TYPE_DOUBLE;
+ val->value.d = g_variant_get_double(v);
+ break;
+ case 'b':
+ val->type = BUXTON_TYPE_BOOLEAN;
+ val->value.b = g_variant_get_boolean(v);
+ break;
+ default:
+ bxt_err("DeSerialz: Invalid variant type: %s", t);
+ errno = EBADMSG;
+
+ return -1;
+ }
+
+ return 0;
+}
+
+static int gv_to_values(GVariant *gv, char **rpriv, char **wpriv,
+ struct buxton_value *val)
+{
+ GVariant *v;
+ const char *vt;
+ const char *rp;
+ const char *wp;
+ int r;
+
+ assert(gv);
+
+ if (!rpriv && !wpriv && !val)
+ return 0;
+
+ vt = g_variant_get_type_string(gv);
+ if (strncmp(vt, "(ssv)", sizeof("(ssv)"))) {
+ bxt_err("Deserialize: Unsupported type: %s", vt);
+ errno = EBADMSG;
+ return -1;
+ }
+
+ g_variant_get(gv, "(&s&sv)", &rp, &wp, &v);
+ assert(rp);
+ assert(wp);
+ assert(v);
+
+ if (rpriv) {
+ *rpriv = strdup(rp);
+ if (!*rpriv) {
+ g_variant_unref(v);
+ return -1;
+ }
+ }
+
+ if (wpriv) {
+ *wpriv = strdup(wp);
+ if (!*wpriv) {
+ if (rpriv)
+ free(*rpriv);
+
+ g_variant_unref(v);
+ return -1;
+ }
+ }
+
+ if (val) {
+ memset(val, 0, sizeof(*val));
+ r = gv_to_val(v, val);
+ if (r == -1) {
+ if (rpriv)
+ free(*rpriv);
+
+ if (wpriv)
+ free(*wpriv);
+
+ g_variant_unref(v);
+ return -1;
+ }
+ }
+
+ g_variant_unref(v);
+
+ return 0;
+}
+
+int deserialz_data(uint8_t *data, int len,
+ char **rpriv, char **wpriv, struct buxton_value *val)
+{
+ GVariant *gv;
+ GVariant *v;
+ char *_rpriv;
+ char *_wpriv;
+ struct buxton_value _val;
+ int r;
+
+ if (!data || len <= 0) {
+ errno = EINVAL;
+ bxt_err("Deserialize data: invalid argument:%s%s",
+ data ? "" : " data", len > 0 ? "" : " len");
+ return -1;
+ }
+
+ gv = g_variant_new_from_data(G_VARIANT_TYPE("v"),
+ data, len, TRUE, NULL, NULL);
+ assert(gv);
+
+ g_variant_get(gv, "v", &v);
+ assert(v);
+
+ r = gv_to_values(v,
+ rpriv ? &_rpriv : NULL,
+ wpriv ? &_wpriv : NULL,
+ val ? &_val : NULL);
+
+ g_variant_unref(v);
+ g_variant_unref(gv);
+
+ if (r == -1)
+ return -1;
+
+ if (rpriv)
+ *rpriv = _rpriv;
+
+ if (wpriv)
+ *wpriv = _wpriv;
+
+ if (val)
+ *val = _val;
+
+ return 0;
+}
+
+void free_request(struct request *req)
+{
+ if (!req)
+ return;
+
+ layer_free(req->layer);
+ free(req->rpriv);
+ free(req->wpriv);
+ free(req->key);
+ value_free(req->val);
+ free(req->val);
+}
+
+static int check_value(const struct buxton_value *val)
+{
+ if (!val) {
+ bxt_err("Serialize: value is NULL");
+ return -1;
+ }
+
+ switch (val->type) {
+ case BUXTON_TYPE_STRING:
+ if (!val->value.s) {
+ bxt_err("Serialize: value has NULL string");
+ return -1;
+ }
+ break;
+ case BUXTON_TYPE_INT32:
+ case BUXTON_TYPE_UINT32:
+ case BUXTON_TYPE_INT64:
+ case BUXTON_TYPE_UINT64:
+ case BUXTON_TYPE_DOUBLE:
+ case BUXTON_TYPE_BOOLEAN:
+ break;
+ default:
+ bxt_err("Serialize: buxton_value has unknown type");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int check_request(enum message_type type,
+ const char *key, const struct buxton_value *val)
+{
+ int r;
+
+ switch (type) {
+ case MSG_SET:
+ case MSG_CREAT:
+ case MSG_NOTI:
+ case MSG_SET_WP:
+ case MSG_SET_RP:
+ r = check_value(val);
+ if (r == -1)
+ goto err;
+ case MSG_GET:
+ case MSG_UNSET:
+ case MSG_NOTIFY:
+ case MSG_UNNOTIFY:
+ case MSG_GET_WP:
+ case MSG_GET_RP:
+ if (!key || !*key) {
+ bxt_err("Serialize: key is NULL or empty string");
+ goto err;
+ }
+
+ r = check_key_name(key);
+ if (r == -1)
+ return -1;
+ case MSG_LIST:
+ break;
+ default:
+ bxt_err("Serialize: message type is invalid: %d", type);
+ goto err;
+ }
+
+ return 0;
+
+err:
+ errno = EINVAL;
+
+ return -1;
+}
+
+int serialz_request(const struct request *req, uint8_t **data, int *len)
+{
+ int r;
+ GVariant *gv;
+ GVariant *vv;
+ GVariant *v;
+ int _len;
+ uint8_t *_data;
+
+ if (!data || !len || !req || !req->layer) {
+ errno = EINVAL;
+ bxt_err("Serialize request: invalid argument:%s%s%s%s",
+ data ? "" : " data",
+ len ? "" : " len",
+ req ? "" : " req",
+ req->layer ? "" : " layer");
+ return -1;
+ }
+
+ r = check_request(req->type, req->key, req->val);
+ if (r == -1)
+ return -1;
+
+ v = val_to_gv(req->val);
+ if (!v) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ vv = g_variant_new("(uuissssv)",
+ req->msgid,
+ req->layer->uid,
+ req->layer->type,
+ req->layer->name,
+ req->rpriv ? req->rpriv : "",
+ req->wpriv ? req->wpriv : "",
+ req->key ? req->key : "",
+ v);
+ assert(vv);
+
+ gv = g_variant_new("(qv)", req->type, vv);
+ assert(gv);
+
+ _data = gv_to_data(gv, &_len);
+
+ g_variant_unref(gv);
+
+ if (!_data)
+ return -1;
+
+ *data = _data;
+ *len = _len;
+
+ return 0;
+}
+
+static inline int _strdup(const char *src, char **dest)
+{
+ char *s;
+
+ assert(dest);
+
+ if (!src) {
+ *dest = NULL;
+ return 0;
+ }
+
+ s = strdup(src);
+ if (!s)
+ return -1;
+
+ *dest = s;
+
+ return 0;
+}
+
+static int set_req(struct buxton_value *val, const char *lnm, uid_t uid,
+ enum buxton_layer_type type, const char *rp, const char *wp,
+ const char *key, struct request *req)
+{
+ int r;
+
+ assert(req);
+
+ req->val = val;
+
+ if (lnm && *lnm) {
+ req->layer = layer_create(lnm);
+ if (!req->layer)
+ return -1;
+
+ req->layer->uid = uid;
+ req->layer->type = type;
+ } else {
+ req->layer = NULL;
+ }
+
+ r = _strdup(rp, &req->rpriv);
+ if (r == -1)
+ return -1;
+
+ r = _strdup(wp, &req->wpriv);
+ if (r == -1)
+ return -1;
+
+ r = _strdup(key, &req->key);
+ if (r == -1)
+ return -1;
+
+ return 0;
+}
+
+static int gv_to_req(GVariant *gv, struct request *req)
+{
+ const char *vt;
+ uint32_t uid;
+ int32_t type;
+ const char *lnm;
+ const char *key;
+ const char *rp;
+ const char *wp;
+ GVariant *v;
+ int r;
+ struct buxton_value *val;
+
+ assert(gv);
+ assert(req);
+
+ vt = g_variant_get_type_string(gv);
+ if (strncmp(vt, "(uuissssv)", sizeof("(uuissssv)"))) {
+ bxt_err("DeSerialz: Unsupported type: %s", vt);
+ errno = EBADMSG;
+ return -1;
+ }
+
+ val = calloc(1, sizeof(*val));
+ if (!val)
+ return -1;
+
+ g_variant_get(gv, "(uui&s&s&s&sv)", &req->msgid, &uid, &type,
+ &lnm, &rp, &wp, &key, &v);
+ assert(v);
+ assert(lnm);
+ assert(rp);
+ assert(wp);
+ assert(key);
+
+ r = gv_to_val(v, val);
+
+ g_variant_unref(v);
+
+ if (r == -1) {
+ free(val);
+ return -1;
+ }
+
+ if (val->type == BUXTON_TYPE_UNKNOWN) {
+ free(val);
+ val = NULL;
+ }
+
+ r = set_req(val, lnm, uid, type, rp, wp, key, req);
+ if (r == -1)
+ free_request(req);
+
+ return r;
+}
+
+int deserialz_request(uint8_t *data, int len, struct request *req)
+{
+ GVariant *gv;
+ GVariant *v;
+ int r;
+ struct request _req;
+
+ if (!data || len <= 0 || !req) {
+ errno = EINVAL;
+ bxt_err("Deserialize request: invalid argument:%s%s%s",
+ data ? "" : " data",
+ len > 0 ? "" : " len",
+ req ? "" : " req");
+ return -1;
+ }
+
+ gv = g_variant_new_from_data(G_VARIANT_TYPE("(qv)"),
+ data, len, TRUE, NULL, NULL);
+ assert(gv);
+
+ memset(&_req, 0, sizeof(_req));
+
+ g_variant_get(gv, "(qv)", &_req.type, &v);
+ assert(v);
+
+ r = gv_to_req(v, &_req);
+
+ g_variant_unref(v);
+ g_variant_unref(gv);
+
+ if (r == -1)
+ return -1;
+
+ *req = _req;
+
+ return 0;
+}
+
+void free_response(struct response *res)
+{
+ if (!res)
+ return;
+
+ value_free(res->val);
+ free(res->val);
+ buxton_free_keys(res->names);
+}
+
+static int check_response(enum message_type type, int32_t res,
+ const struct buxton_value *val, char * const *names)
+{
+ int r;
+
+ if (res)
+ return 0;
+
+ switch (type) {
+ case MSG_LIST:
+ if (!names) {
+ bxt_err("Serialize: names is NULL");
+ goto err;
+ }
+ break;
+ case MSG_GET:
+ case MSG_GET_WP:
+ case MSG_GET_RP:
+ r = check_value(val);
+ if (r == -1)
+ goto err;
+ break;
+ case MSG_SET:
+ case MSG_CREAT:
+ case MSG_UNSET:
+ case MSG_NOTIFY:
+ case MSG_UNNOTIFY:
+ case MSG_SET_WP:
+ case MSG_SET_RP:
+ break;
+ case MSG_NOTI:
+ errno = ENOTSUP;
+ bxt_err("Serialize: MSG_NOTI type has no response");
+ return -1;
+ default:
+ goto err;
+ }
+
+ return 0;
+
+err:
+ errno = EINVAL;
+
+ return -1;
+}
+
+static int res_to_gv(enum message_type type, int32_t res,
+ const struct buxton_value *val, char * const *names,
+ GVariant **gv)
+{
+ GVariantBuilder *builder;
+ GVariant *v;
+
+ assert(gv);
+
+ if (res) {
+ *gv = g_variant_new_tuple(NULL, 0);
+ return 0;
+ }
+
+ switch (type) {
+ case MSG_LIST:
+ builder = g_variant_builder_new(G_VARIANT_TYPE("as"));
+ assert(names);
+ while (*names) {
+ g_variant_builder_add(builder, "s", *names);
+ names++;
+ }
+ v = g_variant_new("as", builder);
+ assert(v);
+ g_variant_builder_unref(builder);
+ break;
+ case MSG_GET:
+ case MSG_GET_WP:
+ case MSG_GET_RP:
+ if (val) {
+ v = val_to_gv(val);
+ if (!v) {
+ errno = EINVAL;
+ return -1;
+ }
+ } else {
+ v = g_variant_new_tuple(NULL, 0);
+ }
+ break;
+ default:
+ v = g_variant_new_tuple(NULL, 0);
+ break;
+ }
+
+ *gv = v;
+
+ return 0;
+}
+
+int serialz_response(enum message_type type, uint32_t msgid, int32_t res,
+ const struct buxton_value *val, uint32_t nmlen,
+ char * const *names, uint8_t **data, int *len)
+{
+ int r;
+ GVariant *gv;
+ GVariant *vv;
+ GVariant *v;
+ int _len;
+ uint8_t *_data;
+
+ if (!data || !len) {
+ errno = EINVAL;
+ bxt_err("Serialize response: invalid argument:%s%s",
+ data ? "" : " data",
+ len ? "" : " len");
+ return -1;
+ }
+
+ r = check_response(type, res, val, names);
+ if (r == -1)
+ return -1;
+
+ r = res_to_gv(type, res, val, names, &v);
+ if (r == -1)
+ return -1;
+
+ assert(v);
+ vv = g_variant_new("(uiuv)", msgid, res, nmlen, v);
+ assert(vv);
+
+ gv = g_variant_new("(qv)", type, vv);
+ assert(gv);
+
+ _data = gv_to_data(gv, &_len);
+
+ g_variant_unref(gv);
+
+ if (!_data)
+ return -1;
+
+ *data = _data;
+ *len = _len;
+
+ return 0;
+}
+
+static int gv_to_res_list(GVariant *gv, struct response *res)
+{
+ GVariantIter iter;
+ gsize len;
+ const char *s;
+ int i;
+
+ g_variant_iter_init(&iter, gv);
+ len = g_variant_iter_n_children(&iter);
+ assert(len >= 0);
+
+ res->names = calloc(len + 1, sizeof(void *));
+ if (!res->names)
+ return -1;
+
+ i = 0;
+ while (g_variant_iter_next(&iter, "&s", &s)) {
+ assert(s);
+ res->names[i] = strdup(s);
+ if (!res->names[i])
+ break;
+ i++;
+
+ assert(i <= len);
+ }
+ /* NULL terminated */
+ res->names[i] = NULL;
+
+ if (i < len) {
+ buxton_free_keys(res->names);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int gv_to_res(GVariant *gv, struct response *res)
+{
+ const char *vt;
+ GVariant *v;
+ struct buxton_value *val;
+ int r;
+
+ assert(gv);
+ assert(res);
+
+ vt = g_variant_get_type_string(gv);
+ if (strncmp(vt, "(uiuv)", sizeof("(uiuv)"))) {
+ bxt_err("DeSerialz: Unsupported type: %s", vt);
+ errno = EBADMSG;
+ return -1;
+ }
+
+ g_variant_get(gv, "(uiuv)", &res->msgid, &res->res, &res->nmlen, &v);
+
+ if (res->res)
+ return 0;
+
+ if (res->type == MSG_LIST) {
+ r = gv_to_res_list(v, res);
+ g_variant_unref(v);
+ return r;
+ }
+
+ val = calloc(1, sizeof(*val));
+ if (!val) {
+ g_variant_unref(v);
+ return -1;
+ }
+
+ r = gv_to_val(v, val);
+ if (r == -1) {
+ free(val);
+ g_variant_unref(v);
+ return -1;
+ }
+
+ g_variant_unref(v);
+
+ if (val->type == BUXTON_TYPE_UNKNOWN) {
+ free(val);
+ val = NULL;
+ }
+
+ res->val = val;
+
+ return 0;
+}
+
+int deserialz_response(uint8_t *data, int len, struct response *res)
+{
+ GVariant *gv;
+ GVariant *v;
+ int r;
+ struct response _res;
+
+ if (!data || len <= 0 || !res) {
+ errno = EINVAL;
+ bxt_err("Deserialize response: invalid argument:%s%s%s",
+ data ? "" : " data",
+ len > 0 ? "" : " len",
+ res ? "" : " response");
+ return -1;
+ }
+
+ gv = g_variant_new_from_data(G_VARIANT_TYPE("(qv)"),
+ data, len, TRUE, NULL, NULL);
+ assert(gv);
+
+ memset(&_res, 0, sizeof(_res));
+
+ g_variant_get(gv, "(qv)", &_res.type, &v);
+ assert(v);
+
+ r = gv_to_res(v, &_res);
+
+ g_variant_unref(v);
+ g_variant_unref(gv);
+
+ if (r == -1) {
+ free_response(&_res);
+ return -1;
+ }
+
+ *res = _res;
+
+ return 0;
+}
+
diff --git a/common/serialize.h b/common/serialize.h
new file mode 100644
index 0000000..e5ef8ef
--- /dev/null
+++ b/common/serialize.h
@@ -0,0 +1,70 @@
+/*
+ * Buxton
+ *
+ * Copyright (C) 2015 Samsung Electronics Co., Ltd.
+ *
+ * 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
+
+#include <stdint.h>
+
+#include "buxton2.h"
+
+#include "common.h"
+
+int check_key_name(const char *key);
+
+int serialz_data(const char *rpriv, const char *wpriv,
+ const struct buxton_value *val,
+ uint8_t **data, int *len);
+
+int deserialz_data(uint8_t *data, int len,
+ char **rpriv, char **wpriv, struct buxton_value *val);
+
+
+struct request {
+ enum message_type type;
+ uint32_t msgid;
+ struct buxton_layer *layer;
+ char *rpriv;
+ char *wpriv;
+ char *key;
+ struct buxton_value *val;
+};
+
+int serialz_request(const struct request *req, uint8_t **data, int *len);
+
+int deserialz_request(uint8_t *data, int len, struct request *req);
+
+void free_request(struct request *req);
+
+
+int serialz_response(enum message_type type, uint32_t msgid, int32_t res,
+ const struct buxton_value *val, uint32_t nmlen,
+ char * const *names, uint8_t **data, int *len);
+
+struct response {
+ enum message_type type;
+ uint32_t msgid;
+ int32_t res;
+ struct buxton_value *val;
+ uint32_t nmlen;
+ char **names;
+};
+
+int deserialz_response(uint8_t *data, int len, struct response *res);
+
+void free_response(struct response *res);
+
diff --git a/daemon/CMakeLists.txt b/daemon/CMakeLists.txt
new file mode 100644
index 0000000..c2e81b8
--- /dev/null
+++ b/daemon/CMakeLists.txt
@@ -0,0 +1,30 @@
+# buxton2d build
+
+PKG_CHECK_MODULES(D_PKGS REQUIRED libsystemd cynara-client-async)
+
+FOREACH(flag ${D_PKGS_CFLAGS})
+ SET(DAEMON_CFLAGS "${DAEMON_CFLAGS} ${flag}")
+ENDFOREACH()
+
+INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/lib/include)
+
+SET(TARGET "buxton2d")
+SET(SRC main.c
+ daemon.c
+ socks.c
+ cynara.c
+ ../common/common.c
+ ../common/config.c
+ ../common/backends.c
+ ../common/serialize.c
+ ../common/direct.c
+ ../common/proto.c
+)
+ADD_EXECUTABLE(${TARGET} ${SRC})
+SET_TARGET_PROPERTIES(${TARGET} PROPERTIES
+ LINK_FLAGS "-fPIE"
+ COMPILE_FLAGS "${DAEMON_CFLAGS}"
+)
+TARGET_LINK_LIBRARIES(${TARGET} ${PKGS_LDFLAGS} ${D_PKGS_LDFLAGS} -ldl)
+INSTALL(TARGETS ${TARGET} DESTINATION sbin)
+
diff --git a/daemon/cynara.c b/daemon/cynara.c
new file mode 100644
index 0000000..79129c7
--- /dev/null
+++ b/daemon/cynara.c
@@ -0,0 +1,348 @@
+/*
+ * Buxton
+ *
+ * Copyright (C) 2015 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <assert.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <glib-unix.h>
+#include <cynara-client-async.h>
+
+#include "log.h"
+
+#include "cynara.h"
+
+#define BUXTON_CYNARA_PERMISSIVE_MODE "BUXTON_CYNARA_PERMISSIVE_MODE"
+
+struct bxt_cyn_cb {
+ cynara_check_id id;
+
+ struct bxt_client *cli;
+ buxton_cynara_callback callback;
+ void *user_data;
+};
+
+static cynara_async *cynara;
+static int cynara_fd = -1;
+static guint cynara_fd_id;
+static gboolean cynara_skip;
+static GHashTable *cynara_tbl;
+
+static void cyn_err(const char *prefix, int err)
+{
+ char errmsg[128];
+
+ errmsg[0] = '\0';
+ cynara_strerror(err, errmsg, sizeof(errmsg));
+ bxt_err("Cynara: %s%s%d : %s", prefix ? prefix : "", prefix ? ": " : "",
+ err, errmsg);
+}
+
+static void free_cb(gpointer data)
+{
+ struct bxt_cyn_cb *cyn_cb = data;
+
+ if (!cyn_cb)
+ return;
+
+ if (cyn_cb->callback) {
+ if (cynara) {
+ int r;
+
+ r = cynara_async_cancel_request(cynara, cyn_cb->id);
+ if (r != CYNARA_API_SUCCESS)
+ cyn_err("cancel", r);
+ }
+
+ cyn_cb->callback(cyn_cb->cli, BUXTON_CYNARA_CANCELED,
+ cyn_cb->user_data);
+ }
+
+ free(cyn_cb);
+ bxt_dbg("Cynara: free %p", cyn_cb);
+}
+
+static gboolean proc_cb(gint fd, GIOCondition cond, gpointer data)
+{
+ int r;
+
+ if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) {
+ cynara_fd_id = 0;
+ return G_SOURCE_REMOVE;
+ }
+
+ r = cynara_async_process(cynara);
+ if (r != CYNARA_API_SUCCESS)
+ cyn_err("process", r);
+
+ return G_SOURCE_CONTINUE;
+}
+
+static void status_cb(int old_fd, int new_fd, cynara_async_status status,
+ void *data)
+{
+ if (old_fd != -1) {
+ if (cynara_fd_id) {
+ g_source_remove(cynara_fd_id);
+ cynara_fd_id = 0;
+ }
+ cynara_fd = -1;
+ }
+
+ if (new_fd != -1) {
+ GIOCondition cond;
+
+ cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+
+ if (status == CYNARA_STATUS_FOR_RW)
+ cond |= G_IO_OUT;
+
+ cynara_fd_id = g_unix_fd_add(new_fd, cond, proc_cb, data);
+ cynara_fd = new_fd;
+ }
+}
+
+static enum buxton_cynara_res check_cache(const char *clabel, const char *sess,
+ const char *uid, const char *priv)
+{
+ int r;
+
+ assert(cynara);
+ assert(clabel);
+ assert(sess);
+ assert(uid);
+ assert(priv);
+
+ r = cynara_async_check_cache(cynara, clabel, sess, uid, priv);
+ switch (r) {
+ case CYNARA_API_ACCESS_ALLOWED:
+ r = BUXTON_CYNARA_ALLOWED;
+ break;
+ case CYNARA_API_ACCESS_DENIED:
+ r = BUXTON_CYNARA_DENIED;
+ break;
+ case CYNARA_API_CACHE_MISS:
+ r = BUXTON_CYNARA_UNKNOWN;
+ break;
+ default:
+ cyn_err("cache", r);
+ r = BUXTON_CYNARA_UNKNOWN;
+ break;
+ }
+
+ return r;
+}
+
+static void resp_cb(cynara_check_id id, cynara_async_call_cause cause,
+ int resp, void *data)
+{
+ struct bxt_cyn_cb *cyn_cb;
+ enum buxton_cynara_res res;
+
+ bxt_dbg("check id %u, cause %d, resp %d", id, cause, resp);
+
+ if (!cynara_tbl)
+ return;
+
+ cyn_cb = g_hash_table_lookup(cynara_tbl, GUINT_TO_POINTER(id));
+ if (!cyn_cb || cyn_cb != data) {
+ bxt_err("Cynara: resp: %u not exist in table", id);
+ return;
+ }
+
+ switch (cause) {
+ case CYNARA_CALL_CAUSE_ANSWER:
+ if (resp == CYNARA_API_ACCESS_ALLOWED)
+ res = BUXTON_CYNARA_ALLOWED;
+ else
+ res = BUXTON_CYNARA_DENIED;
+ break;
+ case CYNARA_CALL_CAUSE_CANCEL:
+ case CYNARA_CALL_CAUSE_FINISH:
+ case CYNARA_CALL_CAUSE_SERVICE_NOT_AVAILABLE:
+ default:
+ bxt_err("Cynara: resp: not answer");
+ res = BUXTON_CYNARA_ERROR;
+ break;
+ }
+
+ if (res == BUXTON_CYNARA_DENIED) {
+ bxt_info("id %u denied%s", id,
+ cynara_skip ? "(ignored)" : "");
+ if (cynara_skip)
+ res = BUXTON_CYNARA_ALLOWED;
+ }
+
+ if (cyn_cb->callback) {
+ cyn_cb->callback(cyn_cb->cli, res, cyn_cb->user_data);
+ cyn_cb->callback = NULL;
+ }
+ g_hash_table_remove(cynara_tbl, GUINT_TO_POINTER(id));
+}
+
+static enum buxton_cynara_res check_server(struct bxt_client *client,
+ const char *clabel, const char *sess,
+ const char *uid, const char *priv,
+ buxton_cynara_callback callback, void *user_data)
+{
+ int r;
+ struct bxt_cyn_cb *cyn_cb;
+
+ assert(cynara);
+ assert(cynara_tbl);
+
+ assert(client);
+ assert(clabel);
+ assert(sess);
+ assert(uid);
+ assert(priv);
+ assert(callback);
+
+ cyn_cb = calloc(1, sizeof(*cyn_cb));
+ if (!cyn_cb)
+ return BUXTON_CYNARA_ERROR;
+
+ r = cynara_async_create_request(cynara, clabel, sess, uid, priv,
+ &cyn_cb->id, resp_cb, cyn_cb);
+ if (r != CYNARA_API_SUCCESS) {
+ cyn_err("request", r);
+ free(cyn_cb);
+ return BUXTON_CYNARA_ERROR;
+ }
+
+ bxt_info("'%s;%s;%s;%s' id %u", clabel, sess, uid, priv, cyn_cb->id);
+
+ cyn_cb->cli = client;
+ cyn_cb->callback = callback;
+ cyn_cb->user_data = user_data;
+
+ g_hash_table_insert(cynara_tbl, GUINT_TO_POINTER(cyn_cb->id), cyn_cb);
+ bxt_dbg("Cynara: %p added", cyn_cb);
+
+ return BUXTON_CYNARA_UNKNOWN;
+}
+
+enum buxton_cynara_res buxton_cynara_check(struct bxt_client *client,
+ const char *client_label, const char *session,
+ uid_t uid, const char *priv,
+ buxton_cynara_callback callback, void *user_data)
+{
+ int r;
+ char uid_str[16];
+
+ if (!client || !client_label || !session || !priv || !callback) {
+ errno = EINVAL;
+ bxt_err("cynara check: invalid argument:%s%s%s%s%s",
+ client ? "" : " client",
+ client_label ? "" : " client_label",
+ session ? "" : " session",
+ priv ? "" : " privilege",
+ callback ? "" : " callback");
+ return BUXTON_CYNARA_ERROR;
+ }
+
+ if (!*priv)
+ return BUXTON_CYNARA_ALLOWED;
+
+ if (!cynara) {
+ bxt_err("Cynara is not initialized");
+ errno = ENOTCONN;
+ return BUXTON_CYNARA_ERROR;
+ }
+
+ snprintf(uid_str, sizeof(uid_str), "%d", uid);
+
+ r = check_cache(client_label, session, uid_str, priv);
+ if (r != BUXTON_CYNARA_UNKNOWN) {
+ /* r should be ALLOWED or DENIED */
+ if (r == BUXTON_CYNARA_DENIED) {
+ bxt_info("'%s;%s;%s;%s' denied%s",
+ client_label, session, uid_str, priv,
+ cynara_skip ? "(ignored)" : "");
+ if (cynara_skip)
+ r = BUXTON_CYNARA_ALLOWED;
+ }
+ return r;
+ }
+
+ return check_server(client, client_label, session, uid_str, priv,
+ callback, user_data);
+}
+
+void buxton_cynara_cancel(struct bxt_client *client)
+{
+ GHashTableIter iter;
+ struct bxt_cyn_cb *cyn_cb;
+
+ if (!cynara || !cynara_tbl || !client)
+ return;
+
+ g_hash_table_iter_init(&iter, cynara_tbl);
+
+ while (g_hash_table_iter_next(&iter, NULL, (gpointer *)&cyn_cb)) {
+ if (cyn_cb->cli == client)
+ g_hash_table_iter_remove(&iter);
+ }
+}
+
+int buxton_cynara_init(void)
+{
+ int r;
+ char *skip;
+
+ if (cynara)
+ return 0;
+
+ skip = getenv(BUXTON_CYNARA_PERMISSIVE_MODE);
+ if (skip && skip[0] == '1') {
+ bxt_info("Permissive mode enabled");
+ cynara_skip = TRUE;
+ }
+
+ cynara_tbl = g_hash_table_new_full(NULL, NULL, NULL, free_cb);
+ if (!cynara_tbl)
+ return -1;
+
+ r = cynara_async_initialize(&cynara, NULL, status_cb, NULL);
+ if (r != CYNARA_API_SUCCESS) {
+ cyn_err("init", r);
+ return -1;
+ }
+
+ return 0;
+}
+
+void buxton_cynara_exit(void)
+{
+ if (!cynara)
+ return;
+
+ if (cynara_fd_id) {
+ g_source_remove(cynara_fd_id);
+ cynara_fd_id = 0;
+ }
+
+ g_hash_table_destroy(cynara_tbl);
+ cynara_tbl = NULL;
+
+ cynara_async_finish(cynara);
+ cynara = NULL;
+ cynara_fd = -1;
+}
+
diff --git a/daemon/cynara.h b/daemon/cynara.h
new file mode 100644
index 0000000..31f629e
--- /dev/null
+++ b/daemon/cynara.h
@@ -0,0 +1,46 @@
+/*
+ * Buxton
+ *
+ * Copyright (C) 2015 Samsung Electronics Co., Ltd.
+ *
+ * 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
+
+#include <sys/types.h>
+
+int buxton_cynara_init(void);
+void buxton_cynara_exit(void);
+
+enum buxton_cynara_res {
+ BUXTON_CYNARA_ERROR = -1,
+ BUXTON_CYNARA_UNKNOWN,
+ BUXTON_CYNARA_ALLOWED,
+ BUXTON_CYNARA_DENIED,
+ BUXTON_CYNARA_CANCELED,
+ BUXTON_CYNARA_MAX /* sentinel value */
+};
+
+struct bxt_client;
+
+typedef void (*buxton_cynara_callback)(struct bxt_client *client,
+ enum buxton_cynara_res res, void *user_data);
+
+enum buxton_cynara_res buxton_cynara_check(struct bxt_client *client,
+ const char *client_label, const char *session,
+ uid_t uid, const char *priv,
+ buxton_cynara_callback callback, void *user_data);
+
+void buxton_cynara_cancel(struct bxt_client *client);
+
diff --git a/daemon/daemon.c b/daemon/daemon.c
new file mode 100644
index 0000000..69c5325
--- /dev/null
+++ b/daemon/daemon.c
@@ -0,0 +1,877 @@
+/*
+ * Buxton
+ *
+ * Copyright (C) 2015 Samsung Electronics Co., Ltd.
+ *
+ * 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 <errno.h>
+#include <assert.h>
+#include <signal.h>
+#include <sys/signalfd.h>
+#include <string.h>
+
+#include <glib.h>
+#include <glib-unix.h>
+
+#include "common.h"
+#include "log.h"
+#include "direct.h"
+#include "proto.h"
+#include "serialize.h"
+#include "config.h"
+
+#include "daemon.h"
+#include "socks.h"
+#include "cynara.h"
+
+struct bxt_noti {
+ char *layer_key;
+ GList *clients; /* struct bxt_client */
+};
+
+static gboolean signal_cb(gint fd, GIOCondition cond, gpointer data)
+{
+ struct bxt_daemon *bxtd = data;
+ int r;
+ struct signalfd_siginfo si;
+
+ assert(bxtd);
+
+ r = read(fd, &si, sizeof(struct signalfd_siginfo));
+ if (r == -1) {
+ bxt_err("Read signalfd: %d", errno);
+ return G_SOURCE_REMOVE;
+ }
+
+ if (r != sizeof(struct signalfd_siginfo)) {
+ bxt_err("Invalid siginfo received");
+ return G_SOURCE_CONTINUE;
+ }
+
+ switch (si.ssi_signo) {
+ case SIGINT:
+ case SIGTERM:
+ assert(bxtd->loop);
+ g_main_loop_quit(bxtd->loop);
+ break;
+ case SIGPIPE:
+ bxt_err("SIGPIPE received");
+ break;
+ }
+
+ return G_SOURCE_CONTINUE;
+}
+
+static int create_sigfd(void)
+{
+ int r;
+ int fd;
+ sigset_t mask;
+ sigset_t old;
+
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGINT);
+ sigaddset(&mask, SIGTERM);
+ sigaddset(&mask, SIGPIPE);
+
+ r = sigprocmask(SIG_BLOCK, &mask, &old);
+ if (r == -1) {
+ bxt_err("sigprocmask: %d", errno);
+ return -1;
+ }
+
+ fd = signalfd(-1, &mask, SFD_NONBLOCK | SFD_CLOEXEC);
+ if (fd == -1) {
+ bxt_err("signalfd: %d", errno);
+ sigprocmask(SIG_SETMASK, &old, NULL);
+ return -1;
+ }
+
+ return fd;
+}
+
+static void remove_noti_cli(struct bxt_daemon *bxtd, struct bxt_client *cli)
+{
+ GList *l;
+ struct bxt_noti *noti;
+
+ for (l = cli->notilist; l; l = g_list_next(l)) {
+ noti = l->data;
+
+ noti->clients = g_list_remove(noti->clients, cli);
+ bxt_dbg("client %p deleted from noti %p list", cli, noti);
+
+ if (!noti->clients) {
+ g_hash_table_remove(bxtd->notis, noti->layer_key);
+ bxt_dbg("noti %p deleted from table", noti);
+ }
+ }
+}
+
+static void free_client(struct bxt_client *cli)
+{
+ if (!cli)
+ return;
+
+ remove_noti_cli(cli->bxtd, cli);
+ g_list_free(cli->notilist);
+ cli->notilist = NULL;
+
+ if (cli->fd_id)
+ g_source_remove(cli->fd_id);
+
+ if (cli->fd != -1)
+ close(cli->fd);
+
+ free(cli->label);
+ free(cli);
+ bxt_dbg("free client %p", cli);
+}
+
+static void remove_notilist(struct bxt_noti *noti)
+{
+ GList *l;
+ struct bxt_client *cli;
+
+ for (l = noti->clients; l; l = g_list_next(l)) {
+ cli = l->data;
+
+ cli->notilist = g_list_remove(cli->notilist, noti);
+ bxt_dbg("noti %p deleted from client %p", noti, cli);
+ }
+}
+
+static void free_noti(struct bxt_noti *noti)
+{
+ if (!noti)
+ return;
+
+ remove_notilist(noti);
+ g_list_free(noti->clients);
+ noti->clients = NULL;
+ free(noti->layer_key);
+
+ free(noti);
+ bxt_dbg("free noti %p", noti);
+}
+
+static gboolean del_client(gpointer data)
+{
+ struct bxt_client *cli = data;
+
+ assert(cli);
+ assert(cli->bxtd);
+ assert(cli->bxtd->clients);
+
+ buxton_cynara_cancel(cli);
+
+ bxt_dbg("Client %p removed", cli);
+ g_hash_table_remove(cli->bxtd->clients, cli);
+
+ return G_SOURCE_REMOVE;
+}
+
+static void send_res(struct bxt_client *cli, struct response *resp)
+{
+ int r;
+ uint8_t *data;
+ int len;
+
+ r = serialz_response(resp->type, resp->msgid, resp->res, resp->val,
+ resp->nmlen, resp->names, &data, &len);
+ if (r == -1) {
+ bxt_err("send res: fd %d msgid %u: serialize error %d",
+ cli->fd, resp->msgid, errno);
+ return;
+ }
+
+ r = proto_send_block(cli->fd, resp->type, data, len);
+
+ free(data);
+
+ if (r == -1)
+ bxt_err("send res: error %d", errno);
+}
+
+static char *get_search_key_u(const struct buxton_layer *layer, const char *key)
+{
+ char uid[16];
+ char *u;
+ const struct layer *ly;
+
+ ly = conf_get_layer(layer->name);
+ if (!ly)
+ return NULL;
+
+ if (ly->type == LAYER_USER) {
+ snprintf(uid, sizeof(uid), "%d", layer->uid);
+ u = uid;
+ } else {
+ u = NULL;
+ }
+
+ return get_search_key(layer, key, u);
+}
+
+static void send_notis(struct bxt_daemon *bxtd, struct request *rqst)
+{
+ int r;
+ char *lykey;
+ struct bxt_noti *noti;
+ GList *l;
+ struct request req;
+ uint8_t *data;
+ int len;
+
+ assert(bxtd);
+ assert(rqst);
+
+ lykey = get_search_key_u(rqst->layer, rqst->key);
+ if (!lykey)
+ return;
+
+ noti = g_hash_table_lookup(bxtd->notis, lykey);
+
+ free(lykey);
+
+ if (!noti)
+ return;
+
+ memset(&req, 0, sizeof(req));
+ req.type = MSG_NOTI;
+ req.layer = rqst->layer;
+ req.key = rqst->key;
+ req.val = rqst->val;
+
+ r = serialz_request(&req, &data, &len);
+ if (r == -1)
+ return;
+
+ for (l = noti->clients; l; l = g_list_next(l)) {
+ struct bxt_client *cli = l->data;
+
+ r = proto_send(cli->fd, req.type, data, len);
+ if (r == -1)
+ bxt_err("send notis: cli %p error %d", cli, errno);
+ }
+
+ free(data);
+}
+
+static void proc_set(struct bxt_client *cli,
+ struct request *rqst, struct response *resp)
+{
+ int r;
+
+ assert(rqst);
+ assert(resp);
+
+ r = direct_set(rqst->layer, rqst->key, rqst->val);
+ if (r == -1) {
+ resp->res = errno;
+ return;
+ }
+ resp->res = 0;
+
+ send_notis(cli->bxtd, rqst);
+}
+
+static void proc_get(struct bxt_client *cli,
+ struct request *rqst, struct response *resp)
+{
+ int r;
+ struct buxton_value *val;
+
+ assert(rqst);
+ assert(resp);
+
+ val = calloc(1, sizeof(*val));
+ if (!val) {
+ resp->res = ENOMEM;
+ return;
+ }
+
+ r = direct_get(rqst->layer, rqst->key, val);
+ if (r == -1) {
+ free(val);
+ resp->res = errno;
+ return;
+ }
+
+ resp->res = 0;
+ resp->val = val;
+}
+
+static void proc_list(struct bxt_client *cli,
+ struct request *rqst, struct response *resp)
+{
+ int r;
+
+ assert(rqst);
+ assert(resp);
+
+ r = direct_list(rqst->layer, &resp->names, &resp->nmlen);
+ resp->res = (r == -1) ? errno : 0;
+}
+
+static void proc_create(struct bxt_client *cli,
+ struct request *rqst, struct response *resp)
+{
+ int r;
+
+ assert(cli);
+ assert(rqst);
+ assert(resp);
+
+ if (cli->cred.uid != 0) {
+ resp->res = EPERM;
+ return;
+ }
+
+ r = direct_create(rqst->layer, rqst->key, rqst->rpriv, rqst->wpriv,
+ rqst->val);
+ resp->res = (r == -1) ? errno : 0;
+}
+
+static void proc_unset(struct bxt_client *cli,
+ struct request *rqst, struct response *resp)
+{
+ int r;
+
+ assert(cli);
+ assert(rqst);
+ assert(resp);
+
+ if (cli->cred.uid != 0) {
+ resp->res = EPERM;
+ return;
+ }
+
+ r = direct_unset(rqst->layer, rqst->key);
+ resp->res = (r == -1) ? errno : 0;
+}
+
+static void add_cli(struct bxt_noti *noti, struct bxt_client *client)
+{
+ GList *l;
+
+ for (l = noti->clients; l; l = g_list_next(l)) {
+ if (l->data == client)
+ return;
+ }
+
+ noti->clients = g_list_append(noti->clients, client);
+ client->notilist = g_list_append(client->notilist, noti);
+ bxt_dbg("proc notify: noti %p '%s' client %p added",
+ noti, noti->layer_key, client);
+}
+
+static void proc_notify(struct bxt_client *cli,
+ struct request *rqst, struct response *resp)
+{
+ int r;
+ char *lykey;
+ struct bxt_noti *noti;
+
+ assert(cli);
+ assert(rqst);
+ assert(resp);
+
+ assert(rqst->layer);
+ assert(rqst->key);
+
+ r = direct_check(rqst->layer, rqst->key);
+ if (r == -1) {
+ resp->res = errno;
+ return;
+ }
+
+ lykey = get_search_key_u(rqst->layer, rqst->key);
+ if (!lykey) {
+ resp->res = errno;
+ return;
+ }
+
+ noti = g_hash_table_lookup(cli->bxtd->notis, lykey);
+ if (!noti) {
+ noti = calloc(1, sizeof(*noti));
+ if (!noti) {
+ resp->res = errno;
+ return;
+ }
+ noti->layer_key = lykey;
+
+ g_hash_table_insert(cli->bxtd->notis, noti->layer_key, noti);
+ bxt_dbg("proc notify: noti %p '%s' added", noti, lykey);
+ } else {
+ free(lykey);
+ }
+
+ add_cli(noti, cli);
+ resp->res = 0;
+}
+
+static void proc_unnotify(struct bxt_client *cli,
+ struct request *rqst, struct response *resp)
+{
+ char *lykey;
+ struct bxt_noti *noti;
+
+ assert(cli);
+ assert(rqst);
+ assert(resp);
+
+ assert(rqst->layer);
+ assert(rqst->key);
+
+ lykey = get_search_key_u(rqst->layer, rqst->key);
+ if (!lykey) {
+ resp->res = errno;
+ return;
+ }
+
+ noti = g_hash_table_lookup(cli->bxtd->notis, lykey);
+
+ free(lykey);
+
+ if (!noti) {
+ resp->res = ENOENT;
+ return;
+ }
+
+ cli->notilist = g_list_remove(cli->notilist, noti);
+ noti->clients = g_list_remove(noti->clients, cli);
+ bxt_dbg("proc notify: noti %p '%s' client %p deleted",
+ noti, noti->layer_key, cli);
+
+ if (!noti->clients) /* no client */
+ g_hash_table_remove(cli->bxtd->notis, lykey);
+
+ resp->res = 0;
+}
+
+static void proc_set_priv(struct bxt_client *cli,
+ struct request *rqst, struct response *resp)
+{
+ int r;
+ enum buxton_priv_type type;
+
+ assert(cli);
+ assert(rqst);
+ assert(resp);
+
+ if (cli->cred.uid != 0) {
+ resp->res = EPERM;
+ return;
+ }
+
+ if (rqst->type == MSG_SET_WP)
+ type = BUXTON_PRIV_WRITE;
+ else
+ type = BUXTON_PRIV_READ;
+
+ r = direct_set_priv(rqst->layer, rqst->key, type, rqst->val->value.s);
+ resp->res = (r == -1) ? errno : 0;
+}
+
+static void proc_get_priv(struct bxt_client *cli,
+ struct request *rqst, struct response *resp)
+{
+ int r;
+ enum buxton_priv_type type;
+ struct buxton_value *val;
+
+ assert(rqst);
+ assert(resp);
+
+ val = calloc(1, sizeof(*val));
+ if (!val) {
+ resp->res = ENOMEM;
+ return;
+ }
+
+ if (rqst->type == MSG_GET_WP)
+ type = BUXTON_PRIV_WRITE;
+ else
+ type = BUXTON_PRIV_READ;
+
+ val->type = BUXTON_TYPE_PRIVILEGE;
+ r = direct_get_priv(rqst->layer, rqst->key, type, &val->value.s);
+ if (r == -1) {
+ free(val);
+ resp->res = errno;
+ return;
+ }
+
+ resp->res = 0;
+ resp->val = val;
+}
+
+typedef void (*proc_func)(struct bxt_client *cli,
+ struct request *, struct response *);
+
+static proc_func proc_funcs[MSG_MAX] = {
+ [MSG_SET] = proc_set,
+ [MSG_GET] = proc_get,
+ [MSG_LIST] = proc_list,
+ [MSG_CREAT] = proc_create,
+ [MSG_UNSET] = proc_unset,
+ [MSG_NOTIFY] = proc_notify,
+ [MSG_UNNOTIFY] = proc_unnotify,
+ [MSG_SET_WP] = proc_set_priv,
+ [MSG_SET_RP] = proc_set_priv,
+ [MSG_GET_WP] = proc_get_priv,
+ [MSG_GET_RP] = proc_get_priv,
+};
+
+static void proc_msg(struct bxt_client *cli,
+ struct request *rqst, struct response *resp)
+{
+ assert(cli);
+ assert(rqst);
+ assert(resp);
+
+ if (rqst->type <= MSG_UNKNOWN || rqst->type >= MSG_MAX) {
+ bxt_err("proc msg: invalid type %d", rqst->type);
+ resp->res = EINVAL;
+ return;
+ }
+
+ assert(rqst->layer);
+ if (cli->cred.uid != 0 && cli->cred.uid != rqst->layer->uid) {
+ /* Only root can access other user's */
+ resp->res = EPERM;
+ return;
+ }
+
+ if (!proc_funcs[rqst->type]) {
+ bxt_err("proc msg: %d not supported", rqst->type);
+ resp->res = ENOTSUP;
+ return;
+ }
+
+ proc_funcs[rqst->type](cli, rqst, resp);
+}
+
+static void cyn_cb(struct bxt_client *cli, enum buxton_cynara_res res,
+ void *data)
+{
+ struct request *rqst = data;
+ struct response resp;
+
+ assert(rqst);
+
+ memset(&resp, 0, sizeof(resp));
+ resp.type = rqst->type;
+ resp.msgid = rqst->msgid;
+
+ switch (res) {
+ case BUXTON_CYNARA_ALLOWED:
+ proc_msg(cli, rqst, &resp);
+ break;
+ case BUXTON_CYNARA_DENIED:
+ default:
+ resp.res = EPERM;
+ break;
+ }
+
+ send_res(cli, &resp);
+
+ free_response(&resp);
+ free_request(rqst);
+ free(rqst);
+}
+
+static int check_priv(struct bxt_client *cli, struct request *rqst)
+{
+ int r;
+ enum buxton_priv_type type;
+ char *priv;
+
+ assert(cli);
+ assert(rqst);
+
+ switch (rqst->type) {
+ case MSG_SET:
+ case MSG_GET:
+ case MSG_NOTIFY:
+ if (rqst->type == MSG_SET)
+ type = BUXTON_PRIV_WRITE;
+ else
+ type = BUXTON_PRIV_READ;
+
+ r = direct_get_priv(rqst->layer, rqst->key, type, &priv);
+ if (r == -1) {
+ r = BUXTON_CYNARA_ERROR;
+ break;
+ }
+
+ bxt_dbg("priv '%s'", priv);
+
+ r = buxton_cynara_check(cli, cli->label, "", cli->cred.uid,
+ priv, cyn_cb, rqst);
+ free(priv);
+ break;
+ default:
+ r = BUXTON_CYNARA_ALLOWED;
+ break;
+ }
+
+ return r;
+}
+
+static int proc_serialized_msg(struct bxt_client *cli, uint8_t *data, int len)
+{
+ int r;
+ struct request *rqst;
+ struct response resp;
+
+ rqst = calloc(1, sizeof(*rqst));
+ if (!rqst)
+ return -1;
+
+ r = deserialz_request(data, len, rqst);
+ if (r == -1) {
+ free(rqst);
+ return -1;
+ }
+
+ r = check_priv(cli, rqst);
+
+ /* wait for cynara response, rqst should be freed in callback */
+ if (r == BUXTON_CYNARA_UNKNOWN)
+ return 0;
+
+ memset(&resp, 0, sizeof(resp));
+
+ resp.type = rqst->type;
+ resp.msgid = rqst->msgid;
+
+ if (r != BUXTON_CYNARA_ALLOWED)
+ resp.res = r == BUXTON_CYNARA_DENIED ? EPERM : errno;
+ else
+ proc_msg(cli, rqst, &resp);
+
+ send_res(cli, &resp);
+
+ free_response(&resp);
+ free_request(rqst);
+ free(rqst);
+
+ return 0;
+}
+
+static int proc_client_msg(struct bxt_client *cli)
+{
+ int r;
+ uint8_t *data;
+ int len;
+ enum message_type type;
+
+ r = proto_recv(cli->fd, &type, &data, &len);
+ if (r == -1)
+ return -1;
+
+ switch (type) {
+ case MSG_SET:
+ case MSG_GET:
+ case MSG_CREAT:
+ case MSG_UNSET:
+ case MSG_LIST:
+ case MSG_NOTIFY:
+ case MSG_UNNOTIFY:
+ case MSG_SET_WP:
+ case MSG_SET_RP:
+ case MSG_GET_WP:
+ case MSG_GET_RP:
+ r = proc_serialized_msg(cli, data, len);
+ break;
+ case MSG_NOTI:
+ default:
+ bxt_err("proc msg: Invalid message type %d", type);
+ r = -1;
+ break;
+ }
+
+ free(data);
+
+ return r;
+}
+
+static gboolean client_cb(gint fd, GIOCondition cond, gpointer data)
+{
+ int r;
+ struct bxt_client *cli = data;
+
+ assert(cli);
+
+ bxt_dbg("Client %d: cond %x", fd, cond);
+
+ if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
+ if (cond & (G_IO_ERR | G_IO_NVAL))
+ bxt_err("Client %d: IO error", fd);
+
+ cli->fd_id = 0;
+ g_idle_add(del_client, cli);
+ return G_SOURCE_REMOVE;
+ }
+
+ if (cli->cred.pid == 0) {
+ sock_get_client_cred(fd, &cli->cred);
+ sock_get_client_label(fd, &cli->label);
+ }
+
+ r = proc_client_msg(cli);
+ if (r == -1) {
+ cli->fd_id = 0;
+ g_idle_add(del_client, cli);
+ return G_SOURCE_REMOVE;
+ }
+
+ return G_SOURCE_CONTINUE;
+}
+
+static void add_client(struct bxt_daemon *bxtd, int fd)
+{
+ int r;
+ struct bxt_client *cli;
+
+ r = sock_set_client(fd);
+ if (r == -1) {
+ close(fd);
+ return;
+ }
+
+ cli = calloc(1, sizeof(*cli));
+ if (!cli) {
+ bxt_err("Client %d: %d", fd, errno);
+ close(fd);
+ return;
+ }
+
+ cli->fd = fd;
+ cli->bxtd = bxtd;
+
+ cli->fd_id = g_unix_fd_add(fd,
+ G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+ client_cb, cli);
+
+ g_hash_table_insert(bxtd->clients, cli, cli);
+ bxt_dbg("Client %p added, fd %d", cli, fd);
+}
+
+static gboolean accept_cb(gint fd, GIOCondition cond, gpointer data)
+{
+ struct bxt_daemon *bxtd = data;
+ int cfd;
+ struct sockaddr sa;
+ socklen_t addrlen;
+
+ assert(bxtd);
+
+ bxt_dbg("Accept: fd %d cond %x", fd, cond);
+
+ cfd = accept(fd, (struct sockaddr *)&sa, &addrlen);
+ if (cfd == -1) {
+ bxt_err("Accept: %d", errno);
+ return G_SOURCE_CONTINUE;
+ }
+
+ add_client(bxtd, cfd);
+
+ return G_SOURCE_CONTINUE;
+}
+
+static void bxt_exit(struct bxt_daemon *bxtd)
+{
+ buxton_cynara_exit();
+
+ if (bxtd->notis)
+ g_hash_table_destroy(bxtd->notis);
+
+ if (bxtd->clients)
+ g_hash_table_destroy(bxtd->clients);
+
+ if (bxtd->loop)
+ g_main_loop_unref(bxtd->loop);
+
+ if (bxtd->sk != -1)
+ close(bxtd->sk);
+
+ direct_exit();
+
+ if (bxtd->sigfd != -1)
+ close(bxtd->sigfd);
+}
+
+static int bxt_init(struct bxt_daemon *bxtd, const char *confpath)
+{
+ int r;
+
+ assert(bxtd);
+
+ bxtd->clients = g_hash_table_new_full(g_direct_hash, g_direct_equal,
+ (GDestroyNotify)free_client, NULL);
+ if (!bxtd->clients)
+ return -1;
+
+ bxtd->notis = g_hash_table_new_full(g_str_hash, g_str_equal,
+ NULL, (GDestroyNotify)free_noti);
+ if (!bxtd->notis)
+ return -1;
+
+ bxtd->sigfd = create_sigfd();
+ g_unix_fd_add(bxtd->sigfd, G_IO_IN, signal_cb, bxtd);
+
+ r = direct_init(MODULE_DIR, confpath);
+ if (r == -1)
+ return -1;
+
+ bxtd->sk = sock_get_server(SOCKPATH);
+ if (!bxtd->sk == -1)
+ return -1;
+
+ bxtd->sk_id = g_unix_fd_add(bxtd->sk, G_IO_IN, accept_cb, bxtd);
+
+ buxton_cynara_init();
+
+ bxtd->loop = g_main_loop_new(NULL, FALSE);
+
+ return 0;
+}
+
+int start_daemon(struct bxt_daemon *bxtd, const char *confpath)
+{
+ int r;
+
+ assert(bxtd);
+
+ if (!confpath)
+ confpath = CONFPATH;
+
+ r = bxt_init(bxtd, confpath);
+ if (r == -1) {
+ bxt_exit(bxtd);
+ return EXIT_FAILURE;
+ }
+
+ g_main_loop_run(bxtd->loop);
+ bxt_exit(bxtd);
+
+ return EXIT_SUCCESS;
+}
+
diff --git a/daemon/daemon.h b/daemon/daemon.h
new file mode 100644
index 0000000..ddcb0ca
--- /dev/null
+++ b/daemon/daemon.h
@@ -0,0 +1,51 @@
+/*
+ * Buxton
+ *
+ * Copyright (C) 2015 Samsung Electronics Co., Ltd.
+ *
+ * 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
+
+#define _GNU_SOURCE
+#include <unistd.h>
+#include <sys/socket.h>
+
+#include <glib.h>
+
+struct bxt_daemon {
+ GMainLoop *loop;
+ int sigfd;
+
+ int sk; /* server socket */
+ guint sk_id; /* source ID for sk */
+
+ GHashTable *clients; /* struct bxt_client */
+ GHashTable *notis; /* struct bxt_noti */
+};
+
+struct bxt_client {
+ int fd;
+ guint fd_id;
+
+ struct ucred cred;
+ char *label;
+
+ GList *notilist; /* struct bxt_noti */
+
+ struct bxt_daemon *bxtd;
+};
+
+int start_daemon(struct bxt_daemon *bxtd, const char *confpath);
+
diff --git a/daemon/main.c b/daemon/main.c
new file mode 100644
index 0000000..1f5d8b1
--- /dev/null
+++ b/daemon/main.c
@@ -0,0 +1,133 @@
+/*
+ * Buxton
+ *
+ * Copyright (C) 2015 Samsung Electronics Co., Ltd.
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include "daemon.h"
+
+static const struct option const opts[] = {
+ { "config-file", required_argument, NULL, 'c' },
+ { "foreground", no_argument, NULL, 'f' },
+ { "help", no_argument, NULL, 'h' },
+ { NULL, 0, NULL, 0 },
+};
+
+static void usage(const char *name)
+{
+ printf(" Usage: %s [OPTION]\n\n", name);
+ printf(" -c, --config-file=[FILE] Path to configuration file\n");
+ printf(" -f, --foreground Don't daemonize\n");
+ printf(" -h, --help Display this help message\n");
+
+ exit(EXIT_FAILURE);
+}
+
+int daemonize(void)
+{
+ pid_t p;
+ int fd;
+ int r;
+
+ p = fork();
+ if (p == -1) {
+ perror("fork");
+ return -1;
+ }
+
+ /* parent exit */
+ if (p)
+ exit(EXIT_SUCCESS);
+
+ /* child process */
+ r = chdir("/");
+ if (r == -1)
+ fprintf(stderr, "chdir failed: %d\n", errno);
+
+ umask(022);
+ setsid();
+
+ close(STDIN_FILENO);
+ close(STDOUT_FILENO);
+ close(STDERR_FILENO);
+
+ fd = open("/dev/null", O_RDWR);
+ if (fd == -1)
+ return 0;
+
+ dup2(fd, STDIN_FILENO);
+ dup2(fd, STDOUT_FILENO);
+ dup2(fd, STDERR_FILENO);
+
+ if (fd > STDERR_FILENO)
+ close(fd);
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ char const *confpath;
+ int fg;
+ int r;
+ struct bxt_daemon bxtd = {
+ .sigfd = -1,
+ .sk = -1,
+ };
+
+ fg = 0;
+ confpath = NULL;
+
+ while (optind < argc) {
+ int c;
+
+ c = getopt_long(argc, argv, "c:fh", opts, NULL);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'c':
+ confpath = optarg;
+ break;
+ case 'f':
+ fg = 1;
+ break;
+ case 'h':
+ default:
+ usage(argv[0]);
+ break;
+ }
+ }
+
+ if (!fg) {
+ r = daemonize();
+ if (r == -1)
+ return EXIT_FAILURE;
+ }
+
+ return start_daemon(&bxtd, confpath);
+}
+
diff --git a/daemon/socks.c b/daemon/socks.c
new file mode 100644
index 0000000..2156a7b
--- /dev/null
+++ b/daemon/socks.c
@@ -0,0 +1,232 @@
+/*
+ * Buxton
+ *
+ * Copyright (C) 2015 Samsung Electronics Co., Ltd.
+ *
+ * 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 <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <assert.h>
+
+#include <systemd/sd-daemon.h>
+
+#include "log.h"
+
+#include "socks.h"
+
+#define SOCKET_TIMEOUT 5 /* seconds */
+
+static int smack_not_supported;
+
+static int sock_create(const char *path)
+{
+ int r;
+ int fd;
+ struct sockaddr_un sa;
+
+ assert(path && *path);
+
+ fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (fd == -1) {
+ bxt_err("Socket '%s': socket %d", path, errno);
+ return -1;
+ }
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sun_family = AF_UNIX;
+ strncpy(sa.sun_path, path, sizeof(sa.sun_path));
+ sa.sun_path[sizeof(sa.sun_path) - 1] = '\0';
+
+ r = unlink(sa.sun_path);
+ if (r == -1 && errno != ENOENT) {
+ bxt_err("Socket '%s': unlink %d", path, errno);
+ close(fd);
+ return -1;
+ }
+
+ r = bind(fd, (struct sockaddr *)&sa, sizeof(sa));
+ if (r == -1) {
+ bxt_err("Socket '%s': bind %d", path, errno);
+ close(fd);
+ return -1;
+ }
+
+ chmod(sa.sun_path, 0666);
+
+ r = listen(fd, 128);
+ if (r == -1) {
+ bxt_err("Socket '%s': listen %d", path, errno);
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+int sock_get_server(const char *path)
+{
+ int n;
+ int i;
+ int r;
+ int fd;
+
+ if (!path || !*path) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ n = sd_listen_fds(0);
+ if (n < 0) {
+ bxt_err("sd_listen_fds: %d", n);
+ return -1;
+ }
+
+ if (n == 0)
+ return sock_create(path);
+
+ fd = -1;
+ for (i = SD_LISTEN_FDS_START; i < SD_LISTEN_FDS_START + n; i++) {
+ r = sd_is_socket_unix(i, SOCK_STREAM, -1, path, 0);
+ if (r > 0) {
+ fd = i;
+ break;
+ }
+ }
+
+ if (fd == -1) {
+ bxt_err("Socket '%s' is not passed", path);
+ return sock_create(path);
+ }
+
+ return fd;
+}
+
+int sock_set_client(int fd)
+{
+ int r;
+ struct timeval tv;
+ int on;
+
+ r = fcntl(fd, F_SETFL, O_NONBLOCK);
+ if (r == -1) {
+ bxt_err("Client %d: set NONBLOCK: %d", fd, errno);
+ return -1;
+ }
+
+ /* need SO_PRIORITY ? */
+
+ tv.tv_sec = SOCKET_TIMEOUT;
+ tv.tv_usec = 0;
+ r = setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (void *)&tv,
+ sizeof(struct timeval));
+ if (r == -1) {
+ bxt_err("Client %d: set SO_RCVTIMEO: %d", fd, errno);
+ return -1;
+ }
+
+ on = 1;
+ r = setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
+ if (r == -1)
+ bxt_err("Client %d: set SO_PASSCRED: %d", fd, errno);
+
+ return 0;
+}
+
+int sock_get_client_cred(int fd, struct ucred *cred)
+{
+ int r;
+ socklen_t len;
+
+ if (fd < 0 || !cred) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ len = sizeof(*cred);
+ r = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, cred, &len);
+ if (r == -1) {
+ bxt_err("Client %d: get SO_PEERCRED: %d", fd, errno);
+ return -1;
+ }
+
+ bxt_dbg("Client %d: pid %d uid %u gid %u", fd,
+ cred->pid, cred->uid, cred->gid);
+
+ return 0;
+}
+
+int sock_get_client_label(int fd, char **label)
+{
+ int r;
+ socklen_t len;
+ char *l;
+
+ if (fd < 0 || !label) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (smack_not_supported) {
+ *label = NULL;
+ return 0;
+ }
+
+ len = 0;
+ r = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, NULL, &len);
+ if (r == 0) {
+ /* Never reach here */
+ bxt_err("Client %d: get SO_PEERSEC: 0", fd);
+ *label = NULL;
+ smack_not_supported = 1;
+ return 0;
+ }
+
+ if (errno == ENOPROTOOPT) {
+ smack_not_supported = 1;
+ *label = NULL;
+ return 0;
+ }
+
+ if (errno != ERANGE) {
+ bxt_err("Client %d: get SO_PEERSEC: %d", fd, errno);
+ return -1;
+ }
+
+ l = calloc(1, len + 1);
+ if (!l)
+ return -1;
+
+ r = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, l, &len);
+ if (r == -1) {
+ bxt_err("Cleint %d: get SO_PEERSEC: %d", fd, errno);
+ free(l);
+ return -1;
+ }
+
+ bxt_dbg("Client %d: Label '%s'", fd, l);
+
+ *label = l;
+
+ return 0;
+}
+
diff --git a/daemon/socks.h b/daemon/socks.h
new file mode 100644
index 0000000..46043ce
--- /dev/null
+++ b/daemon/socks.h
@@ -0,0 +1,29 @@
+/*
+ * Buxton
+ *
+ * Copyright (C) 2015 Samsung Electronics Co., Ltd.
+ *
+ * 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
+
+#define _GNU_SOURCE
+#include <sys/socket.h>
+
+int sock_get_server(const char *path);
+
+int sock_set_client(int fd);
+int sock_get_client_cred(int fd, struct ucred *cred);
+int sock_get_client_label(int fd, char **label);
+
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
new file mode 100644
index 0000000..0f5b356
--- /dev/null
+++ b/lib/CMakeLists.txt
@@ -0,0 +1,23 @@
+# libbuxton2.so build
+SET(TARGET buxton2)
+
+SET(SRC buxton2.c
+ ../common/proto.c
+ ../common/serialize.c
+ ../common/common.c)
+INCLUDE_DIRECTORIES(include)
+ADD_LIBRARY(${TARGET} SHARED ${SRC})
+SET_TARGET_PROPERTIES(${TARGET} PROPERTIES
+ COMPILE_FLAGS "-fvisibility=hidden"
+ VERSION ${VERSION}
+ SOVERSION ${MAJVER}
+)
+TARGET_LINK_LIBRARIES(${TARGET} ${PKGS_LDFLAGS})
+INSTALL(TARGETS ${TARGET} DESTINATION ${LIB_INSTALL_DIR} COMPONENT RuntimeLibraries)
+
+# buxton2.pc
+CONFIGURE_FILE(buxton2.pc.in buxton2.pc @ONLY)
+INSTALL(FILES buxton2.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig)
+
+# buxton2.h
+INSTALL(FILES include/buxton2.h DESTINATION ${INCLUDE_INSTALL_DIR})
diff --git a/lib/buxton2.c b/lib/buxton2.c
new file mode 100644
index 0000000..7a9fe86
--- /dev/null
+++ b/lib/buxton2.c
@@ -0,0 +1,2024 @@
+/*
+ * Buxton
+ *
+ * Copyright (C) 2015 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ */
+
+
+#include <stdlib.h>
+#include <errno.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <poll.h>
+#include <time.h>
+
+#include <glib.h>
+#include <glib-unix.h>
+
+#include "buxton2.h"
+
+#include "common.h"
+#include "log.h"
+#include "serialize.h"
+#include "proto.h"
+
+#ifndef EXPORT
+# define EXPORT __attribute__((visibility("default")))
+#endif
+
+struct bxt_req {
+ guint32 msgid;
+ struct buxton_layer *layer;
+ char *key;
+
+ buxton_response_callback callback;
+ buxton_list_callback list_cb;
+ void *data;
+
+ buxton_notify_callback notify;
+ void *notify_data;
+};
+
+struct bxt_noti_cb {
+ gboolean deleted;
+ buxton_notify_callback callback;
+ void *data;
+};
+
+struct bxt_noti {
+ guint id;
+ char *layer_key; /* layer + <tab>(0x09) + key */
+ gboolean reg;
+ GList *callbacks; /* data: bxt_noti_cb */
+};
+
+struct bxt_noti_res {
+ int res;
+ struct buxton_layer *layer;
+ char *key;
+ buxton_response_callback callback;
+ void *data;
+};
+
+struct buxton_client {
+ int fd;
+ guint fd_id;
+
+ buxton_status_callback st_callback;
+ void *st_data;
+
+ GHashTable *req_cbs; /* key: msgid, value: bxt_req */
+ GHashTable *noti_cbs; /* key: keyname, value: bxt_noti */
+};
+
+static GList *clients; /* data: buxton_client */
+static guint32 client_msgid;
+
+static struct buxton_value *value_create(enum buxton_key_type type, void *value)
+{
+ struct buxton_value *val;
+
+ if (!value) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ val = calloc(1, sizeof(*val));
+ if (!val)
+ return NULL;
+
+ switch (type) {
+ case BUXTON_TYPE_STRING:
+ val->value.s = *(char **)value;
+ break;
+ case BUXTON_TYPE_INT32:
+ val->value.i = *(int32_t *)value;
+ break;
+ case BUXTON_TYPE_UINT32:
+ val->value.u = *(uint32_t *)value;
+ break;
+ case BUXTON_TYPE_INT64:
+ val->value.i64 = *(int64_t *)value;
+ break;
+ case BUXTON_TYPE_UINT64:
+ val->value.u64 = *(uint64_t *)value;
+ break;
+ case BUXTON_TYPE_DOUBLE:
+ val->value.d = *(double *)value;
+ break;
+ case BUXTON_TYPE_BOOLEAN:
+ val->value.b = *(int32_t *)value;
+ break;
+ default:
+ free(val);
+ errno = EINVAL;
+ return NULL;
+ }
+ val->type = type;
+
+ return val;
+}
+
+EXPORT struct buxton_value *buxton_value_create_string(const char *s)
+{
+ struct buxton_value *v;
+ char *str;
+
+ if (!s) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ str = strdup(s);
+ if (!str)
+ return NULL;
+
+ v = value_create(BUXTON_TYPE_STRING, &str);
+ if (!v) {
+ free(str);
+ return NULL;
+ }
+
+ return v;
+}
+
+EXPORT struct buxton_value *buxton_value_create_int32(int32_t i)
+{
+ return value_create(BUXTON_TYPE_INT32, &i);
+}
+
+EXPORT struct buxton_value *buxton_value_create_uint32(uint32_t u)
+{
+ return value_create(BUXTON_TYPE_UINT32, &u);
+}
+
+EXPORT struct buxton_value *buxton_value_create_int64(int64_t i64)
+{
+ return value_create(BUXTON_TYPE_INT64, &i64);
+}
+
+EXPORT struct buxton_value *buxton_value_create_uint64(uint64_t u64)
+{
+ return value_create(BUXTON_TYPE_UINT64, &u64);
+}
+
+EXPORT struct buxton_value *buxton_value_create_double(double d)
+{
+ return value_create(BUXTON_TYPE_DOUBLE, &d);
+}
+
+EXPORT struct buxton_value *buxton_value_create_boolean(int32_t b)
+{
+ return value_create(BUXTON_TYPE_BOOLEAN, &b);
+}
+
+EXPORT int buxton_value_get_type(const struct buxton_value *val,
+ enum buxton_key_type *type)
+{
+ if (!val || !type) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ *type = val->type;
+
+ return 0;
+}
+
+static int value_get(const struct buxton_value *val, void *dest,
+ enum buxton_key_type type)
+{
+ if (!val || !dest) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (val->type != type) {
+ errno = ENOTSUP;
+ return -1;
+ }
+
+ switch (type) {
+ case BUXTON_TYPE_STRING:
+ *(char **)dest = val->value.s;
+ break;
+ case BUXTON_TYPE_INT32:
+ *(int32_t *)dest = val->value.i;
+ break;
+ case BUXTON_TYPE_UINT32:
+ *(uint32_t *)dest = val->value.u;
+ break;
+ case BUXTON_TYPE_INT64:
+ *(int64_t *)dest = val->value.i64;
+ break;
+ case BUXTON_TYPE_UINT64:
+ *(uint64_t *)dest = val->value.u64;
+ break;
+ case BUXTON_TYPE_DOUBLE:
+ *(double *)dest = val->value.d;
+ break;
+ case BUXTON_TYPE_BOOLEAN:
+ *(int32_t *)dest = val->value.b;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+EXPORT int buxton_value_get_string(const struct buxton_value *val,
+ const char **s)
+{
+ return value_get(val, s, BUXTON_TYPE_STRING);
+}
+
+EXPORT int buxton_value_get_int32(const struct buxton_value *val, int32_t *i)
+{
+ return value_get(val, i, BUXTON_TYPE_INT32);
+}
+
+EXPORT int buxton_value_get_uint32(const struct buxton_value *val, uint32_t *u)
+{
+ return value_get(val, u, BUXTON_TYPE_UINT32);
+}
+
+EXPORT int buxton_value_get_int64(const struct buxton_value *val, int64_t *i64)
+{
+ return value_get(val, i64, BUXTON_TYPE_INT64);
+}
+
+EXPORT int buxton_value_get_uint64(const struct buxton_value *val,
+ uint64_t *u64)
+{
+ return value_get(val, u64, BUXTON_TYPE_UINT64);
+}
+
+EXPORT int buxton_value_get_double(const struct buxton_value *val, double *d)
+{
+ return value_get(val, d, BUXTON_TYPE_DOUBLE);
+}
+
+EXPORT int buxton_value_get_boolean(const struct buxton_value *val, int32_t *b)
+{
+ return value_get(val, b, BUXTON_TYPE_BOOLEAN);
+}
+
+EXPORT struct buxton_value *buxton_value_duplicate(
+ const struct buxton_value *val)
+{
+ struct buxton_value *_val;
+
+ if (!val) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ _val = malloc(sizeof(*_val));
+ if (!_val)
+ return NULL;
+
+ *_val = *val;
+
+ if (val->type == BUXTON_TYPE_STRING && val->value.s) {
+ _val->value.s = strdup(val->value.s);
+ if (!_val->value.s)
+ return NULL;
+ }
+
+ return _val;
+}
+
+EXPORT void buxton_value_free(struct buxton_value *val)
+{
+ value_free(val);
+ free(val);
+}
+
+EXPORT struct buxton_layer *buxton_create_layer(const char *layer_name)
+{
+ return layer_create(layer_name);
+}
+
+EXPORT const char *buxton_layer_get_name(const struct buxton_layer *layer)
+{
+ if (!layer)
+ return NULL;
+
+ return layer->name;
+}
+
+EXPORT void buxton_layer_set_uid(struct buxton_layer *layer, uid_t uid)
+{
+ if (!layer)
+ return;
+
+ layer->uid = uid;
+}
+
+EXPORT void buxton_layer_set_type(struct buxton_layer *layer,
+ enum buxton_layer_type type)
+{
+ if (!layer)
+ return;
+
+ switch (type) {
+ case BUXTON_LAYER_NORMAL:
+ case BUXTON_LAYER_BASE:
+ break;
+ default:
+ return;
+ }
+
+ layer->type = type;
+}
+
+EXPORT void buxton_free_layer(struct buxton_layer *layer)
+{
+ layer_unref(layer);
+}
+
+static struct bxt_req *create_req(const struct buxton_layer *layer,
+ const char *key, buxton_response_callback callback,
+ buxton_list_callback list_cb, void *data)
+{
+ struct bxt_req *req;
+
+ assert(layer);
+ assert(callback || list_cb);
+
+ req = calloc(1, sizeof(*req));
+ if (!req)
+ return NULL;
+
+ if (key) {
+ req->key = strdup(key);
+ if (!req->key) {
+ free(req);
+ return NULL;
+ }
+ }
+
+ req->layer = layer_ref((struct buxton_layer *)layer);
+ req->callback = callback;
+ req->list_cb = list_cb;
+ req->data = data;
+ req->msgid = ++client_msgid;
+
+ return req;
+}
+
+static int find_noti(struct buxton_client *client,
+ const struct buxton_layer *layer, const char *key,
+ struct bxt_noti **noti)
+{
+ char *lykey;
+ struct bxt_noti *_noti;
+
+ assert(client);
+ assert(layer);
+ assert(key && *key);
+ assert(noti);
+
+ lykey = get_search_key(layer, key, NULL);
+ if (!lykey)
+ return -1;
+
+ _noti = g_hash_table_lookup(client->noti_cbs, lykey);
+
+ free(lykey);
+
+ *noti = _noti;
+
+ return 0;
+}
+
+static int proc_msg_noti(struct buxton_client *client, uint8_t *data, int len)
+{
+ int r;
+ struct request rqst;
+ struct bxt_noti *noti;
+ GList *l;
+
+ assert(client);
+ assert(data);
+ assert(len > 0);
+
+ r = deserialz_request(data, len, &rqst);
+ if (r == -1) {
+ bxt_err("proc noti: deserialize errno %d", errno);
+ return -1;
+ }
+
+ noti = NULL;
+ r = find_noti(client, rqst.layer, rqst.key, &noti);
+ if (r == -1) {
+ bxt_err("proc noti: '%s' '%s' not registered",
+ rqst.layer->name, rqst.key);
+ free_request(&rqst);
+ return -1;
+ }
+
+ if (!noti) {
+ bxt_err("proc noti: '%s' '%s' callback not exist",
+ rqst.layer->name, rqst.key);
+ free_request(&rqst);
+ return -1;
+ }
+
+ for (l = noti->callbacks; l; l = g_list_next(l)) {
+ struct bxt_noti_cb *noticb = l->data;
+
+ if (noticb->deleted)
+ continue;
+
+ assert(noticb->callback);
+ noticb->callback(rqst.layer, rqst.key, rqst.val, noticb->data);
+ }
+
+ free_request(&rqst);
+
+ return 0;
+}
+
+static int add_noti(struct buxton_client *client,
+ const struct buxton_layer *layer, const char *key,
+ struct bxt_noti **noti)
+{
+ char *lykey;
+ struct bxt_noti *_noti;
+
+ assert(client);
+ assert(layer);
+ assert(key && *key);
+ assert(noti);
+
+ lykey = get_search_key(layer, key, NULL);
+ if (!lykey)
+ return -1;
+
+ _noti = g_hash_table_lookup(client->noti_cbs, lykey);
+ if (_noti) {
+ free(lykey);
+ *noti = _noti;
+ return 0;
+ }
+
+ _noti = calloc(1, sizeof(*_noti));
+ if (!_noti) {
+ free(lykey);
+ return -1;
+ }
+ _noti->layer_key = lykey;
+ g_hash_table_insert(client->noti_cbs, lykey, _noti);
+
+ *noti = _noti;
+
+ return 0;
+}
+
+static int add_noticb(struct bxt_noti *noti, buxton_notify_callback notify,
+ void *notify_data)
+{
+ GList *l;
+ struct bxt_noti_cb *noticb;
+
+ assert(noti);
+ assert(notify);
+
+ for (l = noti->callbacks; l; l = g_list_next(l)) {
+ noticb = l->data;
+
+ if (noticb->callback == notify) {
+ if (noticb->deleted == FALSE) {
+ errno = EEXIST;
+ return -1;
+ }
+
+ noticb->deleted = FALSE;
+ noticb->callback = notify;
+ noticb->data = notify_data;
+
+ return 0;
+ }
+ }
+
+ noticb = calloc(1, sizeof(*noticb));
+ if (!noticb)
+ return -1;
+
+ noticb->deleted = FALSE;
+ noticb->callback = notify;
+ noticb->data = notify_data;
+
+ noti->callbacks = g_list_append(noti->callbacks, noticb);
+
+ return 0;
+}
+
+static void del_noti(struct buxton_client *client,
+ const struct buxton_layer *layer, const char *key)
+{
+ char *lykey;
+
+ assert(client);
+ assert(layer);
+ assert(key && *key);
+
+ lykey = get_search_key(layer, key, NULL);
+ if (!lykey)
+ return;
+
+ g_hash_table_remove(client->noti_cbs, lykey);
+
+ free(lykey);
+}
+
+static void proc_msg_notify(struct buxton_client *client, struct bxt_req *req,
+ struct response *resp)
+{
+ struct bxt_noti *noti;
+ int r;
+
+ assert(client);
+ assert(req);
+ assert(resp);
+
+ if (resp->res == 0) {
+ r = add_noti(client, req->layer, req->key, &noti);
+ if (r == -1) {
+ resp->res = errno;
+ bxt_err("add noti: errno %d", errno);
+ } else {
+ if (noti->reg == FALSE)
+ noti->reg = (resp->res == 0);
+
+ r = add_noticb(noti, req->notify, req->notify_data);
+ if (r == -1 && errno != EEXIST) {
+ resp->res = errno;
+ bxt_err("add noticb: errno %d", errno);
+ }
+ }
+ }
+
+ assert(req->callback);
+ req->callback(resp->res, req->layer, req->key, resp->val, req->data);
+}
+
+static int proc_msg_res(struct buxton_client *client, uint8_t *data, int len)
+{
+ int r;
+ struct response resp;
+ struct bxt_req *req;
+
+ assert(client);
+ assert(data);
+ assert(len > 0);
+
+ r = deserialz_response(data, len, &resp);
+ if (r == -1) {
+ bxt_err("proc msg: deserialize errno %d", errno);
+ return -1;
+ }
+
+ req = g_hash_table_lookup(client->req_cbs,
+ GUINT_TO_POINTER(resp.msgid));
+ if (!req) {
+ bxt_err("proc msg: msgid %d not exist", resp.msgid);
+ free_response(&resp);
+ return 0;
+ }
+
+ switch (resp.type) {
+ case MSG_LIST:
+ assert(req->list_cb);
+ req->list_cb(resp.res, req->layer, resp.names, resp.nmlen,
+ req->data);
+ break;
+ case MSG_NOTIFY:
+ proc_msg_notify(client, req, &resp);
+ break;
+ case MSG_UNNOTIFY:
+ del_noti(client, req->layer, req->key);
+
+ assert(req->callback);
+ req->callback(resp.res, req->layer, req->key, resp.val,
+ req->data);
+ break;
+ default:
+ assert(req->callback);
+ req->callback(resp.res, req->layer, req->key, resp.val,
+ req->data);
+ break;
+ }
+
+ free_response(&resp);
+
+ g_hash_table_remove(client->req_cbs, GUINT_TO_POINTER(resp.msgid));
+
+ return 0;
+}
+
+static void proc_msg_cb(void *user_data,
+ enum message_type type, uint8_t *data, int32_t len)
+{
+ struct buxton_client *client = user_data;
+
+ assert(client);
+
+ switch (type) {
+ case MSG_NOTI:
+ proc_msg_noti(client, data, len);
+ break;
+ case MSG_SET:
+ case MSG_GET:
+ case MSG_CREAT:
+ case MSG_UNSET:
+ case MSG_LIST:
+ case MSG_NOTIFY:
+ case MSG_UNNOTIFY:
+ case MSG_SET_WP:
+ case MSG_SET_RP:
+ case MSG_GET_WP:
+ case MSG_GET_RP:
+ proc_msg_res(client, data, len);
+ break;
+ default:
+ bxt_err("proc msg: unknown message type %d", type);
+ break;
+ }
+}
+
+static int proc_msg(struct buxton_client *client)
+{
+ int r;
+
+ r = proto_recv_frag(client->fd, proc_msg_cb, client);
+ if (r == -1) {
+ bxt_err("recv msg: fd %d errno %d", client->fd, errno);
+ return -1;
+ }
+
+ return 0;
+}
+
+#define TS_SUB(a, b) (((a)->tv_sec - (b)->tv_sec) * 1000 \
+ + ((a)->tv_nsec - (b)->tv_nsec) / 1000000)
+static int wait_msg(struct buxton_client *client, guint32 msgid)
+{
+ int r;
+ struct pollfd fds[1];
+ struct timespec to;
+ struct timespec t;
+ int32_t ms;
+ struct bxt_req *req;
+
+ assert(client);
+ assert(client->fd >= 0);
+
+ fds[0].fd = client->fd;
+ fds[0].events = POLLIN;
+ fds[0].revents = 0;
+
+ clock_gettime(CLOCK_MONOTONIC, &t);
+ to.tv_sec = t.tv_sec + 5; /* TIMEOUT 5 seconds */
+ to.tv_nsec = t.tv_nsec;
+
+ ms = TS_SUB(&to, &t);
+
+ while (ms > 0) {
+ r = poll(fds, 1, ms);
+ switch (r) {
+ case -1:
+ bxt_err("wait response: poll: fd %d errno %d",
+ client->fd, errno);
+ break;
+ case 0:
+ bxt_err("wait response: poll: fd %d timeout",
+ client->fd);
+ errno = ETIMEDOUT;
+ r = -1;
+ break;
+ default:
+ r = proc_msg(client);
+ break;
+ }
+
+ /* poll or proc error */
+ if (r == -1)
+ return -1;
+
+ req = g_hash_table_lookup(client->req_cbs,
+ GUINT_TO_POINTER(msgid));
+ /* req is processed */
+ if (!req)
+ return 0;
+
+ clock_gettime(CLOCK_MONOTONIC, &t);
+ ms = TS_SUB(&to, &t);
+ }
+
+ bxt_err("wait response: timeout");
+
+ return -1;
+}
+
+static void free_req(struct bxt_req *req)
+{
+ if (!req)
+ return;
+
+ layer_unref(req->layer);
+ free(req->key);
+ free(req);
+}
+
+static int send_req(struct buxton_client *client, const struct request *rqst)
+{
+ int r;
+ uint8_t *data;
+ int len;
+
+ assert(client);
+ assert(rqst);
+
+ if (client->fd == -1) {
+ errno = ENOTCONN;
+ return -1;
+ }
+
+ r = serialz_request(rqst, &data, &len);
+ if (r == -1) {
+ bxt_err("send req: serialize errno %d", errno);
+ return -1;
+ }
+
+ r = proto_send(client->fd, rqst->type, data, len);
+ if (r == -1)
+ bxt_err("send req: errno %d", errno);
+
+ free(data);
+
+ return r;
+}
+
+static struct bxt_req *set_value(struct buxton_client *client,
+ const struct buxton_layer *layer, const char *key,
+ const struct buxton_value *val,
+ buxton_response_callback callback, void *user_data)
+{
+ int r;
+ struct bxt_req *req;
+ struct request rqst;
+
+ if (!client || !layer || !key || !*key || !val || !callback) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ req = create_req(layer, key, callback, NULL, user_data);
+ if (!req)
+ return NULL;
+
+ memset(&rqst, 0, sizeof(rqst));
+ rqst.type = MSG_SET;
+ rqst.msgid = req->msgid;
+ rqst.layer = req->layer;
+ rqst.key = (char *)key;
+ rqst.val = (struct buxton_value *)val;
+
+ r = send_req(client, &rqst);
+ if (r == -1) {
+ free_req(req);
+ return NULL;
+ }
+
+ g_hash_table_insert(client->req_cbs, GUINT_TO_POINTER(req->msgid), req);
+
+ return req;
+}
+
+EXPORT int buxton_set_value(struct buxton_client *client,
+ const struct buxton_layer *layer, const char *key,
+ const struct buxton_value *val,
+ buxton_response_callback callback, void *user_data)
+{
+ struct bxt_req *req;
+
+ req = set_value(client, layer, key, val, callback, user_data);
+ if (!req)
+ return -1;
+
+ return 0;
+}
+
+static void set_value_sync_cb(int status, const struct buxton_layer *layer,
+ const char *key, const struct buxton_value *val,
+ void *user_data)
+{
+ struct response *resp = user_data;
+
+ assert(resp);
+
+ resp->res = status;
+}
+
+EXPORT int buxton_set_value_sync(struct buxton_client *client,
+ const struct buxton_layer *layer, const char *key,
+ const struct buxton_value *val)
+{
+ int r;
+ struct bxt_req *req;
+ struct response resp;
+
+ memset(&resp, 0, sizeof(resp));
+
+ req = set_value(client, layer, key, val, set_value_sync_cb, &resp);
+ if (!req)
+ return -1;
+
+ r = wait_msg(client, req->msgid);
+ if (r == -1)
+ return -1;
+
+ if (resp.res) {
+ errno = resp.res;
+ return -1;
+ }
+
+ return 0;
+}
+
+static struct bxt_req *get_value(struct buxton_client *client,
+ const struct buxton_layer *layer, const char *key,
+ buxton_response_callback callback, void *user_data)
+{
+ int r;
+ struct bxt_req *req;
+ struct request rqst;
+
+ if (!client || !layer || !key || !*key || !callback) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ req = create_req(layer, key, callback, NULL, user_data);
+ if (!req)
+ return NULL;
+
+ memset(&rqst, 0, sizeof(rqst));
+ rqst.type = MSG_GET;
+ rqst.msgid = req->msgid;
+ rqst.layer = req->layer;
+ rqst.key = (char *)key;
+
+ r = send_req(client, &rqst);
+ if (r == -1) {
+ free_req(req);
+ return NULL;
+ }
+
+ g_hash_table_insert(client->req_cbs, GUINT_TO_POINTER(req->msgid), req);
+
+ return req;
+}
+
+EXPORT int buxton_get_value(struct buxton_client *client,
+ const struct buxton_layer *layer, const char *key,
+ buxton_response_callback callback, void *user_data)
+{
+ struct bxt_req *req;
+
+ req = get_value(client, layer, key, callback, user_data);
+ if (!req)
+ return -1;
+
+ return 0;
+}
+
+static void get_value_sync_cb(int status, const struct buxton_layer *layer,
+ const char *key, const struct buxton_value *val,
+ void *user_data)
+{
+ struct response *resp = user_data;
+
+ assert(resp);
+
+ resp->res = status;
+
+ if (!status)
+ resp->val = buxton_value_duplicate(val);
+}
+
+EXPORT int buxton_get_value_sync(struct buxton_client *client,
+ const struct buxton_layer *layer, const char *key,
+ struct buxton_value **val)
+{
+ int r;
+ struct bxt_req *req;
+ struct response resp;
+
+ if (!val) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ memset(&resp, 0, sizeof(resp));
+
+ req = get_value(client, layer, key, get_value_sync_cb, &resp);
+ if (!req)
+ return -1;
+
+ r = wait_msg(client, req->msgid);
+ if (r == -1)
+ return -1;
+
+ if (resp.res) {
+ errno = resp.res;
+ return -1;
+ }
+
+ *val = resp.val;
+
+ return 0;
+}
+
+static struct bxt_req *list_keys(struct buxton_client *client,
+ const struct buxton_layer *layer,
+ buxton_list_callback callback, void *user_data)
+{
+ int r;
+ struct bxt_req *req;
+ struct request rqst;
+
+ if (!client || !layer || !callback) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ req = create_req(layer, NULL, NULL, callback, user_data);
+ if (!req)
+ return NULL;
+
+ memset(&rqst, 0, sizeof(rqst));
+ rqst.type = MSG_LIST;
+ rqst.msgid = req->msgid;
+ rqst.layer = req->layer;
+
+ r = send_req(client, &rqst);
+ if (r == -1) {
+ free_req(req);
+ return NULL;
+ }
+
+ g_hash_table_insert(client->req_cbs, GUINT_TO_POINTER(req->msgid), req);
+
+ return req;
+}
+
+EXPORT int buxton_list_keys(struct buxton_client *client,
+ const struct buxton_layer *layer,
+ buxton_list_callback callback, void *user_data)
+{
+ struct bxt_req *req;
+
+ req = list_keys(client, layer, callback, user_data);
+ if (!req)
+ return -1;
+
+ return 0;
+}
+
+static void list_keys_sync_cb(int status, const struct buxton_layer *layer,
+ char * const *names, unsigned int len, void *user_data)
+{
+ struct response *resp = user_data;
+ char **nms;
+ char **_names;
+ int i;
+
+ assert(resp);
+
+ resp->res = status;
+
+ if (resp->res)
+ return;
+
+ nms = calloc(len + 1, sizeof(void *));
+ if (!nms) {
+ resp->res = ENOMEM;
+ return;
+ }
+
+ /* steal allocated names */
+ _names = (char **)names;
+ for (i = 0; i < len; i++) {
+ nms[i] = _names[i];
+ _names[i] = NULL;
+ }
+ nms[i] = NULL; /* NULL-terminated */
+
+ resp->names = nms;
+ resp->nmlen = len;
+}
+
+EXPORT int buxton_list_keys_sync(struct buxton_client *client,
+ const struct buxton_layer *layer,
+ char ***names, unsigned int *len)
+{
+ int r;
+ struct bxt_req *req;
+ struct response resp;
+
+ if (!names) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ memset(&resp, 0, sizeof(resp));
+
+ req = list_keys(client, layer, list_keys_sync_cb, &resp);
+ if (!req)
+ return -1;
+
+ r = wait_msg(client, req->msgid);
+ if (r == -1)
+ return -1;
+
+ if (resp.res) {
+ errno = resp.res;
+ return -1;
+ }
+
+ *names = resp.names;
+
+ if (len)
+ *len = resp.nmlen;
+
+ return 0;
+}
+
+static gboolean call_resp_cb(gpointer data)
+{
+ struct bxt_noti_res *res = data;
+
+ assert(res);
+ assert(res->callback);
+
+ res->callback(res->res, res->layer, res->key, NULL, res->data);
+
+ layer_unref(res->layer);
+ free(res->key);
+ free(res);
+
+ return G_SOURCE_REMOVE;
+}
+
+static int call_resp(int status, const struct buxton_layer *layer,
+ const char *key, buxton_response_callback callback,
+ void *user_data)
+{
+ struct bxt_noti_res *res;
+
+ assert(layer);
+ assert(key);
+ assert(callback);
+
+ res = calloc(1, sizeof(*res));
+ if (!res)
+ return -1;
+
+ res->key = strdup(key);
+ if (!res->key) {
+ free(res);
+ return -1;
+ }
+
+ res->layer = layer_ref((struct buxton_layer *)layer);
+ res->res = status;
+ res->callback = callback;
+ res->data = user_data;
+
+ g_idle_add(call_resp_cb, res);
+
+ return 0;
+}
+
+static struct bxt_req *register_noti(struct buxton_client *client,
+ const struct buxton_layer *layer, const char *key,
+ buxton_notify_callback notify, void *notify_data,
+ buxton_response_callback callback, void *user_data)
+{
+ int r;
+ struct bxt_req *req;
+ struct request rqst;
+
+ assert(client);
+ assert(layer);
+ assert(key && *key);
+ assert(notify);
+ assert(callback);
+
+ req = create_req(layer, key, callback, NULL, user_data);
+ if (!req)
+ return NULL;
+
+ req->notify = notify;
+ req->notify_data = notify_data;
+
+ memset(&rqst, 0, sizeof(rqst));
+ rqst.type = MSG_NOTIFY;
+ rqst.msgid = req->msgid;
+ rqst.layer = req->layer;
+ rqst.key = (char *)key;
+
+ r = send_req(client, &rqst);
+ if (r == -1) {
+ free_req(req);
+ return NULL;
+ }
+
+ g_hash_table_insert(client->req_cbs, GUINT_TO_POINTER(req->msgid), req);
+
+ return req;
+}
+
+EXPORT int buxton_register_notification(struct buxton_client *client,
+ const struct buxton_layer *layer, const char *key,
+ buxton_notify_callback notify, void *notify_data,
+ buxton_response_callback callback, void *user_data)
+{
+ int r;
+ struct bxt_noti *noti;
+ struct bxt_req *req;
+
+ if (!client || !layer || !key || !*key || !notify || !callback) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ r = find_noti(client, layer, key, &noti);
+ if (r == -1)
+ return -1;
+
+ if (noti && noti->reg == TRUE) {
+ r = add_noticb(noti, notify, notify_data);
+ return call_resp(r == -1 ? errno : 0, layer, key,
+ callback, user_data);
+ }
+
+ req = register_noti(client, layer, key, notify, notify_data, callback,
+ user_data);
+ if (!req)
+ return -1;
+
+ return 0;
+}
+
+static void reg_noti_sync_cb(int status, const struct buxton_layer *layer,
+ const char *key, const struct buxton_value *val,
+ void *user_data)
+{
+ struct response *resp = user_data;
+
+ assert(resp);
+
+ resp->res = status;
+}
+
+EXPORT int buxton_register_notification_sync(struct buxton_client *client,
+ const struct buxton_layer *layer, const char *key,
+ buxton_notify_callback notify, void *notify_data)
+{
+ int r;
+ struct bxt_noti *noti;
+ struct bxt_req *req;
+ struct response resp;
+
+ if (!client || !layer || !key || !*key || !notify) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ r = find_noti(client, layer, key, &noti);
+ if (r == -1)
+ return -1;
+
+ if (noti && noti->reg == TRUE)
+ return add_noticb(noti, notify, notify_data);
+
+ memset(&resp, 0, sizeof(resp));
+
+ req = register_noti(client, layer, key, notify, notify_data,
+ reg_noti_sync_cb, &resp);
+ if (!req)
+ return -1;
+
+ r = wait_msg(client, req->msgid);
+ if (r == -1)
+ return -1;
+
+ if (resp.res) {
+ errno = resp.res;
+ return -1;
+ }
+
+ return 0;
+}
+
+static gboolean del_noticb_cb(gpointer data)
+{
+ struct bxt_noti *noti = data;
+ struct bxt_noti_cb *noticb;
+ GList *l;
+ GList *ll;
+
+ assert(noti);
+
+ for (l = noti->callbacks, ll = g_list_next(l); l;
+ l = ll, ll = g_list_next(ll)) {
+ noticb = l->data;
+
+ if (noticb->deleted) {
+ noti->callbacks = g_list_delete_link(noti->callbacks,
+ l);
+ free(noticb);
+ }
+ }
+
+ noti->id = 0;
+
+ return G_SOURCE_REMOVE;
+}
+
+static int del_noticb(struct bxt_noti *noti, buxton_notify_callback notify,
+ int *count)
+{
+ GList *l;
+ gboolean f;
+ int cnt;
+
+ assert(noti);
+ assert(notify);
+
+ cnt = 0;
+ f = FALSE;
+ for (l = noti->callbacks; l; l = g_list_next(l)) {
+ struct bxt_noti_cb *noticb = l->data;
+
+ if (noticb->callback == notify) {
+ f = TRUE;
+ noticb->deleted = TRUE;
+ if (!noti->id)
+ noti->id = g_idle_add(del_noticb_cb, noti);
+ }
+
+ if (noticb->deleted == FALSE)
+ cnt++;
+ }
+
+ if (!f) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ if (count)
+ *count = cnt;
+
+ return 0;
+}
+
+static struct bxt_req *unregister_noti(struct buxton_client *client,
+ const struct buxton_layer *layer, const char *key,
+ buxton_response_callback callback, void *user_data)
+{
+ int r;
+ struct bxt_req *req;
+ struct request rqst;
+
+ assert(client);
+ assert(layer);
+ assert(key && *key);
+ assert(callback);
+
+ req = create_req(layer, key, callback, NULL, user_data);
+ if (!req)
+ return NULL;
+
+ memset(&rqst, 0, sizeof(rqst));
+ rqst.type = MSG_UNNOTIFY;
+ rqst.msgid = req->msgid;
+ rqst.layer = req->layer;
+ rqst.key = (char *)key;
+
+ r = send_req(client, &rqst);
+ if (r == -1) {
+ free_req(req);
+ return NULL;
+ }
+
+ g_hash_table_insert(client->req_cbs, GUINT_TO_POINTER(req->msgid), req);
+
+ return req;
+}
+
+EXPORT int buxton_unregister_notification(struct buxton_client *client,
+ const struct buxton_layer *layer, const char *key,
+ buxton_notify_callback notify,
+ buxton_response_callback callback, void *user_data)
+{
+ int r;
+ struct bxt_noti *noti;
+ struct bxt_req *req;
+ int cnt;
+
+ if (!client || !layer || !key || !*key || !notify || !callback) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ r = find_noti(client, layer, key, &noti);
+ if (r == -1)
+ return -1;
+
+ if (!noti) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ r = del_noticb(noti, notify, &cnt);
+ if (r == -1)
+ return call_resp(errno, layer, key, callback, user_data);
+
+ if (cnt || noti->reg == FALSE)
+ return call_resp(0, layer, key, callback, user_data);
+
+ req = unregister_noti(client, layer, key, callback, user_data);
+ if (!req)
+ return -1;
+
+ return 0;
+}
+
+static void unreg_noti_sync_cb(int status, const struct buxton_layer *layer,
+ const char *key, const struct buxton_value *val,
+ void *user_data)
+{
+ struct response *resp = user_data;
+
+ assert(resp);
+
+ resp->res = status;
+}
+
+EXPORT int buxton_unregister_notification_sync(struct buxton_client *client,
+ const struct buxton_layer *layer, const char *key,
+ buxton_notify_callback notify)
+{
+ int r;
+ struct bxt_noti *noti;
+ struct bxt_req *req;
+ int cnt;
+ struct response resp;
+
+ if (!client || !layer || !key || !*key || !notify) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ r = find_noti(client, layer, key, &noti);
+ if (r == -1)
+ return -1;
+
+ if (!noti) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ r = del_noticb(noti, notify, &cnt);
+ if (r == -1)
+ return -1;
+
+ if (cnt || noti->reg == FALSE)
+ return 0;
+
+ memset(&resp, 0, sizeof(resp));
+
+ req = unregister_noti(client, layer, key, unreg_noti_sync_cb, &resp);
+ if (!req)
+ return -1;
+
+ r = wait_msg(client, req->msgid);
+ if (r == -1)
+ return -1;
+
+ if (resp.res) {
+ errno = resp.res;
+ return -1;
+ }
+
+ return 0;
+}
+
+static struct bxt_req *create_value(struct buxton_client *client,
+ const struct buxton_layer *layer, const char *key,
+ const char *read_privilege, const char *write_privilege,
+ const struct buxton_value *val,
+ buxton_response_callback callback, void *user_data)
+{
+ int r;
+ struct bxt_req *req;
+ struct request rqst;
+
+ if (!client || !layer || !key || !*key || !read_privilege
+ || !write_privilege || !val || !callback) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ req = create_req(layer, key, callback, NULL, user_data);
+ if (!req)
+ return NULL;
+
+ memset(&rqst, 0, sizeof(rqst));
+ rqst.type = MSG_CREAT;
+ rqst.msgid = req->msgid;
+ rqst.layer = req->layer;
+ rqst.key = (char *)key;
+ rqst.rpriv = (char *)read_privilege;
+ rqst.wpriv = (char *)write_privilege;
+ rqst.val = (struct buxton_value *)val;
+
+ r = send_req(client, &rqst);
+ if (r == -1) {
+ free_req(req);
+ return NULL;
+ }
+
+ g_hash_table_insert(client->req_cbs, GUINT_TO_POINTER(req->msgid), req);
+
+ return req;
+}
+
+EXPORT int buxton_create_value(struct buxton_client *client,
+ const struct buxton_layer *layer, const char *key,
+ const char *read_privilege, const char *write_privilege,
+ const struct buxton_value *val,
+ buxton_response_callback callback, void *user_data)
+{
+ struct bxt_req *req;
+
+ req = create_value(client, layer, key, read_privilege, write_privilege,
+ val, callback, user_data);
+ if (!req)
+ return -1;
+
+ return 0;
+}
+
+static void create_value_sync_cb(int status, const struct buxton_layer *layer,
+ const char *key, const struct buxton_value *val,
+ void *user_data)
+{
+ struct response *resp = user_data;
+
+ assert(resp);
+
+ resp->res = status;
+}
+
+EXPORT int buxton_create_value_sync(struct buxton_client *client,
+ const struct buxton_layer *layer, const char *key,
+ const char *read_privilege, const char *write_privilege,
+ const struct buxton_value *val)
+{
+ int r;
+ struct bxt_req *req;
+ struct response resp;
+
+ memset(&resp, 0, sizeof(resp));
+
+ req = create_value(client, layer, key, read_privilege, write_privilege,
+ val, create_value_sync_cb, &resp);
+ if (!req)
+ return -1;
+
+ r = wait_msg(client, req->msgid);
+ if (r == -1)
+ return -1;
+
+ if (resp.res) {
+ errno = resp.res;
+ return -1;
+ }
+
+ return 0;
+}
+
+static struct bxt_req *unset_value(struct buxton_client *client,
+ const struct buxton_layer *layer, const char *key,
+ buxton_response_callback callback, void *user_data)
+{
+ int r;
+ struct bxt_req *req;
+ struct request rqst;
+
+ if (!client || !layer || !key || !*key || !callback) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ req = create_req(layer, key, callback, NULL, user_data);
+ if (!req)
+ return NULL;
+
+ memset(&rqst, 0, sizeof(rqst));
+ rqst.type = MSG_UNSET;
+ rqst.msgid = req->msgid;
+ rqst.layer = req->layer;
+ rqst.key = (char *)key;
+
+ r = send_req(client, &rqst);
+ if (r == -1) {
+ free_req(req);
+ return NULL;
+ }
+
+ g_hash_table_insert(client->req_cbs, GUINT_TO_POINTER(req->msgid), req);
+
+ return req;
+}
+
+EXPORT int buxton_unset_value(struct buxton_client *client,
+ const struct buxton_layer *layer, const char *key,
+ buxton_response_callback callback, void *user_data)
+{
+ struct bxt_req *req;
+
+ req = unset_value(client, layer, key, callback, user_data);
+ if (!req)
+ return -1;
+
+ return 0;
+}
+
+static void unset_value_sync_cb(int status, const struct buxton_layer *layer,
+ const char *key, const struct buxton_value *val,
+ void *user_data)
+{
+ struct response *resp = user_data;
+
+ assert(resp);
+
+ resp->res = status;
+}
+
+EXPORT int buxton_unset_value_sync(struct buxton_client *client,
+ const struct buxton_layer *layer, const char *key)
+{
+ int r;
+ struct bxt_req *req;
+ struct response resp;
+
+ memset(&resp, 0, sizeof(resp));
+
+ req = unset_value(client, layer, key, unset_value_sync_cb, &resp);
+ if (!req)
+ return -1;
+
+ r = wait_msg(client, req->msgid);
+ if (r == -1)
+ return -1;
+
+ if (resp.res) {
+ errno = resp.res;
+ return -1;
+ }
+
+ return 0;
+}
+
+static struct bxt_req *set_priv(struct buxton_client *client,
+ const struct buxton_layer *layer, const char *key,
+ enum buxton_priv_type type,
+ const char *privilege,
+ buxton_response_callback callback, void *user_data)
+{
+ int r;
+ struct bxt_req *req;
+ struct request rqst;
+ struct buxton_value val;
+
+ if (!client || !layer || !key || !*key || !privilege || !callback) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (type <= BUXTON_PRIV_UNKNOWN || type >= BUXTON_PRIV_MAX) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ req = create_req(layer, key, callback, NULL, user_data);
+ if (!req)
+ return NULL;
+
+ memset(&rqst, 0, sizeof(rqst));
+ rqst.type = type == BUXTON_PRIV_READ ? MSG_SET_RP : MSG_SET_WP;
+ rqst.msgid = req->msgid;
+ rqst.layer = req->layer;
+ rqst.key = (char *)key;
+ rqst.val = &val;
+
+ val.type = BUXTON_TYPE_PRIVILEGE;
+ val.value.s = (char *)privilege;
+
+ r = send_req(client, &rqst);
+ if (r == -1) {
+ free_req(req);
+ return NULL;
+ }
+
+ g_hash_table_insert(client->req_cbs, GUINT_TO_POINTER(req->msgid), req);
+
+ return req;
+}
+
+EXPORT int buxton_set_privilege(struct buxton_client *client,
+ const struct buxton_layer *layer, const char *key,
+ enum buxton_priv_type type,
+ const char *privilege,
+ buxton_response_callback callback, void *user_data)
+{
+ struct bxt_req *req;
+
+ req = set_priv(client, layer, key, type, privilege,
+ callback, user_data);
+ if (!req)
+ return -1;
+
+ return 0;
+}
+
+static void set_priv_sync_cb(int status, const struct buxton_layer *layer,
+ const char *key, const struct buxton_value *val,
+ void *user_data)
+{
+ struct response *resp = user_data;
+
+ assert(resp);
+
+ resp->res = status;
+}
+
+EXPORT int buxton_set_privilege_sync(struct buxton_client *client,
+ const struct buxton_layer *layer, const char *key,
+ enum buxton_priv_type type,
+ const char *privilege)
+{
+ int r;
+ struct bxt_req *req;
+ struct response resp;
+
+ memset(&resp, 0, sizeof(resp));
+
+ req = set_priv(client, layer, key, type, privilege,
+ set_priv_sync_cb, &resp);
+ if (!req)
+ return -1;
+
+ r = wait_msg(client, req->msgid);
+ if (r == -1)
+ return -1;
+
+ if (resp.res) {
+ errno = resp.res;
+ return -1;
+ }
+
+ return 0;
+}
+
+static struct bxt_req *get_priv(struct buxton_client *client,
+ const struct buxton_layer *layer, const char *key,
+ enum buxton_priv_type type,
+ buxton_response_callback callback, void *user_data)
+{
+ int r;
+ struct bxt_req *req;
+ struct request rqst;
+
+ if (!client || !layer || !key || !*key || !callback) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (type <= BUXTON_PRIV_UNKNOWN || type >= BUXTON_PRIV_MAX) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ req = create_req(layer, key, callback, NULL, user_data);
+ if (!req)
+ return NULL;
+
+ memset(&rqst, 0, sizeof(rqst));
+ rqst.type = type == BUXTON_PRIV_READ ? MSG_GET_RP : MSG_GET_WP;
+ rqst.msgid = req->msgid;
+ rqst.layer = req->layer;
+ rqst.key = (char *)key;
+
+ r = send_req(client, &rqst);
+ if (r == -1) {
+ free_req(req);
+ return NULL;
+ }
+
+ g_hash_table_insert(client->req_cbs, GUINT_TO_POINTER(req->msgid), req);
+
+ return req;
+}
+
+EXPORT int buxton_get_privilege(struct buxton_client *client,
+ const struct buxton_layer *layer, const char *key,
+ enum buxton_priv_type type,
+ buxton_response_callback callback, void *user_data)
+{
+ struct bxt_req *req;
+
+ req = get_priv(client, layer, key, type, callback, user_data);
+ if (!req)
+ return -1;
+
+ return 0;
+}
+
+static void get_priv_sync_cb(int status, const struct buxton_layer *layer,
+ const char *key, const struct buxton_value *val,
+ void *user_data)
+{
+ struct response *resp = user_data;
+
+ assert(resp);
+
+ resp->res = status;
+
+ if (!status)
+ resp->val = buxton_value_duplicate(val);
+}
+
+EXPORT int buxton_get_privilege_sync(struct buxton_client *client,
+ const struct buxton_layer *layer, const char *key,
+ enum buxton_priv_type type,
+ char **privilege)
+{
+ int r;
+ struct bxt_req *req;
+ struct response resp;
+
+ if (!privilege) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ memset(&resp, 0, sizeof(resp));
+
+ req = get_priv(client, layer, key, type, get_priv_sync_cb, &resp);
+ if (!req)
+ return -1;
+
+ r = wait_msg(client, req->msgid);
+ if (r == -1)
+ return -1;
+
+ if (resp.res) {
+ errno = resp.res;
+ return -1;
+ }
+
+ *privilege = resp.val->value.s;
+ resp.val->value.s = NULL;
+ buxton_value_free(resp.val);
+
+ return 0;
+}
+
+static void free_noti(struct bxt_noti *noti)
+{
+ if (!noti)
+ return;
+
+ g_list_free_full(noti->callbacks, (GDestroyNotify)free);
+
+ if (noti->id) {
+ g_source_remove(noti->id);
+ noti->id = 0;
+ }
+
+ free(noti->layer_key);
+ free(noti);
+}
+
+static gboolean close_conn(gpointer data)
+{
+ struct buxton_client *cli = data;
+
+ assert(cli);
+
+ if (cli->fd == -1)
+ return G_SOURCE_REMOVE;
+
+ if (cli->fd_id) {
+ g_source_remove(cli->fd_id);
+ cli->fd_id = 0;
+ }
+
+ close(cli->fd);
+ cli->fd = -1;
+ if (cli->st_callback)
+ cli->st_callback(BUXTON_STATUS_DISCONNECTED, cli->st_data);
+
+ return G_SOURCE_REMOVE;
+}
+
+static void free_client(struct buxton_client *cli)
+{
+ if (!cli)
+ return;
+
+ clients = g_list_remove(clients, cli);
+
+ if (cli->req_cbs)
+ g_hash_table_destroy(cli->req_cbs);
+
+ if (cli->noti_cbs)
+ g_hash_table_destroy(cli->noti_cbs);
+
+ close_conn(cli);
+
+ free(cli);
+}
+
+int connect_server(const char *addr)
+{
+ int fd;
+ struct sockaddr_un sa;
+ int r;
+
+ if (!addr) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ fd = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
+ if (fd == -1) {
+ bxt_err("connect: socket errno %d", errno);
+ return -1;
+ }
+
+ sa.sun_family = AF_UNIX;
+ strncpy(sa.sun_path, addr, sizeof(sa.sun_path));
+ sa.sun_path[sizeof(sa.sun_path) - 1] = '\0';
+
+ r = connect(fd, (struct sockaddr *)&sa, sizeof(sa));
+ if (r == -1) {
+ if (errno == ENOENT)
+ bxt_dbg("connect: '%s' not exist", addr);
+ else
+ bxt_err("connect: connect errno %d", errno);
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+static gboolean recv_cb(gint fd, GIOCondition cond, gpointer data)
+{
+ struct buxton_client *cli = data;
+ int r;
+
+ assert(cli);
+
+ bxt_dbg("recv %d: cond %x", fd, cond);
+
+ if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
+ bxt_err("recv %d: IO cond %x errno %d", fd, cond, errno);
+
+ cli->fd_id = 0;
+ g_idle_add(close_conn, cli);
+ return G_SOURCE_REMOVE;
+ }
+
+ r = proc_msg(cli);
+ if (r == -1) {
+ cli->fd_id = 0;
+ g_idle_add(close_conn, cli);
+ return G_SOURCE_REMOVE;
+ }
+
+ return G_SOURCE_CONTINUE;
+}
+
+EXPORT int buxton_close(struct buxton_client *client)
+{
+ GList *l;
+
+ if (!client) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ l = g_list_find(clients, client);
+ if (!l) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ free_client(client);
+
+ return 0;
+}
+
+EXPORT int buxton_open(struct buxton_client **client,
+ buxton_status_callback callback, void *user_data)
+{
+ struct buxton_client *cli;
+
+ if (!client) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ cli = calloc(1, sizeof(*cli));
+ if (!cli)
+ return -1;
+
+ cli->fd = -1;
+ cli->st_callback = callback;
+ cli->st_data = user_data;
+
+ cli->req_cbs = g_hash_table_new_full(g_direct_hash, g_direct_equal,
+ NULL, (GDestroyNotify)free_req);
+ if (!cli->req_cbs) {
+ free_client(cli);
+ errno = ENOMEM;
+ return -1;
+ }
+
+ cli->noti_cbs = g_hash_table_new_full(g_str_hash, g_str_equal,
+ NULL, (GDestroyNotify)free_noti);
+ if (!cli->noti_cbs) {
+ free_client(cli);
+ errno = ENOMEM;
+ return -1;
+ }
+
+ cli->fd = connect_server(SOCKPATH);
+ if (cli->fd == -1) {
+ free_client(cli);
+ return -1;
+ }
+
+ cli->fd_id = g_unix_fd_add(cli->fd,
+ G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+ recv_cb, cli);
+
+ clients = g_list_append(clients, cli);
+ *client = cli;
+
+ if (callback)
+ callback(BUXTON_STATUS_CONNECTED, user_data);
+
+ return 0;
+}
+
+__attribute__((destructor))
+static void buxton_client_exit(void)
+{
+ GList *l;
+ GList *n;
+
+ for (l = clients, n = g_list_next(l); l; l = n, n = g_list_next(n))
+ free_client(l->data);
+
+ clients = NULL;
+}
+
diff --git a/lib/buxton2.pc.in b/lib/buxton2.pc.in
new file mode 100644
index 0000000..e641e13
--- /dev/null
+++ b/lib/buxton2.pc.in
@@ -0,0 +1,10 @@
+prefix=@CMAKE_INSTALL_PREFIX@
+exec_prefix=${prefix}
+libdir=@LIB_INSTALL_DIR@
+includedir=@INCLUDE_INSTALL_DIR@
+
+Name: buxton
+Description: Library for buxton clients
+Version: @VERSION@
+Libs: -L${libdir} -lbuxton
+Cflags: -I${includedir}
diff --git a/lib/include/buxton2.h b/lib/include/buxton2.h
new file mode 100644
index 0000000..6a8f17b
--- /dev/null
+++ b/lib/include/buxton2.h
@@ -0,0 +1,338 @@
+/*
+ * Buxton
+ *
+ * Copyright (C) 2015 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ */
+
+#ifndef __BUXTON_H__
+#define __BUXTON_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+/*
+ *
+ */
+struct buxton_client;
+
+enum buxton_status {
+ BUXTON_STATUS_UNINITIALIZED = 0,
+ BUXTON_STATUS_CONNECTED,
+ BUXTON_STATUS_DISCONNECTED,
+ BUXTON_STATUS_MAX /* sentinel value */
+};
+
+/*
+ *
+ */
+typedef void (*buxton_status_callback)(enum buxton_status status,
+ void *user_data);
+
+/*
+ *
+ */
+int buxton_open(struct buxton_client **client,
+ buxton_status_callback callback, void *user_data);
+
+/*
+ *
+ */
+int buxton_close(struct buxton_client *client);
+
+
+/*
+ *
+ */
+enum buxton_key_type {
+ BUXTON_TYPE_UNKNOWN = 0,
+ BUXTON_TYPE_STRING,
+ BUXTON_TYPE_INT32,
+ BUXTON_TYPE_UINT32,
+ BUXTON_TYPE_INT64,
+ BUXTON_TYPE_UINT64,
+ BUXTON_TYPE_DOUBLE,
+ BUXTON_TYPE_BOOLEAN,
+ BUXTON_TYPE_MAX /* sentinel value */
+};
+#define BUXTON_TYPE_PRIVILEGE BUXTON_TYPE_STRING
+
+/*
+ *
+ */
+struct buxton_value;
+
+/*
+ *
+ */
+struct buxton_value *buxton_value_create_string(const char *s);
+struct buxton_value *buxton_value_create_int32(int32_t i);
+struct buxton_value *buxton_value_create_uint32(uint32_t u);
+struct buxton_value *buxton_value_create_int64(int64_t i64);
+struct buxton_value *buxton_value_create_uint64(uint64_t u64);
+struct buxton_value *buxton_value_create_double(double d);
+struct buxton_value *buxton_value_create_boolean(int32_t b);
+
+int buxton_value_get_type(const struct buxton_value *val,
+ enum buxton_key_type *type);
+int buxton_value_get_string(const struct buxton_value *val, const char **s);
+int buxton_value_get_int32(const struct buxton_value *val, int32_t *i);
+int buxton_value_get_uint32(const struct buxton_value *val, uint32_t *u);
+int buxton_value_get_int64(const struct buxton_value *val, int64_t *i64);
+int buxton_value_get_uint64(const struct buxton_value *val, uint64_t *u64);
+int buxton_value_get_double(const struct buxton_value *val, double *d);
+int buxton_value_get_boolean(const struct buxton_value *val, int32_t *b);
+
+struct buxton_value *buxton_value_duplicate(const struct buxton_value *val);
+void buxton_value_free(struct buxton_value *val);
+
+
+/*
+ *
+ */
+struct buxton_layer;
+
+/*
+ *
+ */
+typedef void (*buxton_response_callback)(int status,
+ const struct buxton_layer *layer, const char *key,
+ const struct buxton_value *val, void *user_data);
+
+/*
+ *
+ */
+struct buxton_layer *buxton_create_layer(const char *layer_name);
+
+/*
+ *
+ */
+const char *buxton_layer_get_name(const struct buxton_layer *layer);
+
+/*
+ *
+ */
+void buxton_free_layer(struct buxton_layer *layer);
+
+/*
+ *
+ */
+int buxton_set_value(struct buxton_client *client,
+ const struct buxton_layer *layer, const char *key,
+ const struct buxton_value *val,
+ buxton_response_callback callback, void *user_data);
+
+/*
+ *
+ */
+int buxton_set_value_sync(struct buxton_client *client,
+ const struct buxton_layer *layer, const char *key,
+ const struct buxton_value *val);
+
+/*
+ *
+ */
+int buxton_get_value(struct buxton_client *client,
+ const struct buxton_layer *layer, const char *key,
+ buxton_response_callback callback, void *user_data);
+
+/*
+ *
+ */
+int buxton_get_value_sync(struct buxton_client *client,
+ const struct buxton_layer *layer, const char *key,
+ struct buxton_value **val);
+
+
+/*
+ *
+ */
+typedef void (*buxton_list_callback)(int status,
+ const struct buxton_layer *layer,
+ char * const *names, unsigned int len,
+ void *user_data);
+
+/*
+ *
+ */
+int buxton_list_keys(struct buxton_client *client,
+ const struct buxton_layer *layer,
+ buxton_list_callback callback, void *user_data);
+
+/*
+ *
+ */
+int buxton_list_keys_sync(struct buxton_client *client,
+ const struct buxton_layer *layer,
+ char ***names, unsigned int *len);
+
+/*
+ *
+ */
+static inline void buxton_free_keys(char **names)
+{
+ char **k;
+
+ if (!names)
+ return;
+
+ k = names;
+ while (*k) {
+ free(*k);
+ k++;
+ }
+
+ free(names);
+}
+
+/*
+ *
+ */
+typedef void (*buxton_notify_callback)(const struct buxton_layer *layer,
+ const char *key, const struct buxton_value *val,
+ void *user_data);
+
+/*
+ *
+ */
+int buxton_register_notification(struct buxton_client *client,
+ const struct buxton_layer *layer, const char *key,
+ buxton_notify_callback notify, void *notify_data,
+ buxton_response_callback callback, void *user_data);
+
+/*
+ *
+ */
+int buxton_register_notification_sync(struct buxton_client *client,
+ const struct buxton_layer *layer, const char *key,
+ buxton_notify_callback notify, void *notify_data);
+
+/*
+ *
+ */
+int buxton_unregister_notification(struct buxton_client *client,
+ const struct buxton_layer *layer, const char *key,
+ buxton_notify_callback notify,
+ buxton_response_callback callback, void *user_data);
+
+/*
+ *
+ */
+int buxton_unregister_notification_sync(struct buxton_client *client,
+ const struct buxton_layer *layer, const char *key,
+ buxton_notify_callback notify);
+
+/* Wrapper APIs ------------------ */
+
+/* Admin APIs ------------------- */
+
+/*
+ *
+ */
+void buxton_layer_set_uid(struct buxton_layer *layer, uid_t uid);
+
+enum buxton_layer_type {
+ BUXTON_LAYER_NORMAL = 0,
+ BUXTON_LAYER_BASE,
+ BUXTON_LAYER_MAX /* sentinel value */
+};
+
+/*
+ *
+ */
+void buxton_layer_set_type(struct buxton_layer *layer,
+ enum buxton_layer_type type);
+
+/*
+ *
+ */
+int buxton_create_value(struct buxton_client *client,
+ const struct buxton_layer *layer, const char *key,
+ const char *read_privilege, const char *write_privilege,
+ const struct buxton_value *val,
+ buxton_response_callback callback, void *user_data);
+
+/*
+ *
+ */
+int buxton_create_value_sync(struct buxton_client *client,
+ const struct buxton_layer *layer, const char *key,
+ const char *read_privilege, const char *write_privilege,
+ const struct buxton_value *val);
+
+/*
+ *
+ */
+int buxton_unset_value(struct buxton_client *client,
+ const struct buxton_layer *layer, const char *key,
+ buxton_response_callback callback, void *user_data);
+
+/*
+ *
+ */
+int buxton_unset_value_sync(struct buxton_client *client,
+ const struct buxton_layer *layer, const char *key);
+
+/*
+ *
+ */
+enum buxton_priv_type {
+ BUXTON_PRIV_UNKNOWN = 0,
+ BUXTON_PRIV_READ,
+ BUXTON_PRIV_WRITE,
+ BUXTON_PRIV_MAX /* sentinel value */
+};
+
+/*
+ *
+ */
+int buxton_set_privilege(struct buxton_client *client,
+ const struct buxton_layer *layer, const char *key,
+ enum buxton_priv_type type,
+ const char *privilege,
+ buxton_response_callback callback, void *user_data);
+
+/*
+ *
+ */
+int buxton_set_privilege_sync(struct buxton_client *client,
+ const struct buxton_layer *layer, const char *key,
+ enum buxton_priv_type type,
+ const char *privilege);
+
+/*
+ *
+ */
+int buxton_get_privilege(struct buxton_client *client,
+ const struct buxton_layer *layer, const char *key,
+ enum buxton_priv_type type,
+ buxton_response_callback callback, void *user_data);
+
+/*
+ *
+ */
+int buxton_get_privilege_sync(struct buxton_client *client,
+ const struct buxton_layer *layer, const char *key,
+ enum buxton_priv_type type,
+ char **privilege);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* __BUXTON_H__ */
diff --git a/packaging/buxton2-pre.service b/packaging/buxton2-pre.service
new file mode 100644
index 0000000..5099ec5
--- /dev/null
+++ b/packaging/buxton2-pre.service
@@ -0,0 +1,10 @@
+[Unit]
+Description=Setup for Buxton2 service
+
+[Service]
+Type=oneshot
+ExecStart=/bin/sh -c 'mkdir -p /run/buxton2 && chmod 700 /run/buxton2 && chown buxton:buxton /run/buxton2'
+RemainAfterExit=yes
+
+[Install]
+WantedBy=multi-user.target
diff --git a/packaging/buxton2.conf b/packaging/buxton2.conf
new file mode 100644
index 0000000..aee011e
--- /dev/null
+++ b/packaging/buxton2.conf
@@ -0,0 +1,21 @@
+#
+# buxton2 config file for Tizen
+#
+
+[system]
+Type=System
+Backend=gdbm
+Storage=persistent
+Description=System configuration layer
+
+[memory]
+Type=System
+Backend=gdbm
+Storage=volatile
+Description=A termporary layer for scratch settings and data
+
+[user]
+Type=User
+Backend=gdbm
+Storage=persistent
+Description=Per-user settings
diff --git a/packaging/buxton2.manifest b/packaging/buxton2.manifest
new file mode 100644
index 0000000..ee2db1f
--- /dev/null
+++ b/packaging/buxton2.manifest
@@ -0,0 +1,8 @@
+<manifest>
+ <request>
+ <domain name="_"/>
+ </request>
+ <assign>
+ <filesystem path="/var/lib/buxton2" label="System" type="transmutable"/>
+ </assign>
+</manifest>
diff --git a/packaging/buxton2.service b/packaging/buxton2.service
new file mode 100644
index 0000000..425dc9f
--- /dev/null
+++ b/packaging/buxton2.service
@@ -0,0 +1,11 @@
+[Unit]
+Description=Buxton2 Configuration Service
+Requires=buxton2-pre.service
+After=buxton2-pre.service
+
+[Service]
+ExecStart=/usr/sbin/buxton2d -f
+User=buxton
+
+[Install]
+WantedBy=multi-user.target
diff --git a/packaging/buxton2.socket b/packaging/buxton2.socket
new file mode 100644
index 0000000..ecb0ddd
--- /dev/null
+++ b/packaging/buxton2.socket
@@ -0,0 +1,11 @@
+[Unit]
+Description=Buxton Configuration Service
+
+[Socket]
+ListenStream=/run/buxton2-0
+SocketMode=0777
+SmackLabelIPIn=*
+SmackLabelIPOut=@
+
+[Install]
+WantedBy=sockets.target
diff --git a/packaging/buxton2.spec b/packaging/buxton2.spec
new file mode 100644
index 0000000..d91e22a
--- /dev/null
+++ b/packaging/buxton2.spec
@@ -0,0 +1,169 @@
+Name: buxton2
+Version: 1.0
+Release: 0
+License: Apache-2.0
+Summary: A security-enabled configuration system
+Group: System/Configuration
+Source0: %{name}-%{version}.tar.gz
+Source1: %{name}.conf
+Source2: %{name}.service
+Source3: %{name}.socket
+Source4: %{name}-pre.service
+Source1001: %{name}.manifest
+BuildRequires: cmake
+BuildRequires: gdbm-devel
+BuildRequires: pkgconfig(glib-2.0)
+BuildRequires: pkgconfig(libsystemd)
+BuildRequires: pkgconfig(cynara-client-async)
+Requires(post): /usr/bin/getent
+Requires(post): /usr/bin/chown
+Requires(post): /usr/sbin/useradd
+Requires(post): /usr/sbin/groupadd
+Requires(post): /usr/bin/chsmack
+Obsoletes: buxton
+Provides: buxton
+
+%description
+Buxton is a security-enabled configuration management system. It
+features a layered approach to configuration storage, with each
+layer containing key-value pairs. Mandatory Access Control (MAC) is
+implemented at the key-value level. Cynara is used as default for MAC.
+
+Buxton provides a C library (libbuxton) for client applications to
+use. Internally, buxton uses a daemon (buxtond) for processing
+client requests and enforcing MAC. Also, a CLI (buxtonctl) is
+provided for interactive use and for use in shell scripts.
+
+%package devel
+Summary: A security-enabled configuration system - development files
+Requires: %{name} = %{version}
+
+%description devel
+Buxton is a security-enabled configuration management system. It
+features a layered approach to configuration storage, with each
+layer containing key-value pairs. Mandatory Access Control (MAC) is
+implemented at the key-value level. Cynara is used as default for MAC.
+
+Buxton provides a C library (libbuxton) for client applications to
+use. Internally, buxton uses a daemon (buxtond) for processing
+client requests and enforcing MAC. Also, a CLI (buxtonctl) is
+provided for interactive use and for use in shell scripts.
+
+This package provides development files for Buxton.
+
+
+%package -n vconf-compat
+Summary: buxton wrapper for vconf APIs
+Requires: %{name} = %{version}-%{release}
+Requires: /usr/bin/getopt
+Obsoletes: vconf-buxton
+Obsoletes: vconf
+Provides: vconf-buxton
+Provides: vconf
+
+%description -n vconf-compat
+Buxton wrapper library for providing vconf APIs
+
+
+%package -n vconf-compat-devel
+Summary: buxton wrapper for vconf APIs (devel)
+Requires: vconf-compat = %{version}-%{release}
+BuildRequires: pkgconfig(vconf-internal-keys)
+Obsoletes: vconf-buxton-devel
+Obsoletes: vconf-buxton-keys-devel
+Provides: vconf-buxton-devel
+Provides: vconf-buxton-keys-devel
+
+%description -n vconf-compat-devel
+Buxton wrapper library for providing vconf APIs (devel)
+
+
+%prep
+%setup -q
+cp %{SOURCE1001} .
+
+%build
+%cmake -DVERSION=%{version} \
+ -DCONFPATH:PATH=%{_sysconfdir}/%{name}.conf \
+ -DMODULE_DIR:PATH=%{_libdir}/%{name} \
+ -DDB_DIR:PATH=%{_localstatedir}/lib/%{name} \
+ -DTMPFS_DIR:PATH=/run/%{name} \
+ -DSOCKPATH:PATH=/run/%{name}-0 \
+ -DNDEBUG:BOOL=TRUE \
+ .
+
+%__make %{?_smp_mflags}
+
+%install
+%make_install
+
+# create the database directory
+install -m 700 -d %{buildroot}%{_localstatedir}/lib/%{name}
+
+# install config file
+install -m 755 -d %{buildroot}%{_sysconfdir}
+install -m 644 %{SOURCE1} %{buildroot}%{_sysconfdir}/%{name}.conf
+
+# install systemd unit files
+install -m 755 -d %{buildroot}%{_unitdir}
+install -m 644 %{SOURCE2} %{buildroot}%{_unitdir}/%{name}.service
+install -m 644 %{SOURCE3} %{buildroot}%{_unitdir}/%{name}.socket
+install -m 644 %{SOURCE4} %{buildroot}%{_unitdir}/%{name}-pre.service
+
+# enable socket activation
+install -m 755 -d %{buildroot}%{_unitdir}/sockets.target.wants
+ln -sf ../%{name}.socket %{buildroot}%{_unitdir}/sockets.target.wants/
+
+%post
+/sbin/ldconfig
+dbdir="%{_localstatedir}/lib/%{name}"
+
+# buxtond runs as user buxton of group buxton
+# create it on need!
+getent group buxton > /dev/null || groupadd -r buxton
+getent passwd buxton > /dev/null || useradd -r -g buxton -d "${dbdir}" buxton
+
+# The initial DBs will not have the correct labels and
+# permissions when created in postinstall during image
+# creation, so we set these file attributes here.
+chown -R buxton:buxton "${dbdir}"
+chsmack -a System "${dbdir}"
+
+%postun -p /sbin/ldconfig
+
+%post -n vconf-compat -p /sbin/ldconfig
+
+%postun -n vconf-compat -p /sbin/ldconfig
+
+%files
+%manifest %{name}.manifest
+%license LICENSE.Apache-2.0
+%config(noreplace) %{_sysconfdir}/%{name}.conf
+%{_bindir}/buxton2ctl
+%{_sbindir}/buxton2d
+%{_libdir}/%{name}/*.so
+%{_libdir}/libbuxton2.so.*
+%{_unitdir}/%{name}.service
+%{_unitdir}/%{name}.socket
+%{_unitdir}/%{name}-pre.service
+%{_unitdir}/sockets.target.wants/%{name}.socket
+%attr(0700,buxton,buxton) %dir %{_localstatedir}/lib/%{name}
+
+%files devel
+%manifest %{name}.manifest
+%{_includedir}/buxton2.h
+%{_libdir}/libbuxton2.so
+%{_libdir}/pkgconfig/buxton2.pc
+
+%files -n vconf-compat
+%manifest %{name}.manifest
+%{_bindir}/vconftool
+%{_libdir}/libvconf.so.*
+
+%files -n vconf-compat-devel
+%manifest %{name}.manifest
+%{_includedir}/vconf/vconf.h
+%{_includedir}/vconf/vconf-keys.h
+%{_libdir}/libvconf.so
+%{_libdir}/pkgconfig/vconf.pc
+
diff --git a/vconf-compat/CMakeLists.txt b/vconf-compat/CMakeLists.txt
new file mode 100644
index 0000000..3f6c086
--- /dev/null
+++ b/vconf-compat/CMakeLists.txt
@@ -0,0 +1,29 @@
+# libvconf build
+
+PKG_CHECK_MODULES(V_PKGS REQUIRED vconf-internal-keys)
+
+FOREACH(flag ${V_PKGS_CFLAGS})
+ SET(VCONF_CFLAGS "${VCONF_CFLAGS} ${flag}")
+ENDFOREACH()
+SET(VCONF_CFLAGS "-fvisibility=hidden ${VCONF_CFLAGS}")
+
+INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/lib/include)
+
+SET(SRC vconf.c)
+ADD_LIBRARY(vconf SHARED ${SRC})
+SET_TARGET_PROPERTIES(vconf PROPERTIES
+ COMPILE_FLAGS ${VCONF_CFLAGS}
+ LINK_FLAGS "-Wl,--version-script=vconf.sym"
+ VERSION 0.3.1
+ SOVERSION 0
+)
+TARGET_LINK_LIBRARIES(vconf ${PKG_LDFLAGS} buxton2)
+INSTALL(TARGETS vconf DESTINATION ${LIB_INSTALL_DIR})
+
+CONFIGURE_FILE(vconf.pc.in vconf.pc @ONLY)
+INSTALL(FILES vconf.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig)
+
+INSTALL(FILES vconf.h vconf-keys.h DESTINATION ${INCLUDE_INSTALL_DIR}/vconf)
+
+INSTALL(PROGRAMS vconftool DESTINATION bin)
+
diff --git a/vconf-compat/vconf-keys.h b/vconf-compat/vconf-keys.h
new file mode 100644
index 0000000..2522827
--- /dev/null
+++ b/vconf-compat/vconf-keys.h
@@ -0,0 +1,24 @@
+/*
+ * Buxton
+ *
+ * Copyright (C) 2015 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ */
+
+#ifndef __VCONF_KEYS_H__
+#define __VCONF_KEYS_H__
+
+#include <vconf-internal-keys.h>
+
+#endif /* __VCONF_KEYS_H__ */
diff --git a/vconf-compat/vconf.c b/vconf-compat/vconf.c
new file mode 100644
index 0000000..18632e9
--- /dev/null
+++ b/vconf-compat/vconf.c
@@ -0,0 +1,680 @@
+/*
+ * Buxton
+ *
+ * Copyright (C) 2015 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ */
+
+#include <errno.h>
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <glib.h>
+
+#include <buxton2.h>
+
+#include "vconf.h"
+
+#ifndef EXPORT
+# define EXPORT __attribute__((visibility("default")))
+#endif
+
+#define LOGE(fmt, ...) fprintf(stderr, fmt "\n", ##__VA_ARGS__)
+
+static int _refcnt;
+static struct buxton_client *client;
+static struct buxton_layer *system_layer;
+static struct buxton_layer *memory_layer;
+static GHashTable *noti_tbl;
+
+struct noti {
+ char *key;
+ GList *noti_list; /* struct noti_cb list */
+};
+
+struct noti_cb {
+ vconf_callback_fn cb;
+ void *user_data;
+ gboolean deleted;
+};
+
+EXPORT char *vconf_keynode_get_name(keynode_t *keynode)
+{
+ if (!keynode || !keynode->keyname) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ return keynode->keyname;
+}
+
+EXPORT int vconf_keynode_get_type(keynode_t *keynode)
+{
+ if (!keynode) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ return keynode->type;
+}
+
+EXPORT int vconf_keynode_get_int(keynode_t *keynode)
+{
+ if (!keynode) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (keynode->type != VCONF_TYPE_INT) {
+ errno = ENOTSUP;
+ return -1;
+ }
+
+ return keynode->value.i;
+}
+
+EXPORT double vconf_keynode_get_dbl(keynode_t *keynode)
+{
+ if (!keynode) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (keynode->type != VCONF_TYPE_DOUBLE) {
+ errno = ENOTSUP;
+ return -1;
+ }
+
+ return keynode->value.d;
+}
+
+EXPORT int vconf_keynode_get_bool(keynode_t *keynode)
+{
+ if (!keynode) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (keynode->type != VCONF_TYPE_BOOL) {
+ errno = ENOTSUP;
+ return -1;
+ }
+
+ return !!(keynode->value.b);
+}
+
+EXPORT char *vconf_keynode_get_str(keynode_t *keynode)
+{
+ if (!keynode) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (keynode->type != VCONF_TYPE_STRING) {
+ errno = ENOTSUP;
+ return NULL;
+ }
+
+ return keynode->value.s;
+}
+
+static struct buxton_layer *get_layer(const char *key)
+{
+ if (key && !strncmp(key, "memory/", strlen("memory/")))
+ return memory_layer;
+
+ return system_layer;
+}
+
+static gboolean free_noti_cb(gpointer data)
+{
+ GList *list = data;
+ GList *l;
+ GList *n;
+
+ if (!list)
+ return G_SOURCE_REMOVE;
+
+ for (l = list, n = g_list_next(l); l; l = n, n = g_list_next(n)) {
+ struct noti_cb *noticb = l->data;
+
+ if (!noticb->deleted)
+ continue;
+
+ list = g_list_delete_link(list, l);
+ free(noticb);
+ }
+
+ return G_SOURCE_REMOVE;
+}
+
+static void free_noti(struct noti *noti)
+{
+ GList *l;
+
+ assert(noti);
+
+ for (l = noti->noti_list; l; l = g_list_next(l)) {
+ struct noti_cb *noticb = l->data;
+
+ noticb->deleted = TRUE;
+ }
+ g_idle_add(free_noti_cb, noti->noti_list);
+
+ free(noti->key);
+ free(noti);
+}
+
+static void _close(void)
+{
+ _refcnt--;
+ if (_refcnt)
+ return;
+
+ buxton_free_layer(system_layer);
+ system_layer = NULL;
+
+ buxton_free_layer(memory_layer);
+ memory_layer = NULL;
+
+ g_hash_table_destroy(noti_tbl);
+ noti_tbl = NULL;
+
+ buxton_close(client);
+ client = NULL;
+}
+
+static int _open(void)
+{
+ int r;
+
+ _refcnt++;
+ if (_refcnt > 1)
+ return 0;
+
+ r = buxton_open(&client, NULL, NULL);
+ if (r == -1) {
+ LOGE("Can't connect to buxton: %d", errno);
+ return -1;
+ }
+
+ noti_tbl = g_hash_table_new_full(g_str_hash, g_str_equal,
+ NULL, (GDestroyNotify)free_noti);
+
+ system_layer = buxton_create_layer("system");
+ memory_layer = buxton_create_layer("memory");
+
+ return 0;
+}
+
+static void to_vconf_t(const struct buxton_value *val, keynode_t *node)
+{
+ int r;
+ enum buxton_key_type type;
+ uint32_t u;
+ int64_t i64;
+ uint64_t u64;
+
+ assert(val);
+ assert(node);
+
+ r = buxton_value_get_type(val, &type);
+ if (r == -1)
+ type = BUXTON_TYPE_UNKNOWN;
+
+ switch (type) {
+ case BUXTON_TYPE_STRING:
+ node->type = VCONF_TYPE_STRING;
+ buxton_value_get_string(val, (const char **)&node->value.s);
+ break;
+ case BUXTON_TYPE_INT32:
+ node->type = VCONF_TYPE_INT;
+ buxton_value_get_int32(val, &node->value.i);
+ break;
+ case BUXTON_TYPE_UINT32:
+ node->type = VCONF_TYPE_INT;
+ buxton_value_get_uint32(val, &u);
+ node->value.i = (int)u;
+ break;
+ case BUXTON_TYPE_INT64:
+ node->type = VCONF_TYPE_INT;
+ buxton_value_get_int64(val, &i64);
+ node->value.i = (int)i64;
+ break;
+ case BUXTON_TYPE_UINT64:
+ node->type = VCONF_TYPE_INT;
+ buxton_value_get_uint64(val, &u64);
+ node->value.i = (int)u64;
+ break;
+ case BUXTON_TYPE_DOUBLE:
+ node->type = VCONF_TYPE_DOUBLE;
+ buxton_value_get_double(val, &node->value.d);
+ break;
+ case BUXTON_TYPE_BOOLEAN:
+ node->type = VCONF_TYPE_BOOL;
+ buxton_value_get_boolean(val, &node->value.b);
+ break;
+ default:
+ node->type = VCONF_TYPE_NONE;
+ break;
+ }
+}
+
+static void notify_cb(const struct buxton_layer *layer, const char *key,
+ const struct buxton_value *val, void *user_data)
+{
+ struct noti *noti = user_data;
+ keynode_t *node;
+ GList *l;
+
+ assert(noti);
+
+ node = calloc(1, sizeof(*node));
+ if (!node)
+ return;
+
+ node->keyname = (char *)key;
+ to_vconf_t(val, node);
+
+ for (l = noti->noti_list; l; l = g_list_next(l)) {
+ struct noti_cb *noticb = l->data;
+
+ if (noticb->deleted)
+ continue;
+
+ assert(noticb->cb);
+ noticb->cb(node, noticb->user_data);
+ }
+
+ free(node);
+}
+
+static struct noti_cb *find_noti_cb(struct noti *noti, vconf_callback_fn cb)
+{
+ GList *l;
+
+ assert(noti);
+ assert(cb);
+
+ for (l = noti->noti_list; l; l = g_list_next(l)) {
+ struct noti_cb *noticb = l->data;
+
+ if (noticb->cb == cb)
+ return noticb;
+ }
+
+ return NULL;
+}
+
+
+static int add_noti(struct noti *noti, vconf_callback_fn cb, void *user_data)
+{
+ struct noti_cb *noticb;
+
+ assert(noti);
+ assert(cb);
+
+ noticb = find_noti_cb(noti, cb);
+ if (noticb) {
+ if (noticb->deleted) { /* reuse */
+ noticb->user_data = user_data;
+ noticb->deleted = FALSE;
+ return 0;
+ }
+
+ errno = EEXIST;
+ return -1;
+ }
+
+ noticb = calloc(1, sizeof(*noticb));
+ if (!noticb)
+ return -1;
+
+ noticb->cb = cb;
+ noticb->user_data = user_data;
+ noticb->deleted = FALSE;
+
+ noti->noti_list = g_list_append(noti->noti_list, noticb);
+
+ return 0;
+}
+
+static int register_noti(const char *key, vconf_callback_fn cb, void *user_data)
+{
+ int r;
+ struct noti *noti;
+
+ assert(key);
+ assert(cb);
+
+ noti = calloc(1, sizeof(*noti));
+ if (!noti)
+ return -1;
+
+ noti->key = strdup(key);
+ if (!noti->key) {
+ free(noti);
+ return -1;
+ }
+
+ r = add_noti(noti, cb, user_data);
+ if (r == -1) {
+ free(noti->key);
+ free(noti);
+ return -1;
+ }
+
+ r = buxton_register_notification_sync(client, get_layer(key), key,
+ notify_cb, noti);
+ if (r == -1) {
+ LOGE("vconf_notify_key_changed: key '%s' add notify error %d",
+ key, errno);
+ free_noti(noti);
+ return -1;
+ }
+
+ /* increase reference count */
+ _open();
+ g_hash_table_insert(noti_tbl, noti->key, noti);
+
+ return 0;
+}
+
+EXPORT int vconf_notify_key_changed(const char *key, vconf_callback_fn cb,
+ void *user_data)
+{
+ int r;
+ struct noti *noti;
+
+ if (!key || !cb) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ r = _open();
+ if (r == -1)
+ return -1;
+
+ noti = g_hash_table_lookup(noti_tbl, key);
+ if (!noti)
+ r = register_noti(key, cb, user_data);
+ else
+ r = add_noti(noti, cb, user_data);
+
+ _close();
+
+ return r;
+}
+
+static int unregister_noti(struct noti *noti)
+{
+ int r;
+ int cnt;
+ GList *l;
+
+ assert(noti);
+
+ cnt = 0;
+ for (l = noti->noti_list; l; l = g_list_next(l)) {
+ struct noti_cb *noticb = l->data;
+
+ if (!noticb->deleted)
+ cnt++;
+ }
+
+ if (cnt > 0)
+ return 0;
+
+ r = buxton_unregister_notification_sync(client, get_layer(noti->key),
+ noti->key, notify_cb);
+ if (r == -1)
+ LOGE("unregister error '%s' %d", noti->key, errno);
+
+ g_hash_table_remove(noti_tbl, noti->key);
+
+ /* decrease reference count */
+ _close();
+
+ return r;
+}
+
+EXPORT int vconf_ignore_key_changed(const char *key, vconf_callback_fn cb)
+{
+ struct noti *noti;
+ struct noti_cb *noticb;
+
+ if (!key || !cb) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ noti = g_hash_table_lookup(noti_tbl, key);
+ if (!noti) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ noticb = find_noti_cb(noti, cb);
+ if (!noticb) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ noticb->deleted = TRUE;
+
+ return unregister_noti(noti);
+}
+
+static int vconf_set(const char *key, const struct buxton_value *val)
+{
+ int r;
+
+ assert(key);
+ assert(val);
+
+ r = _open();
+ if (r == -1)
+ return -1;
+
+ r = buxton_set_value_sync(client, get_layer(key), key, val);
+ if (r == -1)
+ LOGE("set value: key '%s' errno %d", key, errno);
+
+ _close();
+
+ return r;
+}
+
+EXPORT int vconf_set_int(const char *key, int intval)
+{
+ int r;
+ struct buxton_value *val;
+
+ if (!key) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ val = buxton_value_create_int32(intval);
+ if (!val)
+ return -1;
+
+ r = vconf_set(key, val);
+
+ buxton_value_free(val);
+
+ return r;
+}
+
+EXPORT int vconf_set_bool(const char *key, int boolval)
+{
+ int r;
+ struct buxton_value *val;
+
+ if (!key) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ val = buxton_value_create_boolean(boolval);
+ if (!val)
+ return -1;
+
+ r = vconf_set(key, val);
+
+ buxton_value_free(val);
+
+ return r;
+}
+
+EXPORT int vconf_set_str(const char *key, const char *strval)
+{
+ int r;
+ struct buxton_value *val;
+
+ if (!key || !strval) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ val = buxton_value_create_string(strval);
+ if (!val)
+ return -1;
+
+ r = vconf_set(key, val);
+
+ buxton_value_free(val);
+
+ return r;
+}
+
+static int vconf_get(const char *key, enum buxton_key_type type,
+ struct buxton_value **val)
+{
+ int r;
+ struct buxton_value *v;
+
+ assert(key);
+ assert(val);
+
+ r = _open();
+ if (r == -1)
+ return -1;
+
+ r = buxton_get_value_sync(client, get_layer(key), key, &v);
+ if (r == -1) {
+ LOGE("get value: key '%s' errno %d", key, errno);
+ } else {
+ enum buxton_key_type t;
+
+ r = buxton_value_get_type(v, &t);
+ if (r == -1)
+ t = BUXTON_TYPE_UNKNOWN;
+
+ if (t != type) {
+ buxton_value_free(v);
+ errno = ENOTSUP;
+ r = -1;
+ } else {
+ *val = v;
+ }
+ }
+
+ _close();
+
+ return r;
+}
+
+EXPORT int vconf_get_int(const char *key, int *intval)
+{
+ int r;
+ struct buxton_value *val;
+ int32_t i;
+
+ if (!key || !intval) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ r = vconf_get(key, BUXTON_TYPE_INT32, &val);
+ if (r == -1)
+ return -1;
+
+ r = buxton_value_get_int32(val, &i);
+
+ buxton_value_free(val);
+
+ if (r == -1)
+ return -1;
+
+ *intval = i;
+
+ return 0;
+}
+
+EXPORT int vconf_get_bool(const char *key, int *boolval)
+{
+ int r;
+ struct buxton_value *val;
+ int32_t b;
+
+ if (!key || !boolval) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ r = vconf_get(key, BUXTON_TYPE_BOOLEAN, &val);
+ if (r == -1)
+ return -1;
+
+ r = buxton_value_get_boolean(val, &b);
+
+ buxton_value_free(val);
+
+ if (r == -1)
+ return -1;
+
+ *boolval = b;
+
+ return 0;
+}
+
+EXPORT char *vconf_get_str(const char *key)
+{
+ int r;
+ struct buxton_value *val;
+ const char *s;
+ char *str;
+
+ if (!key) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ r = vconf_get(key, BUXTON_TYPE_STRING, &val);
+ if (r == -1)
+ return NULL;
+
+ r = buxton_value_get_string(val, &s);
+ if (r == -1)
+ s = NULL;
+
+ str = s ? strdup(s) : NULL;
+
+ buxton_value_free(val);
+
+ return str;
+}
+
diff --git a/vconf-compat/vconf.h b/vconf-compat/vconf.h
new file mode 100644
index 0000000..a75c80e
--- /dev/null
+++ b/vconf-compat/vconf.h
@@ -0,0 +1,199 @@
+/*
+ * Buxton
+ *
+ * Copyright (C) 2015 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ */
+
+#ifndef __VCONF_H__
+#define __VCONF_H__
+
+#include "vconf-keys.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum vconf_t {
+ VCONF_TYPE_NONE = 0, /**< Vconf none type for Error detection */
+ VCONF_TYPE_STRING = 40, /**< Vconf string type */
+ VCONF_TYPE_INT = 41, /**< Vconf integer type */
+ VCONF_TYPE_DOUBLE = 42, /**< Vconf double type */
+ VCONF_TYPE_BOOL = 43, /**< Vconf boolean type */
+ VCONF_TYPE_DIR /**< Vconf directory type */
+};
+
+/**
+ * keynode_t key node structure
+ */
+typedef struct _keynode_t {
+ char *keyname;
+ int type;
+ union {
+ int i;
+ int b;
+ double d;
+ char *s;
+ } value;
+ struct _keynode_t *next;
+} keynode_t;
+
+/**
+ * Get the name of key
+ *
+ * @param[in] keynode Key node
+ * @return the name
+ * @deprecated use buxton APIs
+ */
+char *vconf_keynode_get_name(keynode_t *keynode);
+
+/**
+ * Get the type of key
+ *
+ * @param[in] keynode Key node
+ * @return the type
+ * @deprecated use buxton APIs
+ */
+int vconf_keynode_get_type(keynode_t *keynode);
+
+/**
+ * Get an integer value of key
+ *
+ * @param[in] keynode Key node
+ * @return An integer value
+ * @deprecated use buxton APIs
+ */
+int vconf_keynode_get_int(keynode_t *keynode);
+
+/**
+ * Get a double-precision float value of key
+ *
+ * @param[in] keynode Key node
+ * @return A double-precision float value
+ * @deprecated use buxton APIs
+ */
+double vconf_keynode_get_dbl(keynode_t *keynode);
+
+/**
+ * Get a boolean value of key
+ *
+ * @param[in] keynode Key node
+ * @return a boolean value
+ * @deprecated use buxton APIs
+ */
+int vconf_keynode_get_bool(keynode_t *keynode);
+
+/**
+ * Get a string value of key
+ *
+ * @param[in] keynode Key node
+ * @return a string value
+ * @deprecated use buxton APIs
+ */
+char *vconf_keynode_get_str(keynode_t *keynode);
+
+/**
+ * The type of function which is called when a key is changed
+ *
+ * @param[in] keynode Key node
+ * @param[in] user_data data passed to callback function
+ */
+typedef void (*vconf_callback_fn)(keynode_t *keynode, void *user_data);
+
+/**
+ * Add a callback function which is called when a key is changed
+ *
+ * @param[in] key the name of key
+ * @param[in] cb callback function
+ * @param[in] user_data data passed to callback function
+ * @return 0 on success, -1 on error
+ * @deprecated use buxton APIs
+ */
+int vconf_notify_key_changed(const char *key, vconf_callback_fn cb,
+ void *user_data);
+
+/**
+ * Remove a change callback function
+ *
+ * @param[in] key the name of key
+ * @param[in] cb callback function
+ * @return 0 on success, -1 on error
+ * @deprecated use buxton APIs
+ */
+int vconf_ignore_key_changed(const char *key, vconf_callback_fn cb);
+
+/**
+ * Set an integer value
+ *
+ * @param[in] key the name of key
+ * @param[in] intval an integer value
+ * @return 0 on success, -1 on error
+ * @deprecated use buxton APIs
+ */
+int vconf_set_int(const char *key, int intval);
+
+/**
+ * Set a boolean value
+ *
+ * @param[in] key the name of key
+ * @param[in] boolval a boolean value
+ * @return 0 on success, -1 on error
+ * @deprecated use buxton APIs
+ */
+int vconf_set_bool(const char *key, int boolval);
+
+/**
+ * Set a string value
+ *
+ * @param[in] key the name of key
+ * @param[in] strval a string value
+ * @return 0 on success, -1 on error
+ * @deprecated use buxton APIs
+ */
+int vconf_set_str(const char *key, const char *strval);
+
+/**
+ * Get an integer value
+ *
+ * @param[in] key the name of key
+ * @param[out] intval a pointer to an integer value to be set
+ * @return 0 on success, -1 on error
+ * @deprecated use buxton APIs
+ */
+int vconf_get_int(const char *key, int *intval);
+
+/**
+ * Get a boolean value
+ *
+ * @param[in] key the name of key
+ * @param[out] boolval a pointer to a boolean value to be set
+ * @return 0 on success, -1 on error
+ * @deprecated use buxton APIs
+ */
+int vconf_get_bool(const char *key, int *boolval);
+
+/**
+ * Get a string value
+ *
+ * @param[in] key the name of key
+ * @return a string on success (should be freed by free()), NULL on error
+ * @deprecated use buxton APIs
+ */
+char *vconf_get_str(const char *key);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __VCONF_H__ */
diff --git a/vconf-compat/vconf.pc.in b/vconf-compat/vconf.pc.in
new file mode 100644
index 0000000..61bf779
--- /dev/null
+++ b/vconf-compat/vconf.pc.in
@@ -0,0 +1,10 @@
+prefix=@CMAKE_INSTALL_PREFIX@
+exec_prefix=${prefix}
+libdir=@LIB_INSTALL_DIR@
+includedir=@INCLUDE_INSTALL_DIR@/vconf
+
+Name: vconf
+Description: configuration system library
+Version: 0.3.1
+Libs: -L${libdir} -lvconf
+Cflags: -I${includedir}
diff --git a/vconf-compat/vconf.sym b/vconf-compat/vconf.sym
new file mode 100644
index 0000000..b9c1b76
--- /dev/null
+++ b/vconf-compat/vconf.sym
@@ -0,0 +1,19 @@
+VCONF_BUXTON_1.0 {
+ global:
+ vconf_keynode_get_name;
+ vconf_keynode_get_type;
+ vconf_keynode_get_int;
+ vconf_keynode_get_dbl;
+ vconf_keynode_get_bool;
+ vconf_keynode_get_str;
+ vconf_notify_key_changed;
+ vconf_ignore_key_changed;
+ vconf_set_int;
+ vconf_set_bool;
+ vconf_set_str;
+ vconf_get_int;
+ vconf_get_bool;
+ vconf_get_str;
+ local:
+ *;
+};
diff --git a/vconf-compat/vconftool b/vconf-compat/vconftool
new file mode 100755
index 0000000..3c46718
--- /dev/null
+++ b/vconf-compat/vconftool
@@ -0,0 +1,235 @@
+#!/bin/sh
+
+BUXTONTOOL=/usr/bin/buxton2ctl
+
+OPT_DIRECT=""
+OPT_TYPE=""
+OPT_RECUR=0
+OPT_INIT=""
+OPT_LABEL=""
+OPT_DEBUG=0
+
+usage() {
+ COMM=`basename $0`
+cat << EOF
+
+ Usage:
+ $COMM set [set-options] <key name> <value>
+ $COMM get [get-options] <key name>
+ $COMM unset <key name>
+
+ set-options:
+ -t, --type=int|bool|double|string Type of key
+ -u, --uid=UID User ID (for compatibility, ignored)
+ -g, --gid=GID Gourp ID (for compatibility, ignored)
+ -i, --install Install memory type key
+ -s, --smack=LABEL Set a SMACK label
+ -f, --force Overwrite by force (for compatibility, ignored)
+
+ ex)
+ $COMM set -t string db/testapp/key1 "This is test"
+ $COMM set -t int -i memory/testapp/status -1
+
+ get-options:
+ -r, --recursive Retrieve all keys having the given prefix
+
+ ex)
+ $COMM get db/testapp/key1
+ $COMM get -r db/testapp
+
+EOF
+ exit 1
+}
+
+dbg() {
+ [ ${OPT_DEBUG} -eq 0 ] && return 0
+ echo "$*" >&2
+ return 0
+}
+
+get_layer() {
+ case "$1" in
+ memory*)
+ echo -n "memory"
+ ;;
+ *)
+ echo -n "system"
+ ;;
+ esac
+}
+
+get_key() {
+ [ -z "$1" ] && exit 1
+
+ LAYER=`get_layer $1`
+
+ dbg ${BUXTONTOOL} ${OPT_DIRECT} get ${LAYER} $1
+ RES=`${BUXTONTOOL} ${OPT_DIRECT} get ${LAYER} $1 2>&1`
+ [ $? -ne 0 ] && echo "Error: $RES" && exit 1
+ VAL=`echo "$RES" | sed 's/^.* = //g; s/\(.*\): \(.*\)$/\2 (\1)/g'`
+ echo "$1, value = $VAL"
+}
+
+do_get() {
+ [ -z "${OPT_KEY}" ] && echo "Invalid key name" && usage
+
+ if [ $OPT_RECUR -eq 0 ]; then
+ get_key ${OPT_KEY}
+ exit $?
+ fi
+
+ LAYER=`get_layer ${OPT_KEY}`
+
+ dbg ${BUXTONTOOL} ${OPT_DIRECT} list-keys ${LAYER} | grep ^${OPT_KEY}
+ LIST=`${BUXTONTOOL} ${OPT_DIRECT} list-keys ${LAYER} | grep ^${OPT_KEY}`
+ for k in $LIST; do
+ get_key $k
+ done
+}
+
+do_unset() {
+ [ -z "${OPT_KEY}" ] && echo "Invalid key name" && usage
+
+ LAYER=`get_layer ${OPT_KEY}`
+
+ dbg ${BUXTONTOOL} ${OPT_DIRECT} unset ${LAYER} ${OPT_KEY}
+ RES=`${BUXTONTOOL} ${OPT_DIRECT} unset ${LAYER} ${OPT_KEY} 2>&1`
+ [ $? -ne 0 ] && echo "Error: $RES" && exit 1
+ exit 0
+}
+
+get_type() {
+ case "$1" in
+ int) echo -n "int32" ;;
+ bool) echo -n "bool" ;;
+ double) echo -n "double" ;;
+ string) echo -n "string" ;;
+ *) echo -n "" ;;
+ esac
+}
+
+do_create() {
+ [ -z "${OPT_KEY}" ] && echo "Invalid key name" && usage
+
+ LAYER=`get_layer ${OPT_KEY}`
+ TYPE=`get_type ${OPT_TYPE}`
+ [ -z "${TYPE}" ] && echo "Type '${OPT_TYPE}': Invalid type" && usage
+
+ dbg ${BUXTONTOOL} ${OPT_DIRECT} create-${TYPE} \
+ ${LAYER} ${OPT_KEY} \"${OPT_VAL}\" \"\" \"${OPT_LABEL}\"
+ RES=`${BUXTONTOOL} ${OPT_DIRECT} create-${TYPE} \
+ ${LAYER} ${OPT_KEY} "${OPT_VAL}" "" "${OPT_LABEL}" 2>&1`
+ [ $? -ne 0 ] && echo "Error: $RES" && exit 1
+ exit 0
+}
+
+do_set() {
+ [ -z "${OPT_KEY}" ] && echo "Invalid key name" && usage
+
+ LAYER=`get_layer ${OPT_KEY}`
+ TYPE=`get_type ${OPT_TYPE}`
+ [ -z "${TYPE}" ] && echo "Type '${OPT_TYPE}': Invalid type" && usage
+
+ dbg ${BUXTONTOOL} ${OPT_DIRECT} ${OPT_INIT} set-${TYPE} \
+ ${LAYER} ${OPT_KEY} \"${OPT_VAL}\"
+ RES=`${BUXTONTOOL} ${OPT_DIRECT} ${OPT_INIT} set-${TYPE} \
+ ${LAYER} ${OPT_KEY} "${OPT_VAL}" 2>&1`
+ if [ $? -ne 0 ]; then
+ echo "$RES" | grep -q "No such file"
+ if [ $? -eq 0 ]; then
+ do_create
+ exit $?
+ fi
+
+ echo "Error: $RES"
+ exit 1
+ fi
+
+ [ -z "${OPT_LABEL}" ] && exit 0
+
+ dbg ${BUXTONTOOL} ${OPT_DIRECT} set-write-priv \
+ ${LAYER} ${OPT_KEY} \"${OPT_LABEL}\"
+ RES=`${BUXTONTOOL} ${OPT_DIRECT} set-write-priv \
+ ${LAYER} ${OPT_KEY} "${OPT_LABEL}" 2>&1 `
+ [ $? -ne 0 ] && echo "Error: $RES" && exit 1
+ exit 0
+}
+
+_getopt() {
+ eval set -- `
+ for x in "$@"; do
+ echo -n "'$x'" \
+ | sed -e "s/\(.\)'\(.\)/\\1\\\\'\\2/g" \
+ -e "s/^'\(-[0-9.]*\)'\$/'protect-sign:\1'/g" \
+ -e "s/$/ /g"
+ done
+ `
+
+ getopt -n `basename $0` \
+ -l type:,recursive,gid:,uid:,force,install,smack:,debug,verbose,quiet \
+ -o t:rg:u:fis:dvq -- "$@" \
+ | sed -e "s/'protect-sign:/'/g"
+}
+
+eval set -- `_getopt "$@"`
+
+while [ "$1" != "--" ]; do
+ case "$1" in
+ -t|--type)
+ OPT_TYPE="$2"
+ shift 2
+ ;;
+ -r|--recursive)
+ OPT_RECUR=1
+ shift
+ ;;
+ -g|--gid|-u|--uid)
+ # ignore
+ shift 2
+ ;;
+ -f|--force|-v|--verbose|-q|--quiet)
+ # ignore
+ shift
+ ;;
+ -i|--install)
+ OPT_INIT="-i"
+ shift
+ ;;
+ -s|--smack)
+ OPT_LABEL="$2"
+ shift 2
+ ;;
+ -d|--debug)
+ OPT_DEBUG=1
+ shift
+ ;;
+ *)
+ echo "Invalid argument $1"
+ usage
+ ;;
+ esac
+done
+
+shift
+OPT_CMD="$1"
+OPT_KEY="$2"
+OPT_VAL="$3"
+
+# check daemon status
+if [ ! -x ${BUXTONTOOL} ]; then
+ echo "${BUXTONTOOL} not exist" >&2
+ exit 1
+fi
+
+${BUXTONTOOL} check 2>&1 > /dev/null
+if [ $? -ne 0 ]; then
+ OPT_DIRECT="-d"
+fi
+
+case "$OPT_CMD" in
+get) do_get ;;
+set) do_set ;;
+unset) do_unset ;;
+*) usage ;;
+esac
+