2 * Copyright 2017 IoT.bzh
4 * author: Loïc Collignon <loic.collignon@iot.bzh>
5 * author: Jose Bollo <jose.bollo@iot.bzh>
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
22 #include <sys/types.h>
26 #include <linux/limits.h>
28 #include <json-c/json.h>
31 #define AFB_BINDING_VERSION 2
32 #include <afb/afb-binding.h>
38 #define DBFILE "ll-database-binding.db"
40 #if !defined(TO_STRING_FLAGS)
41 # if !defined(JSON_C_TO_STRING_NOSLASHESCAPE)
42 # define JSON_C_TO_STRING_NOSLASHESCAPE (1<<4)
44 # define TO_STRING_FLAGS (JSON_C_TO_STRING_PLAIN | JSON_C_TO_STRING_NOSLASHESCAPE)
47 // ----- Globals -----
50 // ----- Binding's declarations -----
51 static int ll_database_binding_init();
52 static void verb_insert(struct afb_req req);
53 static void verb_update(struct afb_req req);
54 static void verb_delete(struct afb_req req);
55 static void verb_read(struct afb_req req);
57 // ----- Binding's implementations -----
60 * @brief Get the path to the database
62 static int get_database_path(char *buffer, size_t size)
64 static const char dbfile[] = DBFILE;
69 config = secure_getenv("XDG_CONFIG_HOME");
71 rc = snprintf(buffer, size, "%s/%s", config, dbfile);
74 home = secure_getenv("HOME");
76 rc = snprintf(buffer, size, "%s/.config/%s", home, dbfile);
79 struct passwd *pwd = getpwuid(getuid());
81 rc = snprintf(buffer, size, "%s/.config/%s", pwd->pw_dir, dbfile);
83 rc = snprintf(buffer, size, "/home/%d/.config/%s", (int)getuid(), dbfile);
90 * @brief Initialize the binding.
91 * @return Exit code, zero if success.
93 static int ll_database_binding_init()
98 ret = get_database_path(path, sizeof path);
99 if (ret < 0 || (int)ret >= (int)(sizeof path))
101 AFB_ERROR("Can't compute the database filename");
105 AFB_INFO("opening database %s", path);
107 ret = db_create(&database, NULL, 0);
110 AFB_ERROR("Failed to create database: %s.", db_strerror(ret));
114 ret = database->open(database, NULL, path, NULL, DB_BTREE, DB_CREATE, 0664);
117 AFB_ERROR("Failed to open the '%s' database: %s.", path, db_strerror(ret));
118 database->close(database, 0);
126 * Returns the database key for the 'req'
128 static int get_key(struct afb_req req, DBT *key)
133 size_t ljkey, lappid, size;
135 struct json_object* args;
136 struct json_object* item;
139 args = afb_req_json(req);
140 if (!json_object_object_get_ex(args, "key", &item))
142 afb_req_fail(req, "no-key", NULL);
146 || !(jkey = json_object_get_string(item))
147 || !(ljkey = strlen(jkey)))
149 afb_req_fail(req, "bad-key", NULL);
154 appid = afb_req_get_application_id(req);
157 appid = strdup("#UNKNOWN-APP#");
161 afb_req_fail(req, "bad-context", NULL);
165 /* make the db-key */
166 lappid = strlen(appid);
167 size = lappid + ljkey + 2;
168 data = realloc(appid, size);
172 afb_req_fail(req, "out-of-memory", NULL);
176 memcpy(&data[lappid + 1], jkey, ljkey + 1);
179 memset(key, 0, sizeof *key);
181 key->size = (uint32_t)size;
185 static void put(struct afb_req req, int replace)
193 struct json_object* args;
194 struct json_object* item;
196 args = afb_req_json(req);
198 if (!json_object_object_get_ex(args, "value", &item))
200 afb_req_fail(req, "no-value", NULL);
204 value = json_object_to_json_string_ext(item, TO_STRING_FLAGS);
207 afb_req_fail(req, "out-of-memory", NULL);
211 if (get_key(req, &key))
214 AFB_INFO("put: key=%s, value=%s", (char*)key.data, value);
216 memset(&data, 0, sizeof data);
217 data.data = (void*)value;
218 data.size = (uint32_t)strlen(value) + 1; /* includes the tailing null */
220 ret = database->put(database, NULL, &key, &data, replace ? 0 : DB_NOOVERWRITE);
222 afb_req_success(req, NULL, NULL);
225 AFB_ERROR("can't %s key %s with %s", replace ? "replace" : "insert", (char*)key.data, (char*)data.data);
226 afb_req_fail_f(req, "failed", "%s", db_strerror(ret));
231 static void verb_insert(struct afb_req req)
236 static void verb_update(struct afb_req req)
241 static void verb_delete(struct afb_req req)
246 if (get_key(req, &key))
249 AFB_INFO("delete: key=%s", (char*)key.data);
251 if ((ret = database->del(database, NULL, &key, 0)) == 0)
252 afb_req_success_f(req, NULL, "db success: delete %s.", (char *)key.data);
254 afb_req_fail_f(req, "Failed to delete datas.", "db fail: delete %s - %s", (char*)key.data, db_strerror(ret));
259 static void verb_read(struct afb_req req)
267 struct json_object* result;
268 struct json_object* val;
271 if (get_key(req, &key))
274 AFB_INFO("read: key=%s", (char*)key.data);
276 memset(&data, 0, sizeof data);
279 data.flags = DB_DBT_USERMEM;
281 ret = database->get(database, NULL, &key, &data, 0);
284 result = json_object_new_object();
285 val = json_tokener_parse((char*)data.data);
286 json_object_object_add(result, "value", val ? val : json_object_new_string((char*)data.data));
288 afb_req_success_f(req, result, "db success: read %s=%s.", (char*)key.data, (char*)data.data);
291 afb_req_fail_f(req, "Failed to read datas.", "db fail: read %s - %s", (char*)key.data, db_strerror(ret));
296 // ----- Binding's configuration -----
298 static const struct afb_auth ll_database_binding_auths[] = {
302 #define VERB(name_,auth_,info_,sess_) {\
304 .callback = verb_##name_, \
309 static const afb_verb_v2 ll_database_binding_verbs[]= {
310 VERB(insert, NULL, NULL, AFB_SESSION_NONE_V2),
311 VERB(update, NULL, NULL, AFB_SESSION_NONE_V2),
312 VERB(delete, NULL, NULL, AFB_SESSION_NONE_V2),
313 VERB(read, NULL, NULL, AFB_SESSION_NONE_V2),
317 const struct afb_binding_v2 afbBindingV2 = {
318 .api = "ll-database",
319 .specification = NULL,
320 .verbs = ll_database_binding_verbs,
322 .init = ll_database_binding_init,