adaf02346a4910fb8e49aa5ee3eb7a3d8f85af45
[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 #define DBFILE          "/ll-database-binding.db"
35 #define USERNAME        "agl"
36 #define APPNAME         "firefox"
37
38 // ----- Globals -----
39 DB*             database;
40 char*   database_file;
41
42 // ----- Binding's declarations -----
43 int ll_database_binding_init();
44 void verb_read(struct afb_req req);
45 void verb_update(struct afb_req req);
46 void verb_delete(struct afb_req req);
47
48 void verb_insert(struct afb_req req);
49 void verb_update(struct afb_req req);
50 void verb_delete(struct afb_req req);
51 void verb_read(struct afb_req req);
52
53 // ----- Binding's implementations -----
54
55 /**
56  * @brief Initialize the binding.
57  * @return Exit code, zero if success.
58  */
59 int ll_database_binding_init()
60 {
61         struct passwd pwd;
62         struct passwd* result;
63         char* buf;
64         size_t bufsize;
65         int ret;
66         
67         bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
68         if (bufsize == -1) bufsize = 16384;
69         buf = malloc(bufsize);
70         if (buf == NULL)
71         {
72                 AFB_ERROR("Allocation failed!");
73                 return 1;
74         }
75         
76         ret = getpwuid_r(getuid(), &pwd, buf, bufsize, &result);
77         if (result == NULL)
78         {
79                 free(buf);
80                 if (ret == 0) AFB_ERROR("User not found");
81                 else AFB_ERROR("getpwuid_r failed with %d code", ret);
82                 return 1;
83         }
84         
85         bufsize = strlen(result->pw_dir) + strlen(DBFILE) + 1;
86         database_file = malloc(bufsize);
87         if (database_file == NULL)
88         {
89                 free(buf);
90                 AFB_ERROR("Allocation failed!");
91                 return 1;
92         }
93         
94         memset(database_file, 0, bufsize);      
95         strcat(database_file, result->pw_dir);
96         strcat(database_file, DBFILE);
97         free(buf);
98         
99         AFB_INFO("The database file is '%s'", database_file);
100
101         if ((ret = db_create(&database, NULL, 0)) != 0)
102         {
103                 AFB_ERROR("Failed to create database: %s.", db_strerror(ret));
104                 free(database_file);
105                 return 1;
106         }
107         
108         if ((ret = database->open(database, NULL, database_file, NULL, DB_BTREE, DB_CREATE, 0664)) != 0)
109         {
110                 AFB_ERROR("Failed to open the '%s' database: %s.", database_file, db_strerror(ret));
111                 database->close(database, 0);
112                 free(database_file);
113                 return 1;
114         }
115         
116         return 0;
117 }
118
119 /**
120  * @brief Handle the @c read verb.
121  * @param[in] req The query.
122  */
123 void verb_insert(struct afb_req req)
124 {
125         DBT key;
126         DBT data;
127         int ret;
128         
129         char* rkey;
130         const char* tag;
131         const char* value;
132         
133         struct json_object* args;
134         struct json_object* item;
135         
136         args = afb_req_json(req);
137         
138         if (!args)
139         {
140                 afb_req_fail(req, "No argument provided.", NULL);
141                 return;
142         }
143         
144         if (!json_object_object_get_ex(args, "key", &item) || !item) tag = NULL;
145         else tag = json_object_get_string(item);
146         
147         if (!tag || !strlen(tag))
148         {
149                 afb_req_fail(req, "No tag provided.", NULL);
150                 return;
151         }
152         
153         if (!json_object_object_get_ex(args, "value", &item) || !item) value = NULL;
154         else value = json_object_get_string(item);
155         
156         if (!value || !strlen(value))
157         {
158                 afb_req_fail(req, "No value provided.", NULL);
159                 return;
160         }
161         
162         rkey = malloc(strlen(USERNAME) + strlen(APPNAME) + strlen(tag) + 3);
163         strcpy(rkey, USERNAME);
164         strcat(rkey, ":");
165         strcat(rkey, APPNAME);
166         strcat(rkey, ":");
167         strcat(rkey, tag);
168         
169         AFB_INFO("insert: key=%s, value=%s", rkey, value);
170
171         memset(&key, 0, sizeof(key));
172         memset(&data, 0, sizeof(data));
173         
174         key.data = rkey;
175         key.size = strlen(rkey);
176         
177         data.data = (void*)value;
178         data.size = strlen(value);
179
180         if ((ret = database->put(database, NULL, &key, &data, DB_NOOVERWRITE)) == 0)
181                 afb_req_success_f(req, NULL, "db success: insertion %s=%s.", (char*)key.data, (char*)data.data);
182         else
183                 afb_req_fail_f(req, "Failed to insert datas.", "db fail: insertion : %s=%s - %s", (char*)key.data, (char*)data.data, db_strerror(ret));
184         free(rkey);
185 }
186
187 void verb_update(struct afb_req req)
188 {
189         DBT key;
190         DBT data;
191         int ret;
192         
193         char* rkey;
194         const char* tag;
195         const char* value;
196         
197         struct json_object* args;
198         struct json_object* item;
199         
200         args = afb_req_json(req);
201         // username should be get from identity binding
202         // application should be get from smack
203         // tag should be get using get_json_string(args, "tag");
204         
205         if (!args)
206         {
207                 afb_req_fail(req, "No argument provided.", NULL);
208                 return;
209         }
210         
211         if (!json_object_object_get_ex(args, "tag", &item) || !item) tag = NULL;
212         else tag = json_object_get_string(item);
213         
214         if (!tag || !strlen(tag))
215         {
216                 afb_req_fail(req, "No tag provided.", NULL);
217                 return;
218         }
219         
220         if (!json_object_object_get_ex(args, "value", &item) || !item) value = NULL;
221         else value = json_object_get_string(item);
222         
223         if (!value || !strlen(value))
224         {
225                 afb_req_fail(req, "No value provided.", NULL);
226                 return;
227         }
228         
229         rkey = malloc(strlen(USERNAME) + strlen(APPNAME) + strlen(tag) + 3);
230         strcpy(rkey, USERNAME);
231         strcat(rkey, ":");
232         strcat(rkey, APPNAME);
233         strcat(rkey, ":");
234         strcat(rkey, tag);
235         
236         AFB_INFO("update: key=%s, value=%s", rkey, value);
237
238         memset(&key, 0, sizeof(key));
239         memset(&data, 0, sizeof(data));
240         
241         key.data = rkey;
242         key.size = strlen(rkey);
243         
244         data.data = (void*)value;
245         data.size = strlen(value);
246
247         if ((ret = database->put(database, NULL, &key, &data, 0)) == 0)
248                 afb_req_success_f(req, NULL, "db success: update %s=%s.", (char*)key.data, (char*)data.data);
249         else
250                 afb_req_fail_f(req, "Failed to update datas.", "db fail: update %s=%s - %s", (char*)key.data, (char*)data.data, db_strerror(ret));
251         free(rkey);
252 }
253
254 void verb_delete(struct afb_req req)
255 {
256         DBT key;
257         int ret;
258         
259         char* rkey;
260         const char* tag;
261         
262         struct json_object* args;
263         struct json_object* item;
264         
265         args = afb_req_json(req);
266         
267         if (!args)
268         {
269                 afb_req_fail(req, "No argument provided.", NULL);
270                 return;
271         }
272         
273         if (!json_object_object_get_ex(args, "tag", &item) || !item) tag = NULL;
274         else tag = json_object_get_string(item);
275         
276         if (!tag || !strlen(tag))
277         {
278                 afb_req_fail(req, "No tag provided.", NULL);
279                 return;
280         }
281         
282         rkey = malloc(strlen(USERNAME) + strlen(APPNAME) + strlen(tag) + 3);
283         strcpy(rkey, USERNAME);
284         strcat(rkey, ":");
285         strcat(rkey, APPNAME);
286         strcat(rkey, ":");
287         strcat(rkey, tag);
288         
289         AFB_INFO("delete: key=%s", rkey);
290
291         memset(&key, 0, sizeof(key));
292         
293         key.data = rkey;
294         key.size = strlen(rkey);
295
296         if ((ret = database->del(database, NULL, &key, 0)) == 0)
297                 afb_req_success_f(req, NULL, "db success: delete %s.", (char *)key.data);
298         else
299                 afb_req_fail_f(req, "Failed to delete datas.", "db fail: delete %s - %s", (char*)key.data, db_strerror(ret));
300         free(rkey);
301 }
302
303 void verb_read(struct afb_req req)
304 {
305         DB* dbp;
306         DBT key;
307         DBT data;
308         int ret;
309         
310         char* rkey;
311         const char* tag;
312         char value[4096];
313         
314         struct json_object* args;
315         struct json_object* item;
316         struct json_object* result;
317         struct json_object* val;
318         
319         args = afb_req_json(req);
320         
321         if (!args)
322         {
323                 afb_req_fail(req, "No argument provided.", NULL);
324                 return;
325         }
326         
327         if (!json_object_object_get_ex(args, "tag", &item) || !item) tag = NULL;
328         else tag = json_object_get_string(item);
329         
330         if (!tag || !strlen(tag))
331         {
332                 afb_req_fail(req, "No tag provided.", NULL);
333                 return;
334         }
335         
336         rkey = malloc(strlen(USERNAME) + strlen(APPNAME) + strlen(tag) + 3);
337         strcpy(rkey, USERNAME);
338         strcat(rkey, ":");
339         strcat(rkey, APPNAME);
340         strcat(rkey, ":");
341         strcat(rkey, tag);
342         
343         AFB_INFO("update: key=%s, value=%s", rkey, value);
344
345         memset(&key, 0, sizeof(key));
346         memset(&data, 0, sizeof(data));
347         memset(&value, 0, 4096);
348         
349         key.data = rkey;
350         key.size = strlen(rkey);
351         
352         data.data = value;
353         data.ulen = 4096;
354         data.flags = DB_DBT_USERMEM;
355
356         if ((ret = database->get(database, NULL, &key, &data, 0)) == 0)
357         {
358                 result = json_object_new_object();
359                 val = json_tokener_parse((char*)data.data);
360                 json_object_object_add(result, "value", val ? val : json_object_new_string((char*)data.data));
361                 
362                 afb_req_success_f(req, result, "db success: read %s=%s.", (char*)key.data, (char*)data.data);
363         }
364         else
365                 afb_req_fail_f(req, "Failed to read datas.", "db fail: read %s - %s", (char*)key.data, db_strerror(ret));
366         free(rkey);
367 }
368
369 // ----- Binding's configuration -----
370 static const struct afb_auth ll_database_binding_auths[] = {
371 };
372
373 static const afb_verb_v2 ll_database_binding_verbs[]= {
374                 REGISTER_VERB(insert,   NULL, NULL, AFB_SESSION_NONE_V2),
375                 REGISTER_VERB(update,   NULL, NULL, AFB_SESSION_NONE_V2),
376                 REGISTER_VERB(delete,   NULL, NULL, AFB_SESSION_NONE_V2),
377                 REGISTER_VERB(read,             NULL, NULL, AFB_SESSION_NONE_V2),
378         { .verb = NULL}
379 };
380
381 const struct afb_binding_v2 afbBindingV2 = {
382                 .api = "ll-database",
383                 .specification = NULL,
384                 .verbs = ll_database_binding_verbs,
385                 .preinit = NULL,
386                 .init = ll_database_binding_init,
387                 .onevent = NULL,
388                 .noconcurrency = 0
389 };