1 /******************************************************************************
4 * Company XS Embedded GmbH
5 *****************************************************************************/
6 /******************************************************************************
7 * This Source Code Form is subject to the terms of the
8 * Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed
9 * with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
10 ******************************************************************************/
12 * @file persistence_client_library_data_access.c
13 * @ingroup Persistence client library
14 * @author Ingo Huerner
15 * @brief Implementation of persistence database access
16 * Library provides an API to access persistent data
20 #include "../include_protected/persistence_client_library_db_access.h"
21 #include "../include_protected/persistence_client_library_rc_table.h"
22 #include "persistence_client_library_custom_loader.h"
23 #include "persistence_client_library_itzam_errors.h"
25 #include "persistence_client_library_dbus_service.h"
27 #include <dbus/dbus.h>
33 /// definition of a key-value pair stored in the database
34 typedef struct _KeyValuePair_s
36 char m_key[DbKeySize]; /// the key
37 char m_data[DbValueSize]; /// the data
38 unsigned int m_data_size; /// the size of the data
43 // definition of a cursor entry
44 typedef struct _CursorEntry_s
46 itzam_btree_cursor m_cursor;
52 // cursor array handle
53 CursorEntry_s gCursorArray[MaxPersHandle];
56 static int gHandleIdx = 1;
59 int gFreeCursorHandleArray[MaxPersHandle];
61 int gFreeCursorHandleIdxHead = 0;
63 // mutex to controll access to the cursor array
64 pthread_mutex_t gMtx = PTHREAD_MUTEX_INITIALIZER;
68 static itzam_btree gBtree[DbTableSize][PersistencePolicy_LastEntry];
69 static int gBtreeCreated[DbTableSize][PersistencePolicy_LastEntry] = { {0} };
73 int pers_send_Notification_Signal(const char* key, PersistenceDbContext_s* context, unsigned int reason);
76 itzam_btree* pers_db_open(PersistenceInfo_s* info, const char* dbPath)
79 itzam_btree* btree = NULL;
81 // create array index: index is a combination of resource config table type and group
82 arrayIdx = info->configKey.storage + info->context.ldbid ;
84 //if(arrayIdx <= DbTableSize)
85 if(arrayIdx < DbTableSize)
87 if(gBtreeCreated[arrayIdx][info->configKey.policy] == 0)
89 itzam_state state = ITZAM_FAILED;
90 state = itzam_btree_open(&gBtree[arrayIdx][info->configKey.policy], dbPath,
91 itzam_comparator_string, error_handler, 0/*recover*/, 0/*read_only*/);
92 if (state != ITZAM_OKAY)
94 fprintf(stderr, "pers_db_open ==> Open Itzam problem: %s\n", STATE_MESSAGES[state]);
96 gBtreeCreated[arrayIdx][info->configKey.policy] = 1;
99 btree = &gBtree[arrayIdx][info->configKey.policy];
103 printf("btree_get ==> invalid storage type\n");
110 void pers_db_close(PersistenceInfo_s* info)
112 int arrayIdx = info->configKey.storage + info->context.ldbid;
114 if(info->configKey.storage <= PersistenceStorage_shared )
116 itzam_state state = ITZAM_FAILED;
117 state = itzam_btree_close(&gBtree[arrayIdx][info->configKey.policy]);
118 if (state != ITZAM_OKAY)
120 fprintf(stderr, "pers_db_close ==> Close Itzam problem: %s\n", STATE_MESSAGES[state]);
122 gBtreeCreated[arrayIdx][info->configKey.policy] = 0;
126 printf("pers_db_close ==> invalid storage type\n");
132 void pers_db_close_all()
136 for(i=0; i<DbTableSize; i++)
138 // close write cached database
139 if(gBtreeCreated[i][PersistencePolicy_wc] == 1)
141 itzam_state state = ITZAM_FAILED;
142 state = itzam_btree_close(&gBtree[i][PersistencePolicy_wc]);
143 if (state != ITZAM_OKAY)
145 fprintf(stderr, "pers_db_close_all ==> Close WC: Itzam problem: %s\n", STATE_MESSAGES[state]);
147 gBtreeCreated[i][PersistencePolicy_wc] = 0;
150 // close write through database
151 if(gBtreeCreated[i][PersistencePolicy_wt] == 1)
153 itzam_state state = ITZAM_FAILED;
154 state = itzam_btree_close(&gBtree[i][PersistencePolicy_wt]);
155 if (state != ITZAM_OKAY)
157 fprintf(stderr, "pers_db_close_all ==> Close WT: Itzam problem: %s\n", STATE_MESSAGES[state]);
159 gBtreeCreated[i][PersistencePolicy_wt] = 0;
166 int pers_db_read_key(char* dbPath, char* key, PersistenceInfo_s* info, unsigned char* buffer, unsigned int buffer_size)
170 if( PersistenceStorage_shared == info->configKey.storage
171 || PersistenceStorage_local == info->configKey.storage)
173 itzam_btree* btree = NULL;
174 itzam_state state = ITZAM_FAILED;
175 KeyValuePair_s search;
177 btree = pers_db_open(info, dbPath);
180 if(itzam_true == itzam_btree_find(btree, key, &search))
182 read_size = search.m_data_size;
183 if(read_size > buffer_size)
185 read_size = buffer_size; // truncate data size to buffer size
187 memcpy(buffer, search.m_data, read_size);
191 read_size = EPERS_NOKEY;
196 read_size = EPERS_NOPRCTABLE;
197 fprintf(stderr, "\npersistence_get_data ==> Open Itzam problem: %s\n", STATE_MESSAGES[state]);
200 else if(PersistenceStorage_custom == info->configKey.storage) // custom storage implementation via custom library
202 int idx = custom_client_name_to_id(dbPath, 1);
203 char workaroundPath[128]; // workaround, because /sys/ can not be accessed on host!!!!
204 snprintf(workaroundPath, 128, "%s%s", "/Data", dbPath );
206 if( (idx < PersCustomLib_LastEntry) && (*gPersCustomFuncs[idx].custom_plugin_handle_get_data != NULL) )
208 if(info->configKey.customID[0] == '\0') // if we have not a customID we use the key
209 read_size = gPersCustomFuncs[idx].custom_plugin_get_data(key, (char*)buffer, buffer_size);
211 read_size = gPersCustomFuncs[idx].custom_plugin_get_data(info->configKey.customID, (char*)buffer, buffer_size);
215 read_size = EPERS_NOPLUGINFUNCT;
223 int pers_db_write_key(char* dbPath, char* key, PersistenceInfo_s* info, unsigned char* buffer, unsigned int buffer_size)
227 if( PersistenceStorage_local == info->configKey.storage
228 || PersistenceStorage_shared == info->configKey.storage )
230 write_size = buffer_size;
231 itzam_btree* btree = NULL;
232 itzam_state state = ITZAM_FAILED;
233 KeyValuePair_s insert;
235 btree = pers_db_open(info, dbPath);
239 keySize = (int)strlen((const char*)key);
240 if(keySize < DbKeySize)
243 dataSize = (int)strlen( (const char*)buffer);
244 if(dataSize < DbValueSize)
246 // -----------------------------------------------------------------------------
248 itzam_btree_transaction_start(btree);
251 memset(insert.m_key, 0, DbKeySize);
252 memcpy(insert.m_key, key, keySize);
253 if(itzam_true == itzam_btree_find(btree, key, &insert))
255 // key already available, so delete "old" key
256 state = itzam_btree_remove(btree, (const void *)&insert);
260 memset(insert.m_data, 0, DbValueSize);
261 memcpy(insert.m_data, buffer, dataSize);
264 insert.m_data_size = buffer_size;
266 state = itzam_btree_insert(btree,(const void *)&insert);
267 if (state != ITZAM_OKAY)
269 fprintf(stderr, "\npersistence_set_data ==> Insert Itzam problem: %s\n", STATE_MESSAGES[state]);
270 write_size = EPERS_DB_ERROR_INTERNAL;
274 itzam_btree_transaction_commit(btree);
276 // -----------------------------------------------------------------------------
278 if(PersistenceStorage_shared == info->configKey.storage)
280 pers_send_Notification_Signal(key, &info->context, pclNotifyStatus_deleted);
285 fprintf(stderr, "\npersistence_set_data ==> set_value_to_table_itzam => data to long » size %d | maxSize: %d\n", dataSize, DbKeySize);
286 write_size = EPERS_DB_VALUE_SIZE;
291 fprintf(stderr, "\nset_value_to_table_itzam => key to long » size: %d | maxSize: %d\n", keySize, DbKeySize);
292 write_size = EPERS_DB_KEY_SIZE;
297 write_size = EPERS_NOPRCTABLE;
298 fprintf(stderr, "\npersistence_set_data ==> Open Itzam problem: %s\n", STATE_MESSAGES[state]);
301 else if(PersistenceStorage_custom == info->configKey.storage) // custom storage implementation via custom library
303 int idx = custom_client_name_to_id(dbPath, 1);
304 if((idx < PersCustomLib_LastEntry) && (*gPersCustomFuncs[idx].custom_plugin_handle_set_data != NULL) )
306 if(info->configKey.customID[0] == '\0') // if we have not a customID we use the key
307 write_size = gPersCustomFuncs[idx].custom_plugin_set_data(key, (char*)buffer, buffer_size);
309 write_size = gPersCustomFuncs[idx].custom_plugin_set_data(info->configKey.customID, (char*)buffer, buffer_size);
313 write_size = EPERS_NOPLUGINFUNCT;
321 int pers_db_get_key_size(char* dbPath, char* key, PersistenceInfo_s* info)
325 if( PersistenceStorage_shared == info->configKey.storage
326 || PersistenceStorage_local == info->configKey.storage)
329 itzam_btree* btree = NULL;
330 itzam_state state = ITZAM_FAILED;
331 KeyValuePair_s search;
333 btree = pers_db_open(info, dbPath);
336 keySize = (int)strlen((const char*)key);
337 if(keySize < DbKeySize)
339 memset(search.m_key,0, DbKeySize);
340 memcpy(search.m_key, key, keySize);
341 if(itzam_true == itzam_btree_find(btree, key, &search))
343 read_size = search.m_data_size;
347 read_size = EPERS_NOKEY;
352 fprintf(stderr, "persistence_get_data_size => key to long » size: %d | maxSize: %d\n", keySize, DbKeySize);
353 read_size = EPERS_DB_KEY_SIZE;
358 read_size = EPERS_NOPRCTABLE;
359 fprintf(stderr, "\npersistence_get_data_size ==> Open Itzam problem: %s\n", STATE_MESSAGES[state]);
362 else if(PersistenceStorage_custom == info->configKey.storage) // custom storage implementation via custom library
364 int idx = custom_client_name_to_id(dbPath, 1);
365 if((idx < PersCustomLib_LastEntry) && (*gPersCustomFuncs[idx].custom_plugin_handle_set_data != NULL) )
367 if(info->configKey.customID[0] == '\0') // if we have not a customID we use the key
368 read_size = gPersCustomFuncs[idx].custom_plugin_get_size(key);
370 read_size = gPersCustomFuncs[idx].custom_plugin_get_size(info->configKey.customID);
374 read_size = EPERS_NOPLUGINFUNCT;
382 int pers_db_delete_key(char* dbPath, char* dbKey, PersistenceInfo_s* info)
385 if(PersistenceStorage_custom != info->configKey.storage)
387 itzam_btree* btree = NULL;
388 KeyValuePair_s delete;
390 //printf("delete_key_from_table_itzam => Path: \"%s\" | key: \"%s\" \n", dbPath, key);
391 btree = pers_db_open(info, dbPath);
395 keySize = (int)strlen((const char*)dbKey);
396 if(keySize < DbKeySize)
398 // -----------------------------------------------------------------------------
400 itzam_btree_transaction_start(btree);
404 memset(delete.m_key,0, DbKeySize);
405 memcpy(delete.m_key, dbKey, keySize);
406 state = itzam_btree_remove(btree, (const void *)&delete);
407 if (state != ITZAM_OKAY)
409 fprintf(stderr, "persistence_delete_data ==> Remove Itzam problem: %s\n", STATE_MESSAGES[state]);
410 ret = EPERS_DB_ERROR_INTERNAL;
412 itzam_btree_transaction_commit(btree);
414 // -----------------------------------------------------------------------------
416 if(PersistenceStorage_shared == info->configKey.storage)
418 pers_send_Notification_Signal(dbKey, &info->context, pclNotifyStatus_changed);
423 fprintf(stderr, "persistence_delete_data => key to long » size: %d | maxSize: %d\n", keySize, DbKeySize);
424 ret = EPERS_DB_KEY_SIZE;
429 fprintf(stderr, "persistence_delete_data => no prct table\n");
430 ret = EPERS_NOPRCTABLE;
433 else // custom storage implementation via custom library
435 int idx = custom_client_name_to_id(dbPath, 1);
436 if((idx < PersCustomLib_LastEntry) && (*gPersCustomFuncs[idx].custom_plugin_handle_set_data != NULL) )
438 if(info->configKey.customID[0] == '\0') // if we have not a customID we use the key
439 ret = gPersCustomFuncs[idx].custom_plugin_delete_data(dbKey);
441 ret = gPersCustomFuncs[idx].custom_plugin_delete_data(info->configKey.customID);
445 ret = EPERS_NOPLUGINFUNCT;
452 int persistence_reg_notify_on_change(char* dbPath, char* key, unsigned int ldbid, unsigned int user_no, unsigned int seat_no,
453 pclChangeNotifyCallback_t callback)
457 dbus_error_init (&error);
458 char ruleChanged[DbusMatchRuleSize];
459 char ruleDeleted[DbusMatchRuleSize];
462 gChangeNotifyCallback = callback;
464 // add match for c h a n g e
465 snprintf(ruleChanged, DbusMatchRuleSize, "type='signal',interface='org.genivi.persistence.adminconsumer',member='PersistenceResChange',path='/org/genivi/persistence/adminconsumer',arg0='%s',arg1='%u',arg2='%u',arg3='%u'",
466 key, ldbid, user_no, seat_no);
467 dbus_bus_add_match(get_dbus_connection(), ruleChanged, &error);
470 // add match for d e l e t e
471 snprintf(ruleDeleted, DbusMatchRuleSize, "type='signal',interface='org.genivi.persistence.adminconsumer',member='PersistenceResDelete',path='/org/genivi/persistence/adminconsumer',arg0='%s',arg1='%u',arg2='%u',arg3='%u'",
472 key, ldbid, user_no, seat_no);
473 dbus_bus_add_match(get_dbus_connection(), ruleDeleted, &error);
476 // add match for c r e a t e
477 snprintf(ruleDeleted, DbusMatchRuleSize, "type='signal',interface='org.genivi.persistence.adminconsumer',member='PersistenceResCreate',path='/org/genivi/persistence/adminconsumer',arg0='%s',arg1='%u',arg2='%u',arg3='%u'",
478 key, ldbid, user_no, seat_no);
479 dbus_bus_add_match(get_dbus_connection(), ruleDeleted, &error);
485 int pers_send_Notification_Signal(const char* key, PersistenceDbContext_s* context, pclNotifyStatus_e reason)
487 DBusMessage* message;
490 char ldbid_array[DbusSubMatchSize];
491 char user_array[DbusSubMatchSize];
492 char seat_array[DbusSubMatchSize];
493 const char* ldbid_ptr = ldbid_array;
494 const char* user_ptr = user_array;
495 const char* seat_ptr = seat_array;
497 char* changeSignal = "PersistenceResChange";
498 char* deleteSignal = "PersistenceResDelete";
499 char* createSignal = "PersistenceResCreate";
500 char* theReason = NULL;
502 memset(ldbid_array, 0, DbusSubMatchSize);
503 memset(user_array, 0, DbusSubMatchSize);
504 memset(seat_array, 0, DbusSubMatchSize);
507 // dbus_bus_add_match is used for the notification mechanism,
508 // and this works only for type DBUS_TYPE_STRING as message arguments
509 // this is the reason to use string instead of integer types directly
510 snprintf(ldbid_array, DbusSubMatchSize, "%d", context->ldbid);
511 snprintf(user_array, DbusSubMatchSize, "%d", context->user_no);
512 snprintf(seat_array, DbusSubMatchSize, "%d", context->seat_no);
516 case pclNotifyStatus_deleted:
517 theReason = deleteSignal;
519 case pclNotifyStatus_created:
520 theReason = createSignal;
522 case pclNotifyStatus_changed:
523 theReason = changeSignal;
526 theReason = changeSignal;
530 if(theReason != NULL)
532 message = dbus_message_new_signal("/org/genivi/persistence/adminconsumer", // const char *path,
533 "org.genivi.persistence.adminconsumer", // const char *interface,
534 theReason); // const char *name
536 ret = dbus_message_append_args(message,
537 DBUS_TYPE_STRING, &key,
538 DBUS_TYPE_STRING, &ldbid_ptr,
539 DBUS_TYPE_STRING, &user_ptr,
540 DBUS_TYPE_STRING, &seat_ptr,
545 if(dbus_connection_send(get_dbus_connection(), message, NULL) == TRUE)
547 // Free the signal now we have finished with it
548 dbus_message_unref(message);
552 rval = EPERS_NOTIFY_SIG;
557 printf("pers_send_Notification_Signal: \n");
558 rval = EPERS_NOTIFY_SIG;
563 rval = EPERS_NOTIFY_SIG;
570 //---------------------------------------------------------------------------------------------------------
571 // C U R S O R F U N C T I O N S
572 //---------------------------------------------------------------------------------------------------------
574 int get_cursor_handle()
578 if(pthread_mutex_lock(&gMtx) == 0)
580 if(gFreeCursorHandleIdxHead > 0) // check if we have a free spot in the array before the current max
582 handle = gFreeCursorHandleArray[--gFreeCursorHandleIdxHead];
586 if(gHandleIdx < MaxPersHandle-1)
588 handle = gHandleIdx++; // no free spot before current max, increment handle index
593 printf("get_persistence_handle_idx => Reached maximum of open handles: %d \n", MaxPersHandle);
596 pthread_mutex_unlock(&gMtx);
602 void close_cursor_handle(int handlerDB)
604 if(pthread_mutex_lock(&gMtx) == 0)
606 if(gFreeCursorHandleIdxHead < MaxPersHandle)
608 gFreeCursorHandleArray[gFreeCursorHandleIdxHead++] = handlerDB;
610 pthread_mutex_unlock(&gMtx);
616 int pers_db_cursor_create(char* dbPath)
619 itzam_state state = ITZAM_FAILED;
621 handle = get_cursor_handle();
623 if(handle < MaxPersHandle && handle >= 0)
626 state = itzam_btree_open(&gCursorArray[handle].m_btree, dbPath, itzam_comparator_string, error_handler, 1/*recover*/, 0/*read_only*/);
627 if (state != ITZAM_OKAY)
629 fprintf(stderr, "pers_db_open ==> Open Itzam problem: %s\n", STATE_MESSAGES[state]);
635 state = itzam_btree_cursor_create(&gCursorArray[handle].m_cursor, &gCursorArray[handle].m_btree);
636 if(state == ITZAM_OKAY)
638 gCursorArray[handle].m_empty = 0;
642 gCursorArray[handle].m_empty = 1;
651 int pers_db_cursor_next(unsigned int handlerDB)
654 //if(handlerDB < MaxPersHandle && handlerDB >= 0)
655 if(handlerDB < MaxPersHandle )
657 if(gCursorArray[handlerDB].m_empty != 1)
660 success = itzam_btree_cursor_next(&gCursorArray[handlerDB].m_cursor);
662 if(success == itzam_true)
668 rval = EPERS_LAST_ENTRY_IN_DB;
673 printf("persistence_db_cursor_get_key ==> invalid handle: %u \n", handlerDB);
678 printf("persistence_db_cursor_get_key ==> handle bigger than max » handleDB: %u | max: : %d \n", handlerDB, MaxPersHandle);
685 int pers_db_cursor_get_key(unsigned int handlerDB, char * bufKeyName_out, int bufSize)
688 KeyValuePair_s search;
690 if(handlerDB < MaxPersHandle)
692 if(gCursorArray[handlerDB].m_empty != 1)
695 itzam_btree_cursor_read(&gCursorArray[handlerDB].m_cursor ,(void *)&search);
696 length = strlen(search.m_key);
699 memcpy(bufKeyName_out, search.m_key, length);
704 printf("persistence_db_cursor_get_key ==> buffer to small » keySize: %d | bufSize: %d \n", length, bufSize);
709 printf("persistence_db_cursor_get_key ==> invalid handle: %u \n", handlerDB);
714 printf("persistence_db_cursor_get_key ==> handle bigger than max » handleDB: %u | max: : %d \n", handlerDB, MaxPersHandle);
721 int pers_db_cursor_get_data(unsigned int handlerDB, char * bufData_out, int bufSize)
724 KeyValuePair_s search;
726 if(handlerDB < MaxPersHandle)
728 if(gCursorArray[handlerDB].m_empty != 1)
731 itzam_btree_cursor_read(&gCursorArray[handlerDB].m_cursor ,(void *)&search);
733 length = strlen(search.m_data);
736 memcpy(bufData_out, search.m_data, length);
741 printf("persistence_db_cursor_get_data ==> buffer to small » keySize: %d | bufSize: %d \n", length, bufSize);
746 printf("persistence_db_cursor_get_data ==> invalid handle: %u \n", handlerDB);
751 printf("persistence_db_cursor_get_data ==> handle bigger than max » handleDB: %u | max: : %d \n", handlerDB, MaxPersHandle);
758 int pers_db_cursor_get_data_size(unsigned int handlerDB)
761 KeyValuePair_s search;
763 if(handlerDB < MaxPersHandle)
765 if(gCursorArray[handlerDB].m_empty != 1)
767 itzam_btree_cursor_read(&gCursorArray[handlerDB].m_cursor ,(void *)&search);
768 size = strlen(search.m_data);
772 printf("persistence_db_cursor_get_data ==> invalid handle: %u \n", handlerDB);
777 printf("persistence_db_cursor_get_data ==> handle bigger than max » handleDB: %u | max: : %d \n", handlerDB, MaxPersHandle);
784 int pers_db_cursor_destroy(unsigned int handlerDB)
787 if(handlerDB < MaxPersHandle)
789 itzam_btree_cursor_free(&gCursorArray[handlerDB].m_cursor);
790 gCursorArray[handlerDB].m_empty = 1;
792 itzam_state state = ITZAM_FAILED;
793 state = itzam_btree_close(&gCursorArray[handlerDB].m_btree);
794 if (state != ITZAM_OKAY)
796 fprintf(stderr, "pers_db_cursor_destroy ==> Close: Itzam problem: %s\n", STATE_MESSAGES[state]);
799 close_cursor_handle(handlerDB);
809 //-----------------------------------------------------------------------------
810 // code to print database content (for debugging)
811 //-----------------------------------------------------------------------------
815 itzam_btree_cursor cursor;
816 state = itzam_btree_cursor_create(&cursor, &btree);
817 if(state == ITZAM_OKAY)
819 printf("==> Database content ==> db size: %d\n", (int)itzam_btree_count(&btree));
822 // get the key pointed to by the cursor
823 state = itzam_btree_cursor_read(&cursor,(void *)&rec);
824 if (state == ITZAM_OKAY)
826 printf(" Key: %s \n ==> data: %s\n", rec.m_key, rec.m_data);
829 fprintf(stderr, "\nItzam problem: %s\n", STATE_MESSAGES[state]);
831 while (itzam_btree_cursor_next(&cursor));
833 state = itzam_btree_cursor_free(&cursor);
836 //-----------------------------------------------------------------------------