From a756aac7ed08de6ab876fbfe88a46b07360a8922 Mon Sep 17 00:00:00 2001 From: Ingo Huerner Date: Fri, 24 Jan 2014 14:33:15 +0100 Subject: [PATCH] File backups on seperate partition; Fixed bug 146; created performance benchmark; minor optimization; disabled PAS interface by default, to enable use configure --enable-pasinterface --- configure.ac | 20 + .../persistence_client_library_data_organization.h | 4 + .../persistence_client_library_db_access.h | 5 + src/persistence_client_library.c | 22 +- src/persistence_client_library_backup_filelist.c | 457 +++++++++++++++++++ src/persistence_client_library_backup_filelist.h | 68 +++ src/persistence_client_library_custom_loader.h | 4 +- src/persistence_client_library_data_organization.c | 39 +- src/persistence_client_library_db_access.c | 28 +- src/persistence_client_library_dbus_cmd.c | 23 +- src/persistence_client_library_dbus_service.c | 13 +- src/persistence_client_library_file.c | 493 +-------------------- src/persistence_client_library_key.c | 1 + src/persistence_client_library_prct_access.c | 2 +- src/persistence_client_library_prct_access.h | 4 +- test/Makefile.am | 5 + test/data/Data.tar.gz | Bin 49874 -> 56183 bytes test/persistence_admin_service_mockup.c | 39 +- test/persistence_client_library_benchmark.c | 389 ++++++++++++++++ test/persistence_client_library_test.c | 13 +- test/persistence_lifeCycle_mockup.c | 38 +- 21 files changed, 1134 insertions(+), 533 deletions(-) create mode 100644 test/persistence_client_library_benchmark.c diff --git a/configure.ac b/configure.ac index 27f7587..03af087 100644 --- a/configure.ac +++ b/configure.ac @@ -96,6 +96,26 @@ AC_MSG_NOTICE([Tests enabled: $enable_tests]) AC_MSG_NOTICE([Local check enabled: $localcheck]) +# enable persistence administration service dbus interface ########### +AC_ARG_ENABLE([pasinterface], + [AS_HELP_STRING([--enable-pasinterface],[Enable pas interface])], + [use_pasinterface=$enableval], + [use_pasinterface="no"]) + +AM_CONDITIONAL([USE_PASINTERFACE], [test x"$use_pasinterface" = "no"]) + +if test "$use_pasinterface" != "yes" -a "$use_pasinterface" != "no"; then + AC_MSG_ERROR([Invalid pas interface check mode specified: $use_pasinterface. Only "yes" or "no" is valid]) +else + AC_MSG_NOTICE([Use pas interface check is: $use_pasinterface]) + + if test "$use_pasinterface" = "yes"; then + AC_DEFINE_UNQUOTED([USE_PASINTERFACE], [1], [pasinterface enabled]) + fi +fi +###################################################################### + + AC_ARG_ENABLE(debug, AS_HELP_STRING([--enable-debug], [enable debugging, default: no]), diff --git a/include_protected/persistence_client_library_data_organization.h b/include_protected/persistence_client_library_data_organization.h index 2e6842c..17fa9f7 100644 --- a/include_protected/persistence_client_library_data_organization.h +++ b/include_protected/persistence_client_library_data_organization.h @@ -124,6 +124,10 @@ extern const char* gUser; /// directory structure seat name defintion extern const char* gSeat; +extern const char* gBackupPrefix; + +extern const int gCPathPrefixSize; +extern const int gWTPathPrefixSize; /// path prefix for local cached database: /Data/mnt_c// extern const char* gLocalCachePath; diff --git a/include_protected/persistence_client_library_db_access.h b/include_protected/persistence_client_library_db_access.h index feb7a8d..3c560f6 100644 --- a/include_protected/persistence_client_library_db_access.h +++ b/include_protected/persistence_client_library_db_access.h @@ -107,6 +107,11 @@ void pers_db_close(PersistenceInfo_s* info); void pers_db_close_all(); +/** + * @brief close all rct's + */ +void pers_rct_close_all(); + /** * @brief register or unregister for change notifications of a key diff --git a/src/persistence_client_library.c b/src/persistence_client_library.c index ac1c830..34c13ac 100644 --- a/src/persistence_client_library.c +++ b/src/persistence_client_library.c @@ -66,6 +66,15 @@ int pclInitLibrary(const char* appName, int shutdownMode) /// blacklist path environment variable const char *pBlacklistPath = getenv("PERS_BLACKLIST_PATH"); +#if USE_PASINTERFACE == 1 + //printf("* ADMIN INTERFACE is - e n a b l e d - \n"); + DLT_LOG(gDLTContext, DLT_LOG_INFO, DLT_STRING("PAS interface is enabled!!")); +#else + //printf("* ADMIN INTERFACE is - d i s a b l e d - enable with \"./configure --enable-pasinterface\"\n"); + DLT_LOG(gDLTContext, DLT_LOG_WARN, DLT_STRING("PAS interface is not enabled, enable with \"./configure --enable-pasinterface\"")); +#endif + + pthread_mutex_lock(&gDbusPendingRegMtx); // block until pending received if(pDataSize != NULL) @@ -90,6 +99,7 @@ int pclInitLibrary(const char* appName, int shutdownMode) return EPERS_DBUS_MAINLOOP; } + if(gShutdownMode != PCL_SHUTDOWN_TYPE_NONE) { // register for lifecycle and persistence admin service dbus messages @@ -100,13 +110,14 @@ int pclInitLibrary(const char* appName, int shutdownMode) return EPERS_REGISTER_LIFECYCLE; } } - +#if USE_PASINTERFACE == 1 if(register_pers_admin_service() == -1) { DLT_LOG(gDLTContext, DLT_LOG_ERROR, DLT_STRING("pclInitLibrary => Failed to register to pers admin dbus interface")); pthread_mutex_unlock(&gDbusPendingRegMtx); return EPERS_REGISTER_ADMIN; } +#endif /// get custom library names to load status = get_custom_libraries(); @@ -186,12 +197,13 @@ int pclDeinitLibrary(void) { DLT_LOG(gDLTContext, DLT_LOG_INFO, DLT_STRING("pclDeinitLibrary -> D E I N I T client library - "), DLT_STRING(gAppId), DLT_STRING("- init counter: "), DLT_INT(gPclInitialized)); - // unregister for lifecycle and persistence admin service dbus messages if(gShutdownMode != PCL_SHUTDOWN_TYPE_NONE) rval = unregister_lifecycle(gShutdownMode); +#if USE_PASINTERFACE == 1 rval = unregister_pers_admin_service(); +#endif // unload custom client libraries for(i=0; i= 24) + { + validPath = 0; + break; + } + } + else + { + break; + } + } + + if(validPath == 1) + { + char createPath[DbPathMaxLen] = {0}; + snprintf(createPath, DbPathMaxLen, "/%s",tokenArray[0] ); + for(i=1; i no valid path to create: "), DLT_STRING(path) ); + } + + return handle; +} + + +int pclVerifyConsistency(const char* origPath, const char* backupPath, const char* csumPath, int openFlags) +{ + int handle = 0, readSize = 0; + int backupAvail = 0, csumAvail = 0; + int fdCsum = 0, fdBackup = 0; + + char origCsumBuf[ChecksumBufSize] = {0}; + char backCsumBuf[ChecksumBufSize] = {0}; + char csumBuf[ChecksumBufSize] = {0}; + + // check if we have a backup and checksum file + backupAvail = access(backupPath, F_OK); + csumAvail = access(csumPath, F_OK); + + // ************************************************* + // there is a backup file and a checksum + // ************************************************* + if( (backupAvail == 0) && (csumAvail == 0) ) + { + DLT_LOG(gDLTContext, DLT_LOG_INFO, DLT_STRING("pclVerifyConsistency => there is a backup file AND a checksum")); + // calculate checksum form backup file + fdBackup = open(backupPath, O_RDONLY); + if(fdBackup != -1) + { + pclCalcCrc32Csum(fdBackup, backCsumBuf); + + fdCsum = open(csumPath, O_RDONLY); + if(fdCsum != -1) + { + readSize = read(fdCsum, csumBuf, ChecksumBufSize); + if(readSize > 0) + { + if(strcmp(csumBuf, backCsumBuf) == 0) + { + // checksum matches ==> replace with original file + handle = pclRecoverFromBackup(fdBackup, origPath); + } + else + { + // checksum does not match, check checksum with original file + handle = open(origPath, openFlags); + if(handle != -1) + { + pclCalcCrc32Csum(handle, origCsumBuf); + if(strcmp(csumBuf, origCsumBuf) != 0) + { + close(handle); + handle = -1; // error: file corrupt + } + // else case: checksum matches ==> keep original file ==> nothing to do + + } + else + { + close(handle); + handle = -1; // error: file corrupt + } + } + } + close(fdCsum); + } + else + { + close(fdCsum); + handle = -1; // error: file corrupt + } + } + else + { + handle = -1; + } + close(fdBackup); + } + // ************************************************* + // there is ONLY a checksum file + // ************************************************* + else if(csumAvail == 0) + { + DLT_LOG(gDLTContext, DLT_LOG_INFO, DLT_STRING("pclVerifyConsistency => there is ONLY a checksum file")); + + fdCsum = open(csumPath, O_RDONLY); + if(fdCsum != -1) + { + readSize = read(fdCsum, csumBuf, ChecksumBufSize); + if(readSize <= 0) + { + DLT_LOG(gDLTContext, DLT_LOG_ERROR, DLT_STRING("pclVerifyConsistency => read checksum: invalid readSize")); + } + close(fdCsum); + + // calculate the checksum form the original file to see if it matches + handle = open(origPath, openFlags); + if(handle != -1) + { + pclCalcCrc32Csum(handle, origCsumBuf); + + if(strcmp(csumBuf, origCsumBuf) != 0) + { + close(handle); + handle = -1; // checksum does NOT match ==> error: file corrupt + } + // else case: checksum matches ==> keep original file ==> nothing to do + } + else + { + close(handle); + handle = -1; // error: file corrupt + } + } + else + { + close(fdCsum); + handle = -1; // error: file corrupt + } + } + // ************************************************* + // there is ONLY a backup file + // ************************************************* + else if(backupAvail == 0) + { + DLT_LOG(gDLTContext, DLT_LOG_INFO, DLT_STRING("pclVerifyConsistency => there is ONLY a backup file")); + + // calculate checksum form backup file + fdBackup = open(backupPath, O_RDONLY); + if(fdBackup != -1) + { + pclCalcCrc32Csum(fdBackup, backCsumBuf); + close(fdBackup); + + // calculate the checksum form the original file to see if it matches + handle = open(origPath, openFlags); + if(handle != -1) + { + pclCalcCrc32Csum(handle, origCsumBuf); + + if(strcmp(backCsumBuf, origCsumBuf) != 0) + { + close(handle); + handle = -1; // checksum does NOT match ==> error: file corrupt + } + // else case: checksum matches ==> keep original file ==> nothing to do + + } + else + { + close(handle); + handle = -1; // error: file corrupt + } + } + else + { + close(fdBackup); + handle = -1; // error: file corrupt + } + } + // for else case: nothing to do + + + // if we are in an inconsistent state: delete file, backup and checksum + if(handle == -1) + { + remove(origPath); + remove(backupPath); + remove(csumPath); + } + + return handle; +} + + + +int pclRecoverFromBackup(int backupFd, const char* original) +{ + int handle = 0; + int readSize = 0; + char buffer[RDRWBufferSize]; + + handle = open(original, O_TRUNC | O_RDWR); + if(handle != -1) + { + // copy data from one file to another + while((readSize = read(backupFd, buffer, RDRWBufferSize)) > 0) + { + if(write(handle, buffer, readSize) != readSize) + { + DLT_LOG(gDLTContext, DLT_LOG_ERROR, DLT_STRING("pclRecoverFromBackup => couldn't write whole buffer")); + break; + } + } + + } + + return handle; +} + + + +int pclCreateBackup(const char* dstPath, int srcfd, const char* csumPath, const char* csumBuf) +{ + int dstFd = 0, csfd = 0; + int readSize = -1; + char buffer[RDRWBufferSize]; + + if(access(dstPath, F_OK) != 0) + { + char pathToCreate[DbPathMaxLen] = {0}; + strncpy(pathToCreate, dstPath, DbPathMaxLen); + pclCreateFileAndPath(pathToCreate); + } + + // create checksum file and and write checksum + csfd = open(csumPath, O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); + if(csfd != -1) + { + int csumSize = strlen(csumBuf); + if(write(csfd, csumBuf, csumSize) != csumSize) + { + DLT_LOG(gDLTContext, DLT_LOG_ERROR, DLT_STRING("pclCreateBackup => failed to write checksum to file")); + } + close(csfd); + } + else + { + DLT_LOG(gDLTContext, DLT_LOG_ERROR, DLT_STRING("pclCreateBackup => failed to create checksum file:"), DLT_STRING(strerror(errno)) ); + } + + // create backup file, user and group has read/write permission, others have read permission + dstFd = open(dstPath, O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); + if(dstFd != -1) + { + off_t curPos = 0; + // remember the current position + curPos = lseek(srcfd, 0, SEEK_CUR); + + // copy data from one file to another + while((readSize = read(srcfd, buffer, RDRWBufferSize)) > 0) + { + if(write(dstFd, buffer, readSize) != readSize) + { + DLT_LOG(gDLTContext, DLT_LOG_ERROR, DLT_STRING("pclCreateBackup => couldn't write whole buffer")); + break; + } + } + + if(readSize == -1) + DLT_LOG(gDLTContext, DLT_LOG_ERROR, DLT_STRING("pcl_create_backup => error copying file")); + + if((readSize = close(dstFd)) == -1) + DLT_LOG(gDLTContext, DLT_LOG_ERROR, DLT_STRING("pcl_create_backup => error closing fd")); + + // set back to the position + lseek(srcfd, curPos, SEEK_SET); + } + else + { + DLT_LOG(gDLTContext, DLT_LOG_ERROR, DLT_STRING("pclCreateBackup => failed to open backup file"), + DLT_STRING(dstPath), DLT_STRING(strerror(errno))); + } + + return readSize; +} + + + +int pclCalcCrc32Csum(int fd, char crc32sum[]) +{ + int rval = 1; + + if(crc32sum != 0) + { + char* buf; + struct stat statBuf; + + fstat(fd, &statBuf); + buf = malloc((unsigned int)statBuf.st_size); + + if(buf != 0) + { + off_t curPos = 0; + // remember the current position + curPos = lseek(fd, 0, SEEK_CUR); + + if(curPos != 0) + { + // set to beginning of the file + lseek(fd, 0, SEEK_SET); + } + + while((rval = read(fd, buf, statBuf.st_size)) > 0) + { + unsigned int crc = 0; + crc = pclCrc32(crc, (unsigned char*)buf, statBuf.st_size); + snprintf(crc32sum, ChecksumBufSize-1, "%x", crc); + } + + // set back to the position + lseek(fd, curPos, SEEK_SET); + + free(buf); + } + } + return rval; +} + + + +int pclBackupNeeded(const char* path) +{ + return need_backup_key(pclCrc32(0, (const unsigned char*)path, strlen(path))); +} + + + +int pclCreateFileAndPath(const char* path) +{ + const char* delimiters = "/\n"; // search for blank and end of line + char* tokenArray[24]; + char* thePath = (char*)path; + char createPath[DbPathMaxLen] = {0}; + int numTokens = 0, i = 0, validPath = 1; + int rval = -1; + + tokenArray[numTokens++] = strtok(thePath, delimiters); + while(tokenArray[numTokens-1] != NULL ) + { + tokenArray[numTokens] = strtok(NULL, delimiters); + if(tokenArray[numTokens] != NULL) + { + numTokens++; + if(numTokens >= 24) + { + validPath = 0; + break; + } + } + else + { + break; + } + } + + if(validPath == 1) + { + snprintf(createPath, DbPathMaxLen, "/%s",tokenArray[0] ); + for(i=1; i no valid path to create:"), DLT_STRING(path)); + } + + return rval; +} + + + +int pclGetPosixPermission(PersistencePermission_e permission) +{ + int posixPerm = 0; + + switch( (int)permission) + { + case PersistencePermission_ReadWrite: + posixPerm = O_RDWR; + break; + case PersistencePermission_ReadOnly: + posixPerm = O_RDONLY; + break; + case PersistencePermission_WriteOnly: + posixPerm = O_WRONLY; + break; + default: + posixPerm = O_RDONLY; + break; + } + + return posixPerm; +} + + + diff --git a/src/persistence_client_library_backup_filelist.h b/src/persistence_client_library_backup_filelist.h index d89181d..65a6a3c 100644 --- a/src/persistence_client_library_backup_filelist.h +++ b/src/persistence_client_library_backup_filelist.h @@ -30,7 +30,75 @@ int readBlacklistConfigFile(const char* filename); +/** + * @brief + * + * @param + * + * @return + */ int need_backup_key(unsigned int key); +/** + * @brief create the file + * + * @param + * + * @return + */ +int pclCreateFile(const char* path); + + +/** + * @brief create a backup of a file + * + * @param + * + * @return + */ +int pclCreateBackup(const char* srcPath, int srcfd, const char* csumPath, const char* csumBuf); + + +/** + * @brief recover file form backup + * + * @param + * + * @return + */ +int pclRecoverFromBackup(int backupFd, const char* original); + + +/** + * @brief calculate crc32 checksum + * + * @param + * + * @return + */ +int pclCalcCrc32Csum(int fd, char crc32sum[]); + + +/** + * @brief verify file for consistency + * + * @param + * + * @return + */ +int pclVerifyConsistency(const char* origPath, const char* backupPath, const char* csumPath, int openFlags); + + +/** + * @brief check if file needs to be backuped + * + * @param + * + * @return + */ +inline int pclBackupNeeded(const char* path); + + + #endif /* PERS_BACKUP_BLACKLIST_H */ diff --git a/src/persistence_client_library_custom_loader.h b/src/persistence_client_library_custom_loader.h index b1b90fe..ff92fcd 100644 --- a/src/persistence_client_library_custom_loader.h +++ b/src/persistence_client_library_custom_loader.h @@ -158,7 +158,7 @@ int load_all_custom_libraries(); * * @return the array position or -1 if the position can't be found */ -int check_valid_idx(int idx); +inline int check_valid_idx(int idx); @@ -167,7 +167,7 @@ int check_valid_idx(int idx); * * @return the name of the custom library ot NULL if invalid */ -char* get_custom_client_lib_name(int idx); +inline char* get_custom_client_lib_name(int idx); /** diff --git a/src/persistence_client_library_data_organization.c b/src/persistence_client_library_data_organization.c index 73d45cf..12d5eea 100644 --- a/src/persistence_client_library_data_organization.c +++ b/src/persistence_client_library_data_organization.c @@ -45,34 +45,47 @@ const char* gUser = "/user/"; const char* gSeat = "/seat/"; +/// cached path location +#define CACHEPREFIX "/Data/mnt-c/" +/// write through path location +#define WTPREFIX "/Data/mnt-wt/" + +/// size of cached path string +const int gCPathPrefixSize = sizeof(CACHEPREFIX)-1; +/// size of write through string +const int gWTPathPrefixSize = sizeof(WTPREFIX)-1; + +/// path for the backup location +const char* gBackupPrefix = "/Data/mnt-backup/"; + /// path prefix for local cached database: /Data/mnt_c// ( -const char* gLocalCachePath = "/Data/mnt-c/%s"; +const char* gLocalCachePath = CACHEPREFIX "%s"; /// path prefix for local write through database /Data/mnt_wt// -const char* gLocalWtPath = "/Data/mnt-wt/%s"; +const char* gLocalWtPath = WTPREFIX "%s"; /// path prefix for shared cached database: /Data/mnt_c/Shared/Group// -const char* gSharedCachePath = "/Data/mnt-c/%s/Shared_Group_%x"; +const char* gSharedCachePath = CACHEPREFIX "%s/Shared_Group_%x"; /// path prefix for shared write through database: /Data/mnt_wt/Shared/Group// -const char* gSharedWtPath = "/Data/mnt-wt/%s/Shared_Group_%x"; +const char* gSharedWtPath = WTPREFIX "%s/Shared_Group_%x"; /// path prefix for shared public cached database: /Data/mnt_c/Shared/Public// -const char* gSharedPublicCachePath = "/Data/mnt-c/%s/Shared_Public"; +const char* gSharedPublicCachePath = CACHEPREFIX "%s/Shared_Public"; /// path prefix for shared public write through database: /Data/mnt_wt/Shared/Public/ -const char* gSharedPublicWtPath = "/Data/mnt-wt/%s/Shared_Public"; +const char* gSharedPublicWtPath = WTPREFIX "%s/Shared_Public"; /// path prefix for local cached database: /Data/mnt_c// ( -const char* gLocalCachePathKey = "/Data/mnt-c/%s%s"; +const char* gLocalCachePathKey = CACHEPREFIX "%s%s"; /// path prefix for local write through database /Data/mnt_wt// -const char* gLocalWtPathKey = "/Data/mnt-wt/%s%s"; +const char* gLocalWtPathKey = WTPREFIX "%s%s"; /// path prefix for shared cached database: /Data/mnt_c/Shared/Group// -const char* gSharedCachePathKey = "/Data/mnt-c/%s/Shared_Group_%x%s"; +const char* gSharedCachePathKey = CACHEPREFIX "%s/Shared_Group_%x%s"; /// path prefix for shared write through database: /Data/mnt_wt/Shared/Group// -const char* gSharedWtPathKey = "/Data/mnt-wt/%s/Shared_Group_%x%s"; +const char* gSharedWtPathKey = WTPREFIX "%s/Shared_Group_%x%s"; /// path prefix for shared public cached database: /Data/mnt_c/Shared/Public// -const char* gSharedPublicCachePathKey = "/Data/mnt-c/%s/Shared_Public%s"; +const char* gSharedPublicCachePathKey = CACHEPREFIX "%s/Shared_Public%s"; /// path prefix for shared public write through database: /Data/mnt_wt/Shared/Public/ -const char* gSharedPublicWtPathKey = "/Data/mnt-wt/%s/Shared_Public%s"; +const char* gSharedPublicWtPathKey = WTPREFIX "%s/Shared_Public%s"; /// path prefix for local cached files: /Data/mnt_c//// -const char* gLocalCacheFilePath = "/Data/mnt-c/%s/user/%d/seat/%d/%s"; +const char* gLocalCacheFilePath = CACHEPREFIX "%s/user/%d/seat/%d/%s"; const char* gChangeSignal = "PersistenceResChange"; diff --git a/src/persistence_client_library_db_access.c b/src/persistence_client_library_db_access.c index e974198..8c10ae9 100644 --- a/src/persistence_client_library_db_access.c +++ b/src/persistence_client_library_db_access.c @@ -209,6 +209,29 @@ void pers_db_close_all() } +void pers_rct_close_all() +{ + int i = 0; + itzam_btree* resourceTable = NULL; + itzam_state state = ITZAM_FAILED; + + // close open persistence resource configuration table + for(i=0; i< PrctDbTableSize; i++) + { + resourceTable = (itzam_btree*)get_resource_cfg_table_by_idx(i); + // dereference opend database + if(resourceTable != NULL && get_resource_cfg_table_status(i) == 1) + { + state = itzam_btree_close(resourceTable); + invalidate_resource_cfg_table(i); + if (state != ITZAM_OKAY) + { + DLT_LOG(gDLTContext, DLT_LOG_ERROR, DLT_STRING("process_prepare_shutdown => itzam_btree_close: Itzam problem"), DLT_STRING(STATE_MESSAGES[state])); + } + } + } +} + int pers_db_read_key(char* dbPath, char* key, PersistenceInfo_s* info, unsigned char* buffer, unsigned int buffer_size) { int read_size = -1; @@ -766,7 +789,8 @@ int pers_db_cursor_create(char* dbPath) int pers_db_cursor_next(unsigned int handlerDB) { - int rval = -1; + int rval = -99; + //if(handlerDB < MaxPersHandle && handlerDB >= 0) if(handlerDB < MaxPersHandle ) { @@ -786,11 +810,13 @@ int pers_db_cursor_next(unsigned int handlerDB) } else { + printf("Invalid handle\n"); DLT_LOG(gDLTContext, DLT_LOG_ERROR, DLT_STRING("pers_db_cursor_next ==> invalid handle: "), DLT_INT(handlerDB)); } } else { + printf("Handle bigger than max\n"); DLT_LOG(gDLTContext, DLT_LOG_ERROR, DLT_STRING("pers_db_cursor_next ==> handle bigger than max:"), DLT_INT(MaxPersHandle)); } return rval; diff --git a/src/persistence_client_library_dbus_cmd.c b/src/persistence_client_library_dbus_cmd.c index c3f69af..b9a99b4 100644 --- a/src/persistence_client_library_dbus_cmd.c +++ b/src/persistence_client_library_dbus_cmd.c @@ -170,8 +170,6 @@ void process_block_and_write_data_back(unsigned int requestID, unsigned int stat void process_prepare_shutdown(unsigned char requestId, unsigned int status) { int i = 0; - itzam_btree* resourceTable = NULL; - itzam_state state = ITZAM_FAILED; // block write pers_lock_access(); @@ -187,23 +185,10 @@ void process_prepare_shutdown(unsigned char requestId, unsigned int status) } } - // close open gvdb persistence resource configuration table - for(i=0; i< PrctDbTableSize; i++) - { - resourceTable = get_resource_cfg_table_by_idx(i); - // dereference opend database - if(resourceTable != NULL && get_resource_cfg_table_status(i) == 1) - { - state = itzam_btree_close(resourceTable); - invalidate_resource_cfg_table(i); - if (state != ITZAM_OKAY) - { - DLT_LOG(gDLTContext, DLT_LOG_ERROR, DLT_STRING("process_prepare_shutdown => itzam_btree_close: Itzam problem"), DLT_STRING(STATE_MESSAGES[state])); - } - } - } + // close all opend rct + pers_rct_close_all(); - //close opend database + // close opend database pers_db_close_all(); @@ -305,7 +290,7 @@ void process_send_pas_register(DBusConnection* conn, int regType, int notificati if(!dbus_pending_call_set_notify(pending, msg_pending_func, method, NULL)) { - printf("process_send_pas_register => dbus_pending_call_set_notify: FAILED\n"); + DLT_LOG(gDLTContext, DLT_LOG_ERROR, DLT_STRING("process_send_pas_register => dbus_pending_call_set_notify: FAILED\n") ); } dbus_pending_call_unref(pending); } diff --git a/src/persistence_client_library_dbus_service.c b/src/persistence_client_library_dbus_service.c index 486b04a..ae45186 100644 --- a/src/persistence_client_library_dbus_service.c +++ b/src/persistence_client_library_dbus_service.c @@ -243,6 +243,8 @@ void* run_mainloop(void* dataPtr) // setup the dbus mainLoop(vtablePersAdmin, vtableLifecycle, vtableFallback, dataPtr); + printf("<== run_mainloop\n"); + return NULL; } @@ -501,8 +503,10 @@ int mainLoop(DBusObjectPathVTable vtable, DBusObjectPathVTable vtable2, dbus_bus_add_match(conn, "type='signal',interface='org.genivi.persistence.admin',member='PersistenceModeChanged',path='/org/genivi/persistence/admin'", &err); // register for messages - if ( (TRUE==dbus_connection_register_object_path(conn, "/org/genivi/persistence/adminconsumer", &vtable, userData)) - && (TRUE==dbus_connection_register_object_path(conn, "/org/genivi/NodeStateManager/LifeCycleConsumer", &vtable2, userData)) + if ( (TRUE==dbus_connection_register_object_path(conn, "/org/genivi/NodeStateManager/LifeCycleConsumer", &vtable2, userData)) +#if USE_PASINTERFACE == 1 + && (TRUE==dbus_connection_register_object_path(conn, "/org/genivi/persistence/adminconsumer", &vtable, userData)) +#endif && (TRUE==dbus_connection_register_fallback(conn, "/", &vtableFallback, userData)) ) { if( (TRUE!=dbus_connection_set_watch_functions(conn, addWatch, removeWatch, watchToggled, NULL, NULL)) @@ -593,7 +597,7 @@ int mainLoop(DBusObjectPathVTable vtable, DBusObjectPathVTable vtable2, process_send_lifecycle_register(conn, (buf[1]), buf[2]); break; case CMD_QUIT: - bContinue = FALSE; + bContinue = 0; break; // ****************************************************** @@ -643,7 +647,9 @@ int mainLoop(DBusObjectPathVTable vtable, DBusObjectPathVTable vtable2, } while (0!=bContinue); } +#if USE_PASINTERFACE == 1 dbus_connection_unregister_object_path(conn, "/org/genivi/persistence/adminconsumer"); +#endif dbus_connection_unregister_object_path(conn, "/org/genivi/NodeStateManager/LifeCycleConsumer"); dbus_connection_unregister_object_path(conn, "/"); } @@ -656,6 +662,7 @@ int mainLoop(DBusObjectPathVTable vtable, DBusObjectPathVTable vtable2, pthread_cond_signal(&gDbusInitializedCond); pthread_mutex_unlock(&gDbusInitializedMtx); + printf("End Mainloop\n"); return 0; } diff --git a/src/persistence_client_library_file.c b/src/persistence_client_library_file.c index fea9c68..d73fe12 100644 --- a/src/persistence_client_library_file.c +++ b/src/persistence_client_library_file.c @@ -38,21 +38,6 @@ #include -// header prototype definition of internal functions -int pclCreateFile(const char* path); - -int pclCreateBackup(const char* srcPath, int srcfd, const char* csumPath, const char* csumBuf); - -int pclRecoverFromBackup(int backupFd, const char* original); - -int pclCalcCrc32Csum(int fd, char crc32sum[]); - -int pclVerifyConsistency(const char* origPath, const char* backupPath, const char* csumPath, int openFlags); - -int pclBackupNeeded(const char* path); -//------------------------------------------------------------- - - int pclFileClose(int fd) { @@ -160,6 +145,23 @@ int pclFileOpen(unsigned int ldbid, const char* resource_id, unsigned int user_n if(dbContext.configKey.type == PersistenceResourceType_file) // check if the resource is really a file { + // create backup path + int length = 0; + char fileSubPath[DbPathMaxLen] = {0}; + + if(dbContext.configKey.policy == PersistencePolicy_wc) + { + length = gCPathPrefixSize; + } + else + { + length = gWTPathPrefixSize; + } + + strncpy(fileSubPath, dbPath+length, DbPathMaxLen); + snprintf(backupPath, DbPathMaxLen, "%s%s", gBackupPrefix, fileSubPath); + snprintf(csumPath, DbPathMaxLen, "%s%s%s", gBackupPrefix, fileSubPath, ".crc"); + if(shared_DB >= 0) // check valid database context { int flags = pclGetPosixPermission(dbContext.configKey.permission); @@ -168,9 +170,6 @@ int pclFileOpen(unsigned int ldbid, const char* resource_id, unsigned int user_n if( (dbContext.configKey.permission != PersistencePermission_ReadOnly) && pclBackupNeeded(dbPath) ) { - snprintf(backupPath, DbPathMaxLen, "%s%s", dbPath, "~"); - snprintf(csumPath, DbPathMaxLen, "%s%s", dbPath, "~.crc"); - if((handle = pclVerifyConsistency(dbPath, backupPath, csumPath, flags)) == -1) { DLT_LOG(gDLTContext, DLT_LOG_ERROR, DLT_STRING("pclFileOpen: error => file inconsistent, recovery N O T possible!")); @@ -194,7 +193,6 @@ int pclFileOpen(unsigned int ldbid, const char* resource_id, unsigned int user_n { strcpy(gFileHandleArray[handle].backupPath, backupPath); strcpy(gFileHandleArray[handle].csumPath, csumPath); - gFileHandleArray[handle].backupCreated = 0; gFileHandleArray[handle].permission = dbContext.configKey.permission; } @@ -220,10 +218,8 @@ int pclFileOpen(unsigned int ldbid, const char* resource_id, unsigned int user_n { if(handle < MaxPersHandle) { - snprintf(backupPath, DbPathMaxLen, "%s%s", dbPath, "~"); - snprintf(csumPath, DbPathMaxLen, "%s%s", dbPath, "~.crc"); - __sync_fetch_and_add(&gOpenFdArray[handle], FileOpen); // set open flag + strcpy(gFileHandleArray[handle].backupPath, backupPath); strcpy(gFileHandleArray[handle].csumPath, csumPath); gFileHandleArray[handle].backupCreated = 0; @@ -378,7 +374,6 @@ int pclFileWriteData(int fd, const void * buffer, int buffer_size) // calculate checksum pclCalcCrc32Csum(fd, csumBuf); - // create checksum and backup file pclCreateBackup(gFileHandleArray[fd].backupPath, fd, gFileHandleArray[fd].csumPath, csumBuf); @@ -562,455 +557,3 @@ int pclFileReleasePath(int pathHandle) -/**************************************************************************************** - * Functions to create backup files - ****************************************************************************************/ - -int pclCreateFile(const char* path) -{ - const char* delimiters = "/\n"; // search for blank and end of line - char* tokenArray[24]; - char* thePath = (char*)path; - int numTokens = 0, i = 0, validPath = 1; - int handle = 0; - - tokenArray[numTokens++] = strtok(thePath, delimiters); - while(tokenArray[numTokens-1] != NULL ) - { - tokenArray[numTokens] = strtok(NULL, delimiters); - if(tokenArray[numTokens] != NULL) - { - numTokens++; - if(numTokens >= 24) - { - validPath = 0; - break; - } - } - else - { - break; - } - } - - if(validPath == 1) - { - char createPath[DbPathMaxLen] = {0}; - snprintf(createPath, DbPathMaxLen, "/%s",tokenArray[0] ); - for(i=1; i no valid path to create: "), DLT_STRING(path) ); - } - - return handle; -} - - -int pclVerifyConsistency(const char* origPath, const char* backupPath, const char* csumPath, int openFlags) -{ - int handle = 0, readSize = 0; - int backupAvail = 0, csumAvail = 0; - int fdCsum = 0, fdBackup = 0; - - char origCsumBuf[ChecksumBufSize] = {0}; - char backCsumBuf[ChecksumBufSize] = {0}; - char csumBuf[ChecksumBufSize] = {0}; - - // check if we have a backup and checksum file - backupAvail = access(backupPath, F_OK); - csumAvail = access(csumPath, F_OK); - - // ************************************************* - // there is a backup file and a checksum - // ************************************************* - if( (backupAvail == 0) && (csumAvail == 0) ) - { - DLT_LOG(gDLTContext, DLT_LOG_INFO, DLT_STRING("pclVerifyConsistency => there is a backup file AND a checksum")); - // calculate checksum form backup file - fdBackup = open(backupPath, O_RDONLY); - if(fdBackup != -1) - { - pclCalcCrc32Csum(fdBackup, backCsumBuf); - - fdCsum = open(csumPath, O_RDONLY); - if(fdCsum != -1) - { - readSize = read(fdCsum, csumBuf, ChecksumBufSize); - if(readSize > 0) - { - if(strcmp(csumBuf, backCsumBuf) == 0) - { - // checksum matches ==> replace with original file - handle = pclRecoverFromBackup(fdBackup, origPath); - } - else - { - // checksum does not match, check checksum with original file - handle = open(origPath, openFlags); - if(handle != -1) - { - pclCalcCrc32Csum(handle, origCsumBuf); - if(strcmp(csumBuf, origCsumBuf) != 0) - { - close(handle); - handle = -1; // error: file corrupt - } - // else case: checksum matches ==> keep original file ==> nothing to do - - } - else - { - close(handle); - handle = -1; // error: file corrupt - } - } - } - close(fdCsum); - } - else - { - close(fdCsum); - handle = -1; // error: file corrupt - } - } - else - { - handle = -1; - } - close(fdBackup); - } - // ************************************************* - // there is ONLY a checksum file - // ************************************************* - else if(csumAvail == 0) - { - DLT_LOG(gDLTContext, DLT_LOG_INFO, DLT_STRING("pclVerifyConsistency => there is ONLY a checksum file")); - - fdCsum = open(csumPath, O_RDONLY); - if(fdCsum != -1) - { - readSize = read(fdCsum, csumBuf, ChecksumBufSize); - if(readSize <= 0) - { - DLT_LOG(gDLTContext, DLT_LOG_ERROR, DLT_STRING("pclVerifyConsistency => read checksum: invalid readSize")); - } - close(fdCsum); - - // calculate the checksum form the original file to see if it matches - handle = open(origPath, openFlags); - if(handle != -1) - { - pclCalcCrc32Csum(handle, origCsumBuf); - - if(strcmp(csumBuf, origCsumBuf) != 0) - { - close(handle); - handle = -1; // checksum does NOT match ==> error: file corrupt - } - // else case: checksum matches ==> keep original file ==> nothing to do - } - else - { - close(handle); - handle = -1; // error: file corrupt - } - } - else - { - close(fdCsum); - handle = -1; // error: file corrupt - } - } - // ************************************************* - // there is ONLY a backup file - // ************************************************* - else if(backupAvail == 0) - { - DLT_LOG(gDLTContext, DLT_LOG_INFO, DLT_STRING("pclVerifyConsistency => there is ONLY a backup file")); - - // calculate checksum form backup file - fdBackup = open(backupPath, O_RDONLY); - if(fdBackup != -1) - { - pclCalcCrc32Csum(fdBackup, backCsumBuf); - close(fdBackup); - - // calculate the checksum form the original file to see if it matches - handle = open(origPath, openFlags); - if(handle != -1) - { - pclCalcCrc32Csum(handle, origCsumBuf); - - if(strcmp(backCsumBuf, origCsumBuf) != 0) - { - close(handle); - handle = -1; // checksum does NOT match ==> error: file corrupt - } - // else case: checksum matches ==> keep original file ==> nothing to do - - } - else - { - close(handle); - handle = -1; // error: file corrupt - } - } - else - { - close(fdBackup); - handle = -1; // error: file corrupt - } - } - // for else case: nothing to do - - - // if we are in an inconsistent state: delete file, backup and checksum - if(handle == -1) - { - remove(origPath); - remove(backupPath); - remove(csumPath); - } - - return handle; -} - - - -int pclRecoverFromBackup(int backupFd, const char* original) -{ - int handle = 0; - int readSize = 0; - char buffer[RDRWBufferSize]; - - handle = open(original, O_TRUNC | O_RDWR); - if(handle != -1) - { - // copy data from one file to another - while((readSize = read(backupFd, buffer, RDRWBufferSize)) > 0) - { - if(write(handle, buffer, readSize) != readSize) - { - DLT_LOG(gDLTContext, DLT_LOG_ERROR, DLT_STRING("pclRecoverFromBackup => couldn't write whole buffer")); - break; - } - } - - } - - return handle; -} - - - -int pclCreateBackup(const char* dstPath, int srcfd, const char* csumPath, const char* csumBuf) -{ - int dstFd = 0, csfd = 0; - int readSize = -1; - char buffer[RDRWBufferSize]; - - // create checksum file and and write checksum - csfd = open(csumPath, O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); - if(csfd != -1) - { - int csumSize = strlen(csumBuf); - if(write(csfd, csumBuf, csumSize) != csumSize) - { - DLT_LOG(gDLTContext, DLT_LOG_ERROR, DLT_STRING("pclCreateBackup => failed to write checksum to file")); - } - close(csfd); - } - else - { - DLT_LOG(gDLTContext, DLT_LOG_ERROR, DLT_STRING("pclCreateBackup => failed to create checksum file:"), DLT_STRING(strerror(errno)) ); - } - - - // create backup file, user and group has read/write permission, others have read permission - dstFd = open(dstPath, O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); - if(dstFd != -1) - { - off_t curPos = 0; - // remember the current position - curPos = lseek(srcfd, 0, SEEK_CUR); - - // copy data from one file to another - while((readSize = read(srcfd, buffer, RDRWBufferSize)) > 0) - { - if(write(dstFd, buffer, readSize) != readSize) - { - DLT_LOG(gDLTContext, DLT_LOG_ERROR, DLT_STRING("pclCreateBackup => couldn't write whole buffer")); - break; - } - } - - if(readSize == -1) - DLT_LOG(gDLTContext, DLT_LOG_ERROR, DLT_STRING("pcl_create_backup => error copying file")); - - if((readSize = close(dstFd)) == -1) - DLT_LOG(gDLTContext, DLT_LOG_ERROR, DLT_STRING("pcl_create_backup => error closing fd")); - - // set back to the position - lseek(srcfd, curPos, SEEK_SET); - } - else - { - DLT_LOG(gDLTContext, DLT_LOG_ERROR, DLT_STRING("pclCreateBackup => failed to open backup file"), - DLT_STRING(dstPath), DLT_STRING(strerror(errno))); - } - - return readSize; -} - - - -int pclCalcCrc32Csum(int fd, char crc32sum[]) -{ - int rval = 1; - - if(crc32sum != 0) - { - char* buf; - struct stat statBuf; - - fstat(fd, &statBuf); - buf = malloc((unsigned int)statBuf.st_size); - - if(buf != 0) - { - off_t curPos = 0; - // remember the current position - curPos = lseek(fd, 0, SEEK_CUR); - - if(curPos != 0) - { - // set to beginning of the file - lseek(fd, 0, SEEK_SET); - } - - while((rval = read(fd, buf, statBuf.st_size)) > 0) - { - unsigned int crc = 0; - crc = pclCrc32(crc, (unsigned char*)buf, statBuf.st_size); - snprintf(crc32sum, ChecksumBufSize-1, "%x", crc); - } - - // set back to the position - lseek(fd, curPos, SEEK_SET); - - free(buf); - } - } - return rval; -} - - - -int pclBackupNeeded(const char* path) -{ - return need_backup_key(pclCrc32(0, (const unsigned char*)path, strlen(path))); -} - - - -int pclCreateFileAndPath(const char* path) -{ - const char* delimiters = "/\n"; // search for blank and end of line - char* tokenArray[24]; - char* thePath = (char*)path; - char createPath[DbPathMaxLen] = {0}; - int numTokens = 0, i = 0, validPath = 1; - int rval = -1; - - tokenArray[numTokens++] = strtok(thePath, delimiters); - while(tokenArray[numTokens-1] != NULL ) - { - tokenArray[numTokens] = strtok(NULL, delimiters); - if(tokenArray[numTokens] != NULL) - { - numTokens++; - if(numTokens >= 24) - { - validPath = 0; - break; - } - } - else - { - break; - } - } - - if(validPath == 1) - { - snprintf(createPath, DbPathMaxLen, "/%s",tokenArray[0] ); - for(i=1; i no valid path to create:"), DLT_STRING(path)); - } - - return rval; -} - - - -int pclGetPosixPermission(PersistencePermission_e permission) -{ - int posixPerm = 0; - - switch(permission) - { - case PersistencePermission_ReadWrite: - posixPerm = O_RDWR; - break; - case PersistencePermission_ReadOnly: - posixPerm = O_RDONLY; - break; - case PersistencePermission_WriteOnly: - posixPerm = O_WRONLY; - break; - default: - posixPerm = O_RDONLY; - break; - } - - return posixPerm; -} - diff --git a/src/persistence_client_library_key.c b/src/persistence_client_library_key.c index 6f42dcc..a080a1e 100644 --- a/src/persistence_client_library_key.c +++ b/src/persistence_client_library_key.c @@ -134,6 +134,7 @@ int pclKeyHandleClose(int key_handle) else { set_persistence_handle_close_idx(key_handle); + rval = 1; } // invalidate entries diff --git a/src/persistence_client_library_prct_access.c b/src/persistence_client_library_prct_access.c index b27c125..6dac0e1 100644 --- a/src/persistence_client_library_prct_access.c +++ b/src/persistence_client_library_prct_access.c @@ -192,7 +192,7 @@ int get_db_context(PersistenceInfo_s* dbContext, const char* resource_id, unsign // dbContext->configKey.policy = PersistencePolicy_wc; dbContext->configKey.storage = PersistenceStorage_local; - dbContext->configKey.permission = O_RDWR; + dbContext->configKey.permission = PersistencePermission_ReadWrite; dbContext->configKey.max_size = defaultMaxKeyValDataSize; if(isFile == PersistenceResourceType_file) { diff --git a/src/persistence_client_library_prct_access.h b/src/persistence_client_library_prct_access.h index 44982f3..a14ebdc 100644 --- a/src/persistence_client_library_prct_access.h +++ b/src/persistence_client_library_prct_access.h @@ -79,7 +79,7 @@ itzam_btree* get_resource_cfg_table_by_idx(int i); * * @return status of database, 1 is db is opened and 0 is closed */ -int get_resource_cfg_table_status(int i); +inline int get_resource_cfg_table_status(int i); @@ -88,7 +88,7 @@ int get_resource_cfg_table_status(int i); * * @param i the index */ -void invalidate_resource_cfg_table(int i); +inline void invalidate_resource_cfg_table(int i); diff --git a/test/Makefile.am b/test/Makefile.am index fb7e5f7..da7b652 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -9,6 +9,7 @@ endif noinst_PROGRAMS = persistence_client_library_test \ persistence_client_library_dbus_test \ + persistence_client_library_benchmark \ persistence_admin_service_mockup \ persistence_lifeCycle_mockup @@ -16,6 +17,10 @@ persistence_client_library_dbus_test_SOURCES = persistence_client_library_dbus_t persistence_client_library_dbus_test_LDADD = $(DEPS_LIBS) \ $(top_srcdir)/src/libpersistence_client_library.la +persistence_client_library_benchmark_SOURCES = persistence_client_library_benchmark.c +persistence_client_library_benchmark_LDADD = $(DEPS_LIBS) \ + $(top_srcdir)/src/libpersistence_client_library.la + persistence_client_library_test_SOURCES = persistence_client_library_test.c persistence_client_library_test_LDADD = $(DEPS_LIBS) $(CHECK_LIBS) \ $(top_srcdir)/src/libpersistence_client_library.la diff --git a/test/data/Data.tar.gz b/test/data/Data.tar.gz index 180fbc28e7f0ef9716874df81cc911173e407889..93e23cd7215eba866a289c5dd2743ae9ba9857a8 100644 GIT binary patch literal 56183 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 diff --git a/test/persistence_admin_service_mockup.c b/test/persistence_admin_service_mockup.c index ea7c644..c21adb3 100644 --- a/test/persistence_admin_service_mockup.c +++ b/test/persistence_admin_service_mockup.c @@ -109,11 +109,41 @@ static int endLoop = 0; void sigHandler(int signo) { - endLoop = 1; + switch(signo) + { + case SIGHUP: + // noting to do + printf("* * * * S I G H U P * * * *\n"); + break; + default: + endLoop = 1; + break; + } } //------------------------------------------------------------------------ +static int setupSignalHandler(const int nSignal, void (*pHandler)(int)) +{ + struct sigaction sa_old; + int ret = sigaction(nSignal, NULL, &sa_old); + if (0==ret) + { + if (pHandler!=sa_old.sa_handler) + { + /* setup own signal handler */ + struct sigaction sa_new; + memset(&sa_new, 0, sizeof(sa_new)); + sa_new.sa_handler = pHandler; + sa_new.sa_flags = 0; + ret = sigaction(nSignal, &sa_new, 0); + } + } + + return ret; +} + + #define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0])) @@ -322,9 +352,10 @@ int mainLoop(DBusObjectPathVTable vtable, DBusObjectPathVTable vtableFallback, v // lock mutex to make sure dbus main loop is running pthread_mutex_lock(&gDbusInitializedMtx); - signal(SIGTERM, sigHandler); - signal(SIGQUIT, sigHandler); - signal(SIGINT, sigHandler); + setupSignalHandler(SIGTERM, sigHandler); + setupSignalHandler(SIGQUIT, sigHandler); + setupSignalHandler(SIGINT, sigHandler); + setupSignalHandler(SIGHUP, sigHandler); DBusConnection* conn = (DBusConnection*)userData; dbus_error_init(&err); diff --git a/test/persistence_client_library_benchmark.c b/test/persistence_client_library_benchmark.c new file mode 100644 index 0000000..6b6c51c --- /dev/null +++ b/test/persistence_client_library_benchmark.c @@ -0,0 +1,389 @@ +/****************************************************************************** + * Project Persistency + * (c) copyright 2014 + * Company XS Embedded GmbH + *****************************************************************************/ +/****************************************************************************** + * This Source Code Form is subject to the terms of the + * Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed + * with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +******************************************************************************/ + /** + * @file persistence_client_library_benchmark.c + * @ingroup Persistence client library test + * @author Ingo Huerner + * @brief Test of persistence client library + * @see + */ + +#include "../include/persistence_client_library_key.h" +#include "../include/persistence_client_library_file.h" +#include "../include/persistence_client_library_error_def.h" + +#include +#include + +#include +#include + +#include +#include + + +#define SECONDS2NANO 1000000000L +#define NANO2MIL 1000000L +#define MIL2SEC 1000L + +#define BUFFER_SIZE 1024 + +// define for the used clock: "CLOCK_MONOTONIC" or "CLOCK_REALTIME" +#define CLOCK_ID CLOCK_MONOTONIC + +// definition of weekday to generate random string +char* dayOfWeek[] = { "Sunday ", + "Monday ", + "Tuesday ", + "Wednesday", + "Thursday ", + "Friday ", + "Saturday "}; + + +char sysTimeBuffer[BUFFER_SIZE]; +char sysTimeBuffer2[BUFFER_SIZE]; + + + +inline long long getNsDuration(struct timespec* start, struct timespec* end) +{ + return ((end->tv_sec * SECONDS2NANO) + end->tv_nsec) - ((start->tv_sec * SECONDS2NANO) + start->tv_nsec); +} + +inline double getMsDuration(struct timespec* start, struct timespec* end) +{ + return (double)((end->tv_sec * SECONDS2NANO) + end->tv_nsec) - ((start->tv_sec * SECONDS2NANO) + start->tv_nsec)/NANO2MIL; +} + + +void read_benchmark(int numLoops) +{ + int ret = 0, i = 0; + long long duration = 0; + struct timespec readStart, readEnd; + + unsigned char buffer[BUFFER_SIZE] = {0}; + + printf("\nTest r e a d performance: %d times\n", numLoops); + + clock_gettime(CLOCK_ID, &readStart); + ret = pclKeyReadData(0xFF, "pos/last_position_ro_bench", 1, 2, buffer, BUFFER_SIZE); + clock_gettime(CLOCK_ID, &readEnd); + duration += getNsDuration(&readStart, &readEnd); + printf(" INITIAL read 1 \"pos/last_position_ro_bench\" => %f ms [%d bytes]\n", (double)((double)duration/NANO2MIL), ret); + + duration = 0; + memset(buffer, 0, BUFFER_SIZE); + for(i=0; i %f ms [%d bytes]\n", (double)((double)duration/NANO2MIL/numLoops), ret); + + + duration = 0; + memset(buffer, 0, BUFFER_SIZE); + for(i=0; i %f ms [%d bytes]\n", (double)((double)duration/NANO2MIL)/numLoops, ret); + + + duration = 0; + memset(buffer, 0, BUFFER_SIZE); + + clock_gettime(CLOCK_ID, &readStart); + ret = pclKeyReadData(0xFF, "pos/last_position_ro_bench2", 1, 2, buffer, BUFFER_SIZE); + clock_gettime(CLOCK_ID, &readEnd); + + duration = getNsDuration(&readStart, &readEnd); + printf(" INITIAL read 2 \"pos/last_position_ro_bench2\" => %f ms [%d bytes]\n", (double)((double)duration/NANO2MIL), ret); + + + duration = 0; + memset(buffer, 0, BUFFER_SIZE); + for(i=0; i %f ms [%d bytes]\n", (double)((double)duration/NANO2MIL)/numLoops, ret); + + + duration = 0; + memset(buffer, 0, BUFFER_SIZE); + for(i=0; i %f ms [%d bytes]\n", (double)((double)duration/NANO2MIL)/numLoops, ret); + + +#if 0 + printf(" Size [pos/last_position_ro_bench] : %d bytes\n", pclKeyGetSize(0xFF, "pos/last_position_ro_bench", 1, 2)); + printf(" Size [pos/last_position_ro_bench2]: %d bytes\n", pclKeyGetSize(0xFF, "pos/last_position_ro_bench2", 1, 2)); +#endif + +} + + + +void write_benchmark(int numLoops) +{ + int ret = 0, ret2 = 0, i = 0; + long long duration = 0; + struct timespec writeStart, writeEnd; + unsigned char buffer[BUFFER_SIZE] = {0}; + + printf("\nTest w r i t e performance: %d times\n", numLoops); + + clock_gettime(CLOCK_ID, &writeStart); + ret = pclKeyWriteData(0xFF, "pos/last_position_w_bench", 1, 2, (unsigned char*)sysTimeBuffer, strlen(sysTimeBuffer)); + clock_gettime(CLOCK_ID, &writeEnd); + duration = getNsDuration(&writeStart, &writeEnd); + printf(" Initial Write 1 => %f ms [%d bytes]\n", (double)((double)duration/NANO2MIL), ret); + + duration = 0; + for(i=0; i %f ms [%d bytes]\n", (double)((double)duration/NANO2MIL)/numLoops, ret); + + + duration = 0; + for(i=0; i %f ms [%d bytes]\n", (double)((double)duration/NANO2MIL)/numLoops, ret2); + + + duration = 0; + for(i=0; i %f ms [%d bytes]\n", (double)((double)duration/NANO2MIL)/numLoops, ret2); + + +#if 0 + printf(" Size [pos/last_position_w_bench]: %d\n", ret); + printf(" Size [pos/last_position_w_bench]: %d\n", ret2); +#endif + + clock_gettime(CLOCK_ID, &writeStart); + ret = pclKeyReadData(0xFF, "pos/last_position_w_bench2", 1, 2, buffer, BUFFER_SIZE); + clock_gettime(CLOCK_ID, &writeEnd); + duration = getNsDuration(&writeStart, &writeEnd); + printf(" Write verification, pclKeyReadData => %f ms [%d bytes]\n", (double)((double)duration/NANO2MIL), ret); + + +#if 0 + printf(" Buffer [pos/last_position_w_bench2]:\n %s \n\n", buffer); +#endif +} + + + +void handle_benchmark(int numLoops) +{ + int hdl = 0, hdl2 = 0, hdl3 = 0, hdl4 = 0, ret = 0, i = 0; + long long duration = 0; + struct timespec openStart, openEnd; + unsigned char buffer[BUFFER_SIZE] = {0}; + + printf("\nTest h a n d l e performance: %d times\n", numLoops); + + duration = 0; + for(i=0; i<1; i++) + { + clock_gettime(CLOCK_ID, &openStart); + hdl = pclKeyHandleOpen(0xFF, "handlePos/last_position_ro_bench", 1, 2); + clock_gettime(CLOCK_ID, &openEnd); + + //pclKeyHandleClose(hdl); + + duration += getNsDuration(&openStart, &openEnd); + } + printf(" Open 1 => %f ms\n", (double)((double)duration/NANO2MIL)); + + duration = 0; + for(i=0; i<2; i++) + { + clock_gettime(CLOCK_ID, &openStart); + hdl2 = pclKeyHandleOpen(0xFF, "handlePos/last_position_ro_bench2", 1, 2); + clock_gettime(CLOCK_ID, &openEnd); + + //pclKeyHandleClose(hdl2); + + duration += getNsDuration(&openStart, &openEnd); + } + printf(" Open 2 => %f ms\n", (double)((double)duration/NANO2MIL)); + + /* + hdl = pclKeyHandleOpen(0xFF, "handlePos/last_position_ro_bench", 1, 2); + hdl2 = pclKeyHandleOpen(0xFF, "handlePos/last_position_ro_bench2", 1, 2); + */ + + + duration = 0; + for(i=0; i %f ms [%d bytes]\n", (double)((double)duration/NANO2MIL)/numLoops, ret); +#if 0 + printf(" Buffer [handlePos/last_position_ro_bench] => %d :\n %s \n\n", ret, buffer); +#endif + + + + duration = 0; + for(i=0; i %f ms [%d bytes]\n", (double)((double)duration/NANO2MIL)/numLoops, ret); +#if 0 + printf(" Buffer [handlePos/last_position_ro_bench2] => %d :\n %s \n\n", ret, buffer); +#endif + + + + + clock_gettime(CLOCK_ID, &openStart); + hdl3 = pclKeyHandleOpen(0xFF, "handlePos/last_position_w_bench", 1, 2); + clock_gettime(CLOCK_ID, &openEnd); + duration = getNsDuration(&openStart, &openEnd); + printf(" Open 3 => %f ms\n", (double)((double)duration/NANO2MIL)); + + + clock_gettime(CLOCK_ID, &openStart); + hdl4 = pclKeyHandleOpen(0xFF, "handlePos/last_position_w_bench2", 1, 2); + clock_gettime(CLOCK_ID, &openEnd); + duration = getNsDuration(&openStart, &openEnd); + printf(" Open 4 => %f ms\n", (double)((double)duration/NANO2MIL)); + + + pclKeyHandleClose(hdl); + pclKeyHandleClose(hdl2); + pclKeyHandleClose(hdl3); + pclKeyHandleClose(hdl4); +} + + + + +int main(int argc, char *argv[]) +{ + int ret = 0; + int numLoops = 5000; + long long duration = 0, resolution = 0; + unsigned int shutdownReg = PCL_SHUTDOWN_TYPE_FAST | PCL_SHUTDOWN_TYPE_NORMAL; + const char* appName = "lt-persistence_client_library_test"; + struct timespec initStart, initEnd, clockRes; + struct tm *locTime; + + time_t t = time(0); + locTime = localtime(&t); + snprintf(sysTimeBuffer, BUFFER_SIZE, "The benchmark string to do write benchmarking: \"%s %.2d.%.2d.%d - %d:%.2d:%.2d Uhr\" [time and date]", dayOfWeek[locTime->tm_wday], + locTime->tm_mday, (locTime->tm_mon)+1, (locTime->tm_year+1900), + locTime->tm_hour, locTime->tm_min, locTime->tm_sec); + + snprintf(sysTimeBuffer2, BUFFER_SIZE, "The benchmark string to do write benchmarking: \"%s %.2d.%.2d.%d - %d:%.2d:%.2d Uhr\" [time and date] ==> The benchmark string to do write benchmarking: The quick brown fox jumps over the lazy dog !!!", + dayOfWeek[locTime->tm_wday], + locTime->tm_mday, (locTime->tm_mon)+1, (locTime->tm_year+1900), + locTime->tm_hour, locTime->tm_min, locTime->tm_sec); + + printf("\n\n============================\n"); + printf(" PCL benchmark\n"); + printf("============================\n\n"); + + /// debug log and trace (DLT) setup + DLT_REGISTER_APP("noty","tests the persistence client library"); + + + clock_getres(CLOCK_ID, &clockRes); + resolution = ((clockRes.tv_sec * SECONDS2NANO) + clockRes.tv_nsec); + printf("Clock resolution => %f ms\n\n", (double)((double)resolution/NANO2MIL)); + + + clock_gettime(CLOCK_ID, &initStart); + ret = pclInitLibrary(appName , shutdownReg); + clock_gettime(CLOCK_ID, &initEnd); + duration = getNsDuration(&initStart, &initEnd); + printf("Init library => %lld ns | %f ms\n", duration, (double)((double)duration/NANO2MIL)); + + + read_benchmark(numLoops); + + write_benchmark(numLoops); + + handle_benchmark(numLoops); + + +#if 0 + printf("\nPress a key to end test\n"); + getchar(); +#endif + + clock_gettime(CLOCK_ID, &initStart); + pclDeinitLibrary(); + clock_gettime(CLOCK_ID, &initEnd); + duration = ((initEnd.tv_sec * SECONDS2NANO) + initEnd.tv_nsec) - ((initStart.tv_sec * SECONDS2NANO) + initStart.tv_nsec); + printf("\nDeinit library => %lld ns | %f ms\n", duration, (double)((double)duration/NANO2MIL)); + + + // unregister debug log and trace + DLT_UNREGISTER_APP(); + + dlt_free(); + + return ret; +} diff --git a/test/persistence_client_library_test.c b/test/persistence_client_library_test.c index 0e187aa..9f9a7aa 100644 --- a/test/persistence_client_library_test.c +++ b/test/persistence_client_library_test.c @@ -663,6 +663,7 @@ START_TEST(test_DataFileRecovery) ret = pclInitLibrary(gTheAppId, shutdownReg); fail_unless(ret <= 1, "Failed to init PCL"); #if 1 + // test backup creation -------------------------------------------- fd_RO = pclFileOpen(0xFF, "media/mediaDB_ReadOnly.db", 1, 1); fail_unless(fd_RO != -1, "Could not open file ==> /media/mediaDB_ReadOnly.db"); @@ -896,10 +897,11 @@ START_TEST(test_Cursor) memset(bufferDataDst, 0, READ_SIZE); // get key - rval = pers_db_cursor_get_key(handle, bufferKeySrc, 128); + rval = pers_db_cursor_get_key(handle, bufferKeySrc, 256); fail_unless(rval != -1, "Cursor failed to get key!!"); // get data - rval = pers_db_cursor_get_data(handle, bufferDataSrc, 128); + rval = pers_db_cursor_get_data(handle, bufferDataSrc, 256); + fail_unless(rval != -1, "Cursor failed to get data!!"); // get size size = pers_db_cursor_get_data_size(handle); @@ -907,10 +909,10 @@ START_TEST(test_Cursor) //printf("1. Key: %s | Data: %s » Size: %d \n", bufferKeySrc, bufferDataSrc, size); // get key - rval = pers_db_cursor_get_key(handle1, bufferKeyDst, 128); + rval = pers_db_cursor_get_key(handle1, bufferKeyDst, 256); fail_unless(rval != -1, "Cursor failed to get key!!"); // get data - rval = pers_db_cursor_get_data(handle1, bufferDataDst, 128); + rval = pers_db_cursor_get_data(handle1, bufferDataDst, 256); fail_unless(rval != -1, "Cursor failed to get data!!"); // get size size = pers_db_cursor_get_data_size(handle1); @@ -1127,8 +1129,6 @@ static Suite * persistencyClientLib_suite() tcase_add_test(tc_GetPath, test_GetPath); suite_add_tcase(s, tc_persSetData); - -#if 1 suite_add_tcase(s, tc_persGetData); suite_add_tcase(s, tc_persSetDataNoPRCT); suite_add_tcase(s, tc_persGetDataSize); @@ -1142,7 +1142,6 @@ static Suite * persistencyClientLib_suite() suite_add_tcase(s, tc_ReadDefault); suite_add_tcase(s, tc_ReadConfDefault); suite_add_tcase(s, tc_GetPath); -#endif //suite_add_tcase(s, tc_Plugin); // activate only if the plugins are available return s; } diff --git a/test/persistence_lifeCycle_mockup.c b/test/persistence_lifeCycle_mockup.c index 115d1eb..eaab68d 100644 --- a/test/persistence_lifeCycle_mockup.c +++ b/test/persistence_lifeCycle_mockup.c @@ -97,11 +97,40 @@ static int endLoop = 0; void sigHandler(int signo) { - endLoop = 1; + switch(signo) + { + case SIGHUP: + // nothing to do + printf("* * * * S I G H U P * * * *\n"); + break; + default: + endLoop = 1; + break; + } } //------------------------------------------------------------------------ +static int setupSignalHandler(const int nSignal, void (*pHandler)(int)) +{ + struct sigaction sa_old; + int ret = sigaction(nSignal, NULL, &sa_old); + if (0==ret) + { + if (pHandler!=sa_old.sa_handler) + { + /* setup own signal handler */ + struct sigaction sa_new; + memset(&sa_new, 0, sizeof(sa_new)); + sa_new.sa_handler = pHandler; + sa_new.sa_flags = 0; + ret = sigaction(nSignal, &sa_new, 0); + } + } + + return ret; +} + #define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0])) @@ -343,9 +372,10 @@ int mainLoop(DBusObjectPathVTable vtable, DBusObjectPathVTable vtableFallback, v // lock mutex to make sure dbus main loop is running pthread_mutex_lock(&gDbusInitializedMtx); - signal(SIGTERM, sigHandler); - signal(SIGQUIT, sigHandler); - signal(SIGINT, sigHandler); + setupSignalHandler(SIGTERM, sigHandler); + setupSignalHandler(SIGQUIT, sigHandler); + setupSignalHandler(SIGINT, sigHandler); + setupSignalHandler(SIGHUP, sigHandler); DBusConnection* conn = (DBusConnection*)userData; dbus_error_init(&err); -- 2.7.4