From 97325dd67f3b7858bd093fc161d0a56e7c7bc9bd Mon Sep 17 00:00:00 2001 From: Loïc Collignon Date: Tue, 24 Oct 2017 13:34:38 +0200 Subject: replaced store binding with a database binding based on a berkeley db MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I03978ecbf996ebc6d53a88dfd2b275051080016f Signed-off-by: Loïc Collignon --- ll-database-binding/src/.ll-database-binding.c.swp | Bin 0 -> 16384 bytes ll-database-binding/src/CMakeLists.txt | 13 + ll-database-binding/src/export.map | 1 + ll-database-binding/src/ll-database-binding.c | 389 +++++++++++++++++++++ ll-database-binding/src/utils.h | 72 ++++ 5 files changed, 475 insertions(+) create mode 100644 ll-database-binding/src/.ll-database-binding.c.swp create mode 100644 ll-database-binding/src/CMakeLists.txt create mode 100644 ll-database-binding/src/export.map create mode 100644 ll-database-binding/src/ll-database-binding.c create mode 100644 ll-database-binding/src/utils.h (limited to 'll-database-binding/src') diff --git a/ll-database-binding/src/.ll-database-binding.c.swp b/ll-database-binding/src/.ll-database-binding.c.swp new file mode 100644 index 0000000..54fb691 Binary files /dev/null and b/ll-database-binding/src/.ll-database-binding.c.swp differ diff --git a/ll-database-binding/src/CMakeLists.txt b/ll-database-binding/src/CMakeLists.txt new file mode 100644 index 0000000..904b857 --- /dev/null +++ b/ll-database-binding/src/CMakeLists.txt @@ -0,0 +1,13 @@ +PROJECT_TARGET_ADD(ll-database-binding) + +find_package(BerkeleyDB REQUIRED) +include_directories(${DB_INCLUDE_DIR}) + +add_library(ll-database-binding MODULE ll-database-binding.c utils.h) +target_link_libraries(ll-database-binding ${DB_LIBRARY}) + +set_target_properties(ll-database-binding PROPERTIES + PREFIX "afb-" + LABELS "BINDING" + LINK_FLAGS ${BINDINGS_LINK_FLAG} + OUTPUT_NAME ${TARGET_NAME}) diff --git a/ll-database-binding/src/export.map b/ll-database-binding/src/export.map new file mode 100644 index 0000000..ee2f413 --- /dev/null +++ b/ll-database-binding/src/export.map @@ -0,0 +1 @@ +{ global: afbBindingV*; local: *; }; diff --git a/ll-database-binding/src/ll-database-binding.c b/ll-database-binding/src/ll-database-binding.c new file mode 100644 index 0000000..adaf023 --- /dev/null +++ b/ll-database-binding/src/ll-database-binding.c @@ -0,0 +1,389 @@ +/* + * Copyright 2017 IoT.bzh + * + * author: Loïc Collignon + * + * 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 + +#define AFB_BINDING_VERSION 2 +#include + +#include "utils.h" + +#define DBFILE "/ll-database-binding.db" +#define USERNAME "agl" +#define APPNAME "firefox" + +// ----- Globals ----- +DB* database; +char* database_file; + +// ----- Binding's declarations ----- +int ll_database_binding_init(); +void verb_read(struct afb_req req); +void verb_update(struct afb_req req); +void verb_delete(struct afb_req req); + +void verb_insert(struct afb_req req); +void verb_update(struct afb_req req); +void verb_delete(struct afb_req req); +void verb_read(struct afb_req req); + +// ----- Binding's implementations ----- + +/** + * @brief Initialize the binding. + * @return Exit code, zero if success. + */ +int ll_database_binding_init() +{ + struct passwd pwd; + struct passwd* result; + char* buf; + size_t bufsize; + int ret; + + bufsize = sysconf(_SC_GETPW_R_SIZE_MAX); + if (bufsize == -1) bufsize = 16384; + buf = malloc(bufsize); + if (buf == NULL) + { + AFB_ERROR("Allocation failed!"); + return 1; + } + + ret = getpwuid_r(getuid(), &pwd, buf, bufsize, &result); + if (result == NULL) + { + free(buf); + if (ret == 0) AFB_ERROR("User not found"); + else AFB_ERROR("getpwuid_r failed with %d code", ret); + return 1; + } + + bufsize = strlen(result->pw_dir) + strlen(DBFILE) + 1; + database_file = malloc(bufsize); + if (database_file == NULL) + { + free(buf); + AFB_ERROR("Allocation failed!"); + return 1; + } + + memset(database_file, 0, bufsize); + strcat(database_file, result->pw_dir); + strcat(database_file, DBFILE); + free(buf); + + AFB_INFO("The database file is '%s'", database_file); + + if ((ret = db_create(&database, NULL, 0)) != 0) + { + AFB_ERROR("Failed to create database: %s.", db_strerror(ret)); + free(database_file); + return 1; + } + + if ((ret = database->open(database, NULL, database_file, NULL, DB_BTREE, DB_CREATE, 0664)) != 0) + { + AFB_ERROR("Failed to open the '%s' database: %s.", database_file, db_strerror(ret)); + database->close(database, 0); + free(database_file); + return 1; + } + + return 0; +} + +/** + * @brief Handle the @c read verb. + * @param[in] req The query. + */ +void verb_insert(struct afb_req req) +{ + DBT key; + DBT data; + int ret; + + char* rkey; + const char* tag; + const char* value; + + struct json_object* args; + struct json_object* item; + + args = afb_req_json(req); + + if (!args) + { + afb_req_fail(req, "No argument provided.", NULL); + return; + } + + if (!json_object_object_get_ex(args, "key", &item) || !item) tag = NULL; + else tag = json_object_get_string(item); + + if (!tag || !strlen(tag)) + { + afb_req_fail(req, "No tag provided.", NULL); + return; + } + + if (!json_object_object_get_ex(args, "value", &item) || !item) value = NULL; + else value = json_object_get_string(item); + + if (!value || !strlen(value)) + { + afb_req_fail(req, "No value provided.", NULL); + return; + } + + rkey = malloc(strlen(USERNAME) + strlen(APPNAME) + strlen(tag) + 3); + strcpy(rkey, USERNAME); + strcat(rkey, ":"); + strcat(rkey, APPNAME); + strcat(rkey, ":"); + strcat(rkey, tag); + + AFB_INFO("insert: key=%s, value=%s", rkey, value); + + memset(&key, 0, sizeof(key)); + memset(&data, 0, sizeof(data)); + + key.data = rkey; + key.size = strlen(rkey); + + data.data = (void*)value; + data.size = strlen(value); + + if ((ret = database->put(database, NULL, &key, &data, DB_NOOVERWRITE)) == 0) + afb_req_success_f(req, NULL, "db success: insertion %s=%s.", (char*)key.data, (char*)data.data); + else + afb_req_fail_f(req, "Failed to insert datas.", "db fail: insertion : %s=%s - %s", (char*)key.data, (char*)data.data, db_strerror(ret)); + free(rkey); +} + +void verb_update(struct afb_req req) +{ + DBT key; + DBT data; + int ret; + + char* rkey; + const char* tag; + const char* value; + + struct json_object* args; + struct json_object* item; + + args = afb_req_json(req); + // username should be get from identity binding + // application should be get from smack + // tag should be get using get_json_string(args, "tag"); + + if (!args) + { + afb_req_fail(req, "No argument provided.", NULL); + return; + } + + if (!json_object_object_get_ex(args, "tag", &item) || !item) tag = NULL; + else tag = json_object_get_string(item); + + if (!tag || !strlen(tag)) + { + afb_req_fail(req, "No tag provided.", NULL); + return; + } + + if (!json_object_object_get_ex(args, "value", &item) || !item) value = NULL; + else value = json_object_get_string(item); + + if (!value || !strlen(value)) + { + afb_req_fail(req, "No value provided.", NULL); + return; + } + + rkey = malloc(strlen(USERNAME) + strlen(APPNAME) + strlen(tag) + 3); + strcpy(rkey, USERNAME); + strcat(rkey, ":"); + strcat(rkey, APPNAME); + strcat(rkey, ":"); + strcat(rkey, tag); + + AFB_INFO("update: key=%s, value=%s", rkey, value); + + memset(&key, 0, sizeof(key)); + memset(&data, 0, sizeof(data)); + + key.data = rkey; + key.size = strlen(rkey); + + data.data = (void*)value; + data.size = strlen(value); + + if ((ret = database->put(database, NULL, &key, &data, 0)) == 0) + afb_req_success_f(req, NULL, "db success: update %s=%s.", (char*)key.data, (char*)data.data); + else + afb_req_fail_f(req, "Failed to update datas.", "db fail: update %s=%s - %s", (char*)key.data, (char*)data.data, db_strerror(ret)); + free(rkey); +} + +void verb_delete(struct afb_req req) +{ + DBT key; + int ret; + + char* rkey; + const char* tag; + + struct json_object* args; + struct json_object* item; + + args = afb_req_json(req); + + if (!args) + { + afb_req_fail(req, "No argument provided.", NULL); + return; + } + + if (!json_object_object_get_ex(args, "tag", &item) || !item) tag = NULL; + else tag = json_object_get_string(item); + + if (!tag || !strlen(tag)) + { + afb_req_fail(req, "No tag provided.", NULL); + return; + } + + rkey = malloc(strlen(USERNAME) + strlen(APPNAME) + strlen(tag) + 3); + strcpy(rkey, USERNAME); + strcat(rkey, ":"); + strcat(rkey, APPNAME); + strcat(rkey, ":"); + strcat(rkey, tag); + + AFB_INFO("delete: key=%s", rkey); + + memset(&key, 0, sizeof(key)); + + key.data = rkey; + key.size = strlen(rkey); + + if ((ret = database->del(database, NULL, &key, 0)) == 0) + afb_req_success_f(req, NULL, "db success: delete %s.", (char *)key.data); + else + afb_req_fail_f(req, "Failed to delete datas.", "db fail: delete %s - %s", (char*)key.data, db_strerror(ret)); + free(rkey); +} + +void verb_read(struct afb_req req) +{ + DB* dbp; + DBT key; + DBT data; + int ret; + + char* rkey; + const char* tag; + char value[4096]; + + struct json_object* args; + struct json_object* item; + struct json_object* result; + struct json_object* val; + + args = afb_req_json(req); + + if (!args) + { + afb_req_fail(req, "No argument provided.", NULL); + return; + } + + if (!json_object_object_get_ex(args, "tag", &item) || !item) tag = NULL; + else tag = json_object_get_string(item); + + if (!tag || !strlen(tag)) + { + afb_req_fail(req, "No tag provided.", NULL); + return; + } + + rkey = malloc(strlen(USERNAME) + strlen(APPNAME) + strlen(tag) + 3); + strcpy(rkey, USERNAME); + strcat(rkey, ":"); + strcat(rkey, APPNAME); + strcat(rkey, ":"); + strcat(rkey, tag); + + AFB_INFO("update: key=%s, value=%s", rkey, value); + + memset(&key, 0, sizeof(key)); + memset(&data, 0, sizeof(data)); + memset(&value, 0, 4096); + + key.data = rkey; + key.size = strlen(rkey); + + data.data = value; + data.ulen = 4096; + data.flags = DB_DBT_USERMEM; + + if ((ret = database->get(database, NULL, &key, &data, 0)) == 0) + { + result = json_object_new_object(); + val = json_tokener_parse((char*)data.data); + json_object_object_add(result, "value", val ? val : json_object_new_string((char*)data.data)); + + afb_req_success_f(req, result, "db success: read %s=%s.", (char*)key.data, (char*)data.data); + } + else + afb_req_fail_f(req, "Failed to read datas.", "db fail: read %s - %s", (char*)key.data, db_strerror(ret)); + free(rkey); +} + +// ----- Binding's configuration ----- +static const struct afb_auth ll_database_binding_auths[] = { +}; + +static const afb_verb_v2 ll_database_binding_verbs[]= { + REGISTER_VERB(insert, NULL, NULL, AFB_SESSION_NONE_V2), + REGISTER_VERB(update, NULL, NULL, AFB_SESSION_NONE_V2), + REGISTER_VERB(delete, NULL, NULL, AFB_SESSION_NONE_V2), + REGISTER_VERB(read, NULL, NULL, AFB_SESSION_NONE_V2), + { .verb = NULL} +}; + +const struct afb_binding_v2 afbBindingV2 = { + .api = "ll-database", + .specification = NULL, + .verbs = ll_database_binding_verbs, + .preinit = NULL, + .init = ll_database_binding_init, + .onevent = NULL, + .noconcurrency = 0 +}; diff --git a/ll-database-binding/src/utils.h b/ll-database-binding/src/utils.h new file mode 100644 index 0000000..093d591 --- /dev/null +++ b/ll-database-binding/src/utils.h @@ -0,0 +1,72 @@ +/* +* Copyright 2017 IoT.bzh +* +* author: Loïc Collignon +* +* 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 _BINDING_UTILS_H_ +#define _BINDING_UTILS_H_ + +#include +#include + +#ifndef AFB_BINDING_VERSION +#define AFB_BINDING_VERSION 2 +#endif +#include + +#define REGISTER_VERB(n, a, i, s) { .verb = #n, .callback = verb_##n, .auth = a, .info = i, .session = s } + +/** + * @brief Get a string from a json object. + * @param[in] obj Json object from wich the string is queried. + * @param[in] name Name of the string to get. + * @return The string value. + */ +static inline const char* get_json_string(struct json_object* obj, const char* name) +{ + struct json_object* item = NULL; + if (!obj || !name || !strlen(name)) return NULL; + if (!json_object_object_get_ex(obj, name, &item) || !item) return NULL; + return json_object_get_string(item); +} + +/** + * @brief Add a string key/value to a json object. + * @param[in] obj The json object to which the key/value is added. + * @param[in] key The key to add. + * @param[in] value The value to add. + */ +static inline void json_object_add_string(struct json_object* obj, const char* key, const char* value) +{ + json_object_object_add(obj, key, json_object_new_string(value)); +} + +/** + * @brief Send an @c event with the specified @c message then fail with the same @c message. + * @param[in] req The query to fail. + * @param[in] event The event to push. + * @param[in] message The message to push with the event and use as a fail message. + */ +static inline void afb_fail_ex(struct afb_req req, struct afb_event event, const char* message) +{ + struct json_object* result = json_object_new_object(); + json_object_add_string(result, "message", message); + afb_event_push(event, result); + + afb_req_fail(req, message, NULL); +} + +#endif // _BINDING_UTILS_H_ -- cgit 1.2.3-korg