Add database APIs to update device service payload info
[platform/core/connectivity/ua-manager.git] / ua-daemon / src / ua-manager-db.c
1 /*
2  * Copyright (c) 2018 Samsung Electronics Co., Ltd. All rights reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License")
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <stdbool.h>
21
22 #include <sqlite3.h>
23 #include <dlfcn.h>
24
25 #include "ua-manager-common.h"
26 #include "ua-manager-database.h"
27
28 #define UAM_DB_USERDATA_TABLE "userdata" /**< User data DB table name */
29 #define UAM_DB_DEVICES_TABLE "devices" /**< Device DB table name */
30 #define UAM_DB_SERVICES_TABLE "services" /**< Service DB table name */
31 #define UAM_DB_DEVICE_SERVICES_TABLE "device_services" /**< Device services DB table name */
32 #define UAM_DB_IBEACON_ADV_TABLE "ibeacon_adv" /**< iBeacon adv DB table name */
33 #define UAM_DB_PAYLOADS_TABLE "payloads" /**< payload DB table name */
34
35 #define CREATE_USERDATA_TABLE "CREATE TABLE IF NOT EXISTS userdata ( " \
36         "name TEXT, " \
37         "user_id INTEGER PRIMARY KEY AUTOINCREMENT, " \
38         "account TEXT, " \
39         "UNIQUE (account) " \
40         "); "
41
42 #define CREATE_DEVICES_TABLE "CREATE TABLE IF NOT EXISTS devices ( " \
43         "device_number INTEGER PRIMARY KEY AUTOINCREMENT, " \
44         "device_id TEXT, " \
45         "user_id INTEGER, " \
46         "tech_type INTEGER, " \
47         "address TEXT, " \
48         "ip_address TEXT, " \
49         "last_seen LONG, " \
50         "presence_state INTEGER, " \
51         "os_type INTEGER, " \
52         "discriminant INTEGER, " \
53         "FOREIGN KEY(user_id) REFERENCES userdata(user_id), " \
54         "UNIQUE (device_id, tech_type, address) " \
55         "); "
56
57 #define CREATE_PAYLOADS_TABLE "CREATE TABLE IF NOT EXISTS payloads ( " \
58         "payload_number INTEGER PRIMARY KEY AUTOINCREMENT, " \
59         "primary_key TEXT, " \
60         "secondary_key TEXT, " \
61         "device_uid TEXT, " \
62         "device_icon TEXT, " \
63         "device_number INTEGER, " \
64         "FOREIGN KEY(device_number) REFERENCES devices(device_number), " \
65         "UNIQUE (primary_key, secondary_key, device_uid) " \
66         "); "
67
68 #define CREATE_SERVICES_TABLE "CREATE TABLE IF NOT EXISTS services ( " \
69         "service_number INTEGER PRIMARY KEY AUTOINCREMENT, " \
70         "service_name TEXT, " \
71         "cycle INTEGER, " \
72         "presence_threshold INTEGER, " \
73         "absence_threshold INTEGER, " \
74         "UNIQUE (service_name) " \
75         "); "
76
77 #define CREATE_DEVICE_SERVICES_TABLE "CREATE TABLE IF NOT EXISTS device_services ( " \
78         "device_number INTEGER, " \
79         "service_number INTEGER, " \
80         "payload_number INTEGER, " \
81         "discriminant INTEGER, " \
82         "last_seen LONG, " \
83         "FOREIGN KEY(device_number) REFERENCES devices(device_number), " \
84         "FOREIGN KEY(service_number) REFERENCES services(service_number), " \
85         "FOREIGN KEY(payload_number) REFERENCES payloads(payload_number), " \
86         "PRIMARY KEY(device_number, service_number) " \
87         "); "
88
89 #define CREATE_IBEACON_ADV_TABLE "CREATE TABLE IF NOT EXISTS ibeacon_adv ( " \
90         "ibeacon_id INTEGER PRIMARY KEY AUTOINCREMENT, " \
91         "ibeacon_adv TEXT, " \
92         "adv_len INTEGER, " \
93         "UNIQUE (ibeacon_adv) " \
94         "); "
95
96 #define VERSION 1
97
98 sqlite3 *database_handle;
99 static sqlite3_stmt *select_version;
100 static int transaction_cnt;
101
102 static int __uam_db_exec_sql(char *sql, void *cb)
103 {
104         FUNC_ENTRY;
105         int sql_ret;
106         char *error = NULL;
107
108         if (NULL == database_handle) {
109                 UAM_ERR("database_handle is NULL");
110                 return UAM_ERROR_DB_FAILED;
111         }
112
113         if (NULL == sql) {
114                 UAM_ERR("sql is NULL");
115                 return UAM_ERROR_DB_FAILED;
116         }
117
118         sql_ret = sqlite3_exec(database_handle, sql, cb, NULL, &error);
119         if (SQLITE_OK != sql_ret) {
120                 UAM_ERR("Failed to execute sql: (%d) %s", sql_ret, error);
121                 sqlite3_free(error);
122                 sqlite3_close(database_handle);
123                 database_handle = NULL;
124                 return UAM_ERROR_DB_FAILED;
125         }
126
127         return UAM_ERROR_NONE;
128 }
129
130 static int __uam_db_open(void)
131 {
132         FUNC_ENTRY;
133         int res = 0;
134
135         res = sqlite3_open_v2(DATABASE_FULL_PATH, &(database_handle),
136                                 SQLITE_OPEN_FULLMUTEX | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
137         if (SQLITE_OK != res) {
138                 UAM_ERR("sqlite3_open_v2 failed, (%d)", res);
139                 FUNC_EXIT;
140                 return UAM_ERROR_DB_FAILED;
141         }
142         UAM_DBG("Successfully opened database");
143
144         FUNC_EXIT;
145         return res;
146 }
147
148 static int __uam_db_busy(void *data, int attempt)
149 {
150         FUNC_ENTRY;
151         int max_attempts = SQLITE_MAX_RETRY;
152
153         if (attempt < max_attempts) {
154                 UAM_WARN("DB locked, attempt number %d, Attempt Remaining %d",
155                         attempt, max_attempts - attempt);
156                 usleep(SQLITE_BUSY_TIMEOUT); /* wait for a half second*/
157                 return 1;
158         }
159
160         UAM_ERR("DB locked by another process, attempts tried %d, Exit",
161                         attempt);
162         FUNC_EXIT;
163         return 0;
164 }
165
166 static int __uam_db_get_version(unsigned int *ver)
167 {
168         FUNC_ENTRY;
169         int error_code = UAM_ERROR_NONE;
170         int sql_ret;
171         char *sql = NULL;
172         sql = sqlite3_mprintf("PRAGMA user_version");
173         sqlite3_stmt *stmt = select_version;
174
175         sql_ret = sqlite3_prepare_v2(database_handle, sql, -1, &stmt, NULL);
176         if (sql_ret != SQLITE_OK) {
177                 stmt = NULL;
178                 FINALIZE(select_version);
179                 UAM_ERR("Failed to prepare \"%s\" query", sql);
180                 sqlite3_free(sql);
181                 return sql_ret;
182         }
183
184         do {
185                 sql_ret = sqlite3_step(stmt);
186
187                 switch (sql_ret) {
188                 case SQLITE_DONE:
189                         break;
190                 case SQLITE_ROW:
191                         *ver = sqlite3_column_int(stmt, 0);
192                         UAM_INFO("database version %d", *ver);
193                         break;
194                 case SQLITE_ERROR:
195                 default:
196                         UAM_ERR("Failed to select database version info [%d:%s]",
197                                 sql_ret, sqlite3_errmsg(database_handle));
198                 }
199         } while (sql_ret == SQLITE_ROW);
200
201         FINALIZE(select_version);
202         sqlite3_reset(stmt);
203         sqlite3_finalize(stmt);
204         sqlite3_free(sql);
205         FUNC_EXIT;
206         return error_code;
207 }
208
209 static int __uam_db_update_version(unsigned int version)
210 {
211         FUNC_ENTRY;
212         char *sql = NULL;
213         int ret = UAM_ERROR_NONE;
214
215         sql = sqlite3_mprintf("PRAGMA user_version = '%d';", version);
216
217         ret = __uam_db_exec_sql(sql, NULL);
218         if (UAM_ERROR_NONE != ret)
219                 UAM_DBG("failed to updated database version to %d", version);
220         else
221                 UAM_DBG("Successfully updated database version to %d", version);
222
223         sqlite3_free(sql);
224
225         FUNC_EXIT;
226         return ret;
227 }
228
229 static int __uam_db_upgrade(unsigned int version)
230 {
231         FUNC_ENTRY;
232         /*
233         This code should handle all the database upgrade after initial version
234         */
235
236         /* update database version */
237         retv_if(UAM_ERROR_NONE != __uam_db_update_version(version), UAM_ERROR_DB_FAILED);
238         UAM_DBG("Successfully upgraded database version to %d", version);
239
240         FUNC_EXIT;
241         return UAM_ERROR_NONE;
242 }
243
244 static int __uam_db_upgrade_version(unsigned int old_ver, unsigned int new_ver)
245 {
246         FUNC_ENTRY;
247         int ret = UAM_ERROR_NONE;
248         unsigned int i;
249
250         for (i = old_ver + 1; i <= new_ver; i++) {
251                 ret = __uam_db_upgrade(i);
252                 if (UAM_ERROR_NONE != ret) {
253                         UAM_ERR("Faild to __uam_db_upgrade(%d)", i);
254                         ret = UAM_ERROR_DB_FAILED;
255                         break;
256                 }
257         }
258
259         FUNC_EXIT;
260         return ret;
261 }
262
263 int _uam_db_check_version(void)
264 {
265         FUNC_ENTRY;
266         unsigned int curr_ver = VERSION;
267         unsigned int old_ver = 0;
268
269         /* check database version */
270         retv_if(UAM_ERROR_NONE != __uam_db_get_version(&old_ver), UAM_ERROR_DB_FAILED);
271
272         if (0 == old_ver) {
273                 retv_if(UAM_ERROR_NONE != __uam_db_update_version(curr_ver), UAM_ERROR_DB_FAILED);
274         } else if (old_ver != curr_ver) {
275                 UAM_DBG("Database version changed, needs upgrade");
276                 /* upgrade database to new version */
277                 retv_if(UAM_ERROR_NONE != __uam_db_upgrade_version(old_ver, curr_ver), UAM_ERROR_DB_FAILED);
278         }
279
280         FUNC_EXIT;
281         return UAM_ERROR_NONE;
282 }
283
284 static bool __uam_db_is_table_existing(const char *table)
285 {
286         char *sql;
287         int count;
288         bool result = false;
289         int sql_ret = SQLITE_OK;
290
291         sql = sqlite3_mprintf(
292                 "SELECT count(*) FROM sqlite_master WHERE type='table' AND name ='%s';", table);
293         if (NULL == sql) {
294                 UAM_ERR("sqlite3_mprintf failed");
295                 return false;
296         }
297         sqlite3_stmt *stmt = NULL;
298
299         sql_ret = sqlite3_prepare_v2(database_handle, sql, strlen(sql), &stmt, NULL);
300         if (SQLITE_OK != sql_ret) {
301                 UAM_ERR("sqlite3_prepare_v2 failed, [%d:%s]", sql_ret, sqlite3_errmsg(database_handle));
302                 result = false;
303                 goto is_table_existing_done;
304         }
305
306         sql_ret = sqlite3_step(stmt);
307         if (sql_ret != SQLITE_ROW) {
308                 UAM_ERR("sqlite3_step failed, [%d:%s]", sql_ret, sqlite3_errmsg(database_handle));
309                 result = false;
310                 goto is_table_existing_done;
311         }
312
313         count = sqlite3_column_int(stmt, 0);
314         if (count > 0)
315                 result = true;
316         else
317                 result = false;
318
319 is_table_existing_done:
320         sqlite3_finalize(stmt);
321         sqlite3_free(sql);
322
323         return result;
324 }
325
326 static int __uam_db_check_integrity_cb(
327         void *err, int count, char **data, char **columns)
328 {
329         FUNC_ENTRY;
330
331         UAM_DBG("%s = %s\n", columns[0], data[0] ? data[0] : "NULL");
332         if (!g_strcmp0(columns[0], "integrity_check") && !g_strcmp0(data[0], "ok"))
333                 return SQLITE_OK;
334
335         return SQLITE_ERROR;
336 }
337
338 static int __uam_db_check_integrity(void)
339 {
340         FUNC_ENTRY;
341         int ret = UAM_ERROR_NONE;
342         char *sql = NULL;
343
344         sql = sqlite3_mprintf("PRAGMA integrity_check");
345
346         ret = __uam_db_exec_sql(sql, __uam_db_check_integrity_cb);
347         if (UAM_ERROR_NONE != ret)
348                 UAM_ERR("Faild to __uam_db_exec_sql()");
349         else
350                 UAM_DBG("Successfully verified database integrity");
351
352         sqlite3_free(sql);
353
354         FUNC_EXIT;
355         return ret;
356 }
357
358 static int __uam_db_set_locking_mode(void)
359 {
360         char *sql = NULL;
361         int ret = UAM_ERROR_NONE;
362
363         sql = sqlite3_mprintf("PRAGMA locking_mode = NORMAL");
364         ret = __uam_db_exec_sql(sql, NULL);
365         if (UAM_ERROR_NONE != ret)
366                 ret = UAM_ERROR_DB_FAILED;
367
368         sqlite3_free(sql);
369
370         return ret;
371 }
372
373 static int __uam_db_create_table(const char *table_name)
374 {
375         char *sql = NULL;
376         int ret = UAM_ERROR_NONE;
377
378         sql = sqlite3_mprintf(table_name);
379         ret = __uam_db_exec_sql(sql, NULL);
380
381         if (UAM_ERROR_NONE != ret)
382                 UAM_ERR("Faild to __uam_db_exec_sql()");
383         else
384                 UAM_DBG("Successfully created table %s", table_name);
385
386         sqlite3_free(sql);
387
388         return ret;
389 }
390
391 static int __uam_db_check_table_creation(void)
392 {
393         FUNC_ENTRY;
394         int error_code = UAM_ERROR_DB_FAILED;
395
396         if (!__uam_db_is_table_existing(UAM_DB_USERDATA_TABLE))
397                 retv_if(UAM_ERROR_NONE != __uam_db_create_table(CREATE_USERDATA_TABLE), error_code);
398
399         if (!__uam_db_is_table_existing(UAM_DB_DEVICES_TABLE))
400                 retv_if(UAM_ERROR_NONE != __uam_db_create_table(CREATE_DEVICES_TABLE), error_code);
401
402         if (!__uam_db_is_table_existing(UAM_DB_SERVICES_TABLE))
403                 retv_if(UAM_ERROR_NONE != __uam_db_create_table(CREATE_SERVICES_TABLE), error_code);
404
405         if (!__uam_db_is_table_existing(UAM_DB_DEVICE_SERVICES_TABLE))
406                 retv_if(UAM_ERROR_NONE != __uam_db_create_table(CREATE_DEVICE_SERVICES_TABLE), error_code);
407
408         if (!__uam_db_is_table_existing(UAM_DB_IBEACON_ADV_TABLE))
409                 retv_if(UAM_ERROR_NONE != __uam_db_create_table(CREATE_IBEACON_ADV_TABLE), error_code);
410
411         if (!__uam_db_is_table_existing(UAM_DB_PAYLOADS_TABLE))
412                 retv_if(UAM_ERROR_NONE != __uam_db_create_table(CREATE_PAYLOADS_TABLE), error_code);
413
414         UAM_DBG("Successfully verified table creation");
415         FUNC_EXIT;
416         return UAM_ERROR_NONE;
417 }
418
419 static int __uam_db_verify()
420 {
421         FUNC_ENTRY;
422
423         /* check table existance*/
424         retv_if(UAM_ERROR_NONE != __uam_db_check_table_creation(), UAM_ERROR_DB_FAILED);
425         /* check db size */
426         _uam_get_file_size(DATABASE_FULL_PATH);
427         /* check db integrity */
428         retv_if(UAM_ERROR_NONE != __uam_db_check_integrity(), UAM_ERROR_DB_FAILED);
429         /* set locking mode */
430         retv_if(UAM_ERROR_NONE != __uam_db_set_locking_mode(), UAM_ERROR_DB_FAILED);
431
432         UAM_DBG("Successfully verified database");
433
434         return UAM_ERROR_NONE;
435 }
436
437 int _uam_db_initialize_once(void)
438 {
439         FUNC_ENTRY;
440         char *sql;
441         int ret = UAM_ERROR_NONE;
442         int max_retries = 2;
443
444         do {
445                 /* open database */
446                 retv_if(SQLITE_OK != __uam_db_open(), UAM_ERROR_DB_FAILED);
447
448                 if (UAM_ERROR_NONE != __uam_db_verify()) {
449                         UAM_ERR("Failed to verify database");
450                         sqlite3_close(database_handle);
451                         unlink(DATABASE_FULL_PATH);
452                         database_handle = NULL;
453                         retv_if(0 == max_retries, UAM_ERROR_DB_FAILED);
454                 } else {
455                         break;
456                 }
457         } while (max_retries--);
458
459         /* Enable persist journal mode */
460         sql = sqlite3_mprintf("PRAGMA journal_mode = PERSIST");
461         ret = __uam_db_exec_sql(sql, NULL);
462         sqlite3_free(sql);
463         if (UAM_ERROR_NONE != ret) {
464                 UAM_ERR("Faile to __uam_db_exec_sql()");
465                 return UAM_ERROR_DB_FAILED;
466         }
467
468         if (NULL == database_handle) {
469                 unlink(DATABASE_FULL_PATH);
470                 return UAM_ERROR_DB_FAILED;
471         }
472
473         /* Set how many times we'll repeat our attempts for sqlite_step */
474         if (SQLITE_OK != sqlite3_busy_handler(database_handle, __uam_db_busy, NULL)) {
475                 UAM_ERR("Couldn't set busy handler!");
476                 return UAM_ERROR_DB_FAILED;
477         }
478
479         FUNC_EXIT;
480         return ret;
481 }
482
483 int _uam_db_initialize(void)
484 {
485         FUNC_ENTRY;
486         database_handle = NULL;
487
488         /* initialize database */
489         EXEC(UAM_ERROR_NONE, _uam_db_initialize_once(), handle_error);
490
491         /* check database version */
492         EXEC(UAM_ERROR_NONE, _uam_db_check_version(), handle_error);
493
494         /* initialize tables */
495         EXEC(UAM_ERROR_NONE, _uam_user_db_initialize(), handle_error);
496         EXEC(UAM_ERROR_NONE, _uam_device_db_initialize(), handle_error);
497         EXEC(UAM_ERROR_NONE, _uam_service_db_initialize(), handle_error);
498         EXEC(UAM_ERROR_NONE, _uam_device_service_db_initialize(), handle_error);
499         EXEC(UAM_ERROR_NONE, _uam_adv_db_initialize(), handle_error);
500         EXEC(UAM_ERROR_NONE, _uam_payload_db_initialize(), handle_error);
501
502         transaction_cnt = 0;
503         FUNC_EXIT;
504         return UAM_ERROR_NONE;
505
506 handle_error:
507         _uam_db_deinitialize();
508         FUNC_EXIT;
509         return UAM_ERROR_DB_FAILED;
510 }
511
512 int _uam_db_deinitialize(void)
513 {
514         FUNC_ENTRY;
515
516         retv_if(NULL == database_handle, UAM_ERROR_NONE);
517
518         _uam_user_db_deinitialize();
519         _uam_device_db_deinitialize();
520         _uam_service_db_deinitialize();
521         _uam_device_service_db_deinitialize();
522         _uam_adv_db_deinitialize();
523         _uam_payload_db_deinitialize();
524
525         FUNC_EXIT;
526         return UAM_ERROR_NONE;
527 }
528
529 int _uam_db_clear(void)
530 {
531         int error_code = UAM_ERROR_DB_FAILED;
532         retv_if(NULL == database_handle, UAM_ERROR_DB_FAILED);
533
534         EXEC(UAM_ERROR_NONE, _uam_user_db_clear(), handle_error);
535         EXEC(UAM_ERROR_NONE, _uam_device_db_clear(), handle_error);
536         EXEC(UAM_ERROR_NONE, _uam_service_db_clear(), handle_error);
537         EXEC(UAM_ERROR_NONE, _uam_device_service_db_clear(), handle_error);
538         EXEC(UAM_ERROR_NONE, _uam_adv_db_clear(), handle_error);
539         EXEC(UAM_ERROR_NONE, _uam_payload_db_clear(), handle_error);
540         UAM_DBG("Table data deleted ");
541
542         error_code = UAM_ERROR_NONE;
543
544 handle_error:
545         return error_code;
546 }
547
548 int __uam_db_begin_transaction(void)
549 {
550         FUNC_ENTRY;
551         char *sql = NULL;
552         int ret = UAM_ERROR_NONE;
553
554         if (transaction_cnt <= 0) {
555                 transaction_cnt = 0;
556                 sql = sqlite3_mprintf("BEGIN IMMEDIATE TRANSACTION");
557
558                 ret = __uam_db_exec_sql(sql, NULL);
559                 if (UAM_ERROR_NONE != ret)
560                         UAM_DBG("failed to begin transaction");
561                 else
562                         UAM_DBG("Successful to begin transaction");
563
564                 sqlite3_free(sql);
565         }
566         transaction_cnt++;
567         UAM_DBG("transaction_cnt: %d", transaction_cnt);
568
569         FUNC_EXIT;
570         return ret;
571 }
572
573 static int __uam_db_rollback_transaction(void)
574 {
575         FUNC_ENTRY;
576         char *sql = NULL;
577         int ret = UAM_ERROR_NONE;
578
579         sql = sqlite3_mprintf("ROLLBACK TRANSACTION");
580
581         ret = __uam_db_exec_sql(sql, NULL);
582         if (UAM_ERROR_NONE != ret)
583                 UAM_DBG("failed to rollback transaction");
584         else
585                 UAM_DBG("Successful to rollback transaction");
586
587         sqlite3_free(sql);
588
589         FUNC_EXIT;
590         return ret;
591 }
592
593 int __uam_db_end_transaction(gboolean is_success)
594 {
595         FUNC_ENTRY;
596         char *sql = NULL;
597         int ret = UAM_ERROR_NONE;
598
599         transaction_cnt--;
600
601         if (0 != transaction_cnt) {
602                 UAM_DBG("transaction_cnt: %d", transaction_cnt);
603                 return ret;
604         }
605
606         if (false == is_success) {
607                 ret = __uam_db_rollback_transaction();
608                 return ret;
609         }
610
611         sql = sqlite3_mprintf("COMMIT TRANSACTION");
612
613         ret = __uam_db_exec_sql(sql, NULL);
614         if (UAM_ERROR_NONE != ret) {
615                 UAM_DBG("failed to commit transaction");
616                 ret = __uam_db_rollback_transaction();
617         } else
618                 UAM_DBG("Successful to commit transaction");
619
620         sqlite3_free(sql);
621
622         FUNC_EXIT;
623         return ret;
624 }