From: Suchang Woo Date: Thu, 16 Jul 2015 08:12:54 +0000 (+0900) Subject: Initial import X-Git-Tag: accepted/tizen/mobile/20150717.060730~7 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=HEAD;p=platform%2Fcore%2Fsystem%2Fbuxton2.git Initial import Signed-off-by: Suchang Woo --- 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 +#include +#include +#include +#include +#include + +#include +#include + +#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 +#include +#include +#include +#include + +#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 +#include + +#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 +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include +#include + +#include + +#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 +#include + +#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 + +#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 +#include +#include +#include +#include +#include + +#include + +#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 + +#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 +#include +#include +#include +#include +#include + +#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 +#include + +#include + +#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 +#include +#include +#include +#include + +#include + +#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 + +#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 +#include +#include +#include +#include +#include + +#include + +#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 + +# 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 +#include +#include +#include +#include +#include +#include + +#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 + +#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 +#include +#include +#include + +#include + +#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 + +#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 +#include +#include + +#include +#include +#include + +#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 + +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 +#include +#include +#include +#include +#include + +#include +#include + +#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 +#include + +#include + +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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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 + +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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#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 + (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, ¬i); + 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, ¬i); + 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, ¬i); + 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, ¬i); + 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, ¬i); + 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, ¬i); + 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 +#include +#include + +/* + * + */ +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 @@ + + + + + + + + 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 + +#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 +#include +#include +#include + +#include + +#include + +#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] + $COMM get [get-options] + $COMM unset + + 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 +