2 * Copyright 2017 IoT.bzh
4 * author: Loïc Collignon <loic.collignon@iot.bzh>
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
21 #include <sys/types.h>
25 #include <linux/limits.h>
26 #include <json-c/json.h>
29 #define AFB_BINDING_VERSION 2
30 #include <afb/afb-binding.h>
38 #define DBFILE "/ll-database-binding.db"
39 #define USERNAME "agl"
40 #define APPNAME "firefox"
42 // ----- Globals -----
44 static char database_file[MAX_PATH];
46 // ----- Binding's declarations -----
47 static int ll_database_binding_init();
48 static void verb_insert(struct afb_req req);
49 static void verb_update(struct afb_req req);
50 static void verb_delete(struct afb_req req);
51 static void verb_read(struct afb_req req);
53 // ----- Binding's implementations -----
56 * @brief Initialize the binding.
57 * @return Exit code, zero if success.
59 static int ll_database_binding_init()
62 struct passwd* result;
67 bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
68 if (bufsize == -1 || bufsize > MAX_PATH) bufsize = MAX_PATH;
70 ret = getpwuid_r(getuid(), &pwd, buf, bufsize, &result);
73 if (ret == 0) AFB_ERROR("User not found");
74 else AFB_ERROR("getpwuid_r failed with %d code", ret);
78 memset(database_file, 0, MAX_PATH);
79 strcat(database_file, result->pw_dir);
80 strcat(database_file, DBFILE);
82 AFB_INFO("The database file is '%s'", database_file);
84 if ((ret = db_create(&database, NULL, 0)) != 0)
86 AFB_ERROR("Failed to create database: %s.", db_strerror(ret));
90 if ((ret = database->open(database, NULL, database_file, NULL, DB_BTREE, DB_CREATE, 0664)) != 0)
92 AFB_ERROR("Failed to open the '%s' database: %s.", database_file, db_strerror(ret));
93 database->close(database, 0);
101 * Returns the database key for the 'req'
103 static int get_key(struct afb_req req, DBT *key)
108 size_t ljkey, lappid, size;
110 struct json_object* args;
111 struct json_object* item;
114 args = afb_req_json(req);
115 if (!json_object_object_get_ex(args, "key", &item))
117 afb_req_fail(req, "no-key", NULL);
121 || !(jkey = json_object_get_string(item))
122 || !(ljkey = strlen(jkey)))
124 afb_req_fail(req, "bad-key", NULL);
129 appid = afb_req_get_application_id(req);
132 afb_req_fail(req, "bad-context", NULL);
136 /* make the db-key */
137 lappid = strlen(appid);
138 size = lappid + ljkey + 2;
139 data = realloc(appid, size);
143 afb_req_fail(req, "out-of-memory", NULL);
147 memcpy(&data[lappid + 1], jkey, ljkey + 1);
151 key->size = (uint32_t)size;
156 * @brief Handle the @c read verb.
157 * @param[in] req The query.
159 static void verb_insert(struct afb_req req)
167 struct json_object* args;
168 struct json_object* item;
170 args = afb_req_json(req);
172 if (!json_object_object_get_ex(args, "value", &item) || !item) value = NULL;
173 else value = json_object_get_string(item);
175 if (!value || !strlen(value))
177 afb_req_fail(req, "No value provided.", NULL);
181 if (get_key(req, &key))
184 AFB_INFO("insert: key=%s, value=%s", (char*)key.data, value);
186 data.data = (void*)value;
187 data.size = (uint32_t)strlen(value);
189 if ((ret = database->put(database, NULL, &key, &data, DB_NOOVERWRITE)) == 0)
190 afb_req_success_f(req, NULL, "db success: insertion %s=%s.", (char*)key.data, (char*)data.data);
192 afb_req_fail_f(req, "Failed to insert datas.", "db fail: insertion : %s=%s - %s", (char*)key.data, (char*)data.data, db_strerror(ret));
196 static void verb_update(struct afb_req req)
204 struct json_object* args;
205 struct json_object* item;
207 args = afb_req_json(req);
209 if (!json_object_object_get_ex(args, "value", &item) || !item) value = NULL;
210 else value = json_object_get_string(item);
212 if (!value || !strlen(value))
214 afb_req_fail(req, "No value provided.", NULL);
218 if (get_key(req, &key))
221 AFB_INFO("update: key=%s, value=%s", (char*)key.data, value);
223 data.data = (void*)value;
224 data.size = (uint32_t)strlen(value);
226 if ((ret = database->put(database, NULL, &key, &data, 0)) == 0)
227 afb_req_success_f(req, NULL, "db success: update %s=%s.", (char*)key.data, (char*)data.data);
229 afb_req_fail_f(req, "Failed to update datas.", "db fail: update %s=%s - %s", (char*)key.data, (char*)data.data, db_strerror(ret));
234 static void verb_delete(struct afb_req req)
239 if (get_key(req, &key))
242 AFB_INFO("delete: key=%s", (char*)key.data);
244 if ((ret = database->del(database, NULL, &key, 0)) == 0)
245 afb_req_success_f(req, NULL, "db success: delete %s.", (char *)key.data);
247 afb_req_fail_f(req, "Failed to delete datas.", "db fail: delete %s - %s", (char*)key.data, db_strerror(ret));
252 static void verb_read(struct afb_req req)
260 struct json_object* result;
261 struct json_object* val;
264 if (get_key(req, &key))
267 AFB_INFO("read: key=%s", (char*)key.data);
271 data.flags = DB_DBT_USERMEM;
273 if ((ret = database->get(database, NULL, &key, &data, 0)) == 0)
275 result = json_object_new_object();
276 val = json_tokener_parse((char*)data.data);
277 json_object_object_add(result, "value", val ? val : json_object_new_string((char*)data.data));
279 afb_req_success_f(req, result, "db success: read %s=%s.", (char*)key.data, (char*)data.data);
282 afb_req_fail_f(req, "Failed to read datas.", "db fail: read %s - %s", (char*)key.data, db_strerror(ret));
287 // ----- Binding's configuration -----
289 static const struct afb_auth ll_database_binding_auths[] = {
293 static const afb_verb_v2 ll_database_binding_verbs[]= {
294 REGISTER_VERB(insert, NULL, NULL, AFB_SESSION_NONE_V2),
295 REGISTER_VERB(update, NULL, NULL, AFB_SESSION_NONE_V2),
296 REGISTER_VERB(delete, NULL, NULL, AFB_SESSION_NONE_V2),
297 REGISTER_VERB(read, NULL, NULL, AFB_SESSION_NONE_V2),
301 const struct afb_binding_v2 afbBindingV2 = {
302 .api = "ll-database",
303 .specification = NULL,
304 .verbs = ll_database_binding_verbs,
306 .init = ll_database_binding_init,