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