2206bb003163af9a0b29c3abe324bd7ced1e7c54
[apps/agl-service-data-persistence.git] / ll-database-binding / src / ll-database-binding.c
1 /*
2  * Copyright 2017 IoT.bzh
3  *
4  * author: Loïc Collignon <loic.collignon@iot.bzh>
5  *
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
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
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.
17  */
18
19 #define _GNU_SOURCE
20 #include <unistd.h>
21 #include <sys/types.h>
22 #include <pwd.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <linux/limits.h>
26 #include <json-c/json.h>
27 #include <db.h>
28
29 #define AFB_BINDING_VERSION 2
30 #include <afb/afb-binding.h>
31
32 #include "utils.h"
33
34 #ifndef MAX_PATH
35 #define MAX_PATH 1024
36 #endif
37
38 #define DBFILE          "/ll-database-binding.db"
39
40 #if !defined(TO_STRING_FLAGS)
41 # if !defined(JSON_C_TO_STRING_NOSLASHESCAPE)
42 #  define JSON_C_TO_STRING_NOSLASHESCAPE (1<<4)
43 # endif
44 # define TO_STRING_FLAGS (JSON_C_TO_STRING_PLAIN | JSON_C_TO_STRING_NOSLASHESCAPE)
45 #endif
46
47 // ----- Globals -----
48 static DB*              database;
49 static char     database_file[MAX_PATH];
50
51 // ----- Binding's declarations -----
52 static int ll_database_binding_init();
53 static void verb_insert(struct afb_req req);
54 static void verb_update(struct afb_req req);
55 static void verb_delete(struct afb_req req);
56 static void verb_read(struct afb_req req);
57
58 // ----- Binding's implementations -----
59
60 /**
61  * @brief Initialize the binding.
62  * @return Exit code, zero if success.
63  */
64 static int ll_database_binding_init()
65 {
66         struct passwd pwd;
67         struct passwd* result;
68         char buf[MAX_PATH];
69         size_t bufsize;
70         int ret;
71
72         bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
73         if (bufsize == -1 || bufsize > MAX_PATH) bufsize = MAX_PATH;
74
75         ret = getpwuid_r(getuid(), &pwd, buf, bufsize, &result);
76         if (result == NULL)
77         {
78                 if (ret == 0) AFB_ERROR("User not found");
79                 else AFB_ERROR("getpwuid_r failed with %d code", ret);
80                 return -1;
81         }
82
83         memset(database_file, 0, MAX_PATH);
84         strcat(database_file, result->pw_dir);
85         strcat(database_file, DBFILE);
86
87         AFB_INFO("The database file is '%s'", database_file);
88
89         if ((ret = db_create(&database, NULL, 0)) != 0)
90         {
91                 AFB_ERROR("Failed to create database: %s.", db_strerror(ret));
92                 return -1;
93         }
94
95         if ((ret = database->open(database, NULL, database_file, NULL, DB_BTREE, DB_CREATE, 0664)) != 0)
96         {
97                 AFB_ERROR("Failed to open the '%s' database: %s.", database_file, db_strerror(ret));
98                 database->close(database, 0);
99                 return -1;
100         }
101
102         return 0;
103 }
104
105 /**
106  * Returns the database key for the 'req'
107  */
108 static int get_key(struct afb_req req, DBT *key)
109 {
110         char *appid, *data;
111         const char *jkey;
112
113         size_t ljkey, lappid, size;
114
115         struct json_object* args;
116         struct json_object* item;
117
118         /* get the key */
119         args = afb_req_json(req);
120         if (!json_object_object_get_ex(args, "key", &item))
121         {
122                 afb_req_fail(req, "no-key", NULL);
123                 return -1;
124         }
125         if (!item
126          || !(jkey = json_object_get_string(item))
127          || !(ljkey = strlen(jkey)))
128         {
129                 afb_req_fail(req, "bad-key", NULL);
130                 return -1;
131         }
132
133         /* get the appid */
134         appid = afb_req_get_application_id(req);
135         if (!appid)
136         {
137                 afb_req_fail(req, "bad-context", NULL);
138                 return -1;
139         }
140
141         /* make the db-key */
142         lappid = strlen(appid);
143         size = lappid + ljkey + 2;
144         data = realloc(appid, size);
145         if (!data)
146         {
147                 free(appid);
148                 afb_req_fail(req, "out-of-memory", NULL);
149                 return -1;
150         }
151         data[lappid] = ':';
152         memcpy(&data[lappid + 1], jkey, ljkey + 1);
153
154         /* return the key */
155         key->data = data;
156         key->size = (uint32_t)size;
157         return 0;
158 }
159
160 static void put(struct afb_req req, int replace)
161 {
162         int ret;
163         DBT key;
164         DBT data;
165
166         const char* value;
167
168         struct json_object* args;
169         struct json_object* item;
170
171         args = afb_req_json(req);
172
173         if (!json_object_object_get_ex(args, "value", &item))
174         {
175                 afb_req_fail(req, "no-value", NULL);
176                 return;
177         }
178
179         value = json_object_to_json_string_ext(item, TO_STRING_FLAGS);
180         if (!value)
181         {
182                 afb_req_fail(req, "out-of-memory", NULL);
183                 return;
184         }
185
186         if (get_key(req, &key))
187                 return;
188
189         AFB_INFO("put: key=%s, value=%s", (char*)key.data, value);
190
191         data.data = (void*)value;
192         data.size = (uint32_t)strlen(value);
193
194         ret = database->put(database, NULL, &key, &data, replace ? 0 : DB_NOOVERWRITE);
195         if (ret == 0)
196                 afb_req_success(req, NULL, NULL);
197         else
198         {
199                 AFB_ERROR("can't %s key %s with %s", replace ? "replace" : "insert", (char*)key.data, (char*)data.data);
200                 afb_req_fail_f(req, "failed", "%s", db_strerror(ret));
201         }
202         free(key.data);
203 }
204
205 static void verb_insert(struct afb_req req)
206 {
207         put(req, 0);
208 }
209
210 static void verb_update(struct afb_req req)
211 {
212         put(req, 1);
213 }
214
215 static void verb_delete(struct afb_req req)
216 {
217         DBT key;
218         int ret;
219
220         if (get_key(req, &key))
221                 return;
222
223         AFB_INFO("delete: key=%s", (char*)key.data);
224
225         if ((ret = database->del(database, NULL, &key, 0)) == 0)
226                 afb_req_success_f(req, NULL, "db success: delete %s.", (char *)key.data);
227         else
228                 afb_req_fail_f(req, "Failed to delete datas.", "db fail: delete %s - %s", (char*)key.data, db_strerror(ret));
229
230         free(key.data);
231 }
232
233 static void verb_read(struct afb_req req)
234 {
235         DBT key;
236         DBT data;
237         int ret;
238
239         char value[4096];
240
241         struct json_object* result;
242         struct json_object* val;
243
244
245         if (get_key(req, &key))
246                 return;
247
248         AFB_INFO("read: key=%s", (char*)key.data);
249
250         data.data = value;
251         data.ulen = 4096;
252         data.flags = DB_DBT_USERMEM;
253
254         if ((ret = database->get(database, NULL, &key, &data, 0)) == 0)
255         {
256                 result = json_object_new_object();
257                 val = json_tokener_parse((char*)data.data);
258                 json_object_object_add(result, "value", val ? val : json_object_new_string((char*)data.data));
259
260                 afb_req_success_f(req, result, "db success: read %s=%s.", (char*)key.data, (char*)data.data);
261         }
262         else
263                 afb_req_fail_f(req, "Failed to read datas.", "db fail: read %s - %s", (char*)key.data, db_strerror(ret));
264
265         free(key.data);
266 }
267
268 // ----- Binding's configuration -----
269 /*
270 static const struct afb_auth ll_database_binding_auths[] = {
271 };
272 */
273
274 static const afb_verb_v2 ll_database_binding_verbs[]= {
275                 REGISTER_VERB(insert,   NULL, NULL, AFB_SESSION_NONE_V2),
276                 REGISTER_VERB(update,   NULL, NULL, AFB_SESSION_NONE_V2),
277                 REGISTER_VERB(delete,   NULL, NULL, AFB_SESSION_NONE_V2),
278                 REGISTER_VERB(read,             NULL, NULL, AFB_SESSION_NONE_V2),
279         { .verb = NULL}
280 };
281
282 const struct afb_binding_v2 afbBindingV2 = {
283                 .api = "ll-database",
284                 .specification = NULL,
285                 .verbs = ll_database_binding_verbs,
286                 .preinit = NULL,
287                 .init = ll_database_binding_init,
288                 .onevent = NULL,
289                 .noconcurrency = 0
290 };