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} };
72 itzam_btree* pers_db_open(PersistenceInfo_s* info, const char* dbPath)
75 itzam_btree* btree = NULL;
77 // create array index: index is a combination of resource config table type and group
78 arrayIdx = info->configKey.storage + info->context.ldbid ;
80 //if(arrayIdx <= DbTableSize)
81 if(arrayIdx < DbTableSize)
83 if(gBtreeCreated[arrayIdx][info->configKey.policy] == 0)
85 itzam_state state = ITZAM_FAILED;
86 state = itzam_btree_open(&gBtree[arrayIdx][info->configKey.policy], dbPath,
87 itzam_comparator_string, error_handler, 0/*recover*/, 0/*read_only*/);
88 if (state != ITZAM_OKAY)
90 fprintf(stderr, "pers_db_open ==> Open Itzam problem: %s\n", STATE_MESSAGES[state]);
92 gBtreeCreated[arrayIdx][info->configKey.policy] = 1;
95 btree = &gBtree[arrayIdx][info->configKey.policy];
99 printf("btree_get ==> invalid storage type\n");
106 void pers_db_close(PersistenceInfo_s* info)
108 int arrayIdx = info->configKey.storage + info->context.ldbid;
110 if(info->configKey.storage <= PersistenceStorage_shared )
112 itzam_state state = ITZAM_FAILED;
113 state = itzam_btree_close(&gBtree[arrayIdx][info->configKey.policy]);
114 if (state != ITZAM_OKAY)
116 fprintf(stderr, "pers_db_close ==> Close Itzam problem: %s\n", STATE_MESSAGES[state]);
118 gBtreeCreated[arrayIdx][info->configKey.policy] = 0;
122 printf("pers_db_close ==> invalid storage type\n");
128 void pers_db_close_all()
132 for(i=0; i<DbTableSize; i++)
134 // close write cached database
135 if(gBtreeCreated[i][PersistencePolicy_wc] == 1)
137 itzam_state state = ITZAM_FAILED;
138 state = itzam_btree_close(&gBtree[i][PersistencePolicy_wc]);
139 if (state != ITZAM_OKAY)
141 fprintf(stderr, "pers_db_close_all ==> Close WC: Itzam problem: %s\n", STATE_MESSAGES[state]);
143 gBtreeCreated[i][PersistencePolicy_wc] = 0;
146 // close write through database
147 if(gBtreeCreated[i][PersistencePolicy_wt] == 1)
149 itzam_state state = ITZAM_FAILED;
150 state = itzam_btree_close(&gBtree[i][PersistencePolicy_wt]);
151 if (state != ITZAM_OKAY)
153 fprintf(stderr, "pers_db_close_all ==> Close WT: Itzam problem: %s\n", STATE_MESSAGES[state]);
155 gBtreeCreated[i][PersistencePolicy_wt] = 0;
162 int pers_db_read_key(char* dbPath, char* key, PersistenceInfo_s* info, unsigned char* buffer, unsigned int buffer_size)
166 if( PersistenceStorage_shared == info->configKey.storage
167 || PersistenceStorage_local == info->configKey.storage)
169 itzam_btree* btree = NULL;
170 itzam_state state = ITZAM_FAILED;
171 KeyValuePair_s search;
173 btree = pers_db_open(info, dbPath);
176 if(itzam_true == itzam_btree_find(btree, key, &search))
178 read_size = search.m_data_size;
179 if(read_size > buffer_size)
181 read_size = buffer_size; // truncate data size to buffer size
183 memcpy(buffer, search.m_data, read_size);
187 read_size = EPERS_NOKEY;
192 read_size = EPERS_NOPRCTABLE;
193 fprintf(stderr, "\npersistence_get_data ==> Open Itzam problem: %s\n", STATE_MESSAGES[state]);
196 else if(PersistenceStorage_custom == info->configKey.storage) // custom storage implementation via custom library
198 int idx = custom_client_name_to_id(dbPath, 1);
199 char workaroundPath[128]; // workaround, because /sys/ can not be accessed on host!!!!
200 snprintf(workaroundPath, 128, "%s%s", "/Data", dbPath );
202 if( (idx < PersCustomLib_LastEntry) && (gPersCustomFuncs[idx].custom_plugin_handle_get_data != NULL) )
204 if(info->configKey.customID[0] == '\0') // if we have not a customID we use the key
205 gPersCustomFuncs[idx].custom_plugin_get_data(key, (char*)buffer, buffer_size);
207 gPersCustomFuncs[idx].custom_plugin_get_data(info->configKey.customID, (char*)buffer, buffer_size);
211 read_size = EPERS_NOPLUGINFUNCT;
219 int pers_db_write_key(char* dbPath, char* key, PersistenceInfo_s* info, unsigned char* buffer, unsigned int buffer_size)
223 if( PersistenceStorage_local == info->configKey.storage
224 || PersistenceStorage_shared == info->configKey.storage )
226 write_size = buffer_size;
227 itzam_btree* btree = NULL;
228 itzam_state state = ITZAM_FAILED;
229 KeyValuePair_s insert;
231 btree = pers_db_open(info, dbPath);
235 keySize = (int)strlen((const char*)key);
236 if(keySize < DbKeySize)
239 dataSize = (int)strlen( (const char*)buffer);
240 if(dataSize < DbValueSize)
242 // -----------------------------------------------------------------------------
244 itzam_btree_transaction_start(btree);
247 memset(insert.m_key, 0, DbKeySize);
248 memcpy(insert.m_key, key, keySize);
249 if(itzam_true == itzam_btree_find(btree, key, &insert))
251 // key already available, so delete "old" key
252 state = itzam_btree_remove(btree, (const void *)&insert);
256 memset(insert.m_data, 0, DbValueSize);
257 memcpy(insert.m_data, buffer, dataSize);
260 insert.m_data_size = buffer_size;
262 state = itzam_btree_insert(btree,(const void *)&insert);
263 if (state != ITZAM_OKAY)
265 fprintf(stderr, "\npersistence_set_data ==> Insert Itzam problem: %s\n", STATE_MESSAGES[state]);
266 write_size = EPERS_DB_ERROR_INTERNAL;
270 itzam_btree_transaction_commit(btree);
272 // -----------------------------------------------------------------------------
274 if(PersistenceStorage_shared == info->configKey.storage)
276 // send changed notification
277 DBusMessage* message;
278 char ldbid_array[12];
281 const char* ldbid_ptr = ldbid_array;
282 const char* user_ptr = user_array;
283 const char* seat_ptr = seat_array;
285 memset(ldbid_array, 0, 12);
286 memset(user_array, 0, 12);
287 memset(seat_array, 0, 12);
289 // dbus_bus_add_match is used for the notification mechanism,
290 // and this works only for type DBUS_TYPE_STRING as message arguments
291 // this is the reason to use string instead of integer types directly
292 snprintf(ldbid_array, 12, "%d", info->context.ldbid);
293 snprintf(user_array, 12, "%d", info->context.user_no);
294 snprintf(seat_array, 12, "%d", info->context.seat_no);
296 message = dbus_message_new_signal("/org/genivi/persistence/adminconsumer", // const char *path,
297 "org.genivi.persistence.adminconsumer", // const char *interface,
298 "PersistenceValueChanged" ); // const char *name
300 dbus_message_append_args(message,
301 DBUS_TYPE_STRING, &key,
302 DBUS_TYPE_STRING, &ldbid_ptr,
303 DBUS_TYPE_STRING, &user_ptr,
304 DBUS_TYPE_STRING, &seat_ptr,
308 dbus_connection_send(get_dbus_connection(), message, NULL);
310 // Free the signal now we have finished with it
311 dbus_message_unref(message);
316 fprintf(stderr, "\npersistence_set_data ==> set_value_to_table_itzam => data to long » size %d | maxSize: %d\n", dataSize, DbKeySize);
317 write_size = EPERS_DB_VALUE_SIZE;
322 fprintf(stderr, "\nset_value_to_table_itzam => key to long » size: %d | maxSize: %d\n", keySize, DbKeySize);
323 write_size = EPERS_DB_KEY_SIZE;
328 write_size = EPERS_NOPRCTABLE;
329 fprintf(stderr, "\npersistence_set_data ==> Open Itzam problem: %s\n", STATE_MESSAGES[state]);
332 else if(PersistenceStorage_custom == info->configKey.storage) // custom storage implementation via custom library
334 int idx = custom_client_name_to_id(dbPath, 1);
335 if((idx < PersCustomLib_LastEntry) && (gPersCustomFuncs[idx].custom_plugin_handle_set_data) )
337 if(info->configKey.customID[0] == '\0') // if we have not a customID we use the key
338 gPersCustomFuncs[idx].custom_plugin_set_data(key, (char*)buffer, buffer_size);
340 gPersCustomFuncs[idx].custom_plugin_set_data(info->configKey.customID, (char*)buffer, buffer_size);
344 write_size = EPERS_NOPLUGINFUNCT;
352 int pers_db_get_key_size(char* dbPath, char* key, PersistenceInfo_s* info)
356 if( PersistenceStorage_shared == info->configKey.storage
357 || PersistenceStorage_local == info->configKey.storage)
360 itzam_btree* btree = NULL;
361 itzam_state state = ITZAM_FAILED;
362 KeyValuePair_s search;
364 btree = pers_db_open(info, dbPath);
367 keySize = (int)strlen((const char*)key);
368 if(keySize < DbKeySize)
370 memset(search.m_key,0, DbKeySize);
371 memcpy(search.m_key, key, keySize);
372 if(itzam_true == itzam_btree_find(btree, key, &search))
374 read_size = search.m_data_size;
378 read_size = EPERS_NOKEY;
383 fprintf(stderr, "persistence_get_data_size => key to long » size: %d | maxSize: %d\n", keySize, DbKeySize);
384 read_size = EPERS_DB_KEY_SIZE;
389 read_size = EPERS_NOPRCTABLE;
390 fprintf(stderr, "\npersistence_get_data_size ==> Open Itzam problem: %s\n", STATE_MESSAGES[state]);
393 else if(PersistenceStorage_custom == info->configKey.storage) // custom storage implementation via custom library
395 int idx = custom_client_name_to_id(dbPath, 1);
396 if((idx < PersCustomLib_LastEntry) && (gPersCustomFuncs[idx].custom_plugin_handle_set_data) )
398 if(info->configKey.customID[0] == '\0') // if we have not a customID we use the key
399 gPersCustomFuncs[idx].custom_plugin_get_size(key);
401 gPersCustomFuncs[idx].custom_plugin_get_size(info->configKey.customID);
405 read_size = EPERS_NOPLUGINFUNCT;
413 int pers_db_delete_key(char* dbPath, char* dbKey, PersistenceInfo_s* info)
416 if(PersistenceStorage_custom != info->configKey.storage)
418 itzam_btree* btree = NULL;
419 KeyValuePair_s delete;
421 //printf("delete_key_from_table_itzam => Path: \"%s\" | key: \"%s\" \n", dbPath, key);
422 btree = pers_db_open(info, dbPath);
426 keySize = (int)strlen((const char*)dbKey);
427 if(keySize < DbKeySize)
429 // -----------------------------------------------------------------------------
431 itzam_btree_transaction_start(btree);
435 memset(delete.m_key,0, DbKeySize);
436 memcpy(delete.m_key, dbKey, keySize);
437 state = itzam_btree_remove(btree, (const void *)&delete);
438 if (state != ITZAM_OKAY)
440 fprintf(stderr, "persistence_delete_data ==> Remove Itzam problem: %s\n", STATE_MESSAGES[state]);
441 ret = EPERS_DB_ERROR_INTERNAL;
443 itzam_btree_transaction_commit(btree);
445 // -----------------------------------------------------------------------------
449 fprintf(stderr, "persistence_delete_data => key to long » size: %d | maxSize: %d\n", keySize, DbKeySize);
450 ret = EPERS_DB_KEY_SIZE;
455 fprintf(stderr, "persistence_delete_data => no prct table\n");
456 ret = EPERS_NOPRCTABLE;
459 else // custom storage implementation via custom library
461 int idx = custom_client_name_to_id(dbPath, 1);
462 if((idx < PersCustomLib_LastEntry) && (gPersCustomFuncs[idx].custom_plugin_handle_set_data) )
464 if(info->configKey.customID[0] == '\0') // if we have not a customID we use the key
465 gPersCustomFuncs[idx].custom_plugin_delete_data(dbKey);
467 gPersCustomFuncs[idx].custom_plugin_delete_data(info->configKey.customID);
471 ret = EPERS_NOPLUGINFUNCT;
478 int persistence_reg_notify_on_change(char* dbPath, char* key, unsigned int ldbid, unsigned int user_no, unsigned int seat_no,
479 pclChangeNotifyCallback_t callback)
483 dbus_error_init (&error);
485 char ldbid_array[12];
489 memset(ldbid_array, 0, 12);
490 memset(user_array, 0, 12);
491 memset(seat_array, 0, 12);
494 gChangeNotifyCallback = callback;
496 // dbus_bus_add_match works only for type DBUS_TYPE_STRING as message arguments
497 // this is the reason to use string instead of integer types directly
498 snprintf(ldbid_array, 12, "%u", ldbid);
499 snprintf(user_array, 12, "%u", user_no);
500 snprintf(seat_array, 12, "%u", seat_no);
502 snprintf(rule, 256, "type='signal',interface='org.genivi.persistence.adminconsumer',member='PersistenceValueChanged',path='/org/genivi/persistence/adminconsumer',arg0='%s',arg1='%s',arg2='%s',arg3='%s'",
503 key, ldbid_array, user_array, seat_array);
505 dbus_bus_add_match(get_dbus_connection(), rule, &error);
512 //---------------------------------------------------------------------------------------------------------
513 // C U R S O R F U N C T I O N S
514 //---------------------------------------------------------------------------------------------------------
516 int get_cursor_handle()
520 if(pthread_mutex_lock(&gMtx) == 0)
522 if(gFreeCursorHandleIdxHead > 0) // check if we have a free spot in the array before the current max
524 handle = gFreeCursorHandleArray[--gFreeCursorHandleIdxHead];
528 if(gHandleIdx < MaxPersHandle-1)
530 handle = gHandleIdx++; // no free spot before current max, increment handle index
535 printf("get_persistence_handle_idx => Reached maximum of open handles: %d \n", MaxPersHandle);
538 pthread_mutex_unlock(&gMtx);
544 void close_cursor_handle(int handlerDB)
546 if(pthread_mutex_lock(&gMtx) == 0)
548 if(gFreeCursorHandleIdxHead < MaxPersHandle)
550 gFreeCursorHandleArray[gFreeCursorHandleIdxHead++] = handlerDB;
552 pthread_mutex_unlock(&gMtx);
558 int pers_db_cursor_create(char* dbPath)
561 itzam_state state = ITZAM_FAILED;
563 handle = get_cursor_handle();
565 if(handle < MaxPersHandle && handle >= 0)
568 state = itzam_btree_open(&gCursorArray[handle].m_btree, dbPath, itzam_comparator_string, error_handler, 1/*recover*/, 0/*read_only*/);
569 if (state != ITZAM_OKAY)
571 fprintf(stderr, "pers_db_open ==> Open Itzam problem: %s\n", STATE_MESSAGES[state]);
577 state = itzam_btree_cursor_create(&gCursorArray[handle].m_cursor, &gCursorArray[handle].m_btree);
578 if(state == ITZAM_OKAY)
580 gCursorArray[handle].m_empty = 0;
584 gCursorArray[handle].m_empty = 1;
593 int pers_db_cursor_next(unsigned int handlerDB)
596 //if(handlerDB < MaxPersHandle && handlerDB >= 0)
597 if(handlerDB < MaxPersHandle )
599 if(gCursorArray[handlerDB].m_empty != 1)
602 success = itzam_btree_cursor_next(&gCursorArray[handlerDB].m_cursor);
604 if(success == itzam_true)
610 rval = EPERS_LAST_ENTRY_IN_DB;
615 printf("persistence_db_cursor_get_key ==> invalid handle: %u \n", handlerDB);
620 printf("persistence_db_cursor_get_key ==> handle bigger than max » handleDB: %u | max: : %d \n", handlerDB, MaxPersHandle);
627 int pers_db_cursor_get_key(unsigned int handlerDB, char * bufKeyName_out, int bufSize)
630 KeyValuePair_s search;
632 if(handlerDB < MaxPersHandle)
634 if(gCursorArray[handlerDB].m_empty != 1)
637 itzam_btree_cursor_read(&gCursorArray[handlerDB].m_cursor ,(void *)&search);
638 length = strlen(search.m_key);
641 memcpy(bufKeyName_out, search.m_key, length);
646 printf("persistence_db_cursor_get_key ==> buffer to small » keySize: %d | bufSize: %d \n", length, bufSize);
651 printf("persistence_db_cursor_get_key ==> invalid handle: %u \n", handlerDB);
656 printf("persistence_db_cursor_get_key ==> handle bigger than max » handleDB: %u | max: : %d \n", handlerDB, MaxPersHandle);
663 int pers_db_cursor_get_data(unsigned int handlerDB, char * bufData_out, int bufSize)
666 KeyValuePair_s search;
668 if(handlerDB < MaxPersHandle)
670 if(gCursorArray[handlerDB].m_empty != 1)
673 itzam_btree_cursor_read(&gCursorArray[handlerDB].m_cursor ,(void *)&search);
675 length = strlen(search.m_data);
678 memcpy(bufData_out, search.m_data, length);
683 printf("persistence_db_cursor_get_data ==> buffer to small » keySize: %d | bufSize: %d \n", length, bufSize);
688 printf("persistence_db_cursor_get_data ==> invalid handle: %u \n", handlerDB);
693 printf("persistence_db_cursor_get_data ==> handle bigger than max » handleDB: %u | max: : %d \n", handlerDB, MaxPersHandle);
700 int pers_db_cursor_get_data_size(unsigned int handlerDB)
703 KeyValuePair_s search;
705 if(handlerDB < MaxPersHandle)
707 if(gCursorArray[handlerDB].m_empty != 1)
709 itzam_btree_cursor_read(&gCursorArray[handlerDB].m_cursor ,(void *)&search);
710 size = strlen(search.m_data);
714 printf("persistence_db_cursor_get_data ==> invalid handle: %u \n", handlerDB);
719 printf("persistence_db_cursor_get_data ==> handle bigger than max » handleDB: %u | max: : %d \n", handlerDB, MaxPersHandle);
726 int pers_db_cursor_destroy(unsigned int handlerDB)
729 if(handlerDB < MaxPersHandle)
731 itzam_btree_cursor_free(&gCursorArray[handlerDB].m_cursor);
732 gCursorArray[handlerDB].m_empty = 1;
734 itzam_state state = ITZAM_FAILED;
735 state = itzam_btree_close(&gCursorArray[handlerDB].m_btree);
736 if (state != ITZAM_OKAY)
738 fprintf(stderr, "pers_db_cursor_destroy ==> Close: Itzam problem: %s\n", STATE_MESSAGES[state]);
741 close_cursor_handle(handlerDB);
751 //-----------------------------------------------------------------------------
752 // code to print database content (for debugging)
753 //-----------------------------------------------------------------------------
757 itzam_btree_cursor cursor;
758 state = itzam_btree_cursor_create(&cursor, &btree);
759 if(state == ITZAM_OKAY)
761 printf("==> Database content ==> db size: %d\n", (int)itzam_btree_count(&btree));
764 // get the key pointed to by the cursor
765 state = itzam_btree_cursor_read(&cursor,(void *)&rec);
766 if (state == ITZAM_OKAY)
768 printf(" Key: %s \n ==> data: %s\n", rec.m_key, rec.m_data);
771 fprintf(stderr, "\nItzam problem: %s\n", STATE_MESSAGES[state]);
773 while (itzam_btree_cursor_next(&cursor));
775 state = itzam_btree_cursor_free(&cursor);
778 //-----------------------------------------------------------------------------