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;
191 // workaround till lifecycle is working correctly
197 read_size = EPERS_NOPRCTABLE;
198 fprintf(stderr, "\npersistence_get_data ==> Open Itzam problem: %s\n", STATE_MESSAGES[state]);
201 else if(PersistenceStorage_custom == info->configKey.storage) // custom storage implementation via custom library
203 int idx = custom_client_name_to_id(dbPath, 1);
204 char workaroundPath[128]; // workaround, because /sys/ can not be accessed on host!!!!
205 snprintf(workaroundPath, 128, "%s%s", "/Data", dbPath );
207 if( (idx < PersCustomLib_LastEntry) && (gPersCustomFuncs[idx].custom_plugin_handle_get_data != NULL) )
209 if(info->configKey.customID[0] == '\0') // if we have not a customID we use the key
210 gPersCustomFuncs[idx].custom_plugin_get_data(key, (char*)buffer, buffer_size);
212 gPersCustomFuncs[idx].custom_plugin_get_data(info->configKey.customID, (char*)buffer, buffer_size);
216 read_size = EPERS_NOPLUGINFUNCT;
224 int pers_db_write_key(char* dbPath, char* key, PersistenceInfo_s* info, unsigned char* buffer, unsigned int buffer_size)
228 if( PersistenceStorage_local == info->configKey.storage
229 || PersistenceStorage_shared == info->configKey.storage )
231 write_size = buffer_size;
232 itzam_btree* btree = NULL;
233 itzam_state state = ITZAM_FAILED;
234 KeyValuePair_s insert;
236 btree = pers_db_open(info, dbPath);
240 keySize = (int)strlen((const char*)key);
241 if(keySize < DbKeySize)
244 dataSize = (int)strlen( (const char*)buffer);
245 if(dataSize < DbValueSize)
248 memset(insert.m_key, 0, DbKeySize);
249 memcpy(insert.m_key, key, keySize);
250 if(itzam_true == itzam_btree_find(btree, key, &insert))
252 // key already available, so delete "old" key
253 state = itzam_btree_remove(btree, (const void *)&insert);
257 memset(insert.m_data, 0, DbValueSize);
258 memcpy(insert.m_data, buffer, dataSize);
261 insert.m_data_size = buffer_size;
263 state = itzam_btree_insert(btree,(const void *)&insert);
264 if (state != ITZAM_OKAY)
266 fprintf(stderr, "\npersistence_set_data ==> Insert Itzam problem: %s\n", STATE_MESSAGES[state]);
267 write_size = EPERS_DB_ERROR_INTERNAL;
270 if(PersistenceStorage_shared == info->configKey.storage)
272 // send changed notification
273 DBusMessage* message;
274 char ldbid_array[12];
277 const char* ldbid_ptr = ldbid_array;
278 const char* user_ptr = user_array;
279 const char* seat_ptr = seat_array;
281 memset(ldbid_array, 0, 12);
282 memset(user_array, 0, 12);
283 memset(seat_array, 0, 12);
285 // dbus_bus_add_match is used for the notification mechanism,
286 // and this works only for type DBUS_TYPE_STRING as message arguments
287 // this is the reason to use string instead of integer types directly
288 snprintf(ldbid_array, 12, "%d", info->context.ldbid);
289 snprintf(user_array, 12, "%d", info->context.user_no);
290 snprintf(seat_array, 12, "%d", info->context.seat_no);
292 message = dbus_message_new_signal("/org/genivi/persistence/adminconsumer", // const char *path,
293 "org.genivi.persistence.adminconsumer", // const char *interface,
294 "PersistenceValueChanged" ); // const char *name
296 dbus_message_append_args(message,
297 DBUS_TYPE_STRING, &key,
298 DBUS_TYPE_STRING, &ldbid_ptr,
299 DBUS_TYPE_STRING, &user_ptr,
300 DBUS_TYPE_STRING, &seat_ptr,
304 dbus_connection_send(get_dbus_connection(), message, NULL);
306 // Free the signal now we have finished with it
307 dbus_message_unref(message);
312 fprintf(stderr, "\npersistence_set_data ==> set_value_to_table_itzam => data to long » size %d | maxSize: %d\n", dataSize, DbKeySize);
313 write_size = EPERS_DB_VALUE_SIZE;
317 // workaround till lifecycle is working correctly
323 fprintf(stderr, "\nset_value_to_table_itzam => key to long » size: %d | maxSize: %d\n", keySize, DbKeySize);
324 write_size = EPERS_DB_KEY_SIZE;
329 write_size = EPERS_NOPRCTABLE;
330 fprintf(stderr, "\npersistence_set_data ==> Open Itzam problem: %s\n", STATE_MESSAGES[state]);
333 else if(PersistenceStorage_custom == info->configKey.storage) // custom storage implementation via custom library
335 int idx = custom_client_name_to_id(dbPath, 1);
336 if((idx < PersCustomLib_LastEntry) && (gPersCustomFuncs[idx].custom_plugin_handle_set_data) )
338 if(info->configKey.customID[0] == '\0') // if we have not a customID we use the key
339 gPersCustomFuncs[idx].custom_plugin_set_data(key, (char*)buffer, buffer_size);
341 gPersCustomFuncs[idx].custom_plugin_set_data(info->configKey.customID, (char*)buffer, buffer_size);
345 write_size = EPERS_NOPLUGINFUNCT;
353 int pers_db_get_key_size(char* dbPath, char* key, PersistenceInfo_s* info)
357 if( PersistenceStorage_shared == info->configKey.storage
358 || PersistenceStorage_local == info->configKey.storage)
361 itzam_btree* btree = NULL;
362 itzam_state state = ITZAM_FAILED;
363 KeyValuePair_s search;
365 btree = pers_db_open(info, dbPath);
368 keySize = (int)strlen((const char*)key);
369 if(keySize < DbKeySize)
371 memset(search.m_key,0, DbKeySize);
372 memcpy(search.m_key, key, keySize);
373 if(itzam_true == itzam_btree_find(btree, key, &search))
375 read_size = strlen(search.m_data);
379 read_size = EPERS_NOKEY;
384 fprintf(stderr, "persistence_get_data_size => key to long » size: %d | maxSize: %d\n", keySize, DbKeySize);
385 read_size = EPERS_DB_KEY_SIZE;
388 // workaround till lifecycle is working correctly
394 read_size = EPERS_NOPRCTABLE;
395 fprintf(stderr, "\npersistence_get_data_size ==> Open Itzam problem: %s\n", STATE_MESSAGES[state]);
398 else if(PersistenceStorage_custom == info->configKey.storage) // custom storage implementation via custom library
400 int idx = custom_client_name_to_id(dbPath, 1);
401 if((idx < PersCustomLib_LastEntry) && (gPersCustomFuncs[idx].custom_plugin_handle_set_data) )
403 if(info->configKey.customID[0] == '\0') // if we have not a customID we use the key
404 gPersCustomFuncs[idx].custom_plugin_get_size(key);
406 gPersCustomFuncs[idx].custom_plugin_get_size(info->configKey.customID);
410 read_size = EPERS_NOPLUGINFUNCT;
418 int pers_db_delete_key(char* dbPath, char* dbKey, PersistenceInfo_s* info)
421 if(PersistenceStorage_custom != info->configKey.storage)
423 itzam_btree* btree = NULL;
424 KeyValuePair_s delete;
426 //printf("delete_key_from_table_itzam => Path: \"%s\" | key: \"%s\" \n", dbPath, key);
427 btree = pers_db_open(info, dbPath);
431 keySize = (int)strlen((const char*)dbKey);
432 if(keySize < DbKeySize)
436 memset(delete.m_key,0, DbKeySize);
437 memcpy(delete.m_key, dbKey, keySize);
438 state = itzam_btree_remove(btree, (const void *)&delete);
439 if (state != ITZAM_OKAY)
441 fprintf(stderr, "persistence_delete_data ==> Remove Itzam problem: %s\n", STATE_MESSAGES[state]);
442 ret = EPERS_DB_ERROR_INTERNAL;
447 fprintf(stderr, "persistence_delete_data => key to long » size: %d | maxSize: %d\n", keySize, DbKeySize);
448 ret = EPERS_DB_KEY_SIZE;
451 // workaround till lifecycle is working correctly
457 fprintf(stderr, "persistence_delete_data => no prct table\n");
458 ret = EPERS_NOPRCTABLE;
461 else // custom storage implementation via custom library
463 int idx = custom_client_name_to_id(dbPath, 1);
464 if((idx < PersCustomLib_LastEntry) && (gPersCustomFuncs[idx].custom_plugin_handle_set_data) )
466 if(info->configKey.customID[0] == '\0') // if we have not a customID we use the key
467 gPersCustomFuncs[idx].custom_plugin_delete_data(dbKey);
469 gPersCustomFuncs[idx].custom_plugin_delete_data(info->configKey.customID);
473 ret = EPERS_NOPLUGINFUNCT;
480 int persistence_reg_notify_on_change(char* dbPath, char* key, unsigned int ldbid, unsigned int user_no, unsigned int seat_no,
481 changeNotifyCallback_t callback)
485 dbus_error_init (&error);
487 char ldbid_array[12];
491 memset(ldbid_array, 0, 12);
492 memset(user_array, 0, 12);
493 memset(seat_array, 0, 12);
496 gChangeNotifyCallback = callback;
498 // dbus_bus_add_match works only for type DBUS_TYPE_STRING as message arguments
499 // this is the reason to use string instead of integer types directly
500 snprintf(ldbid_array, 12, "%d", ldbid);
501 snprintf(user_array, 12, "%d", user_no);
502 snprintf(seat_array, 12, "%d", seat_no);
504 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'",
505 key, ldbid_array, user_array, seat_array);
507 dbus_bus_add_match(get_dbus_connection(), rule, &error);
514 //---------------------------------------------------------------------------------------------------------
515 // C U R S O R F U N C T I O N S
516 //---------------------------------------------------------------------------------------------------------
518 int get_cursor_handle()
522 if(pthread_mutex_lock(&gMtx) == 0)
524 if(gFreeCursorHandleIdxHead > 0) // check if we have a free spot in the array before the current max
526 handle = gFreeCursorHandleArray[--gFreeCursorHandleIdxHead];
530 if(gHandleIdx < MaxPersHandle-1)
532 handle = gHandleIdx++; // no free spot before current max, increment handle index
537 printf("get_persistence_handle_idx => Reached maximum of open handles: %d \n", MaxPersHandle);
540 pthread_mutex_unlock(&gMtx);
546 void close_cursor_handle(int handlerDB)
548 if(pthread_mutex_lock(&gMtx) == 0)
550 if(gFreeCursorHandleIdxHead < MaxPersHandle)
552 gFreeCursorHandleArray[gFreeCursorHandleIdxHead++] = handlerDB;
554 pthread_mutex_unlock(&gMtx);
560 int pers_db_cursor_create(char* dbPath)
563 itzam_state state = ITZAM_FAILED;
565 handle = get_cursor_handle();
567 if(handle < MaxPersHandle && handle >= 0)
570 state = itzam_btree_open(&gCursorArray[handle].m_btree, dbPath, itzam_comparator_string, error_handler, 0/*recover*/, 0/*read_only*/);
571 if (state != ITZAM_OKAY)
573 fprintf(stderr, "pers_db_open ==> Open Itzam problem: %s\n", STATE_MESSAGES[state]);
579 state = itzam_btree_cursor_create(&gCursorArray[handle].m_cursor, &gCursorArray[handle].m_btree);
580 if(state == ITZAM_OKAY)
582 gCursorArray[handle].m_empty = 0;
586 gCursorArray[handle].m_empty = 1;
595 int pers_db_cursor_next(unsigned int handlerDB)
598 //if(handlerDB < MaxPersHandle && handlerDB >= 0)
599 if(handlerDB < MaxPersHandle )
601 if(gCursorArray[handlerDB].m_empty != 1)
604 success = itzam_btree_cursor_next(&gCursorArray[handlerDB].m_cursor);
606 if(success == itzam_true)
612 rval = EPERS_LAST_ENTRY_IN_DB;
617 printf("persistence_db_cursor_get_key ==> invalid handle: %u \n", handlerDB);
622 printf("persistence_db_cursor_get_key ==> handle bigger than max » handleDB: %u | max: : %d \n", handlerDB, MaxPersHandle);
629 int pers_db_cursor_get_key(unsigned int handlerDB, char * bufKeyName_out, int bufSize)
632 KeyValuePair_s search;
634 if(handlerDB < MaxPersHandle)
636 if(gCursorArray[handlerDB].m_empty != 1)
639 itzam_btree_cursor_read(&gCursorArray[handlerDB].m_cursor ,(void *)&search);
640 length = strlen(search.m_key);
643 memcpy(bufKeyName_out, search.m_key, length);
648 printf("persistence_db_cursor_get_key ==> buffer to small » keySize: %d | bufSize: %d \n", length, bufSize);
653 printf("persistence_db_cursor_get_key ==> invalid handle: %u \n", handlerDB);
658 printf("persistence_db_cursor_get_key ==> handle bigger than max » handleDB: %u | max: : %d \n", handlerDB, MaxPersHandle);
665 int pers_db_cursor_get_data(unsigned int handlerDB, char * bufData_out, int bufSize)
668 KeyValuePair_s search;
670 if(handlerDB < MaxPersHandle)
672 if(gCursorArray[handlerDB].m_empty != 1)
675 itzam_btree_cursor_read(&gCursorArray[handlerDB].m_cursor ,(void *)&search);
677 length = strlen(search.m_data);
680 memcpy(bufData_out, search.m_data, length);
685 printf("persistence_db_cursor_get_data ==> buffer to small » keySize: %d | bufSize: %d \n", length, bufSize);
690 printf("persistence_db_cursor_get_data ==> invalid handle: %u \n", handlerDB);
695 printf("persistence_db_cursor_get_data ==> handle bigger than max » handleDB: %u | max: : %d \n", handlerDB, MaxPersHandle);
702 int pers_db_cursor_get_data_size(unsigned int handlerDB)
705 KeyValuePair_s search;
707 if(handlerDB < MaxPersHandle)
709 if(gCursorArray[handlerDB].m_empty != 1)
711 itzam_btree_cursor_read(&gCursorArray[handlerDB].m_cursor ,(void *)&search);
712 size = strlen(search.m_data);
716 printf("persistence_db_cursor_get_data ==> invalid handle: %u \n", handlerDB);
721 printf("persistence_db_cursor_get_data ==> handle bigger than max » handleDB: %u | max: : %d \n", handlerDB, MaxPersHandle);
728 int pers_db_cursor_destroy(unsigned int handlerDB)
731 if(handlerDB < MaxPersHandle)
733 itzam_btree_cursor_free(&gCursorArray[handlerDB].m_cursor);
734 gCursorArray[handlerDB].m_empty = 1;
736 itzam_state state = ITZAM_FAILED;
737 state = itzam_btree_close(&gCursorArray[handlerDB].m_btree);
738 if (state != ITZAM_OKAY)
740 fprintf(stderr, "pers_db_cursor_destroy ==> Close: Itzam problem: %s\n", STATE_MESSAGES[state]);
743 close_cursor_handle(handlerDB);
753 //-----------------------------------------------------------------------------
754 // code to print database content (for debugging)
755 //-----------------------------------------------------------------------------
759 itzam_btree_cursor cursor;
760 state = itzam_btree_cursor_create(&cursor, &btree);
761 if(state == ITZAM_OKAY)
763 printf("==> Database content ==> db size: %d\n", (int)itzam_btree_count(&btree));
766 // get the key pointed to by the cursor
767 state = itzam_btree_cursor_read(&cursor,(void *)&rec);
768 if (state == ITZAM_OKAY)
770 printf(" Key: %s \n ==> data: %s\n", rec.m_key, rec.m_data);
773 fprintf(stderr, "\nItzam problem: %s\n", STATE_MESSAGES[state]);
775 while (itzam_btree_cursor_next(&cursor));
777 state = itzam_btree_cursor_free(&cursor);
780 //-----------------------------------------------------------------------------