/*! * \file CPluginExecutor.cpp * \author S. Eisenhauer * \date 27.10.2011 * \brief Implementation of CPluginExecutor */ #include "CPluginExecutor.h" #include #include #include "global.h" #include #include #include #include #include #include "protocol.h" #define nAUTOLOAD_FILENAME "autoload_plugins" #define nSEPARATOR "," static tstPluginConfig g_astPluginConfigList[nNUMBER_OF_CAN_INTERFACES]; CPluginExecutor::CPluginExecutor( ) :m_u8PluginCounter(0) ,m_xTcpServer(this,g_xIoService,nPORT) ,m_xConnector(this,m_xTcpServer) ,m_xConnectorThread(m_xConnector) ,m_pxLogPlugin(NULL) { int res = 0; int schedulerPolicy = SCHED_RR; sched_param sp; sp.sched_priority = tenPrio::nenNORMAL; // rt priority tenRetCodes enRetVal = enAutoLoadPlugins(); if( enRetVal != nenOK ) { ERROR_PRINT("error auto loading plugins: %d",enRetVal); exit(1); } if( (res = pthread_setschedparam(m_xConnectorThread.native_handle(), schedulerPolicy, &sp)) != 0 ) { ERROR_PRINT("pthread_setschedparam failed: %d errno: %d",res,errno); } if( (res = pthread_getschedparam(m_xConnectorThread.native_handle(),&schedulerPolicy,&sp)) != 0 ) { ERROR_PRINT("pthread_getschedparam failed: %d errno: %d",res,errno); } else { DEBUG_PRINT("network IO thread prio: %d scheduler: %s" ,sp.sched_priority ,(schedulerPolicy==SCHED_OTHER)?"SCHED_OTHER": (schedulerPolicy==SCHED_FIFO)?"SCHED_FIFO": (schedulerPolicy==SCHED_RR)?"SCHED_RR":"other"); } } CPluginExecutor::~CPluginExecutor() { DEBUG_PRINT("entry"); DEBUG_PRINT("exit"); } tenRetCodes CPluginExecutor::enAddPlugin( const char* pcSoFilename, const int32_t i32Interface, uint8_t& u8PluginId ) { tenRetCodes enRetCode = nenOK; if( i32Interface != nALL_CAN_INTERFACES ) { { boost::lock_guard lock(m_xPluginListMutex); if( findPluginByInterface(i32Interface) != m_astPluginConfig.end() ) { enRetCode = enRemovePlugin(i32Interface); if( enRetCode != nenOK ) { return enRetCode; } } } } tpstPluginConfig pstNewPluginConfig = NULL; for(uint8_t u8Cnt = 0; u8CntpvPluginHandle == NULL) { // DEBUG_PRINT("using memory %d [%#X] for plugin %s",u8Cnt,(int)pstNewPluginConfig,pcSoFilename); break; } else { pstNewPluginConfig = NULL; } } if( !pstNewPluginConfig ) { ERROR_PRINT("could not find free memory for plugin"); return nenERR_MAX_PLUGINS_REACHED; } strncpy(pstNewPluginConfig->acPluginFilename,pcSoFilename,nMAX_FILENAME_LENGTH); enRetCode = this->enInitPlugin(pstNewPluginConfig,i32Interface); if( enRetCode != nenOK ) { return enRetCode; } { boost::lock_guard lock(m_xPluginListMutex); m_astPluginConfig.push_back(*pstNewPluginConfig); m_u8PluginCounter++; u8PluginId = m_u8PluginCounter; } return nenOK; } tenRetCodes CPluginExecutor::enRemovePlugin( const int32_t i32Interface, bool boLock ) { tenRetCodes enRetCode = nenOK; if( boLock ) { m_xPluginListMutex.lock(); } tPluginIter it = findPluginByInterface(i32Interface); if( it != m_astPluginConfig.end() ) { tpstPluginConfig pstPlugin = &(*it); this->enDeinitPlugin(pstPlugin); if( boLock ) { m_astPluginConfig.erase(it); } m_u8PluginCounter--; } if( boLock ) { m_xPluginListMutex.unlock(); } return enRetCode; } tenRetCodes CPluginExecutor::enLoadSoFile( const char* pcSoFilename, void** ppvSoHandle ) { char* pcError = NULL; *ppvSoHandle = dlopen(pcSoFilename,RTLD_NOW|RTLD_GLOBAL|RTLD_DEEPBIND); if( !(*ppvSoHandle) ) { pcError = dlerror(); ERROR_PRINT("Could not open shared object %s: [%s]" ,pcSoFilename,(pcError)?pcError:"dlopen gave no error"); return nenERR_SOFILE; } return nenOK; } void CPluginExecutor::vStop() { DEBUG_PRINT("entry"); { boost::lock_guard lock(m_xStopFlagMutex); m_boDoStop = true; } m_xConnector.vStop(); DEBUG_PRINT("exit"); } tenRetCodes CPluginExecutor::enLoadSymbol( const char* pcSymbolName, void* pvSoHandle, void** ppvHandle ) { char* pcError = NULL; *ppvHandle = dlsym(pvSoHandle,pcSymbolName); if( (pcError = dlerror()) != NULL) { ERROR_PRINT("Error finding symbol %s: [%s]",pcSymbolName,pcError); return nenERR_SYMBOL; } return nenOK; } tenRetCodes CPluginExecutor::enInitPlugin( tpstPluginConfig& pstPluginConfig, const int32_t i32Interface ) { const char* pcPluginFactoryName = "pxCreatePlugin"; const char* pcPluginDestructionName = "vDestroyPlugin"; void* pvFunctionHandle = NULL; tenRetCodes enRetCode; enRetCode = this->enLoadSoFile(pstPluginConfig->acPluginFilename, &(pstPluginConfig->pvPluginHandle)); if( enRetCode != nenOK ) { return enRetCode; } enRetCode = this->enLoadSymbol(pcPluginFactoryName, pstPluginConfig->pvPluginHandle, &pvFunctionHandle); if( enRetCode != nenOK ) { return enRetCode; } pstPluginConfig->pfctPluginFactory = reinterpret_cast(pvFunctionHandle); enRetCode = this->enLoadSymbol(pcPluginDestructionName, pstPluginConfig->pvPluginHandle, &pvFunctionHandle); if( enRetCode != nenOK ) { return enRetCode; } pstPluginConfig->pfctPluginDestruction = reinterpret_cast (pvFunctionHandle); pstPluginConfig->pxPlugin = pstPluginConfig->pfctPluginFactory( reinterpret_cast(this), reinterpret_cast(&m_xInterfaceManager), i32Interface, m_u8PluginCounter ); if( pstPluginConfig->pxPlugin->boIsLogger() ) { m_pxLogPlugin = pstPluginConfig->pxPlugin; } return nenOK; } void CPluginExecutor::vSendCanTxMessages() { x2e::status_t enRes; tCanTxMessageSet::iterator xTxMsgIt = m_xTxMsgList.begin(); int32_t i32TxInterface = 0; for( ; xTxMsgIt != m_xTxMsgList.end(); ++xTxMsgIt ) { tpstCanTxMessage pxCanTxMsg = (*xTxMsgIt).m_pstCanTxMsg; i32TxInterface = (*xTxMsgIt).i32Interface; // DEBUG_PRINT("Sending CAN msg with ID %#x on interface %d" // ,pxCanTxMsg->u32CanId,i32TxInterface); enRes = m_xInterfaceManager.enWriteCanMessage( i32TxInterface, pxCanTxMsg->u32CanId, false, false, pxCanTxMsg->au8Data, pxCanTxMsg->u8Dlc ); if( enRes != x2e::OK) { ERROR_PRINT("Error sending msg 0x%X on Interface %d. Error: %d", pxCanTxMsg->u32CanId, i32TxInterface, enRes); enRes = m_xInterfaceManager.enDisableInterface(i32TxInterface); enRes = m_xInterfaceManager.enEnableInterface(i32TxInterface); if( enRes != x2e::OK ) { break; } } else { if( m_pxLogPlugin ) { tstLogMessage stLogMessage; m_xInterfaceManager.enGetTimestamp(&stLogMessage.u32TsHigh,&stLogMessage.u32TsLow); stLogMessage.u32MsgId = pxCanTxMsg->u32CanId; stLogMessage.i32Interface = i32TxInterface; stLogMessage.u8Dir = static_cast(::nenTx); memcpy( (void*) stLogMessage.au8Data, (const void*) pxCanTxMsg->au8Data, pxCanTxMsg->u8Dlc ); m_pxLogPlugin->vLogMessage(stLogMessage); } } } m_xTxMsgList.clear(); } void CPluginExecutor::vRun() { static tstCanTxMsgWrapper astCanTxMessageWrappers[nMAX_CAN_TX_MESSAGES]; tPluginIter xPluginIter; bool boDoStop = false; { boost::lock_guard lock(m_xStopFlagMutex); boDoStop = m_boDoStop; } while( !m_boDoStop ) { m_xInterfaceManager.enWait(); { boost::lock_guard lock(m_xPluginListMutex); for( xPluginIter = m_astPluginConfig.begin(); xPluginIter != m_astPluginConfig.end(); xPluginIter++ ) { tpstPluginConfig pstActPlugin = &(*xPluginIter); IPlugin* pxPlugin = pstActPlugin->pxPlugin; if( !pxPlugin ) { ERROR_PRINT("Plugin for file %s is NULL",pstActPlugin->acPluginFilename); continue; } pxPlugin->vRun(); uint32_t u32NumOfCanTxMsg = pxPlugin->u32GetNumOfCanTxMessages(); int32_t i32TxInterface = pxPlugin->i32GetCanInterfaceHandle(); if( (i32TxInterface != 0) && (u32NumOfCanTxMsg > 0) ) { // DEBUG_PRINT("Scheduling CAN TX messages for plugin %s",pstActPlugin->acPluginFilename); for( uint32_t u32CanTxMsgCount = 0; u32CanTxMsgCount < u32NumOfCanTxMsg; u32CanTxMsgCount++ ) { if( m_xTxMsgList.size() >= nMAX_CAN_TX_MESSAGES ) { DEBUG_PRINT("too much tx messages in plugin %s" ,pstActPlugin->acPluginFilename); vSendCanTxMessages(); } tpstCanTxMessage pxCanTxMessage = pxPlugin->pxGetCanTxMessage(u32CanTxMsgCount); tstCanTxMsgWrapper* pxCanMsgWrapper = &astCanTxMessageWrappers[m_xTxMsgList.size()]; pxCanMsgWrapper->i32Interface = i32TxInterface; pxCanMsgWrapper->m_pstCanTxMsg = pxCanTxMessage; pxCanTxMessage->u32MillisecondCounter++; if( pxCanTxMessage->u32MillisecondCounter > pxCanTxMessage->u32TxCycleMilliseconds ) { ERROR_PRINT("Missed TX cycle of %d msec for CAN message 0x%x" ,pxCanTxMessage->u32MillisecondCounter,pxCanTxMessage->u32CanId); } if( pxCanTxMessage->u32MillisecondCounter >= pxCanTxMessage->u32TxCycleMilliseconds ) { pxCanTxMessage->u32MillisecondCounter = 0; m_xTxMsgList.insert(*pxCanMsgWrapper); } } } } } vSendCanTxMessages(); } vDeinit(); DEBUG_PRINT("exit"); } tenRetCodes CPluginExecutor::enDeinitPlugin(tpstPluginConfig& pstPluginConfig) { tenRetCodes enRetCode = nenOK; int32_t i32Res = 0; if( pstPluginConfig->pxPlugin ) { if( pstPluginConfig->pxPlugin->boIsLogger() ) { m_pxLogPlugin = NULL; } pstPluginConfig->pfctPluginDestruction(pstPluginConfig->pxPlugin); } if( pstPluginConfig->pvPluginHandle ) { i32Res = dlclose(pstPluginConfig->pvPluginHandle); pstPluginConfig->pvPluginHandle = NULL; } return enRetCode; } tenRetCodes CPluginExecutor::enDeinitAllPlugins() { tPluginIter xPluginIter; tenRetCodes enRetCode = nenOK; FILE* pxFp; pxFp = fopen(nAUTOLOAD_FILENAME,"w"); { boost::lock_guard lock(m_xPluginListMutex); for( xPluginIter = m_astPluginConfig.begin(); xPluginIter != m_astPluginConfig.end(); xPluginIter++ ) { tpstPluginConfig pstPluginconfig = &(*xPluginIter); if( pstPluginconfig ) { tstPluginConfig& stPluginconfig = *pstPluginconfig; IPlugin* pxPlugin = stPluginconfig.pxPlugin; if( pxPlugin ) { bool boAutoload = pxPlugin->boAutoload(); int32_t i32Interface = pxPlugin->i32GetCanInterfaceHandle(); if(boAutoload) { const char* p = stPluginconfig.acPluginFilename; char acLine[nMAX_LINE_LENGTH]; snprintf(acLine,nMAX_LINE_LENGTH,"%s%s%d\n",p,nSEPARATOR,i32Interface); fputs(acLine,pxFp); } enRetCode = this->enRemovePlugin(i32Interface, false); } } } m_astPluginConfig.clear(); } fclose(pxFp); return enRetCode; } void CPluginExecutor::vGetInterfaces(char* pcResponse, uint32_t& u32Length) { for( uint8_t u8IfCtr = 1; u8IfCtr < nNUMBER_OF_CAN_INTERFACES; u8IfCtr++ ) { sprintf(&pcResponse[strlen(pcResponse)],"CAN %d\n",u8IfCtr); } u32Length = strlen(pcResponse); return; } uint8_t CPluginExecutor::u8GetPluginCount() { return m_u8PluginCounter; } void CPluginExecutor::vDeinit() { m_xConnectorThread.join(); DEBUG_PRINT("Connector thread stopped"); enDeinitAllPlugins(); m_xInterfaceManager.vDeinit(); } tenRetCodes CPluginExecutor::enAutoLoadPlugins() { tenRetCodes enRetCode = nenOK; char acActLine[nMAX_LINE_LENGTH]; int iFd = -1; ssize_t xReadRes = -1; int iCtr = 0; memset(acActLine,0,nMAX_LINE_LENGTH); iFd = open(nAUTOLOAD_FILENAME,O_RDONLY); if( iFd < 0) { ERROR_PRINT("Error opening file %s: %d",nAUTOLOAD_FILENAME,errno); } while( ( xReadRes = read(iFd, (void*) &acActLine[iCtr], 1) ) > 0 ) { if(acActLine[iCtr] == '\n') { char *pcFilename, *pcInterface; int32_t i32Interface = -1; uint8_t u8PluginId; pcFilename = strtok(acActLine,nSEPARATOR); pcInterface = strtok(NULL,nSEPARATOR); if( (pcFilename != NULL) && (pcInterface != NULL) ) { i32Interface = atoi(pcInterface); enRetCode = enAddPlugin(pcFilename,i32Interface,u8PluginId); if( enRetCode == nenOK ) { DEBUG_PRINT("auto loaded plugin [%s] on interface [%d]",pcFilename,i32Interface); } else { ERROR_PRINT("error autoloading plugin [%s]: %d",pcFilename,enRetCode); } } memset(acActLine,0,nMAX_LINE_LENGTH); iCtr = 0; } else { iCtr++; } } if( xReadRes == -1 ) { ERROR_PRINT("Error reading file %s: %d",nAUTOLOAD_FILENAME,errno); enRetCode = nenERR_SOFILE; return enRetCode; } close(iFd); return enRetCode; } tenRetCodes CPluginExecutor::enGetLog(const char* pcRequestFilename, char* pcResponseFilename, uint32_t& u32Length) { tenRetCodes enRetCode = nenOK; if( m_pxLogPlugin ) { enRetCode = m_pxLogPlugin->enGetLog(pcRequestFilename,pcResponseFilename); if( enRetCode != nenOK) { return enRetCode; } DEBUG_PRINT("logfile name: %s",pcResponseFilename); u32Length = strlen(pcResponseFilename); } return enRetCode; } void CPluginExecutor::vStartPlugin(const int32_t i32Interface, const char* pcSoFilename) { uint8_t u8PluginId; tenRetCodes enRetVal = enAddPlugin(pcSoFilename,i32Interface,u8PluginId); if( enRetVal == nenOK) { DEBUG_PRINT("Started plugin %d on interface %d from file %s" ,u8PluginId,i32Interface,pcSoFilename); } else { ERROR_PRINT("Error starting plugin %s on interface %d: %d", pcSoFilename,i32Interface,enRetVal); } } void CPluginExecutor::vStopPlugin(int32_t i32Interface) { enRemovePlugin(i32Interface,true); DEBUG_PRINT("Stopped plugin on interface %d", i32Interface); } void CPluginExecutor::vChangeMsgData(const int32_t i32Interface, const uint32_t u32CanId, const uint8_t* pu8Data) { tPluginIter xPluginIter; boost::lock_guard lock(m_xPluginListMutex); { for( xPluginIter = m_astPluginConfig.begin(); xPluginIter != m_astPluginConfig.end(); xPluginIter++ ) { tpstPluginConfig pstActPlugin = &(*xPluginIter); if( !pstActPlugin ) { continue; } IPlugin* pxPlugin = pstActPlugin->pxPlugin; if( !pxPlugin ) { continue; } if( pxPlugin->i32GetCanInterfaceHandle() != i32Interface ) { continue; } uint32_t u32NumOfCanTxMsg = pxPlugin->u32GetNumOfCanTxMessages(); if( u32NumOfCanTxMsg > 0 ) { for( uint32_t u32CanTxMsgCount = 0; u32CanTxMsgCount < u32NumOfCanTxMsg; u32CanTxMsgCount++ ) { tpstCanTxMessage pxCanTxMessage = pxPlugin->pxGetCanTxMessage(u32CanTxMsgCount); if( pxCanTxMessage->u32CanId == u32CanId ) { memcpy( (void*)pxCanTxMessage->au8Data, pu8Data, pxCanTxMessage->u8Dlc ); return; } } } } } } tenRetCodes CPluginExecutor::enEnumerateLogs(char* pcLogfiles, uint32_t& u32Length) { tenRetCodes enRes = nenOK; struct dirent* pstDirEnt; const char* pcLogPath = "../data/"; const char* pcLogFileExtension = ".log"; const char* pcFormat="%s%s\n"; DIR* pDir = opendir(pcLogPath); uint32_t u32StrLen = 0; uint32_t u32ActFilenameLen = 0; while( (pstDirEnt=readdir(pDir)) != NULL ) { u32ActFilenameLen = strlen(pcLogPath) + strlen(pstDirEnt->d_name); u32StrLen = strlen(pcLogfiles) + u32ActFilenameLen + 1; if( strstr(pstDirEnt->d_name,pcLogFileExtension) != NULL ) { if( u32StrLen < u32Length ) { sprintf(&pcLogfiles[strlen(pcLogfiles)],pcFormat,pcLogPath,pstDirEnt->d_name); } else { break; } } } closedir(pDir); u32Length = strlen(pcLogfiles); return enRes; } void CPluginExecutor::vUploadLogMessage(const tstLogMessage& stLogMsg) { m_xConnector.vUploadLogMessage(stLogMsg); }