mirror of
https://github.com/eclipse-openvehicle-api/openvehicle-api.git
synced 2026-08-30 04:15:10 +00:00
connection between 2 systems and bug fixes (#14)
This commit is contained in:
@@ -170,6 +170,14 @@ add_custom_command(
|
||||
COMMAND sdv_idl_compiler ${INTERFACE_DIR}/repository.idl -O${INTERFACE_DIR}
|
||||
VERBATIM
|
||||
)
|
||||
add_custom_command(
|
||||
OUTPUT ${INTERFACE_DIR}/permission.h
|
||||
DEPENDS sdv_idl_compiler
|
||||
MAIN_DEPENDENCY ${INTERFACE_DIR}/permission.idl
|
||||
COMMENT "Compiling permission.idl"
|
||||
COMMAND sdv_idl_compiler ${INTERFACE_DIR}/permission.idl -O${INTERFACE_DIR} --no_ps
|
||||
VERBATIM
|
||||
)
|
||||
add_custom_command(
|
||||
OUTPUT ${INTERFACE_DIR}/timer.h
|
||||
DEPENDS sdv_idl_compiler
|
||||
@@ -198,6 +206,7 @@ add_custom_target(CompileCoreIDL
|
||||
${INTERFACE_DIR}/process.h
|
||||
${INTERFACE_DIR}/param.h
|
||||
${INTERFACE_DIR}/repository.h
|
||||
${INTERFACE_DIR}/permission.h
|
||||
${INTERFACE_DIR}/timer.h
|
||||
)
|
||||
|
||||
@@ -212,10 +221,14 @@ add_subdirectory(task_timer)
|
||||
add_subdirectory(ipc_com)
|
||||
add_subdirectory(ipc_connect)
|
||||
add_subdirectory(ipc_shared_mem)
|
||||
add_subdirectory(uds_unix_sockets)
|
||||
add_subdirectory(uds_unix_tunnel)
|
||||
add_subdirectory(uds_win_sockets)
|
||||
add_subdirectory(uds_win_tunnel)
|
||||
if(UNIX)
|
||||
add_subdirectory(uds_unix_sockets)
|
||||
add_subdirectory(uds_unix_tunnel)
|
||||
endif()
|
||||
if(WIN32)
|
||||
add_subdirectory(uds_win_sockets)
|
||||
add_subdirectory(uds_win_tunnel)
|
||||
endif()
|
||||
add_subdirectory(process_control)
|
||||
add_subdirectory(hardware_ident)
|
||||
add_subdirectory(manifest_util)
|
||||
|
||||
@@ -118,7 +118,7 @@ private:
|
||||
*/
|
||||
void PlaybackFunc(const asc::SCanMessage& rsMsg);
|
||||
|
||||
std::thread m_threadReceive; ///< Receive thread.
|
||||
sdv::core::secure_thread m_threadReceive; ///< Receive thread.
|
||||
mutable std::mutex m_mtxReceivers; ///< Protect the receiver set.
|
||||
std::set<sdv::can::IReceive*> m_setReceivers; ///< Set with receiver interfaces.
|
||||
mutable std::mutex m_mtxInterfaces; ///< Protect the nodes set.
|
||||
|
||||
@@ -64,7 +64,7 @@ bool CCANSockets::OnInitialize()
|
||||
}
|
||||
|
||||
LogConfigurations();
|
||||
m_threadReceive = std::thread(&CCANSockets::ReceiveThreadFunc, this);
|
||||
m_threadReceive = sdv::core::secure_thread(&CCANSockets::ReceiveThreadFunc, this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -146,7 +146,7 @@ private:
|
||||
std::string name; ///< interface name, can be empty in case of an invalid socket element
|
||||
};
|
||||
|
||||
std::thread m_threadReceive; ///< Receive thread.
|
||||
sdv::core::secure_thread m_threadReceive; ///< Receive thread.
|
||||
mutable std::mutex m_mtxReceivers; ///< Protect the receiver set.
|
||||
std::set<sdv::can::IReceive*> m_setReceivers; ///< Set with receiver interfaces.
|
||||
mutable std::mutex m_mtxSockets; ///< Protect the socket list.
|
||||
|
||||
@@ -49,13 +49,14 @@ add_library(core_services SHARED
|
||||
"toml_parser_util.h"
|
||||
"installation_manifest.h"
|
||||
"installation_manifest.cpp"
|
||||
"../../global/tracefifo/trace_fifo.cpp"
|
||||
"local_shutdown_request.h"
|
||||
"iso_monitor.h"
|
||||
"iso_monitor.cpp"
|
||||
"installation_composer.h"
|
||||
"installation_composer.cpp"
|
||||
|
||||
"toml_parser/lexer_toml_token.h" "toml_parser/lexer_toml_token.cpp" "toml_parser/miscellaneous.h" "toml_parser/miscellaneous.cpp" "toml_parser/code_snippet.h" "toml_parser/code_snippet.cpp" "app_settings.h" "app_settings.cpp" "app_config_file.h" "app_config_file.cpp")
|
||||
"toml_parser/lexer_toml_token.h" "toml_parser/lexer_toml_token.cpp" "toml_parser/miscellaneous.h" "toml_parser/miscellaneous.cpp" "toml_parser/code_snippet.h" "toml_parser/code_snippet.cpp" "app_settings.h" "app_settings.cpp" "app_config_file.h" "app_config_file.cpp" "toml_parser/parser_node_indexer.h" "toml_parser/parser_node_indexer.cpp" "permission_control.h" "permission_control.cpp")
|
||||
|
||||
# Compiler settiings
|
||||
add_compile_definitions(SDV_NO_EXPORT_DEFINITION)
|
||||
|
||||
@@ -33,61 +33,6 @@ CAppConfig& GetAppConfig()
|
||||
return app_config;
|
||||
}
|
||||
|
||||
// GetCoreDirectory might have been redirected for unit tests.
|
||||
#ifndef GetCoreDirectory
|
||||
std::filesystem::path GetCoreDirectory()
|
||||
{
|
||||
static std::filesystem::path pathCoreDir;
|
||||
if (!pathCoreDir.empty())
|
||||
return pathCoreDir;
|
||||
|
||||
#ifdef _WIN32
|
||||
// Windows specific
|
||||
std::wstring ssPath(32768, '\0');
|
||||
|
||||
MEMORY_BASIC_INFORMATION sMemInfo{};
|
||||
if (!VirtualQuery(&pathCoreDir, &sMemInfo, sizeof(sMemInfo)))
|
||||
return pathCoreDir;
|
||||
DWORD dwLength = GetModuleFileNameW(reinterpret_cast<HINSTANCE>(sMemInfo.AllocationBase), ssPath.data(), 32767);
|
||||
ssPath.resize(dwLength);
|
||||
pathCoreDir = std::filesystem::path(ssPath);
|
||||
return pathCoreDir.remove_filename();
|
||||
#elif __linux__
|
||||
// Read the maps file. It contains all loaded SOs.
|
||||
std::ifstream fstream("/proc/self/maps");
|
||||
std::stringstream sstreamMap;
|
||||
sstreamMap << fstream.rdbuf();
|
||||
std::string ssMap = sstreamMap.str();
|
||||
if (ssMap.empty())
|
||||
return pathCoreDir; // Some error
|
||||
|
||||
// Find the "core_services.sdv"
|
||||
size_t nPos = ssMap.find("core_services.sdv");
|
||||
if (nPos == std::string::npos)
|
||||
return pathCoreDir;
|
||||
size_t nEnd = nPos;
|
||||
|
||||
// Find the start... runbackwards until the beginning of the line and remember the earliest occurance of a slash
|
||||
size_t nBegin = 0;
|
||||
while (nPos && ssMap[nPos] != '\n')
|
||||
{
|
||||
if (ssMap[nPos] == '/')
|
||||
nBegin = nPos;
|
||||
nPos--;
|
||||
}
|
||||
if (!nBegin)
|
||||
nBegin = nPos;
|
||||
|
||||
// Return the path
|
||||
pathCoreDir = ssMap.substr(nBegin, nEnd - nBegin);
|
||||
|
||||
return pathCoreDir;
|
||||
#else
|
||||
#error The OS is not supported!
|
||||
#endif
|
||||
}
|
||||
#endif // !defined GetCoreDirectory
|
||||
|
||||
bool CAppConfig::LoadInstallationManifests()
|
||||
{
|
||||
// Check for allowance
|
||||
@@ -96,13 +41,14 @@ bool CAppConfig::LoadInstallationManifests()
|
||||
{
|
||||
case sdv::app::EAppContext::main:
|
||||
case sdv::app::EAppContext::isolated:
|
||||
case sdv::app::EAppContext::external:
|
||||
case sdv::app::EAppContext::maintenance:
|
||||
bServerApp = true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
std::filesystem::path pathCore = GetCoreDirectory();
|
||||
std::filesystem::path pathCore = GetAppSettings().GetFrameworkDir();
|
||||
std::filesystem::path pathExe = GetExecDirectory();
|
||||
std::filesystem::path pathInstall = GetAppSettings().GetInstallDir();
|
||||
|
||||
@@ -155,8 +101,8 @@ void CAppConfig::UnloadInstallatonManifests()
|
||||
|
||||
bool CAppConfig::LoadAppConfigs()
|
||||
{
|
||||
// Isolated applications do not load configurations
|
||||
if (GetAppSettings().IsIsolatedApplication()) return false;
|
||||
// Isolated and external applications do not load configurations
|
||||
if (GetAppSettings().IsIsolatedApplication() || GetAppSettings().IsExternalApplication()) return false;
|
||||
|
||||
// When running as server application, load the system configurations
|
||||
if (GetAppSettings().IsMainApplication() || GetAppSettings().IsMaintenanceApplication())
|
||||
@@ -243,6 +189,7 @@ sdv::core::EConfigProcessResult CAppConfig::ProcessConfig(/*in*/ const sdv::u8st
|
||||
{
|
||||
case sdv::app::EAppContext::main:
|
||||
case sdv::app::EAppContext::isolated:
|
||||
case sdv::app::EAppContext::external:
|
||||
case sdv::app::EAppContext::maintenance:
|
||||
return sdv::core::EConfigProcessResult::failed;
|
||||
default:
|
||||
@@ -275,7 +222,7 @@ sdv::core::EConfigProcessResult CAppConfig::ProcessConfig(/*in*/ const sdv::u8st
|
||||
//size_t nLoadable = 0;
|
||||
//size_t nNotLoadable = 0;
|
||||
//if (!GetAppSettings().IsMainApplication() && !GetAppSettings().IsIsolatedApplication() &&
|
||||
// !GetAppSettings().IsMaintenanceApplication())
|
||||
// !GetAppSettings().IsExternalApplication() && !GetAppSettings().IsMaintenanceApplication())
|
||||
//{
|
||||
// // Load all modules in the component section
|
||||
// auto ptrComponents = parser.Root().Direct("Component");
|
||||
@@ -394,7 +341,7 @@ sdv::core::EConfigProcessResult CAppConfig::ProcessConfig(/*in*/ const sdv::u8st
|
||||
// // loaded; get the module ID.
|
||||
// sdv::core::TObjectID tObjectID = 0;
|
||||
// if (!GetAppSettings().IsMainApplication() && !GetAppSettings().IsIsolatedApplication() &&
|
||||
// !GetAppSettings().IsMaintenanceApplication())
|
||||
// !GetAppSettings().IsExternalApplication() && !GetAppSettings().IsMaintenanceApplication())
|
||||
// {
|
||||
// auto itModule = mapModules.find(pathModule);
|
||||
// if (itModule == mapModules.end()) continue; // Module was not loaded before...
|
||||
@@ -880,6 +827,7 @@ std::filesystem::path CAppConfig::FindInstalledModule(const std::filesystem::pat
|
||||
{
|
||||
case sdv::app::EAppContext::main:
|
||||
case sdv::app::EAppContext::isolated:
|
||||
case sdv::app::EAppContext::external:
|
||||
case sdv::app::EAppContext::maintenance:
|
||||
bServerApp = true;
|
||||
default:
|
||||
@@ -914,6 +862,7 @@ std::string CAppConfig::FindInstalledModuleManifest(const std::filesystem::path&
|
||||
{
|
||||
case sdv::app::EAppContext::main:
|
||||
case sdv::app::EAppContext::isolated:
|
||||
case sdv::app::EAppContext::external:
|
||||
case sdv::app::EAppContext::maintenance:
|
||||
bServerApp = true;
|
||||
default:
|
||||
@@ -948,6 +897,7 @@ std::optional<sdv::SClassInfo> CAppConfig::FindInstalledComponent(const std::str
|
||||
{
|
||||
case sdv::app::EAppContext::main:
|
||||
case sdv::app::EAppContext::isolated:
|
||||
case sdv::app::EAppContext::external:
|
||||
case sdv::app::EAppContext::maintenance:
|
||||
bServerApp = true;
|
||||
default:
|
||||
@@ -980,6 +930,7 @@ bool CAppConfig::RemoveFromConfig(const CInstallManifest& /*rManifest*/)
|
||||
//{
|
||||
//case sdv::app::EAppContext::main:
|
||||
//case sdv::app::EAppContext::isolated:
|
||||
//case sdv::app::EAppContext::external:
|
||||
//case sdv::app::EAppContext::maintenance:
|
||||
// bServerApp = true;
|
||||
//default:
|
||||
@@ -1043,7 +994,7 @@ void CAppConfig::AddCurrentPath()
|
||||
if (!m_lstSearchPaths.empty()) return;
|
||||
|
||||
// Add the core directory
|
||||
std::filesystem::path pathCoreDir = GetCoreDirectory().lexically_normal();
|
||||
std::filesystem::path pathCoreDir = GetAppSettings().GetFrameworkDir().lexically_normal();
|
||||
m_lstSearchPaths.push_back(pathCoreDir / "config/");
|
||||
|
||||
// Add the exe dir
|
||||
|
||||
@@ -39,12 +39,6 @@
|
||||
// The installation might need companion files to be installed in various relative sub-directories.
|
||||
// @endcond
|
||||
|
||||
/**
|
||||
* @brief Get the location of the core_services.sdv.
|
||||
* @return Path to the directory containing the loaded core directory.
|
||||
*/
|
||||
std::filesystem::path GetCoreDirectory();
|
||||
|
||||
/**
|
||||
* @brief Application configuration service
|
||||
* @details In the configuration system objects, devices, basic services, complex services and apps are defined and will be started
|
||||
|
||||
@@ -346,9 +346,9 @@ Version = )toml" + std::to_string(SDVFrameworkInterfaceVersion) + R"toml(
|
||||
// Modules leftover in the vector are added to the list
|
||||
auto vecModuleListCopy = m_vecModuleList;
|
||||
sdv::toml::CNodeCollection nodeModules = nodeConfig.GetDirect("Module");
|
||||
for (size_t nIndex = nodeModules.GetCount() - 1; nIndex < nodeModules.GetCount(); --nIndex)
|
||||
for (int64_t iIndex = static_cast<int64_t>(nodeModules.GetCount() - 1); iIndex >= 0; --iIndex)
|
||||
{
|
||||
sdv::toml::CNodeCollection tableModule = nodeModules.Get(nIndex);
|
||||
sdv::toml::CNodeCollection tableModule = nodeModules.Get(static_cast<size_t>(iIndex));
|
||||
std::filesystem::path pathModule = tableModule.GetDirect("Path").GetValue().get<std::string>();
|
||||
auto itModule = std::find_if(vecModuleListCopy.begin(), vecModuleListCopy.end(),
|
||||
[&](const SModule& rsModule) { return pathModule == rsModule.pathModule; });
|
||||
@@ -374,9 +374,9 @@ Version = )toml" + std::to_string(SDVFrameworkInterfaceVersion) + R"toml(
|
||||
// Classes leftover in the vector are added to the list
|
||||
auto vecClassListCopy = m_vecClassList;
|
||||
sdv::toml::CNodeCollection arrayClasses = nodeConfig.GetDirect("Class");
|
||||
for (size_t nIndex = arrayClasses.GetCount() -1; nIndex < arrayClasses.GetCount(); --nIndex)
|
||||
for (int64_t iIndex = static_cast<int64_t>(arrayClasses.GetCount() - 1); iIndex >= 0; --iIndex)
|
||||
{
|
||||
sdv::toml::CNodeCollection tableClass = arrayClasses.Get(nIndex);
|
||||
sdv::toml::CNodeCollection tableClass = arrayClasses.Get(static_cast<size_t>(iIndex));
|
||||
std::filesystem::path pathModule;
|
||||
if (!bServerApp)
|
||||
pathModule = tableClass.GetDirect("Path").GetValue().get<std::string>();
|
||||
@@ -419,8 +419,7 @@ Version = )toml" + std::to_string(SDVFrameworkInterfaceVersion) + R"toml(
|
||||
if (ssExistingTOML != itClass->ssDefaultConfig)
|
||||
{
|
||||
if (tableParams) tableParams.Delete();
|
||||
if (!itClass->ssDefaultConfig.empty())
|
||||
tableClass.InsertTOML(sdv::toml::npos, itClass->ssDefaultConfig);
|
||||
if (!itClass->ssDefaultConfig.empty()) tableClass.InsertTOML("", itClass->ssDefaultConfig);
|
||||
rbChanged = true;
|
||||
}
|
||||
|
||||
@@ -433,8 +432,7 @@ Version = )toml" + std::to_string(SDVFrameworkInterfaceVersion) + R"toml(
|
||||
tableClass.AddValue("Class", rsClass.ssName);
|
||||
if (!bServerApp)
|
||||
tableClass.AddValue("Path", std::filesystem::u8path(static_cast<std::string>(rsClass.ssModulePath)));
|
||||
if (!rsClass.ssDefaultConfig.empty())
|
||||
tableClass.InsertTOML(sdv::toml::npos, rsClass.ssDefaultConfig);
|
||||
if (!rsClass.ssDefaultConfig.empty()) tableClass.InsertTOML("", rsClass.ssDefaultConfig);
|
||||
rbChanged = true;
|
||||
}
|
||||
|
||||
@@ -444,9 +442,9 @@ Version = )toml" + std::to_string(SDVFrameworkInterfaceVersion) + R"toml(
|
||||
// Components leftover in the vector are added to the list
|
||||
auto vecComponentListCopy = m_vecComponentList;
|
||||
sdv::toml::CNodeCollection nodeComponents = nodeConfig.GetDirect("Component");
|
||||
for (size_t nIndex = nodeComponents.GetCount() - 1; nIndex < nodeComponents.GetCount(); --nIndex)
|
||||
for (int64_t iIndex = static_cast<int64_t>(nodeComponents.GetCount() - 1); iIndex >= 0; --iIndex)
|
||||
{
|
||||
sdv::toml::CNodeCollection tableComponent = nodeComponents.Get(nIndex);
|
||||
sdv::toml::CNodeCollection tableComponent = nodeComponents.Get(static_cast<size_t>(iIndex));
|
||||
std::filesystem::path pathModule;
|
||||
if (!bServerApp)
|
||||
pathModule = tableComponent.GetDirect("Path").GetValue().get<std::string>();
|
||||
@@ -518,8 +516,8 @@ Version = )toml" + std::to_string(SDVFrameworkInterfaceVersion) + R"toml(
|
||||
else if (!tableParams)
|
||||
{
|
||||
// Simply add the parameters
|
||||
tableParams = tableComponent.InsertTable(sdv::toml::npos, "Parameters");
|
||||
tableParams.InsertTOML(sdv::toml::npos, itComponent->ssParameterTOML);
|
||||
tableParams = tableComponent.InsertTable("", "Parameters");
|
||||
tableParams.InsertTOML("", itComponent->ssParameterTOML);
|
||||
rbChanged = true;
|
||||
}
|
||||
}
|
||||
@@ -536,8 +534,8 @@ Version = )toml" + std::to_string(SDVFrameworkInterfaceVersion) + R"toml(
|
||||
tableComponent.AddValue("Name", rsComponent.ssInstanceName);
|
||||
if (!rsComponent.ssParameterTOML.empty())
|
||||
{
|
||||
sdv::toml::CNodeCollection tableParams = tableComponent.InsertTable(sdv::toml::npos, "Parameters");
|
||||
tableParams.InsertTOML(sdv::toml::npos, rsComponent.ssParameterTOML);
|
||||
sdv::toml::CNodeCollection tableParams = tableComponent.InsertTable("", "Parameters");
|
||||
tableParams.InsertTOML("", rsComponent.ssParameterTOML);
|
||||
}
|
||||
rbChanged = true;
|
||||
}
|
||||
@@ -628,12 +626,12 @@ bool CAppConfigFile::InsertComponent(size_t nIndex, const std::filesystem::path&
|
||||
// Need to add a group?
|
||||
if (prKey.first != ssGroup)
|
||||
{
|
||||
group = root.InsertTable(sdv::toml::npos, prKey.first);
|
||||
group = root.InsertTable("", prKey.first);
|
||||
ssGroup = prKey.first;
|
||||
}
|
||||
|
||||
// Add the parameter
|
||||
group.InsertValue(sdv::toml::npos, prKey.second, prParameter.second);
|
||||
group.InsertValue("", prKey.second, prParameter.second);
|
||||
}
|
||||
ssParameterTOML = parser.GenerateTOML();
|
||||
}
|
||||
|
||||
@@ -266,6 +266,9 @@ public:
|
||||
*/
|
||||
void RemoveModule(const std::filesystem::path& rpathModule);
|
||||
|
||||
/**
|
||||
* @brief Merge result enum.
|
||||
*/
|
||||
enum class EMergeResult
|
||||
{
|
||||
successful,
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
#include "module_control.h"
|
||||
#include "repository.h"
|
||||
#include "../../global/base64.h"
|
||||
#include "../../global/tracefifo/trace_fifo.cpp"
|
||||
#include "toml_parser/parser_toml.h"
|
||||
#include "local_shutdown_request.h"
|
||||
#include "app_settings.h"
|
||||
@@ -49,6 +48,9 @@ bool CAppControl::Startup(/*in*/ const sdv::u8string& ssConfig, /*in*/ IInterfac
|
||||
{
|
||||
m_pEvent = pEventHandler ? pEventHandler->GetInterface<sdv::app::IAppEvent>() : nullptr;
|
||||
|
||||
// Allow this thread full access
|
||||
m_optpermission = GetPermissionControl().CreatePermissionObject(sdv::core::EAccessPermission::full_access);
|
||||
|
||||
// Intercept the logging...
|
||||
std::stringstream sstreamCOUT, sstreamCLOG, sstreamCERR;
|
||||
std::streambuf* pstreambufCOUT = std::cout.rdbuf(sstreamCOUT.rdbuf());
|
||||
@@ -86,7 +88,7 @@ bool CAppControl::Startup(/*in*/ const sdv::u8string& ssConfig, /*in*/ IInterfac
|
||||
}
|
||||
|
||||
// Open the stream buffer and attach the streams if the application control is initialized as main app.
|
||||
if (GetAppSettings().IsMainApplication())
|
||||
if (!GetAppSettings().RedirectMonitorToConsole() && GetAppSettings().IsMainApplication())
|
||||
{
|
||||
m_fifoTraceStreamBuffer.SetInstanceID(GetAppSettings().GetInstanceID());
|
||||
m_fifoTraceStreamBuffer.Open(1000);
|
||||
@@ -99,7 +101,7 @@ bool CAppControl::Startup(/*in*/ const sdv::u8string& ssConfig, /*in*/ IInterfac
|
||||
std::cerr << sstreamCERR.str();
|
||||
|
||||
// Check for a correctly opened stream buffer
|
||||
if (GetAppSettings().IsMainApplication() && !m_fifoTraceStreamBuffer.IsOpened())
|
||||
if (!GetAppSettings().RedirectMonitorToConsole() && GetAppSettings().IsMainApplication() && !m_fifoTraceStreamBuffer.IsOpened())
|
||||
{
|
||||
if (!GetAppSettings().IsConsoleSilent())
|
||||
std::cerr << "ERROR: Log streaming could not be initialized; cannot continue!" << std::endl;
|
||||
@@ -142,7 +144,7 @@ bool CAppControl::Startup(/*in*/ const sdv::u8string& ssConfig, /*in*/ IInterfac
|
||||
};
|
||||
auto fnCreateObject = [&ssErrorString](const sdv::u8string& rssClass, const sdv::u8string& rssObject, const sdv::u8string& rssConfig) -> bool
|
||||
{
|
||||
bool bLocalRet = GetRepository().CreateObject2(rssClass, rssObject, rssConfig);
|
||||
bool bLocalRet = GetRepository().CreateObject(rssClass, rssObject, rssConfig);
|
||||
if (!bLocalRet)
|
||||
{
|
||||
ssErrorString = std::string("Failed to instantiate a new object from class '") + rssClass + "'";
|
||||
@@ -179,7 +181,7 @@ bool CAppControl::Startup(/*in*/ const sdv::u8string& ssConfig, /*in*/ IInterfac
|
||||
sdv::IInterfaceAccess* pLoggerObj = GetRepository().GetObject(GetAppSettings().GetLoggerClass());
|
||||
if (!pLoggerObj)
|
||||
{
|
||||
GetRepository().DestroyObject2(GetAppSettings().GetLoggerClass());
|
||||
GetRepository().DestroyObject(GetAppSettings().GetLoggerClass());
|
||||
if (!GetAppSettings().IsConsoleSilent())
|
||||
std::cerr << "ERROR: Failed to start the logger. Cannot continue!" << std::endl;
|
||||
Shutdown(true);
|
||||
@@ -247,7 +249,7 @@ bool CAppControl::Startup(/*in*/ const sdv::u8string& ssConfig, /*in*/ IInterfac
|
||||
}
|
||||
|
||||
// Load the application settings.
|
||||
if (!GetAppSettings().LoadSettingsFile())
|
||||
if (!GetAppSettings().LoadSettings())
|
||||
{
|
||||
if (!GetAppSettings().IsConsoleSilent())
|
||||
std::cerr << "ERROR: Failed to load application settings file. Cannot continue!" << std::endl;
|
||||
@@ -275,32 +277,88 @@ bool CAppControl::Startup(/*in*/ const sdv::u8string& ssConfig, /*in*/ IInterfac
|
||||
bRet = fnLoadModule("hardware_ident.sdv") ? true : false;
|
||||
if (bRet) bRet = fnCreateObject("HardwareIdentificationService", "", "");
|
||||
|
||||
// Load shared memory channel
|
||||
if (bRet) bRet = fnLoadModule("ipc_shared_mem.sdv") ? true : false;
|
||||
if (bRet) bRet = fnCreateObject("DefaultSharedMemoryChannelControl", "", "");
|
||||
// Load default communication provider
|
||||
if (bRet) bRet = fnCreateObject(GetAppSettings().GetDefaultComProvider(), "", "");
|
||||
|
||||
// Load IPC service
|
||||
if (bRet) bRet = fnLoadModule("ipc_com.sdv") ? true : false;
|
||||
if (bRet) bRet = fnCreateObject("CommunicationControl", "", "");
|
||||
|
||||
// Load IPC service and create listener local connections
|
||||
if (bRet) bRet = fnLoadModule("ipc_listener.sdv") ? true : false;
|
||||
if (bRet) bRet = fnCreateObject("ConnectionListenerService", "", R"code([Listener]
|
||||
Type = "Local"
|
||||
)code");
|
||||
|
||||
// Load IPC service
|
||||
if (bRet) bRet = fnLoadModule("ipc_connect.sdv") ? true : false;
|
||||
if (bRet) bRet = fnCreateObject("ConnectionService", "", "");
|
||||
|
||||
// Load proxy/stub for core interfaces
|
||||
if (bRet) bRet = fnLoadModule("core_ps.sdv") ? true : false;
|
||||
|
||||
// // Start the listener
|
||||
// if (bRet) bRet = fnLoadModule("ipc_listener.sdv") ? true : false;
|
||||
// if (bRet) bRet = GetRepository().CreateObject("ConnectionListenerService", "ConnectionListenerService", R"code([Listener]
|
||||
//Type="local"
|
||||
//Instance=)code" + std::to_string(GetInstanceID()));
|
||||
// Load the connection services
|
||||
if (bRet) bRet = fnLoadModule("ipc_listener.sdv") ? true : false;
|
||||
if (bRet) bRet = fnLoadModule("ipc_connect.sdv") ? true : false;
|
||||
|
||||
// For the main application start the configured listeners.
|
||||
if (bRet)
|
||||
{
|
||||
auto seqListeners = GetAppSettings().GetListeners();
|
||||
for (const auto& rssListener : seqListeners)
|
||||
{
|
||||
if (!bRet) break;
|
||||
auto ssListenerConfig = GetAppSettings().GetListenerConfig(rssListener);
|
||||
bRet = fnCreateObject("ListenerConnectService", "Listener_" + rssListener, ssListenerConfig);
|
||||
}
|
||||
}
|
||||
|
||||
// For the main application connect the configured connections (except default connection).
|
||||
if (bRet)
|
||||
{
|
||||
auto seqConnections = GetAppSettings().GetConnections();
|
||||
for (const auto& rssConnection : seqConnections)
|
||||
{
|
||||
if (!bRet) break;
|
||||
if (rssConnection == "Default") continue;
|
||||
|
||||
if (!GetAppSettings().IsConsoleSilent())
|
||||
std::cout << "INFO: Trying to connect to " << rssConnection << "..." << std::endl;
|
||||
|
||||
auto ssConnectionConfig = GetAppSettings().GetConnectionConfig(rssConnection);
|
||||
std::string ssObjectName = "Client_" + rssConnection;
|
||||
bRet = fnCreateObject("ClientConnectService", ssObjectName, ssConnectionConfig);
|
||||
|
||||
// Try connect (for at least nTries) and when successful bind repositories
|
||||
sdv::TInterfaceAccessPtr ptrClient = GetRepository().GetObject(ssObjectName);
|
||||
sdv::com::IClientConnect* pClientConnect = ptrClient.GetInterface<sdv::com::IClientConnect>();
|
||||
if (pClientConnect)
|
||||
{
|
||||
for (uint32_t uiCnt = 0; uiCnt < GetAppSettings().GetConnectRetries(); uiCnt++)
|
||||
{
|
||||
// Try to connect. If not working, wait for 300 ms
|
||||
if (pClientConnect->Connect()) break;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(300));
|
||||
}
|
||||
|
||||
if (pClientConnect->IsConnected())
|
||||
{
|
||||
// Register the core repo as repo access
|
||||
sdv::core::TLinkID tLinkID = GetRepository().LinkCoreRepository(pClientConnect->GetRemoteRepository());
|
||||
|
||||
if (!GetAppSettings().IsConsoleSilent())
|
||||
std::cout << "INFO: Connection established... " << std::endl;
|
||||
|
||||
// Store the connection object.
|
||||
m_vecConnections.push_back(std::make_pair(ssObjectName, tLinkID));
|
||||
}
|
||||
else
|
||||
{
|
||||
ssErrorString = "Not connected...";
|
||||
if (!GetAppSettings().IsConsoleSilent())
|
||||
std::cout << "ERROR: Failed to connect to " << ssConnectionConfig << std::endl;
|
||||
bRet = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ssErrorString = "No client connect interface...";
|
||||
if (!GetAppSettings().IsConsoleSilent())
|
||||
std::cerr << "ERROR: Cannot create connection object for " << rssConnection << std::endl;
|
||||
bRet = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!bRet)
|
||||
{
|
||||
@@ -314,25 +372,69 @@ Type = "Local"
|
||||
}
|
||||
else if (bRet && bLoadRPCClient)
|
||||
{
|
||||
// Interpret the connect string...
|
||||
|
||||
|
||||
// Load hardware identification
|
||||
bRet = fnLoadModule("hardware_ident.sdv") ? true : false;
|
||||
if (bRet) bRet = fnCreateObject("HardwareIdentificationService", "", "");
|
||||
|
||||
// Load shared memory channel
|
||||
if (bRet) bRet = fnLoadModule("ipc_shared_mem.sdv") ? true : false;
|
||||
if (bRet) bRet = fnCreateObject("DefaultSharedMemoryChannelControl", "", "");
|
||||
// Load default communication provider
|
||||
if (bRet) bRet = fnCreateObject(GetAppSettings().GetDefaultComProvider(), "", "");
|
||||
|
||||
// Load IPC service
|
||||
if (bRet) bRet = fnLoadModule("ipc_com.sdv") ? true : false;
|
||||
if (bRet) bRet = fnCreateObject("CommunicationControl", "", "");
|
||||
|
||||
// Load IPC service
|
||||
if (bRet) bRet = fnLoadModule("ipc_connect.sdv") ? true : false;
|
||||
if (bRet) bRet = fnCreateObject("ConnectionService", "", "");
|
||||
// Connect to the core system when running as external aplication. Use the default connection for this.
|
||||
if (bRet && GetAppSettings().GetContextType() == sdv::app::EAppContext::external)
|
||||
{
|
||||
// Load the connection services
|
||||
bRet = fnLoadModule("ipc_connect.sdv") ? true : false;
|
||||
|
||||
if (!GetAppSettings().IsConsoleSilent())
|
||||
std::cout << "INFO: Trying to connect to the core system..." << std::endl;
|
||||
|
||||
// And the one connection
|
||||
auto ssConnectionConfig = GetAppSettings().GetConnectionConfig("Default");
|
||||
if (bRet && !ssConnectionConfig.empty())
|
||||
bRet = fnCreateObject("ClientConnectService", "Client_Default", ssConnectionConfig);
|
||||
|
||||
// Try connect (for at least nTries) and when successful bind repositories
|
||||
sdv::TInterfaceAccessPtr ptrClient = GetRepository().GetObject("Client_Default");
|
||||
sdv::com::IClientConnect* pClientConnect = ptrClient.GetInterface<sdv::com::IClientConnect>();
|
||||
if (pClientConnect)
|
||||
{
|
||||
for (uint32_t uiCnt = 0; uiCnt < GetAppSettings().GetConnectRetries(); uiCnt++)
|
||||
{
|
||||
// Try to connect. If not working, wait for 300 ms
|
||||
if (pClientConnect->Connect()) break;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(300));
|
||||
}
|
||||
|
||||
if (pClientConnect->IsConnected())
|
||||
{
|
||||
// Register the core repo as repo access
|
||||
sdv::core::TLinkID tLinkID = GetRepository().LinkCoreRepository(pClientConnect->GetRemoteRepository());
|
||||
|
||||
if (!GetAppSettings().IsConsoleSilent())
|
||||
std::cout << "INFO: Connection established... " << std::endl;
|
||||
|
||||
// Store the connection object.
|
||||
m_vecConnections.push_back(std::make_pair("Default", tLinkID));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!GetAppSettings().IsConsoleSilent())
|
||||
std::cout << "ERROR: Failed to connect to the core system" << std::endl;
|
||||
bRet = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!GetAppSettings().IsConsoleSilent())
|
||||
std::cerr << "ERROR: Cannot create connection object for connecting to the core system." << std::endl;
|
||||
bRet = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Load proxy/stub for core interfaces
|
||||
if (bRet) bRet = fnLoadModule("core_ps.sdv") ? true : false;
|
||||
|
||||
@@ -428,6 +530,7 @@ void CAppControl::RunLoop()
|
||||
{
|
||||
case sdv::app::EAppContext::main:
|
||||
case sdv::app::EAppContext::isolated:
|
||||
case sdv::app::EAppContext::external:
|
||||
bLocal = false;
|
||||
break;
|
||||
case sdv::app::EAppContext::maintenance:
|
||||
@@ -490,8 +593,22 @@ void CAppControl::Shutdown(/*in*/ bool bForce)
|
||||
// Disable automatic configuration saving.
|
||||
m_bAutoSaveConfig = false;
|
||||
|
||||
// Update the application settings file
|
||||
GetAppSettings().SaveSettingsFile();
|
||||
// TODO EVE: This is likely not wanted
|
||||
//// Update the application settings file
|
||||
//GetAppSettings().SaveSettings();
|
||||
|
||||
// Disconnect all automatic connections.
|
||||
for (const auto& rprConnectObject : m_vecConnections)
|
||||
{
|
||||
// If there is a registered link ID, unlink the ID.
|
||||
if (rprConnectObject.second)
|
||||
GetRepository().UnlinkCoreRepository(rprConnectObject.second);
|
||||
|
||||
sdv::TInterfaceAccessPtr ptrClient = GetRepository().GetObject(rprConnectObject.first);
|
||||
sdv::com::IClientConnect* pClientConnect = ptrClient.GetInterface<sdv::com::IClientConnect>();
|
||||
if (pClientConnect) pClientConnect->Disconnect();
|
||||
}
|
||||
m_vecConnections.clear();
|
||||
|
||||
// Destroy all objects... this should also remove any registered services, except the custom logger.
|
||||
GetRepository().DestroyAllObjects(std::vector<std::string>({GetAppSettings().GetLoggerClass()}), bForce);
|
||||
@@ -537,7 +654,7 @@ void CAppControl::Shutdown(/*in*/ bool bForce)
|
||||
}
|
||||
|
||||
// End trace streaming
|
||||
if (GetAppSettings().IsMainApplication())
|
||||
if (!GetAppSettings().RedirectMonitorToConsole() && GetAppSettings().IsMainApplication())
|
||||
{
|
||||
std::cout << "**********************************************" << std::endl;
|
||||
|
||||
@@ -551,6 +668,7 @@ void CAppControl::Shutdown(/*in*/ bool bForce)
|
||||
m_bAutoSaveConfig = false;
|
||||
m_bEnableAutoSave = false;
|
||||
GetAppSettings().Reset();
|
||||
m_optpermission.reset();
|
||||
}
|
||||
|
||||
void CAppControl::RequestShutdown()
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
#include <support/component_impl.h>
|
||||
#include <support/interface_ptr.h>
|
||||
#include "../../global/tracefifo/trace_fifo.h"
|
||||
#include "permission_control.h"
|
||||
#include <optional>
|
||||
|
||||
/**
|
||||
* @brief Application control class.
|
||||
@@ -140,17 +142,20 @@ private:
|
||||
*/
|
||||
void BroadcastOperationState(sdv::app::EAppOperationState eState);
|
||||
|
||||
sdv::app::EAppOperationState m_eState = sdv::app::EAppOperationState::not_started; ///< The current operation state.
|
||||
sdv::app::IAppEvent* m_pEvent = nullptr; ///< Pointer to the app event interface.
|
||||
sdv::core::TModuleID m_tLoggerModuleID = 0; ///< ID of the logger module.
|
||||
bool m_bEnableAutoSave = false; ///< When set and when enabled in the system settings, allows
|
||||
///< the automatic saving of the configuration.
|
||||
bool m_bRunLoop = false; ///< Used to detect end of running loop function.
|
||||
std::filesystem::path m_pathLockFile; ///< Lock file path name.
|
||||
FILE* m_pLockFile = nullptr; ///< Lock file to test for other instances.
|
||||
CTraceFifoStdBuffer m_fifoTraceStreamBuffer; ///< Trace stream buffer to redirect std::log, std::out and
|
||||
///< std::err when running as service.
|
||||
bool m_bAutoSaveConfig = false; ///< System setting for automatic saving of the user configuration.
|
||||
sdv::app::EAppOperationState m_eState = sdv::app::EAppOperationState::not_started; ///< The current operation state.
|
||||
sdv::app::IAppEvent* m_pEvent = nullptr; ///< Pointer to the app event interface.
|
||||
sdv::core::TModuleID m_tLoggerModuleID = 0; ///< ID of the logger module.
|
||||
bool m_bEnableAutoSave = false; ///< When set and when enabled in the system settings, allows
|
||||
///< the automatic saving of the configuration.
|
||||
bool m_bRunLoop = false; ///< Used to detect end of running loop function.
|
||||
std::filesystem::path m_pathLockFile; ///< Lock file path name.
|
||||
FILE* m_pLockFile = nullptr; ///< Lock file to test for other instances.
|
||||
CTraceFifoStdBuffer m_fifoTraceStreamBuffer; ///< Trace stream buffer to redirect std::log, std::out and
|
||||
///< std::err when running as service.
|
||||
bool m_bAutoSaveConfig = false; ///< System setting for automatic saving of the user configuration.
|
||||
std::vector<std::pair<std::string, sdv::core::TLinkID>> m_vecConnections; ///< Connection objects that were successfully
|
||||
///< connected.
|
||||
std::optional<CAccessPermission> m_optpermission; ///< Main access permission for the core.
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -62,6 +62,10 @@
|
||||
* #Console output
|
||||
* [Console]
|
||||
* Report = "Silent" # Either "Silent", "Normal" or "Verbose" for no, normal or extensive messages.
|
||||
* RedirectMon = true # When set, redirects messages to the console instead of the monitor application in main mode.
|
||||
*
|
||||
* [Connections]
|
||||
* Retries = 5 # The amount of retries when trying to connect (value between 3 and 30, default is 5).
|
||||
*
|
||||
* # Search directories
|
||||
* @endcode
|
||||
@@ -103,11 +107,40 @@
|
||||
* # Example
|
||||
* # AppConfig = "app_config.toml"
|
||||
* AppConfig = ""
|
||||
*
|
||||
* # A list of zero or more listener definitions that should be instantiated during startup of the
|
||||
* # main application. If no listener definition is available, the default shared-memory listener
|
||||
* # is being instantiated.
|
||||
* #
|
||||
* # [[Settings.Listener]]
|
||||
* # Name = ""
|
||||
* # [Settings.Listerner.Provider]
|
||||
* # Name = ""
|
||||
* # [Settings.Listener.IpcChannel]
|
||||
*
|
||||
* # A list of zero or more client connections that should be instantiated during startup.
|
||||
* #
|
||||
* # [[Settings.Connection]]
|
||||
* # Name = ""
|
||||
* # [Settings.Connection.Provider]
|
||||
* # Name = ""
|
||||
* # [Settings.Listener.IpcChannel]
|
||||
* @endcode
|
||||
*/
|
||||
class CAppSettings : public sdv::IInterfaceAccess, public sdv::app::IAppContext, public sdv::IAttributes
|
||||
class CAppSettings : public sdv::CSdvParamMap, public sdv::IInterfaceAccess, public sdv::app::IAppContext,
|
||||
public sdv::app::IAppSettingsPersist, public sdv::app::IAppConnections
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Console reporting.
|
||||
*/
|
||||
enum class EAppConsoleReporting
|
||||
{
|
||||
silent, ///< No reporting by application control (default)
|
||||
normal, ///< Normal reporting by application control
|
||||
verbose, ///< Extensive reporting by application control
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Constructor
|
||||
*/
|
||||
@@ -118,11 +151,71 @@ public:
|
||||
*/
|
||||
~CAppSettings();
|
||||
|
||||
#ifndef DOXYGEN_IGNORE
|
||||
// Interface map
|
||||
BEGIN_SDV_INTERFACE_MAP()
|
||||
SDV_INTERFACE_ENTRY(sdv::app::IAppContext)
|
||||
END_SDV_INTERFACE_MAP()
|
||||
|
||||
// Application mode labels
|
||||
BEGIN_SDV_LABEL_MAP(sdv::app::EAppContext)
|
||||
SDV_LABEL_ENTRY(sdv::app::EAppContext::no_context, "Undefined")
|
||||
SDV_LABEL_ENTRY(sdv::app::EAppContext::standalone, "Standalone")
|
||||
SDV_LABEL_ENTRY(sdv::app::EAppContext::external, "External")
|
||||
SDV_LABEL_ENTRY(sdv::app::EAppContext::isolated, "Isolated")
|
||||
SDV_LABEL_ENTRY(sdv::app::EAppContext::main, "Main")
|
||||
SDV_LABEL_ENTRY(sdv::app::EAppContext::essential, "Essential")
|
||||
SDV_LABEL_ENTRY(sdv::app::EAppContext::maintenance, "Maintenance")
|
||||
END_SDV_LABEL_MAP()
|
||||
|
||||
// Log severity labels
|
||||
BEGIN_SDV_LABEL_MAP(sdv::core::ELogSeverity)
|
||||
SDV_LABEL_ENTRY(sdv::core::ELogSeverity::trace, "Trace")
|
||||
SDV_LABEL_ENTRY(sdv::core::ELogSeverity::debug, "Debug")
|
||||
SDV_LABEL_ENTRY(sdv::core::ELogSeverity::info, "Info")
|
||||
SDV_LABEL_ENTRY(sdv::core::ELogSeverity::warning, "Warning")
|
||||
SDV_LABEL_ENTRY(sdv::core::ELogSeverity::error, "Error")
|
||||
SDV_LABEL_ENTRY(sdv::core::ELogSeverity::fatal, "Fatal")
|
||||
END_SDV_LABEL_MAP()
|
||||
|
||||
// Console reporting labels
|
||||
BEGIN_SDV_LABEL_MAP(EAppConsoleReporting)
|
||||
SDV_LABEL_ENTRY(EAppConsoleReporting::normal, "Normal")
|
||||
SDV_LABEL_ENTRY(EAppConsoleReporting::silent, "Silent")
|
||||
SDV_LABEL_ENTRY(EAppConsoleReporting::verbose, "Verbose")
|
||||
END_SDV_LABEL_MAP()
|
||||
|
||||
// Parameter map
|
||||
BEGIN_SDV_PARAM_MAP()
|
||||
SDV_PARAM_SET_READONLY()
|
||||
SDV_PARAM_GROUP("LogHandler")
|
||||
SDV_PARAM_ENTRY(m_ssLoggerClass, "Class", "", "", "Component class name of a custom logger")
|
||||
SDV_PARAM_ENTRY(m_pathLoggerModule, "Path", "", "", "Component module path of a custom logger")
|
||||
SDV_PARAM_ENTRY(m_ssProgramTag, "Tag", "", "", "Program tag to use instead of the name SDV_LOG_<pid>")
|
||||
SDV_PARAM_ENUM_ENTRY(m_eSeverityFilter, "Filter", sdv::core::ELogSeverity::info,
|
||||
"Lowest severity filter to use when logging (Trace, Debug, Info, Warning, Error, Fatal)")
|
||||
SDV_PARAM_ENUM_ENTRY(m_eSeverityViewFilter, "ViewFilter", sdv::core::ELogSeverity::error,
|
||||
"Lowest severity filter to use when logging (Trace, Debug, Info, Warning, Error, Fatal)")
|
||||
|
||||
SDV_PARAM_GROUP("Application")
|
||||
SDV_PARAM_ENUM_ENTRY(m_eAppContextType, "Mode", sdv::app::EAppContext::no_context, "Application mode")
|
||||
SDV_PARAM_ENTRY(m_uiInstanceID, "Instance", 0u, "", "System instance ID")
|
||||
SDV_PARAM_ENTRY(m_pathRootDir, "RootDir", "", "", "Location of user component root directory")
|
||||
SDV_PARAM_ENTRY(m_pathInstallDir, "InstallDir", "", "", "Location of user component installations")
|
||||
SDV_PARAM_ENTRY(m_pathPlatformConfig, "PlatformConfig", "", "", "The platform abstraction configuration file")
|
||||
SDV_PARAM_ENTRY(m_pathVehIfcConfig, "VehIfcConfig", "", "", "The vehicle interface configuration file")
|
||||
SDV_PARAM_ENTRY(m_pathVehAbstrConfig, "VehAbstrConfig", "", "", "The vehicle abstraction configuration file")
|
||||
SDV_PARAM_ENTRY(m_pathUserConfig, "AppConfig", "", "", "The application configuration file")
|
||||
|
||||
SDV_PARAM_GROUP("Console")
|
||||
SDV_PARAM_ENUM_ENTRY(m_eConsoleReporting, "Reporting", EAppConsoleReporting::normal, "Console reporting (Normal, Silent, Verbose)")
|
||||
SDV_PARAM_ENTRY(m_bRedirectMon, "RedirectMon", false, "", "Redirect messages from the monitor onto the console.")
|
||||
|
||||
SDV_PARAM_GROUP("Communication")
|
||||
SDV_PARAM_ENTRY(m_ssDefaultComProvider, "DefaultProvider", "", "", "Name of the default communication provider")
|
||||
END_SDV_PARAM_MAP()
|
||||
#endif // !defined DOXYGEN_IGNORE
|
||||
|
||||
/**
|
||||
* @brief Process the application starrtup configuration.
|
||||
* @param[in] rssConfig Reference to the configuration content (TOML format).
|
||||
@@ -131,19 +224,20 @@ public:
|
||||
bool ProcessAppStartupConfig(const sdv::u8string& rssConfig);
|
||||
|
||||
/**
|
||||
* @brief Load the application settings file.
|
||||
* @brief Load the application settings file. Overload of sdv::app::IAppSettingsPersist::LoadSettings.
|
||||
* @attention Only works if the application is running in main, isolation or maintenance mode.
|
||||
* @remarks When there is no settings file, this is not an error. Default settings will be assumed.
|
||||
* @return Returns whether the loading was successful.
|
||||
*/
|
||||
bool LoadSettingsFile();
|
||||
bool LoadSettings() override;
|
||||
|
||||
/**
|
||||
* @brief Save the application settings file (or create when not existing yet).
|
||||
* @attention Only works if the application is running in main, isolation or maintenance mode.
|
||||
* Overload of sdv::app::IAppSettingsPersist::SaveSettings.
|
||||
* @attention Only works if the application is running in maintenance mode.
|
||||
* @return Returns whether the saving was successful.
|
||||
*/
|
||||
bool SaveSettingsFile();
|
||||
bool SaveSettings() override;
|
||||
|
||||
/**
|
||||
* @brief Return whether the current application is the main application.
|
||||
@@ -181,27 +275,33 @@ public:
|
||||
*/
|
||||
bool IsExternalApplication() const;
|
||||
|
||||
/**
|
||||
* @brief Set the application context type.
|
||||
* @param[in] eContextType The application context type to set.
|
||||
*/
|
||||
void SetContextType(sdv::app::EAppContext eContextType);
|
||||
|
||||
/**
|
||||
* @brief Return the application context mode. Overload of sdv::app::IAppContext::GetContextType.
|
||||
* @return The context mode.
|
||||
*/
|
||||
sdv::app::EAppContext GetContextType() const override;
|
||||
|
||||
/**
|
||||
* @brief Set the instance ID.
|
||||
* @param uiID The instance ID to set.
|
||||
*/
|
||||
void SetInstanceID(uint32_t uiID);
|
||||
|
||||
/**
|
||||
* @brief Return the core instance ID. Overload of sdv::app::IAppContext::GetContextType.
|
||||
* @details Get the instance. If not otherwise specified, the current instance depends on whether the application is running
|
||||
* as main or isolated application, in which case the instance is 1000. In all other cases the instance is 0. An instance
|
||||
* ID can be supplied through the app startup configuration.
|
||||
* as main, isolated or maintenance application, in which case the instance is 1000. In all other cases the instance is 0. An
|
||||
* instance ID can be supplied through the app startup configuration.
|
||||
* @return The core instance ID.
|
||||
*/
|
||||
uint32_t GetInstanceID() const override;
|
||||
|
||||
/**
|
||||
* @brief Return the number of retries to establish a connection. Overload of sdv::app::IAppContext::GetRetries.
|
||||
* @return Number of retries.
|
||||
*/
|
||||
uint32_t GetRetries() const override;
|
||||
|
||||
/**
|
||||
* @brief Get the class name of a logger service, if specified in the application startup configuration.
|
||||
* @return The logger class name.
|
||||
@@ -244,6 +344,30 @@ public:
|
||||
*/
|
||||
bool IsConsoleVerbose() const;
|
||||
|
||||
/**
|
||||
* @brief Set the application console reporting status.
|
||||
* @param eReporting The console reporting status to set.
|
||||
*/
|
||||
void SetConsoleReporting(EAppConsoleReporting eReporting);
|
||||
|
||||
/**
|
||||
* @brief Get the current application console reporting status.
|
||||
* @return The console reporting status of the application.
|
||||
*/
|
||||
EAppConsoleReporting GetConsoleReporting() const;
|
||||
|
||||
/**
|
||||
* @brief Redirect the messages monitored for main application onto the console.
|
||||
* @return Returns whether redirection is switched on.
|
||||
*/
|
||||
bool RedirectMonitorToConsole() const;
|
||||
|
||||
/**
|
||||
* @brief Get the framework directory for the application.
|
||||
* @return The location of framework directory.
|
||||
*/
|
||||
std::filesystem::path GetFrameworkDir() const;
|
||||
|
||||
/**
|
||||
* @brief Get the root directory for the application.
|
||||
* @remarks Is only valid when used in main, isolated and maintenance applications.
|
||||
@@ -279,6 +403,7 @@ public:
|
||||
/**
|
||||
* @brief Get the stored or default configuration path name.
|
||||
* @attention Setting a path is only valid when running as main application.
|
||||
* @param[in] eType The configuration type to get the path for.
|
||||
* @return The path name dependent on the configuration type. If no path name was configured, the default path name is returned.
|
||||
*/
|
||||
std::filesystem::path GetConfigPath(EConfigType eType) const;
|
||||
@@ -326,33 +451,80 @@ public:
|
||||
bool RemoveUserConfigPath();
|
||||
|
||||
/**
|
||||
* @brief Get a sequence with the available attribute names. Overload of sdv::IAttributes::GetNames.
|
||||
* @return The sequence of attribute names.
|
||||
* @brief Get the default communication provider (extracted from listener and connection settings).
|
||||
* @return Name of the default communication provider.
|
||||
*/
|
||||
virtual sdv::sequence<sdv::u8string> GetNames() const override;
|
||||
const std::string& GetDefaultComProvider() const;
|
||||
|
||||
/**
|
||||
* @brief Get the attribute value. Overload of sdv::IAttributes::Get.
|
||||
* @param[in] ssAttribute Name of the attribute.
|
||||
* @return The attribute value or an empty any-value if the attribute wasn't found or didn't have a value.
|
||||
* @brief Get a sequence with listener names. Overload of sdv::app::IAppConnections::GetListeners.
|
||||
* @return Sequence with listener name strings.
|
||||
*/
|
||||
virtual sdv::any_t Get(/*in*/ const sdv::u8string& ssAttribute) const override;
|
||||
sdv::sequence<sdv::u8string> GetListeners() const override;
|
||||
|
||||
/**
|
||||
* @brief Set the attribute value. Overload of sdv::IAttributes::Set.
|
||||
* @param[in] ssAttribute Name of the attribute.
|
||||
* @param[in] anyAttribute Attribute value to set.
|
||||
* @return Returns 'true' when setting the attribute was successful or 'false' when the attribute was not found or the
|
||||
* attribute is read-only or another error occurred.
|
||||
* @brief Get the listener configuration. Overload of sdv::app::IAppConnections::GetListenerConfig.
|
||||
* @param[in] ssName Name of the listener.
|
||||
* @return String containing the listener configuration.
|
||||
*/
|
||||
virtual bool Set(/*in*/ const sdv::u8string& ssAttribute, /*in*/ sdv::any_t anyAttribute) override;
|
||||
sdv::u8string GetListenerConfig(/*in*/ const sdv::u8string& ssName) const override;
|
||||
|
||||
/**
|
||||
* @brief Get the attribute flags belonging to a certain attribute. Overload of sdv::IAttributes::GetFlags.
|
||||
* @param[in] ssAttribute Name of the attribute.
|
||||
* @return Returns the attribute flags (zero or more EAttributeFlags flags) or 0 when the attribute could not be found.
|
||||
* @brief Add or update a listener configuration. Overload of sdv::app::IAppConnections::AddListenerConfig.
|
||||
* @remarks Only accessible when the application runs in maintenance mode.
|
||||
* @param[in] ssName Name of the listener configuration.
|
||||
* @param[in] ssConfig The configuration string for the listener.
|
||||
* @return Returns whether the listener could be added (fails when the listener already exists).
|
||||
*/
|
||||
virtual uint32_t GetFlags(/*in*/ const sdv::u8string& ssAttribute) const override;
|
||||
bool AddListenerConfig(/*in*/ const sdv::u8string& ssName, /*in*/ const sdv::u8string& ssConfig) override;
|
||||
|
||||
/**
|
||||
* @brief Remove a listener configuration with the provided name. Overload of sdv::app::IAppConnections::RemoveListenerConfig.
|
||||
* @remarks Only accessible when the application runs in maintenance mode.
|
||||
* @param[in] ssName Name of the listener configuration.
|
||||
* @return Returns whether the removal was successful.
|
||||
*/
|
||||
bool RemoveListenerConfig(/*in*/ const sdv::u8string& ssName) override;
|
||||
|
||||
/**
|
||||
* @brief Get a sequence with connection names. Overload of sdv::app::IAppConnections::GetConnections.
|
||||
* @return Sequence with connection name strings.
|
||||
*/
|
||||
sdv::sequence<sdv::u8string> GetConnections() const override;
|
||||
|
||||
/**
|
||||
* @brief Get the connection configuration. Overload of sdv::app::IAppConnections::GetConnectionConfig.
|
||||
* @param[in] ssName Name of the connection.
|
||||
* @return String containing the connection configuration.
|
||||
*/
|
||||
sdv::u8string GetConnectionConfig(/*in*/ const sdv::u8string& ssName) const override;
|
||||
|
||||
/**
|
||||
* @brief Add or update a connection configuration. Overload of sdv::app::IAppConnections::AddConnectionConfig.
|
||||
* @remarks Only accessible when the application runs in maintenance mode.
|
||||
* @param[in] ssName Name of the connection configuration.
|
||||
* @param[in] ssConfig The configuration string for the connection.
|
||||
* @param[in] ssInsertBefore Reference to the string to connection to insert the the new connection before, or empty when the
|
||||
* the new connection should be placed at the end.
|
||||
* @return Returns whether the connection could be added (fails when the connection already exists).
|
||||
*/
|
||||
bool AddConnectionConfig(/*in*/ const sdv::u8string& ssName, /*in*/ const sdv::u8string& ssConfig,
|
||||
/*in*/ const sdv::u8string& ssInsertBefore = std::string()) override;
|
||||
|
||||
/**
|
||||
* @brief Remove a connection configuration with the provided name. Overload of
|
||||
* sdv::app::IAppConnections::RemoveConnectionConfig.
|
||||
* @remarks Only accessible when the application runs in maintenance mode.
|
||||
* @param[in] ssName Name of the connection configuration.
|
||||
* @return Returns whether the removal was successful.
|
||||
*/
|
||||
bool RemoveConnectionConfig(/*in*/ const sdv::u8string& ssName) override;
|
||||
|
||||
/**
|
||||
* @brief Return the number of retries to establish a connection. Overload of sdv::app::IAppConnections::GetRetries.
|
||||
* @return Number of retries.
|
||||
*/
|
||||
uint32_t GetConnectRetries() const override;
|
||||
|
||||
/**
|
||||
* @brief Reset the settings after a shutdown.
|
||||
@@ -360,26 +532,42 @@ public:
|
||||
void Reset();
|
||||
|
||||
private:
|
||||
sdv::app::EAppContext m_eContextMode = sdv::app::EAppContext::no_context; ///< The application is running as...
|
||||
uint32_t m_uiInstanceID = 0u; ///< Instance number.
|
||||
uint32_t m_uiRetries = 0u; ///< Number of retries to establish a connection.
|
||||
/**
|
||||
* @brief Get the default listener config.
|
||||
* @return String containing the TOML with the listener config.
|
||||
*/
|
||||
std::string DefaultListenerConfig() const;
|
||||
|
||||
/**
|
||||
* @brief Get the default connection config.
|
||||
* @return String containing the TOML with the connection config.
|
||||
*/
|
||||
std::string DefaultConnectionConfig() const;
|
||||
|
||||
sdv::app::EAppContext m_eAppContextType = sdv::app::EAppContext::no_context; ///< The application is running as...
|
||||
uint32_t m_uiInstanceID = 0u; ///< Instance number (default 1000, but only after startup).
|
||||
uint32_t m_uiConnectRetries = 5u; ///< Number of retries to establish a connection.
|
||||
std::string m_ssLoggerClass; ///< Class name of a logger service.
|
||||
std::filesystem::path m_pathLoggerModule; ///< Module name of a custom logger.
|
||||
std::string m_ssProgramTag; ///< Program tag to use when logging.
|
||||
sdv::core::ELogSeverity m_eSeverityFilter = sdv::core::ELogSeverity::info; ///< Severity level filter while logging.
|
||||
sdv::core::ELogSeverity m_eSeverityViewFilter = sdv::core::ELogSeverity::error; ///< Severity level filter while logging.
|
||||
bool m_bSilent = false; ///< When set, no console reporting takes place.
|
||||
bool m_bVerbose = false; ///< When set, extensive console reporting takes place.
|
||||
EAppConsoleReporting m_eConsoleReporting = EAppConsoleReporting::normal; ///< Console reporting
|
||||
bool m_bRedirectMon = false; ///< When set, redirect the message from the monitor onto console.
|
||||
std::filesystem::path m_pathFrameworkDir; ///< Location of framework component directory.
|
||||
std::filesystem::path m_pathRootDir; ///< Location of user component root directory.
|
||||
std::filesystem::path m_pathInstallDir; ///< Location of user component installations (root with instance).
|
||||
std::filesystem::path m_pathPlatformConfig; ///< The platform configuration from the settings file.
|
||||
std::filesystem::path m_pathVehIfcConfig; ///< The vehicle interface configuration from the settings file.
|
||||
std::filesystem::path m_pathVehAbstrConfig; ///< The vehicle abstraction configuration from the settings file.
|
||||
std::filesystem::path m_pathUserConfig; ///< The user configuration from the settings file.
|
||||
bool m_bPlatformConfig = false; ///< Platform config was explicitly enabled/disabled.
|
||||
bool m_bVehIfcConfig = false; ///< Vehicle interface config was explicitly enabled/disabled.
|
||||
bool m_bVehAbstrConfig = false; ///< Vehicle abstraction config was explicitly enabled/disabled.
|
||||
bool m_bUserConfig = false; ///< User config was explicitly enabled/disabled.
|
||||
bool m_bUpdatePlatformConfig = false; ///< Platform config was explicitly marked for update.
|
||||
bool m_bUpdateVehIfcConfig = false; ///< Vehicle interface config was explicitly marked for update.
|
||||
bool m_bUpdateVehAbstrConfig = false; ///< Vehicle abstraction config was explicitly marked for update.
|
||||
bool m_bUpdateUserConfig = false; ///< User config was explicitly marked for update.
|
||||
std::map<std::string, std::string> m_mapListeners; ///< Map with listener configurations.
|
||||
std::vector<std::pair<std::string, std::string>> m_vecConnections; ///< Vector with connection configurations.
|
||||
std::string m_ssDefaultComProvider; ///< Name of the default communication provider.
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -398,7 +586,11 @@ public:
|
||||
|
||||
// Interface map
|
||||
BEGIN_SDV_INTERFACE_MAP()
|
||||
SDV_INTERFACE_ENTRY_MEMBER(sdv::IAttributes, GetAppSettings())
|
||||
SDV_INTERFACE_SET_SECTION_CONDITION(GetAppSettings().IsMaintenanceApplication(), 1)
|
||||
SDV_INTERFACE_SECTION(1)
|
||||
SDV_INTERFACE_ENTRY_MEMBER(sdv::app::IAppSettingsPersist, GetAppSettings())
|
||||
SDV_INTERFACE_ENTRY_MEMBER(sdv::app::IAppConnections, GetAppSettings())
|
||||
SDV_INTERFACE_DEFAULT_SECTION()
|
||||
END_SDV_INTERFACE_MAP()
|
||||
|
||||
// Object declarations
|
||||
@@ -406,6 +598,11 @@ public:
|
||||
DECLARE_OBJECT_CLASS_NAME("AppSettingsService")
|
||||
DECLARE_OBJECT_SINGLETON()
|
||||
|
||||
// Parameter map
|
||||
BEGIN_SDV_PARAM_MAP()
|
||||
SDV_PARAM_CHAIN_MEMBER(GetAppSettings())
|
||||
END_SDV_PARAM_MAP()
|
||||
|
||||
/**
|
||||
* @brief Get access to the application settings.
|
||||
* @return Returns the one global instance of the application config.
|
||||
|
||||
@@ -38,6 +38,12 @@
|
||||
#error OS is not supported!
|
||||
#endif
|
||||
|
||||
namespace
|
||||
{
|
||||
constexpr float kMaxBLOBSize = (24*1024*1024) + 25000000;
|
||||
}
|
||||
|
||||
|
||||
CInstallComposer::~CInstallComposer()
|
||||
{}
|
||||
|
||||
@@ -1019,10 +1025,16 @@ uint32_t CInstallComposer::SerializeModuleBLOB(uint32_t uiChecksumInit, sdv::poi
|
||||
// Calculate the size of the BLOB (including padding and checksum)
|
||||
sdv::installation::SPackageBLOBChecksum sBLOBChecksum{};
|
||||
size_t nSize = 0;
|
||||
sdv::ser_size(sBLOB, nSize);
|
||||
sdv::ser_size(sBLOB, nSize);
|
||||
sdv::ser_size(sBLOBChecksum, nSize);
|
||||
if (nSize % 8) nSize += 8 - nSize % 8;
|
||||
sBLOB.uiBLOBSize = static_cast<uint32_t>(nSize);
|
||||
if(sBLOB.uiBLOBSize > kMaxBLOBSize)
|
||||
{
|
||||
sdv::installation::XPackageSizeExceeded exception;
|
||||
exception.ssFileName = sBLOB.sFileDesc.ssFileName;
|
||||
throw exception;
|
||||
}
|
||||
|
||||
// Serialize the BLOB
|
||||
sdv::serializer<sdv::GetPlatformEndianess(), sdv::crcCRC32C> serializer;
|
||||
@@ -1246,7 +1258,7 @@ sdv::installation::SPackageBLOB CInstallComposer::DeserializeBLOB(std::ifstream&
|
||||
|
||||
// Extend the buffer and read the BLOB data (after the resize, the psPartialBLOB pointer could be invalidated).
|
||||
uint32_t uiBLOBSize = psPartialBLOB->uiBLOBSize;
|
||||
if (uiBLOBSize > 26*1024*1024) // 26 instead of 24 becasue of door example
|
||||
if (uiBLOBSize > kMaxBLOBSize)
|
||||
throw sdv::installation::XIncompatiblePackage(); // Safety
|
||||
ptrBLOB.resize(uiBLOBSize);
|
||||
if (!ptrBLOB)
|
||||
|
||||
@@ -23,11 +23,11 @@ CIsoMonitor::~CIsoMonitor()
|
||||
GetAppControl().RequestShutdown();
|
||||
}
|
||||
|
||||
void CIsoMonitor::Initialize(/*in*/ const sdv::u8string& ssObjectConfig)
|
||||
void CIsoMonitor::Initialize(/*in*/ const sdv::SObjectInfo& sObjectInfo)
|
||||
{
|
||||
if (m_pObjectControl)
|
||||
{
|
||||
m_pObjectControl->Initialize(ssObjectConfig);
|
||||
m_pObjectControl->Initialize(sObjectInfo);
|
||||
m_eObjectState = m_pObjectControl->GetObjectState();
|
||||
}
|
||||
else
|
||||
|
||||
@@ -42,9 +42,9 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Initialize the object. Overload of sdv::IObjectControl::Initialize.
|
||||
* @param[in] ssObjectConfig Optional configuration string.
|
||||
* @param[in] sObjectInfo The registration information of this object.
|
||||
*/
|
||||
virtual void Initialize(/*in*/ const sdv::u8string& ssObjectConfig) override;
|
||||
virtual void Initialize(/*in*/ const sdv::SObjectInfo& sObjectInfo) override;
|
||||
|
||||
/**
|
||||
* @brief Get the current state of the object. Overload of sdv::IObjectControl::GetObjectState.
|
||||
|
||||
@@ -61,6 +61,8 @@ inline bool RequestShutdown(uint32_t uiInstanceID = 1000u)
|
||||
CloseHandle(hEvent);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
std::cerr << "ERROR: Shutdown event handle is not available: " << m_ssSignalName << std::endl;
|
||||
#elif defined __unix__
|
||||
sem_t* pSemaphore = sem_open(m_ssSignalName.c_str(), 0);
|
||||
if (pSemaphore && pSemaphore != SEM_FAILED)
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "logger.h"
|
||||
#include <sstream>
|
||||
#include "../../global/exec_dir_helper.h"
|
||||
#include "app_settings.h"
|
||||
|
||||
#ifdef __unix__
|
||||
#include <syslog.h>
|
||||
@@ -38,7 +39,7 @@ CLogger::~CLogger()
|
||||
void CLogger::Log(sdv::core::ELogSeverity eSeverity, /*in*/ const sdv::u8string& ssSrcFile, /*in*/ uint32_t uiSrcLine,
|
||||
/*in*/ sdv::process::TProcessID tProcessID, /*in*/ const sdv:: u8string& ssObjectName, /*in*/ const sdv::u8string& ssMessage)
|
||||
{
|
||||
if (static_cast<uint32_t>(eSeverity) >= static_cast<uint32_t>(m_eViewFilter))
|
||||
if (GetAppSettings().IsConsoleVerbose() && static_cast<uint32_t>(eSeverity) >= static_cast<uint32_t>(m_eViewFilter))
|
||||
{
|
||||
if (tProcessID) std::clog << "[PID#" << static_cast<int64_t>(tProcessID) << "] ";
|
||||
if (!ssObjectName.empty()) std::clog << ssObjectName << " ";
|
||||
|
||||
@@ -297,6 +297,10 @@ bool CModuleInst::Load(const std::filesystem::path& rpathModule) noexcept
|
||||
}
|
||||
|
||||
m_mapClassInfo[sClass.ssName] = sClass;
|
||||
|
||||
// Add aliases?
|
||||
for (const auto& rssAlias : sClass.seqClassAliases)
|
||||
m_mapClassInfo[rssAlias] = sClass;
|
||||
}
|
||||
}
|
||||
catch (const sdv::toml::XTOMLParseException&)
|
||||
|
||||
@@ -250,7 +250,7 @@ std::shared_ptr<CModuleInst> CModuleControl::FindModuleByClass(const std::string
|
||||
}
|
||||
}
|
||||
|
||||
// For main and isolated applications, check whether the module is in one of the installation manifests.
|
||||
// For main, isolated and maintenance applications, check whether the module is in one of the installation manifests.
|
||||
auto optManifest = GetAppConfig().FindInstalledComponent(rssClass);
|
||||
if (!optManifest) return nullptr;
|
||||
std::filesystem::path pathModule = std::filesystem::u8path(static_cast<std::string>(optManifest->ssModulePath));
|
||||
@@ -378,8 +378,6 @@ std::string CModuleControl::SaveConfig(const std::set<std::filesystem::path>& /*
|
||||
|
||||
sdv::core::TModuleID CModuleControl::ContextLoad(const std::filesystem::path& rpathModule, const std::string& rssManifest)
|
||||
{
|
||||
if (GetAppSettings().IsMaintenanceApplication()) return 0; // Not allowed
|
||||
|
||||
// Run through the manifest and check for complex services, applications and utilities.
|
||||
// TODO EVE: Temporary suppression of cppcheck warning.
|
||||
// cppcheck-suppress variableScope
|
||||
@@ -462,7 +460,7 @@ void CModuleControl::AddCurrentPath()
|
||||
if (!m_lstSearchPaths.empty()) return;
|
||||
|
||||
// Add the core directory
|
||||
std::filesystem::path pathCoreDir = GetCoreDirectory().lexically_normal();
|
||||
std::filesystem::path pathCoreDir = GetAppSettings().GetFrameworkDir().lexically_normal();
|
||||
m_lstSearchPaths.push_back(pathCoreDir);
|
||||
|
||||
// Add the exe dir
|
||||
|
||||
167
sdv_services/core/permission_control.cpp
Normal file
167
sdv_services/core/permission_control.cpp
Normal file
@@ -0,0 +1,167 @@
|
||||
/********************************************************************************
|
||||
* Copyright (c) 2025-2026 ZF Friedrichshafen AG
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Apache License Version 2.0 which is available at
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Contributors:
|
||||
* Erik Verhoeven - initial API and implementation
|
||||
********************************************************************************/
|
||||
|
||||
#include "permission_control.h"
|
||||
|
||||
#if defined __GNUC__ && defined _WIN32
|
||||
thread_local std::list<std::pair<sdv::core::EAccessPermission, size_t>>&
|
||||
CPermissionControl::m_lstPermissionTracker = CreatePermissionList();
|
||||
thread_local std::map<sdv::core::TPermissionID, CAccessPermission>&
|
||||
CPermissionControl::m_mapPermissions = CreatePermissionMap();
|
||||
#else
|
||||
thread_local std::list<std::pair<sdv::core::EAccessPermission, size_t>> CPermissionControl::m_lstPermissionTracker;
|
||||
thread_local std::map<sdv::core::TPermissionID, CAccessPermission> CPermissionControl::m_mapPermissions;
|
||||
#endif
|
||||
|
||||
CPermissionControl& GetPermissionControl()
|
||||
{
|
||||
static CPermissionControl control;
|
||||
return control;
|
||||
}
|
||||
|
||||
CAccessPermission::CAccessPermission(CPermissionControl& rPermissionControl, sdv::core::EAccessPermission ePermission) :
|
||||
m_rPermissionControl(rPermissionControl), m_ePermission(ePermission)
|
||||
{
|
||||
if (m_ePermission != sdv::core::EAccessPermission::not_set)
|
||||
rPermissionControl.IncrementPermissionCount(ePermission);
|
||||
}
|
||||
|
||||
CAccessPermission::CAccessPermission(const CAccessPermission& rPermission) :
|
||||
m_rPermissionControl(rPermission.m_rPermissionControl), m_ePermission(rPermission.m_ePermission)
|
||||
{
|
||||
if (m_ePermission != sdv::core::EAccessPermission::not_set)
|
||||
m_rPermissionControl.IncrementPermissionCount(m_ePermission);
|
||||
}
|
||||
|
||||
CAccessPermission::CAccessPermission(CAccessPermission&& rPermission) :
|
||||
m_rPermissionControl(rPermission.m_rPermissionControl), m_ePermission(rPermission.m_ePermission)
|
||||
{
|
||||
rPermission.m_ePermission = sdv::core::EAccessPermission::not_set;
|
||||
}
|
||||
|
||||
CAccessPermission& CAccessPermission::operator=(const CAccessPermission& rPermission)
|
||||
{
|
||||
// Remove current permission and copy the new permission
|
||||
if (m_ePermission != sdv::core::EAccessPermission::not_set)
|
||||
m_rPermissionControl.DecrementPermissionCount(m_ePermission);
|
||||
m_ePermission = rPermission.m_ePermission;
|
||||
if (m_ePermission != sdv::core::EAccessPermission::not_set)
|
||||
m_rPermissionControl.IncrementPermissionCount(m_ePermission);
|
||||
return *this;
|
||||
}
|
||||
|
||||
CAccessPermission& CAccessPermission::operator=(CAccessPermission && rPermission)
|
||||
{
|
||||
// Remove current permission and move the new permission
|
||||
if (m_ePermission != sdv::core::EAccessPermission::not_set)
|
||||
m_rPermissionControl.DecrementPermissionCount(m_ePermission);
|
||||
m_ePermission = rPermission.m_ePermission;
|
||||
rPermission.m_ePermission = sdv::core::EAccessPermission::not_set;
|
||||
return *this;
|
||||
}
|
||||
|
||||
CAccessPermission ::~CAccessPermission()
|
||||
{
|
||||
if (m_ePermission != sdv::core::EAccessPermission::not_set)
|
||||
m_rPermissionControl.DecrementPermissionCount(m_ePermission);
|
||||
}
|
||||
|
||||
sdv::core::EAccessPermission CAccessPermission::Permission() const
|
||||
{
|
||||
return m_ePermission;
|
||||
}
|
||||
|
||||
sdv::core::TPermissionID CPermissionControl::RestrictAccessPermission(/*in*/ sdv::core::EAccessPermission ePermission)
|
||||
{
|
||||
// Unlike the CreateAccessPermission this function can only be used to restrict an already available restriction. It cannot be
|
||||
// used to set the initial restriction.
|
||||
if (m_lstPermissionTracker.empty()) return false;
|
||||
|
||||
sdv::core::TPermissionID tPermissionID = m_idgen.Generate();
|
||||
if (!tPermissionID) return 0u;
|
||||
m_mapPermissions.emplace(tPermissionID, CreatePermissionObject(ePermission));
|
||||
return tPermissionID;
|
||||
}
|
||||
|
||||
bool CPermissionControl::ReleaseAccessPermission(/*in*/ sdv::core::TPermissionID tPermissionID)
|
||||
{
|
||||
auto itPermission = m_mapPermissions.find(tPermissionID);
|
||||
if (itPermission == m_mapPermissions.end()) return false;
|
||||
m_mapPermissions.erase(itPermission);
|
||||
return true;
|
||||
}
|
||||
|
||||
sdv::core::TPermissionTransferID CPermissionControl::TransferCurrentPermission()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_mtxPermissionTransfer);
|
||||
sdv::core::TPermissionTransferID tTransferID = m_idgen.Generate();
|
||||
m_mapPermissionTransfer[tTransferID] = GetCurrentPermission();
|
||||
return tTransferID;
|
||||
}
|
||||
|
||||
sdv::core::TPermissionID CPermissionControl::SetAccessPermission(/*in*/ sdv::core::TPermissionTransferID tTransferID)
|
||||
{
|
||||
// Get the access restriction level from the transfer permission map.
|
||||
std::unique_lock<std::mutex> lock(m_mtxPermissionTransfer);
|
||||
auto itTransferPermission = m_mapPermissionTransfer.find(tTransferID);
|
||||
if (itTransferPermission == m_mapPermissionTransfer.end()) return 0u;
|
||||
sdv::core::EAccessPermission ePermission = itTransferPermission->second;
|
||||
m_mapPermissionTransfer.erase(itTransferPermission);
|
||||
|
||||
// Unlike the RestrictAccessPermission function, the SetAccessPermission function can be used to set an initial permission.
|
||||
sdv::core::TPermissionID tPermissionID = m_idgen.Generate();
|
||||
if (!tPermissionID) return 0u;
|
||||
m_mapPermissions.emplace(tPermissionID, CreatePermissionObject(ePermission));
|
||||
return tPermissionID;
|
||||
}
|
||||
|
||||
sdv::core::EAccessPermission CPermissionControl::GetCurrentPermission() const
|
||||
{
|
||||
return m_lstPermissionTracker.empty() ? sdv::core::EAccessPermission::restricted_access : m_lstPermissionTracker.front().first;
|
||||
}
|
||||
|
||||
CAccessPermission CPermissionControl::CreatePermissionObject(sdv::core::EAccessPermission ePermission)
|
||||
{
|
||||
sdv::core::EAccessPermission ePermissionLocal = std::max(ePermission, sdv::core::EAccessPermission::restricted_access);
|
||||
return CAccessPermission(*this, ePermissionLocal);
|
||||
}
|
||||
|
||||
void CPermissionControl::IncrementPermissionCount(sdv::core::EAccessPermission ePermission)
|
||||
{
|
||||
// In the permission tracker list, search for the counter for this permission or, if not existing, the next higher permission.
|
||||
auto itPermissionCounter = m_lstPermissionTracker.begin();
|
||||
for (; itPermissionCounter != m_lstPermissionTracker.end(); ++itPermissionCounter)
|
||||
{
|
||||
if (itPermissionCounter->first >= ePermission)
|
||||
break;
|
||||
}
|
||||
|
||||
// If counter doesn't exist, insert a new counter.
|
||||
if (itPermissionCounter == m_lstPermissionTracker.end() || itPermissionCounter->first > ePermission)
|
||||
itPermissionCounter = m_lstPermissionTracker.insert(itPermissionCounter, std::make_pair(ePermission, 1));
|
||||
else // exists, increase the counter
|
||||
itPermissionCounter->second++;
|
||||
}
|
||||
|
||||
void CPermissionControl::DecrementPermissionCount(sdv::core::EAccessPermission ePermission)
|
||||
{
|
||||
// Find the permission counter
|
||||
auto itPermissionCounter = std::find_if(m_lstPermissionTracker.begin(), m_lstPermissionTracker.end(), [&](const auto& rprPermissionCount)
|
||||
{ return rprPermissionCount.first == ePermission; });
|
||||
if (itPermissionCounter == m_lstPermissionTracker.end()) return;
|
||||
|
||||
// Reduce the counter and if reaching zero, erase the counter
|
||||
itPermissionCounter->second--;
|
||||
if (!itPermissionCounter->second)
|
||||
m_lstPermissionTracker.erase(itPermissionCounter);
|
||||
}
|
||||
263
sdv_services/core/permission_control.h
Normal file
263
sdv_services/core/permission_control.h
Normal file
@@ -0,0 +1,263 @@
|
||||
/********************************************************************************
|
||||
* Copyright (c) 2025-2026 ZF Friedrichshafen AG
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Apache License Version 2.0 which is available at
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Contributors:
|
||||
* Erik Verhoeven - initial API and implementation
|
||||
********************************************************************************/
|
||||
|
||||
#ifndef PERMISSION_CONTROL_H
|
||||
#define PERMISSION_CONTROL_H
|
||||
|
||||
#include <interfaces/permission.h>
|
||||
#include <list>
|
||||
#include <support/component_impl.h>
|
||||
|
||||
#include "../../global/unique_id.h"
|
||||
|
||||
// Forward declaration
|
||||
class CPermissionControl;
|
||||
|
||||
/**
|
||||
* @brief Use a list to create a stack, which allows elements to be deleted.
|
||||
*/
|
||||
using TPermissionStack = std::list<sdv::core::EAccessPermission>;
|
||||
|
||||
/**
|
||||
* @brief Permission class for the requested access. This class manages the lifetime of the permissions.
|
||||
*/
|
||||
class CAccessPermission
|
||||
{
|
||||
private:
|
||||
/// Permission control is allowed to create an access permission object.
|
||||
friend CPermissionControl;
|
||||
|
||||
/**
|
||||
* @brief Constructor
|
||||
* @param[in] rPermissionControl Reference to permission control.
|
||||
* @param[in] ePermission The requested access permission.
|
||||
*/
|
||||
CAccessPermission(CPermissionControl& rPermissionControl, sdv::core::EAccessPermission ePermission);
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
~CAccessPermission();
|
||||
|
||||
/**
|
||||
* @brief Copy constructor
|
||||
* @param[in] rPermission Reference to the permission object to copy from.
|
||||
*/
|
||||
CAccessPermission(const CAccessPermission& rPermission);
|
||||
|
||||
/**
|
||||
* @brief Move constructor.
|
||||
* @param[in] rPermission Reference to the permission object to move from.
|
||||
*/
|
||||
CAccessPermission(CAccessPermission&& rPermission);
|
||||
|
||||
/**
|
||||
* @brief Copy assignment operator.
|
||||
* @param[in] rPermission Reference to the permission object to move from.
|
||||
* @return Reference to this access permission object.
|
||||
*/
|
||||
CAccessPermission& operator=(const CAccessPermission& rPermission);
|
||||
|
||||
/**
|
||||
* @brief Move assignment operator.
|
||||
* @param[in] rPermission Reference to the permission object to move from.
|
||||
* @return Reference to this access permission object.
|
||||
*/
|
||||
CAccessPermission& operator=(CAccessPermission&& rPermission);
|
||||
|
||||
/**
|
||||
* @brief Get the permission level assigned to this object.
|
||||
* @remarks This might not correspond to the current permission level available for this thread.
|
||||
* @return The permission level.
|
||||
*/
|
||||
sdv::core::EAccessPermission Permission() const;
|
||||
|
||||
private:
|
||||
CPermissionControl& m_rPermissionControl; ///< Reference to the permission control.
|
||||
sdv::core::EAccessPermission m_ePermission; ///< Requested permission.
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Permission control provides a simple access mechanism to allow the management of access to system functions.
|
||||
*/
|
||||
class CPermissionControl : public sdv::IInterfaceAccess, public sdv::core::IPermissionControl
|
||||
{
|
||||
public:
|
||||
/// Access permission class can access the permission control.
|
||||
friend CAccessPermission;
|
||||
|
||||
/**
|
||||
* @brief Default constructor
|
||||
*/
|
||||
CPermissionControl() = default;
|
||||
|
||||
// Interface map
|
||||
BEGIN_SDV_INTERFACE_MAP()
|
||||
SDV_INTERFACE_ENTRY(sdv::core::IPermissionControl)
|
||||
END_SDV_INTERFACE_MAP()
|
||||
|
||||
/**
|
||||
* @brief Restrict the access permission for the current thread. Overload of
|
||||
* sdv::core.:IPermissionControl::RestrictAccessPermission.
|
||||
* @remarks The access restriction will be assigned to the current thread and combined with previous and future
|
||||
* permissions. The lowest assigned permission will determine the actual access permission for the current thread.
|
||||
* @remarks The access restriction will stay in effect until it is released by the function ReleaseAccessPermission.
|
||||
* @remarks A newly created thread has fully restricted access. This cannot be changed using this function. Use an access
|
||||
* restriction transfer from one thread to this thread to set a higher level of access permission.
|
||||
* @param[in] ePermission The permission to restrict to.
|
||||
* @return The permission ID for this restriction or 0 when the access permission could not be set. Use the
|
||||
* ReleaseAccessPermission to release the restriction again.
|
||||
*/
|
||||
virtual sdv::core::TPermissionID RestrictAccessPermission(/*in*/ sdv::core::EAccessPermission ePermission) override;
|
||||
|
||||
/**
|
||||
* @brief Release a previously set access restriction for the current thread. Overload of
|
||||
* sdv::core::IPermissionControl::ReleaseAccessPermission.
|
||||
* @param[in] tPermissionID The ID of the access restriction previously set for the current thread.
|
||||
* @return Returns whether the restriction could be released successfully.
|
||||
*/
|
||||
virtual bool ReleaseAccessPermission(/*in*/ sdv::core::TPermissionID tPermissionID) override;
|
||||
|
||||
/**
|
||||
* @brief Prepare to transfer the access restriction from the current thread.Overload of
|
||||
* sdv::core.:IPermissionControl::TransferCurrentPermission.
|
||||
* @return The ID of the transfer object containing the current access permissions or 0 when the transfer preparation has
|
||||
* failed.
|
||||
*/
|
||||
virtual sdv::core::TPermissionTransferID TransferCurrentPermission() override;
|
||||
|
||||
/**
|
||||
* @brief Set the access permission using a transfer object from one thread to another. Overload of
|
||||
* sdv::core.:IPermissionControl::SetAccessPermission.
|
||||
* @remarks An new thread has fully restricted access per default. Use this function to set the required access level. If a
|
||||
* thread has already initialized with the proper access level, this function will set identical or lower access permissions
|
||||
* for the current thread.
|
||||
* @param[in] tTransferID The IS of the prepared access permission transfer.
|
||||
* @return The permission ID for this restriction or 0 when the access permission could not be transferred. Use the
|
||||
* ReleaseAccessPermission to release the restriction again.
|
||||
*/
|
||||
virtual sdv::core::TPermissionID SetAccessPermission(/*in*/ sdv::core::TPermissionTransferID tTransferID) override;
|
||||
|
||||
/**
|
||||
* @brief Get the access permission level for the current thread. This will be the lowest restriction set for the current
|
||||
* thread. Overload of sdv::core.:IPermissionControl::GetCurrentPermission.
|
||||
* @return The current access permission level.
|
||||
*/
|
||||
virtual sdv::core::EAccessPermission GetCurrentPermission() const override;
|
||||
|
||||
/**
|
||||
* @brief Create a permission object that assigns the access permissions to the call thread. The permissions stay in place
|
||||
* until a last copy of the object is destroyed.
|
||||
* @param[in] ePermission The new permission to set for the thread. If the permission has a value smaller than
|
||||
* sdv::core::EAccessPermission::restricted_access, the object will be created with restricted access permission.
|
||||
* @return The permission object. The permission will stay in place during the lifetime of the object.
|
||||
*/
|
||||
CAccessPermission CreatePermissionObject(sdv::core::EAccessPermission ePermission);
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Increment the counter for a specific permission.
|
||||
* @param[in] ePermission The permission the count should be incremented for.
|
||||
*/
|
||||
void IncrementPermissionCount(sdv::core::EAccessPermission ePermission);
|
||||
|
||||
/**
|
||||
* @brief Increment the counter for a specific permission.
|
||||
* @param[in] ePermission The permission the count should be incremented for.
|
||||
*/
|
||||
void DecrementPermissionCount(sdv::core::EAccessPermission ePermission);
|
||||
|
||||
CUniqueID<uint64_t> m_idgen; ///< Unique ID generator.
|
||||
|
||||
#if defined __GNUC__ && defined _WIN32
|
||||
// https://dev.azure.com/SW4ZF/AZP-431_DivDI_Vehicle_API/_workitems/edit/610009
|
||||
// In MINGW GCC implementation of thread_local, there is an issue with complex classes being used after the destruction. This
|
||||
// is caused by an architectural bug (https://github.com/msys2/MINGW-packages/issues/2519) causing the emutls (GCC Emulated TLS)
|
||||
// managing the memory allocation to be executed before the __cxa_thread_exit, the thread cleanup callback of Windows.
|
||||
// The solution is to use trivial data only (pointer, reference, integers, etc.). A workaround is implemented to use a reference
|
||||
// of the permission list and map instead of an instance to the list and map.
|
||||
|
||||
/**
|
||||
* @brief Create a permission list object.
|
||||
* @return Reference to the created list object.
|
||||
*/
|
||||
static std::list<std::pair<sdv::core::EAccessPermission, size_t>>& CreatePermissionList()
|
||||
{
|
||||
static std::mutex mtxPermissionLists;
|
||||
static std::list<std::list<std::pair<sdv::core::EAccessPermission, size_t>>> lstPermissionLists;
|
||||
std::unique_lock<std::mutex> lock(mtxPermissionLists);
|
||||
lstPermissionLists.resize(lstPermissionLists.size() + 1);
|
||||
return lstPermissionLists.back();
|
||||
}
|
||||
|
||||
/// Thread specific permission tracker list (permission, lowest first and the count for each permission second).
|
||||
static thread_local std::list<std::pair<sdv::core::EAccessPermission, size_t>>& m_lstPermissionTracker;
|
||||
|
||||
/**
|
||||
* @brief Create a permission map object.
|
||||
* @return Reference to the created map object.
|
||||
*/
|
||||
static std::map<sdv::core::TPermissionID, CAccessPermission>& CreatePermissionMap()
|
||||
{
|
||||
static std::mutex mtxPermissionMaps;
|
||||
static std::list<std::map<sdv::core::TPermissionID, CAccessPermission>> lstPermissionMaps;
|
||||
std::unique_lock<std::mutex> lock(mtxPermissionMaps);
|
||||
lstPermissionMaps.resize(lstPermissionMaps.size() + 1);
|
||||
return lstPermissionMaps.back();
|
||||
}
|
||||
|
||||
/// Thread specific permission access map.
|
||||
static thread_local std::map<sdv::core::TPermissionID, CAccessPermission>& m_mapPermissions;
|
||||
#else
|
||||
/// Thread specific permission tracker list (permission, lowest first and the count for each permission second).
|
||||
static thread_local std::list<std::pair<sdv::core::EAccessPermission, size_t>> m_lstPermissionTracker;
|
||||
|
||||
/// Thread specific permission access map.
|
||||
static thread_local std::map<sdv::core::TPermissionID, CAccessPermission> m_mapPermissions;
|
||||
#endif
|
||||
|
||||
/// Permission transfer object map (allow shifting permission from one thread to another).
|
||||
std::map<sdv::core::TPermissionTransferID, sdv::core::EAccessPermission> m_mapPermissionTransfer;
|
||||
|
||||
std::mutex m_mtxPermissionTransfer; ///< Protecting the permission transfer map.
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Return the permission control.
|
||||
* @return Reference to the permission control.
|
||||
*/
|
||||
CPermissionControl& GetPermissionControl();
|
||||
|
||||
/**
|
||||
* @brief Permission control service
|
||||
*/
|
||||
class CPermissionControlService : public sdv::CSdvObject
|
||||
{
|
||||
public:
|
||||
CPermissionControlService() = default;
|
||||
|
||||
// Interface map
|
||||
BEGIN_SDV_INTERFACE_MAP()
|
||||
SDV_INTERFACE_CHAIN_MEMBER(GetPermissionControl())
|
||||
END_SDV_INTERFACE_MAP()
|
||||
|
||||
// Object declarations
|
||||
DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::system_object)
|
||||
DECLARE_OBJECT_CLASS_NAME("PermissionControlService")
|
||||
DECLARE_OBJECT_SINGLETON()
|
||||
};
|
||||
|
||||
DEFINE_SDV_OBJECT(CPermissionControlService)
|
||||
|
||||
#endif // !defined PERMISSION_CONTROL_H
|
||||
@@ -16,12 +16,14 @@
|
||||
#include <iostream>
|
||||
#include <cassert>
|
||||
#include <algorithm>
|
||||
#include <optional>
|
||||
#include "object_lifetime_control.h"
|
||||
#include "../../global/base64.h"
|
||||
#include "module_control.h"
|
||||
#include "app_config.h"
|
||||
#include "app_control.h"
|
||||
#include "app_settings.h"
|
||||
#include "permission_control.h"
|
||||
|
||||
// GetRepository might be redirected for unit tests.
|
||||
#ifndef GetRepository
|
||||
@@ -60,39 +62,154 @@ void CRepository::SetRunningMode()
|
||||
|
||||
sdv::IInterfaceAccess* CRepository::GetObject(const sdv::u8string& ssObjectName)
|
||||
{
|
||||
auto eAccessPermission = GetPermissionControl().GetCurrentPermission();
|
||||
if (eAccessPermission < sdv::core::EAccessPermission::remote_access)
|
||||
return nullptr;
|
||||
|
||||
std::shared_lock<std::shared_mutex> lock(m_mtxObjects);
|
||||
auto itService = m_mapServiceObjects.find(ssObjectName);
|
||||
if (itService != m_mapServiceObjects.end()) return (*itService->second)->ptrObject;
|
||||
if (itService != m_mapServiceObjects.end())
|
||||
{
|
||||
// Check access permissions
|
||||
switch ((*itService->second)->sClassInfo.eType)
|
||||
{
|
||||
case sdv::EObjectType::complex_service:
|
||||
case sdv::EObjectType::vehicle_function:
|
||||
case sdv::EObjectType::basic_service:
|
||||
case sdv::EObjectType::sensor:
|
||||
case sdv::EObjectType::actuator:
|
||||
// Allowed for remote access or higher
|
||||
//if (eAccessPermission < sdv::core::EAccessPermission::remote_access)
|
||||
// return nullptr;
|
||||
break;
|
||||
case sdv::EObjectType::system_object:
|
||||
// Allowed for local access or higher
|
||||
if (eAccessPermission < sdv::core::EAccessPermission::local_access)
|
||||
return nullptr;
|
||||
break;
|
||||
default:
|
||||
// Allowed for full access only
|
||||
if (eAccessPermission != sdv::core::EAccessPermission::full_access)
|
||||
return nullptr;
|
||||
}
|
||||
return (*itService->second)->ptrObject;
|
||||
}
|
||||
lock.unlock();
|
||||
|
||||
// In case the object is not in the service map and this is a main or isolated application, create the object if the object is
|
||||
// known in the installation manifest and is a system object.
|
||||
// known in the installation manifest.
|
||||
auto optManifest = GetAppConfig().FindInstalledComponent(ssObjectName);
|
||||
if (optManifest && optManifest->eType == sdv::EObjectType::system_object)
|
||||
return GetObjectByID(CreateObject(optManifest->ssName, optManifest->ssDefaultObjectName, ""));
|
||||
if (optManifest)
|
||||
{
|
||||
// Check access permissions
|
||||
bool bAutoCreate = false;
|
||||
switch (optManifest->eType)
|
||||
{
|
||||
case sdv::EObjectType::complex_service:
|
||||
case sdv::EObjectType::vehicle_function:
|
||||
case sdv::EObjectType::basic_service:
|
||||
case sdv::EObjectType::sensor:
|
||||
case sdv::EObjectType::actuator:
|
||||
// Automatic component creation only for main application.
|
||||
if (!GetAppSettings().IsMainApplication()) break;
|
||||
bAutoCreate = true;
|
||||
|
||||
// Allowed for remote access or higher
|
||||
if (eAccessPermission < sdv::core::EAccessPermission::remote_access)
|
||||
return nullptr;
|
||||
break;
|
||||
case sdv::EObjectType::system_object:
|
||||
// Automatic component creation allowed for system objects. The objects themselves have to block in case of unwanted
|
||||
// access by external or isolated applications.
|
||||
bAutoCreate = true;
|
||||
|
||||
// Forward the request to core repository if one is linked here (this can only occur with isolated and external applications).
|
||||
if (!m_ptrCoreRepoAccess) return nullptr;
|
||||
sdv::core::IObjectAccess* pObjectAccess = m_ptrCoreRepoAccess.GetInterface<sdv::core::IObjectAccess>();
|
||||
if (!pObjectAccess) return nullptr;
|
||||
return pObjectAccess->GetObject(ssObjectName);
|
||||
// Allowed for local access or higher
|
||||
if (eAccessPermission < sdv::core::EAccessPermission::local_access)
|
||||
return nullptr;
|
||||
break;
|
||||
default:
|
||||
// Automatic component creation only for main application.
|
||||
if (!GetAppSettings().IsMainApplication())
|
||||
break;
|
||||
bAutoCreate = true;
|
||||
|
||||
// Allowed for full access only
|
||||
if (eAccessPermission != sdv::core::EAccessPermission::full_access)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Create the object if automatic creation is enabled
|
||||
if (bAutoCreate)
|
||||
return GetObjectByID(CreateObject2(optManifest->ssName, optManifest->ssDefaultObjectName, "", true));
|
||||
}
|
||||
|
||||
// Copy the linked repository and then unlock; could cause a deadlock instead.
|
||||
lock.lock();
|
||||
auto lstCoreRepoAccessCopy = m_lstCoreRepoAccess;
|
||||
lock.unlock();
|
||||
|
||||
// If there is a linked core, use the core to get ther object
|
||||
for (auto& rprLink : lstCoreRepoAccessCopy)
|
||||
{
|
||||
sdv::core::IObjectAccess* pObjectAccess = rprLink.second.GetInterface<sdv::core::IObjectAccess>();
|
||||
if (!pObjectAccess) continue;
|
||||
auto* pObject = pObjectAccess->GetObject(ssObjectName);
|
||||
if (pObject) return pObject;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
sdv::IInterfaceAccess* CRepository::GetObjectByID(/*in*/ sdv::core::TObjectID tObjectID)
|
||||
{
|
||||
auto eAccessPermission = GetPermissionControl().GetCurrentPermission();
|
||||
if (eAccessPermission < sdv::core::EAccessPermission::remote_access)
|
||||
return nullptr;
|
||||
|
||||
// Only controlled objects are allowed to be returned using GetObjectByID.
|
||||
std::shared_lock<std::shared_mutex> lock(m_mtxObjects);
|
||||
auto itObject = m_mapObjects.find(tObjectID);
|
||||
if (itObject != m_mapObjects.end())
|
||||
return itObject->second->bControlled ? itObject->second->ptrObject : nullptr;
|
||||
{
|
||||
// Check access permissions
|
||||
switch (itObject->second->sClassInfo.eType)
|
||||
{
|
||||
case sdv::EObjectType::complex_service:
|
||||
case sdv::EObjectType::vehicle_function:
|
||||
case sdv::EObjectType::basic_service:
|
||||
case sdv::EObjectType::sensor:
|
||||
case sdv::EObjectType::actuator:
|
||||
//// Allowed for remote access or higher
|
||||
//if (eAccessPermission < sdv::core::EAccessPermission::remote_access)
|
||||
// return nullptr;
|
||||
break;
|
||||
case sdv::EObjectType::system_object:
|
||||
// Allowed for local access or higher
|
||||
if (eAccessPermission < sdv::core::EAccessPermission::local_access)
|
||||
return nullptr;
|
||||
break;
|
||||
default:
|
||||
// Allowed for full access only
|
||||
if (eAccessPermission != sdv::core::EAccessPermission::full_access)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return itObject->second->bControlled ? itObject->second->ptrObject : nullptr;
|
||||
}
|
||||
// TODO: Deal with overlapping IDs in this and in core process...
|
||||
|
||||
// Forward the request to core repository if one is linked here (this can only occur with isolated and external applications).
|
||||
if (!m_ptrCoreRepoAccess) return nullptr;
|
||||
sdv::core::IObjectAccess* pObjectAccess = m_ptrCoreRepoAccess.GetInterface<sdv::core::IObjectAccess>();
|
||||
if (!pObjectAccess) return nullptr;
|
||||
return pObjectAccess->GetObjectByID(tObjectID);
|
||||
// Copy the linked repository and then unlock; could cause a deadlock instead.
|
||||
auto lstCoreRepoAccessCopy = m_lstCoreRepoAccess;
|
||||
lock.unlock();
|
||||
|
||||
// If there is a linked core, use the core to get ther object
|
||||
for (auto& rprLink : lstCoreRepoAccessCopy)
|
||||
{
|
||||
sdv::core::IObjectAccess* pObjectAccess = rprLink.second.GetInterface<sdv::core::IObjectAccess>();
|
||||
if (!pObjectAccess) continue;
|
||||
auto* pObject = pObjectAccess->GetObjectByID(tObjectID);
|
||||
if (pObject) return pObject;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
sdv::IInterfaceAccess* CRepository::CreateUtility(/*in*/ const sdv::u8string& ssClassName, /*in*/ const sdv::u8string& ssObjectConfig)
|
||||
@@ -101,6 +218,11 @@ sdv::IInterfaceAccess* CRepository::CreateUtility(/*in*/ const sdv::u8string& ss
|
||||
|
||||
std::unique_lock<std::shared_mutex> lock(m_mtxObjects);
|
||||
|
||||
// TODO EVE: Currently only allowed for local access. When utilities are instantiated using isolation, also allowed for remote
|
||||
// access.
|
||||
if (GetPermissionControl().GetCurrentPermission() < sdv::core::EAccessPermission::local_access)
|
||||
return nullptr;
|
||||
|
||||
// Get a fitting module instance
|
||||
std::shared_ptr<CModuleInst> ptrModule = GetModuleControl().FindModuleByClass(ssClassName);
|
||||
if (!ptrModule)
|
||||
@@ -161,7 +283,7 @@ sdv::IInterfaceAccess* CRepository::CreateUtility(/*in*/ const sdv::u8string& ss
|
||||
auto* pObjectControl = ptrObject.GetInterface<sdv::IObjectControl>();
|
||||
if (pObjectControl)
|
||||
{
|
||||
pObjectControl->Initialize(ssObjectConfig);
|
||||
pObjectControl->Initialize(*ptrObjectEntry);
|
||||
if (pObjectControl->GetObjectState() != sdv::EObjectState::initialized)
|
||||
{
|
||||
// Destroy the object
|
||||
@@ -193,22 +315,23 @@ sdv::IInterfaceAccess* CRepository::CreateProxyObject(/*in*/ sdv::interface_id i
|
||||
// Get a fitting module instance
|
||||
std::string ssClassName = "Proxy_" + std::to_string(id);
|
||||
std::shared_ptr<CModuleInst> ptrModule = GetModuleControl().FindModuleByClass(ssClassName);
|
||||
if (!ptrModule && m_ptrCoreRepoAccess)
|
||||
{
|
||||
// Request the server for the name of the module.
|
||||
const sdv::core::IRepositoryInfo* pRepInfo = m_ptrCoreRepoAccess.GetInterface<sdv::core::IRepositoryInfo>();
|
||||
if (pRepInfo)
|
||||
{
|
||||
std::string ssModuleName =
|
||||
std::filesystem::u8path(static_cast<std::string>(pRepInfo->FindClass(ssClassName).ssModulePath)).
|
||||
filename().u8string();
|
||||
if (!ssModuleName.empty())
|
||||
{
|
||||
sdv::core::TModuleID tModule = GetModuleControl().Load(ssModuleName);
|
||||
if (tModule) ptrModule = GetModuleControl().GetModule(tModule);
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO EVE
|
||||
//if (!ptrModule && m_ptrCoreRepoAccess)
|
||||
//{
|
||||
// // Request the server for the name of the module.
|
||||
// const sdv::core::IRepositoryInfo* pRepInfo = m_ptrCoreRepoAccess.GetInterface<sdv::core::IRepositoryInfo>();
|
||||
// if (pRepInfo)
|
||||
// {
|
||||
// std::string ssModuleName =
|
||||
// std::filesystem::u8path(static_cast<std::string>(pRepInfo->FindClass(ssClassName).ssModulePath)).
|
||||
// filename().u8string();
|
||||
// if (!ssModuleName.empty())
|
||||
// {
|
||||
// sdv::core::TModuleID tModule = GetModuleControl().Load(ssModuleName);
|
||||
// if (tModule) ptrModule = GetModuleControl().GetModule(tModule);
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
if (!ptrModule)
|
||||
{
|
||||
SDV_LOG_ERROR("Object creation requested but object class was not found \"", ssClassName, "\"!");
|
||||
@@ -270,7 +393,7 @@ sdv::IInterfaceAccess* CRepository::CreateProxyObject(/*in*/ sdv::interface_id i
|
||||
auto* pObjectControl = ptrObject.GetInterface<sdv::IObjectControl>();
|
||||
if (pObjectControl)
|
||||
{
|
||||
pObjectControl->Initialize("");
|
||||
pObjectControl->Initialize(*ptrObjectEntry);
|
||||
if (pObjectControl->GetObjectState() != sdv::EObjectState::initialized)
|
||||
{
|
||||
// Destroy the object
|
||||
@@ -302,22 +425,23 @@ sdv::IInterfaceAccess* CRepository::CreateStubObject(/*in*/ sdv::interface_id id
|
||||
// Get a fitting module instance
|
||||
std::string ssClassName = "Stub_" + std::to_string(id);
|
||||
std::shared_ptr<CModuleInst> ptrModule = GetModuleControl().FindModuleByClass(ssClassName);
|
||||
if (!ptrModule && m_ptrCoreRepoAccess)
|
||||
{
|
||||
// Request the server for the name of the module.
|
||||
const sdv::core::IRepositoryInfo* pRepInfo = m_ptrCoreRepoAccess.GetInterface<sdv::core::IRepositoryInfo>();
|
||||
if (pRepInfo)
|
||||
{
|
||||
std::string ssModuleName =
|
||||
std::filesystem::u8path(static_cast<std::string>(pRepInfo->FindClass(ssClassName).ssModulePath)).
|
||||
filename().u8string();
|
||||
if (!ssModuleName.empty())
|
||||
{
|
||||
sdv::core::TModuleID tModule = GetModuleControl().Load(ssModuleName);
|
||||
if (tModule) ptrModule = GetModuleControl().GetModule(tModule);
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO EVE
|
||||
//if (!ptrModule && m_ptrCoreRepoAccess)
|
||||
//{
|
||||
// // Request the server for the name of the module.
|
||||
// const sdv::core::IRepositoryInfo* pRepInfo = m_ptrCoreRepoAccess.GetInterface<sdv::core::IRepositoryInfo>();
|
||||
// if (pRepInfo)
|
||||
// {
|
||||
// std::string ssModuleName =
|
||||
// std::filesystem::u8path(static_cast<std::string>(pRepInfo->FindClass(ssClassName).ssModulePath)).
|
||||
// filename().u8string();
|
||||
// if (!ssModuleName.empty())
|
||||
// {
|
||||
// sdv::core::TModuleID tModule = GetModuleControl().Load(ssModuleName);
|
||||
// if (tModule) ptrModule = GetModuleControl().GetModule(tModule);
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
if (!ptrModule)
|
||||
{
|
||||
SDV_LOG_ERROR("Object creation requested but object class was not found \"", ssClassName, "\"!");
|
||||
@@ -378,7 +502,7 @@ sdv::IInterfaceAccess* CRepository::CreateStubObject(/*in*/ sdv::interface_id id
|
||||
auto* pObjectControl = ptrObject.GetInterface<sdv::IObjectControl>();
|
||||
if (pObjectControl)
|
||||
{
|
||||
pObjectControl->Initialize("");
|
||||
pObjectControl->Initialize(*ptrObjectEntry);
|
||||
if (pObjectControl->GetObjectState() != sdv::EObjectState::initialized)
|
||||
{
|
||||
// Destroy the object
|
||||
@@ -414,11 +538,11 @@ sdv::core::TObjectID CRepository::CreateObject(const sdv::u8string& ssClassName,
|
||||
|
||||
Build dependency list and do these checks on dependent objects as well
|
||||
*/
|
||||
return CreateObject2(ssClassName, ssObjectName, ssObjectConfig);
|
||||
return CreateObject2(ssClassName, ssObjectName, ssObjectConfig, false);
|
||||
}
|
||||
|
||||
sdv::core::TObjectID CRepository::CreateObject2(const sdv::u8string& ssClassName, const sdv::u8string& ssObjectName,
|
||||
const sdv::u8string& ssObjectConfig)
|
||||
const sdv::u8string& ssObjectConfig, bool bIndirect)
|
||||
{
|
||||
// TODO EVE: Link to core repo. Allow only creation of one object if not already created before...
|
||||
// Add support for automatic app control shutdown when utility is closed in case of isolated app.
|
||||
@@ -457,21 +581,29 @@ sdv::core::TObjectID CRepository::CreateObject2(const sdv::u8string& ssClassName
|
||||
switch (optClassInfo->eType)
|
||||
{
|
||||
case sdv::EObjectType::system_object:
|
||||
// Only allowed with full access
|
||||
bError = !bIndirect && (GetPermissionControl().GetCurrentPermission() < sdv::core::EAccessPermission::full_access);
|
||||
break;
|
||||
case sdv::EObjectType::device:
|
||||
case sdv::EObjectType::platform_abstraction:
|
||||
case sdv::EObjectType::vehicle_bus:
|
||||
bError = !bDeviceAndBasicServiceAllowed;
|
||||
// Only allowed with full access
|
||||
bError = !bIndirect && (GetPermissionControl().GetCurrentPermission() < sdv::core::EAccessPermission::full_access);
|
||||
bError |= !bDeviceAndBasicServiceAllowed;
|
||||
break;
|
||||
case sdv::EObjectType::basic_service:
|
||||
case sdv::EObjectType::sensor:
|
||||
case sdv::EObjectType::actuator:
|
||||
bError = !bDeviceAndBasicServiceAllowed;
|
||||
// Only allowed with full access
|
||||
bError = !bIndirect && (GetPermissionControl().GetCurrentPermission() < sdv::core::EAccessPermission::full_access);
|
||||
bError |= !bDeviceAndBasicServiceAllowed;
|
||||
break;
|
||||
case sdv::EObjectType::complex_service:
|
||||
case sdv::EObjectType::vehicle_function:
|
||||
// Only allowed with local access
|
||||
bError = !bIndirect && (GetPermissionControl().GetCurrentPermission() < sdv::core::EAccessPermission::local_access);
|
||||
bIsolate = GetAppSettings().IsMainApplication();
|
||||
bError = !bComplexServiceAllowed;
|
||||
bError |= !bComplexServiceAllowed;
|
||||
m_bIsoObjectLoaded = true;
|
||||
break;
|
||||
default:
|
||||
@@ -498,8 +630,11 @@ sdv::core::TObjectID CRepository::CreateObject2(const sdv::u8string& ssClassName
|
||||
auto itPreviousService = m_mapServiceObjects.find(ssObjectName2);
|
||||
if (itPreviousService != m_mapServiceObjects.end())
|
||||
{
|
||||
// Get the service pointer
|
||||
std::shared_ptr<SObjectEntry> ptrService = *itPreviousService->second;
|
||||
|
||||
// Object entry is valid?
|
||||
if (!*itPreviousService->second)
|
||||
if (!ptrService)
|
||||
{
|
||||
// This should not occur... there is a previous object in the service map, but the object is empty.
|
||||
SDV_LOG_ERROR("Object creation requested for class \"", ssClassName, "\", but object with the same name \"",
|
||||
@@ -508,12 +643,15 @@ sdv::core::TObjectID CRepository::CreateObject2(const sdv::u8string& ssClassName
|
||||
}
|
||||
|
||||
// Trying to create an object with the same name is not an error if the classes are identical.
|
||||
if ((*itPreviousService->second)->sClassInfo.ssName == ssClassName)
|
||||
return (*itPreviousService->second)->tObjectID;
|
||||
const auto& rsClassInfo = ptrService->sClassInfo;
|
||||
if (rsClassInfo.ssName == ssClassName ||
|
||||
std::find(rsClassInfo.seqClassAliases.begin(), rsClassInfo.seqClassAliases.end(), ssClassName) !=
|
||||
rsClassInfo.seqClassAliases.end())
|
||||
return ptrService->tObjectID;
|
||||
|
||||
// Object name was already used by another class. This is an error.
|
||||
SDV_LOG_ERROR("Object creation requested for class \"", ssClassName, "\", but object with the same name \"",
|
||||
ssObjectName2, "\" was already instantiated for class \"", (*itPreviousService->second)->sClassInfo.ssName,
|
||||
ssObjectName2, "\" was already instantiated for class \"", rsClassInfo.ssName,
|
||||
"\"!");
|
||||
return 0;
|
||||
}
|
||||
@@ -549,14 +687,18 @@ sdv::core::TObjectID CRepository::CreateObject2(const sdv::u8string& ssClassName
|
||||
sdv::core::TObjectID CRepository::CreateObjectFromModule(/*in*/ sdv::core::TModuleID tModuleID,
|
||||
/*in*/ const sdv::u8string& ssClassName, /*in*/ const sdv::u8string& ssObjectName, /*in*/ const sdv::u8string& ssObjectConfig)
|
||||
{
|
||||
if(ssClassName.empty()) return false;
|
||||
if(ssClassName.empty()) return 0u;
|
||||
|
||||
// Only allowed with full access
|
||||
if (GetPermissionControl().GetCurrentPermission() != sdv::core::EAccessPermission::full_access)
|
||||
return 0u;
|
||||
|
||||
// Get a fitting module instance
|
||||
std::shared_ptr<CModuleInst> ptrModule = GetModuleControl().GetModule(tModuleID);
|
||||
if (!ptrModule)
|
||||
{
|
||||
SDV_LOG_ERROR("Object creation requested but object class was not found \"", ssClassName, "\"!");
|
||||
return false;
|
||||
return 0u;
|
||||
}
|
||||
|
||||
// Check the class type
|
||||
@@ -564,7 +706,7 @@ sdv::core::TObjectID CRepository::CreateObjectFromModule(/*in*/ sdv::core::TModu
|
||||
if (!optClassInfo)
|
||||
{
|
||||
SDV_LOG_ERROR("Object creation requested but object class was not found \"", ssClassName, "\"!");
|
||||
return false;
|
||||
return 0u;
|
||||
}
|
||||
bool bError = false;
|
||||
bool bDeviceAndBasicServiceAllowed = GetAppSettings().IsMainApplication() || GetAppSettings().IsStandaloneApplication() ||
|
||||
@@ -602,7 +744,7 @@ sdv::core::TObjectID CRepository::CreateObjectFromModule(/*in*/ sdv::core::TModu
|
||||
// Utilities and marshall objects cannot be created using the CreateObject function.
|
||||
SDV_LOG_ERROR("Creation of an object of invalid type \"", sdv::ObjectType2String(optClassInfo->eType),
|
||||
"\" requested for class \"", ssClassName, "\"!");
|
||||
return 0;
|
||||
return 0u;
|
||||
}
|
||||
|
||||
// Check for an object name. If not existing get the default name (being either one specified by the object or the class name).
|
||||
@@ -626,7 +768,7 @@ sdv::core::TObjectID CRepository::CreateObjectFromModule(/*in*/ sdv::core::TModu
|
||||
"\", but object with the same name \"",
|
||||
ssObjectName2,
|
||||
"\" was already instantiated, but cannot be found!");
|
||||
return 0;
|
||||
return 0u;
|
||||
}
|
||||
|
||||
// Trying to create an object with the same name is not an error if the classes are identical.
|
||||
@@ -641,7 +783,7 @@ sdv::core::TObjectID CRepository::CreateObjectFromModule(/*in*/ sdv::core::TModu
|
||||
"\" was already instantiated for class \"",
|
||||
(*itPreviousService->second)->sClassInfo.ssName,
|
||||
"\"!");
|
||||
return 0;
|
||||
return 0u;
|
||||
}
|
||||
|
||||
// Check with singleton objects if the object was already instantiated.
|
||||
@@ -657,7 +799,7 @@ sdv::core::TObjectID CRepository::CreateObjectFromModule(/*in*/ sdv::core::TModu
|
||||
SDV_LOG_ERROR("Object creation requested but object from the same class \"",
|
||||
ssClassName,
|
||||
"\" was already instantiated and only one instance is allowed!");
|
||||
return 0;
|
||||
return 0u;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -671,17 +813,41 @@ sdv::core::TObjectID CRepository::CreateObjectFromModule(/*in*/ sdv::core::TModu
|
||||
|
||||
bool CRepository::DestroyObject(/*in*/ const sdv::u8string& ssObjectName)
|
||||
{
|
||||
return DestroyObject2(ssObjectName);
|
||||
TDeferredObjectDestructionList lstDeferredObjectDestruction;
|
||||
bool bRes = DestroyObject2(ssObjectName, false, lstDeferredObjectDestruction);
|
||||
for (auto& rprObject : lstDeferredObjectDestruction)
|
||||
rprObject.first->ptrModule->DestroyObject(rprObject.second);
|
||||
return bRes;
|
||||
}
|
||||
|
||||
bool CRepository::DestroyObject2(/*in*/ const sdv::u8string& ssObjectName)
|
||||
bool CRepository::DestroyObject2(const sdv::u8string& ssObjectName, bool bIndirect,
|
||||
TDeferredObjectDestructionList& rlstDeferredObjectDestruction)
|
||||
{
|
||||
std::unique_lock<std::shared_mutex> lock(m_mtxObjects);
|
||||
auto itService = m_mapServiceObjects.find(ssObjectName);
|
||||
if (itService == m_mapServiceObjects.end()) return false;
|
||||
auto ptrObjectEntry = *itService->second;
|
||||
|
||||
// When directly called, check the access permissions
|
||||
if (!bIndirect)
|
||||
{
|
||||
switch (ptrObjectEntry->sClassInfo.eType)
|
||||
{
|
||||
case sdv::EObjectType::complex_service:
|
||||
case sdv::EObjectType::vehicle_function:
|
||||
// Only allowed having local or higher access permission
|
||||
if (GetPermissionControl().GetCurrentPermission() < sdv::core::EAccessPermission::local_access)
|
||||
return false;
|
||||
break;
|
||||
default:
|
||||
// Only allowed having full access permission
|
||||
if (GetPermissionControl().GetCurrentPermission() != sdv::core::EAccessPermission::full_access)
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Print info
|
||||
auto ptrObjectEntry = *itService->second;
|
||||
if (GetAppSettings().IsConsoleVerbose())
|
||||
std::cout << "Destroy a " << sdv::ObjectType2String(ptrObjectEntry->sClassInfo.eType) << " #" << ptrObjectEntry->tObjectID <<
|
||||
" of type " << ptrObjectEntry->sClassInfo.ssName << " with the name " << ssObjectName << std::endl;
|
||||
@@ -717,13 +883,25 @@ bool CRepository::DestroyObject2(/*in*/ const sdv::u8string& ssObjectName)
|
||||
lock.unlock();
|
||||
|
||||
// Only controlled objects can be destroyed this way.
|
||||
if (ptrDependingObject->bControlled) DestroyObject(ptrDependingObject->ssName);
|
||||
if (ptrDependingObject->bControlled) DestroyObject2(ptrDependingObject->ssName, false, rlstDeferredObjectDestruction);
|
||||
}
|
||||
};
|
||||
|
||||
// Static dependance from object class
|
||||
fnDestroyDependingObjects(GetDependingObjectInstancesByClass(ptrObjectEntry->sClassInfo.ssName));
|
||||
if (ptrObjectEntry->sClassInfo.ssName != ptrObjectEntry->sClassInfo.ssDefaultObjectName)
|
||||
fnDestroyDependingObjects(GetDependingObjectInstancesByClass(ptrObjectEntry->sClassInfo.ssDefaultObjectName));
|
||||
|
||||
// Dynamic dependance from object entry
|
||||
std::vector<sdv::core::TObjectID> vecDependingObjects;
|
||||
for (const std::string& rssDependingObject : ptrObjectEntry->setDependentObjects)
|
||||
{
|
||||
auto itObject = m_mapServiceObjects.find(rssDependingObject);
|
||||
if (itObject != m_mapServiceObjects.end())
|
||||
vecDependingObjects.push_back((*itObject->second)->tObjectID);
|
||||
}
|
||||
fnDestroyDependingObjects(vecDependingObjects);
|
||||
|
||||
// Shutdown the object (not all objects expose IObjectControl).
|
||||
auto* pObjectControl = ptrObjectEntry->ptrObject.GetInterface<sdv::IObjectControl>();
|
||||
if (pObjectControl && pObjectControl->GetObjectState() != sdv::EObjectState::destruction_pending)
|
||||
@@ -735,7 +913,8 @@ bool CRepository::DestroyObject2(/*in*/ const sdv::u8string& ssObjectName)
|
||||
sdv::IInterfaceAccess* pObject = ptrObjectEntry->ptrObject;
|
||||
if (ptrObjectEntry->ptrIsoMon)
|
||||
pObject = ptrObjectEntry->ptrIsoMon->GetContainedObject();
|
||||
ptrObjectEntry->ptrModule->DestroyObject(pObject);
|
||||
rlstDeferredObjectDestruction.push_back(std::make_pair(ptrObjectEntry, pObject));
|
||||
//ptrObjectEntry->ptrModule->DestroyObject(pObject);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -805,15 +984,22 @@ sdv::core::TObjectID CRepository::RegisterObject(IInterfaceAccess* pObjectIfc, c
|
||||
return tObjectID;
|
||||
}
|
||||
|
||||
void CRepository::LinkCoreRepository(/*in*/ sdv::IInterfaceAccess* pCoreRepository)
|
||||
sdv::core::TLinkID CRepository::LinkCoreRepository(/*in*/ sdv::IInterfaceAccess* pCoreRepository)
|
||||
{
|
||||
if (!pCoreRepository) return;
|
||||
m_ptrCoreRepoAccess = pCoreRepository;
|
||||
if (!pCoreRepository) return 0;
|
||||
std::unique_lock<std::shared_mutex> lock(m_mtxObjects);
|
||||
sdv::core::TLinkID tLinkID = CreateObjectID();
|
||||
m_lstCoreRepoAccess.push_back(std::make_pair(tLinkID, pCoreRepository));
|
||||
return tLinkID;
|
||||
}
|
||||
|
||||
void CRepository::UnlinkCoreRepository()
|
||||
void CRepository::UnlinkCoreRepository(sdv::core::TLinkID tLinkID)
|
||||
{
|
||||
m_ptrCoreRepoAccess = nullptr;
|
||||
std::unique_lock<std::shared_mutex> lock(m_mtxObjects);
|
||||
auto itLink = std::find_if(
|
||||
m_lstCoreRepoAccess.begin(), m_lstCoreRepoAccess.end(), [&](const auto rprLink)
|
||||
{ return rprLink.first == tLinkID; });
|
||||
m_lstCoreRepoAccess.erase(itLink);
|
||||
}
|
||||
|
||||
sdv::SClassInfo CRepository::FindClass(/*in*/ const sdv::u8string& ssClassName) const
|
||||
@@ -824,16 +1010,34 @@ sdv::SClassInfo CRepository::FindClass(/*in*/ const sdv::u8string& ssClassName)
|
||||
if (GetAppSettings().IsMainApplication() || GetAppSettings().IsIsolatedApplication())
|
||||
{
|
||||
auto optManifest = GetAppConfig().FindInstalledComponent(ssClassName);
|
||||
if (!optManifest) return {};
|
||||
return *optManifest;
|
||||
if (optManifest)
|
||||
return *optManifest;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Get the information through the module.
|
||||
auto ptrModule = GetModuleControl().FindModuleByClass(ssClassName);
|
||||
if (ptrModule)
|
||||
{
|
||||
auto optClassInfo = ptrModule->GetClassInfo(ssClassName);
|
||||
if (optClassInfo) return *optClassInfo;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the information through the module.
|
||||
auto ptrModule = GetModuleControl().FindModuleByClass(ssClassName);
|
||||
if (!ptrModule) return {};
|
||||
auto optClassInfo = ptrModule->GetClassInfo(ssClassName);
|
||||
if (!optClassInfo) return {};
|
||||
return *optClassInfo;
|
||||
// Copy the linked repository and then unlock; could cause a deadlock instead.
|
||||
auto lstCoreRepoAccessCopy = m_lstCoreRepoAccess;
|
||||
lock.unlock();
|
||||
|
||||
// If there is a linked core, use the core to get class list
|
||||
for (auto& rprLink : lstCoreRepoAccessCopy)
|
||||
{
|
||||
const sdv::core::IRepositoryInfo* pCoreRepoInfo = rprLink.second.GetInterface<sdv::core::IRepositoryInfo>();
|
||||
sdv::SClassInfo sInfo = pCoreRepoInfo->FindClass(ssClassName);
|
||||
if (!sInfo.ssName.empty()) return sInfo;
|
||||
}
|
||||
|
||||
// Nothing found
|
||||
return {};
|
||||
}
|
||||
|
||||
sdv::sequence<sdv::core::SObjectInfo> CRepository::GetObjectList() const
|
||||
@@ -841,13 +1045,6 @@ sdv::sequence<sdv::core::SObjectInfo> CRepository::GetObjectList() const
|
||||
std::shared_lock<std::shared_mutex> lock(m_mtxObjects);
|
||||
sdv::sequence<sdv::core::SObjectInfo> seqObjects;
|
||||
|
||||
// If there is a linked core, use the core to get object list
|
||||
if (m_ptrCoreRepoAccess)
|
||||
{
|
||||
const sdv::core::IRepositoryInfo* pCoreRepoInfo = m_ptrCoreRepoAccess.GetInterface<sdv::core::IRepositoryInfo>();
|
||||
return pCoreRepoInfo ? pCoreRepoInfo->GetObjectList() : seqObjects;
|
||||
}
|
||||
|
||||
// Add object function
|
||||
auto fnAddObject = [&seqObjects](const std::shared_ptr<SObjectEntry>& rptrObjectEntry)
|
||||
{
|
||||
@@ -878,6 +1075,21 @@ sdv::sequence<sdv::core::SObjectInfo> CRepository::GetObjectList() const
|
||||
for (const auto& prLocalObject : m_mapLocalObjects)
|
||||
fnAddObject(prLocalObject.second);
|
||||
|
||||
// Copy the linked repository and then unlock; could cause a deadlock instead.
|
||||
auto lstCoreRepoAccessCopy = m_lstCoreRepoAccess;
|
||||
lock.unlock();
|
||||
|
||||
// If there is a linked core, use the core to get object list
|
||||
for (auto& rprLink : lstCoreRepoAccessCopy)
|
||||
{
|
||||
const sdv::core::IRepositoryInfo* pCoreRepoInfo = rprLink.second.GetInterface<sdv::core::IRepositoryInfo>();
|
||||
if (pCoreRepoInfo)
|
||||
{
|
||||
auto seqListRemote = pCoreRepoInfo->GetObjectList();
|
||||
seqObjects.insert(seqObjects.end(), seqListRemote.begin(), seqListRemote.end());
|
||||
}
|
||||
}
|
||||
|
||||
return seqObjects;
|
||||
}
|
||||
|
||||
@@ -885,54 +1097,78 @@ sdv::core::SObjectInfo CRepository::GetObjectInfo(/*in*/ sdv::core::TObjectID tO
|
||||
{
|
||||
std::shared_lock<std::shared_mutex> lock(m_mtxObjects);
|
||||
|
||||
// If there is a linked core, use the core to get object info
|
||||
if (m_ptrCoreRepoAccess)
|
||||
// Use the internal object map for this.
|
||||
sdv::core::SObjectInfo sInfo{};
|
||||
auto itObject = m_mapObjects.find(tObjectID);
|
||||
if (itObject != m_mapObjects.end())
|
||||
{
|
||||
const sdv::core::IRepositoryInfo* pCoreRepoInfo = m_ptrCoreRepoAccess.GetInterface<sdv::core::IRepositoryInfo>();
|
||||
return pCoreRepoInfo ? pCoreRepoInfo->GetObjectInfo(tObjectID) : sdv::core::SObjectInfo{};
|
||||
if (!itObject->second) return {};
|
||||
|
||||
// Fill in information
|
||||
sInfo.tObjectID = itObject->second->tObjectID;
|
||||
sInfo.tModuleID = itObject->second->ptrModule ? itObject->second->ptrModule->GetModuleID() : 0;
|
||||
sInfo.sClassInfo = itObject->second->sClassInfo;
|
||||
sInfo.ssObjectName = itObject->second->ssName;
|
||||
sInfo.ssObjectConfig = itObject->second->ssConfig;
|
||||
sInfo.uiFlags = 0;
|
||||
if (itObject->second->bControlled)
|
||||
sInfo.uiFlags |= static_cast<uint32_t>(sdv::core::EObjectInfoFlags::object_controlled);
|
||||
if (!itObject->second->ptrModule)
|
||||
sInfo.uiFlags |= static_cast<uint32_t>(sdv::core::EObjectInfoFlags::object_foreign);
|
||||
if (!itObject->second->bIsolated)
|
||||
sInfo.uiFlags |= static_cast<uint32_t>(sdv::core::EObjectInfoFlags::object_isolated);
|
||||
|
||||
return sInfo;
|
||||
}
|
||||
|
||||
// Use the internal object map for this.
|
||||
auto itObject = m_mapObjects.find(tObjectID);
|
||||
if (itObject == m_mapObjects.end()) return {};
|
||||
if (!itObject->second) return {};
|
||||
// Copy the linked repository and then unlock; could cause a deadlock instead.
|
||||
auto lstCoreRepoAccessCopy = m_lstCoreRepoAccess;
|
||||
lock.unlock();
|
||||
|
||||
// Fill in information
|
||||
sdv::core::SObjectInfo sInfo{};
|
||||
sInfo.tObjectID = itObject->second->tObjectID;
|
||||
sInfo.tModuleID = itObject->second->ptrModule ? itObject->second->ptrModule->GetModuleID() : 0;
|
||||
sInfo.sClassInfo = itObject->second->sClassInfo;
|
||||
sInfo.ssObjectName = itObject->second->ssName;
|
||||
sInfo.ssObjectConfig = itObject->second->ssConfig;
|
||||
sInfo.uiFlags = 0;
|
||||
if (itObject->second->bControlled)
|
||||
sInfo.uiFlags |= static_cast<uint32_t>(sdv::core::EObjectInfoFlags::object_controlled);
|
||||
if (!itObject->second->ptrModule)
|
||||
sInfo.uiFlags |= static_cast<uint32_t>(sdv::core::EObjectInfoFlags::object_foreign);
|
||||
if (!itObject->second->bIsolated)
|
||||
sInfo.uiFlags |= static_cast<uint32_t>(sdv::core::EObjectInfoFlags::object_isolated);
|
||||
// If there is a linked core, use the core to get object info
|
||||
for (auto& rprLink : lstCoreRepoAccessCopy)
|
||||
{
|
||||
const sdv::core::IRepositoryInfo* pCoreRepoInfo = rprLink.second.GetInterface<sdv::core::IRepositoryInfo>();
|
||||
if (pCoreRepoInfo)
|
||||
{
|
||||
sInfo = pCoreRepoInfo->GetObjectInfo(tObjectID);
|
||||
if (sInfo.tObjectID == tObjectID) return sInfo;
|
||||
}
|
||||
}
|
||||
|
||||
return sInfo;
|
||||
// Info not found
|
||||
return {};
|
||||
}
|
||||
|
||||
sdv::core::SObjectInfo CRepository::FindObject(/*in*/ const sdv::u8string& ssObjectName) const
|
||||
{
|
||||
std::shared_lock<std::shared_mutex> lock(m_mtxObjects);
|
||||
|
||||
// If there is a linked core, use the core to get object info
|
||||
if (m_ptrCoreRepoAccess)
|
||||
{
|
||||
const sdv::core::IRepositoryInfo* pCoreRepoInfo = m_ptrCoreRepoAccess.GetInterface<sdv::core::IRepositoryInfo>();
|
||||
return pCoreRepoInfo ? pCoreRepoInfo->FindObject(ssObjectName) : sdv::core::SObjectInfo{};
|
||||
}
|
||||
|
||||
// Use internal service map for this.
|
||||
auto itService = m_mapServiceObjects.find(ssObjectName);
|
||||
if (itService == m_mapServiceObjects.end()) return {};
|
||||
auto ptrObjectEntry = *itService->second;
|
||||
if (!ptrObjectEntry) return {};
|
||||
if (itService != m_mapServiceObjects.end())
|
||||
{
|
||||
auto ptrObjectEntry = *itService->second;
|
||||
if (!ptrObjectEntry) return {};
|
||||
return GetObjectInfo(ptrObjectEntry->tObjectID);
|
||||
}
|
||||
|
||||
// Copy the linked repository and then unlock; could cause a deadlock instead.
|
||||
auto lstCoreRepoAccessCopy = m_lstCoreRepoAccess;
|
||||
lock.unlock();
|
||||
return GetObjectInfo(ptrObjectEntry->tObjectID);
|
||||
|
||||
// If there is a linked core, use the core to get object info
|
||||
for (auto& rprLink : lstCoreRepoAccessCopy)
|
||||
{
|
||||
const sdv::core::IRepositoryInfo* pCoreRepoInfo = rprLink.second.GetInterface<sdv::core::IRepositoryInfo>();
|
||||
if (pCoreRepoInfo)
|
||||
{
|
||||
sdv::core::SObjectInfo sInfo = pCoreRepoInfo->FindObject(ssObjectName);
|
||||
if (sInfo.tObjectID) return sInfo;
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void CRepository::OnDestroyObject(sdv::IInterfaceAccess* pObject)
|
||||
@@ -998,9 +1234,10 @@ void CRepository::DestroyAllObjects(const std::vector<std::string>& rvecIgnoreOb
|
||||
lock.unlock();
|
||||
|
||||
// Destroy the objects in reverse order
|
||||
TDeferredObjectDestructionList lstDeferredObjectDestruction;
|
||||
while (lstCopy.size())
|
||||
{
|
||||
std::string ssObjectName = lstCopy.back() ? lstCopy.back()->ssName : std::string{};
|
||||
sdv::u8string ssObjectName = lstCopy.back() ? lstCopy.back()->ssName : sdv::u8string{};
|
||||
lstCopy.pop_back();
|
||||
if (ssObjectName.empty()) continue;
|
||||
|
||||
@@ -1009,7 +1246,7 @@ void CRepository::DestroyAllObjects(const std::vector<std::string>& rvecIgnoreOb
|
||||
continue;
|
||||
|
||||
// Destroy the object... -> call the repository function to remove it from the list.
|
||||
DestroyObject(ssObjectName);
|
||||
DestroyObject2(ssObjectName, true, lstDeferredObjectDestruction);
|
||||
|
||||
// Needs force!
|
||||
if (bForce)
|
||||
@@ -1038,6 +1275,34 @@ void CRepository::DestroyAllObjects(const std::vector<std::string>& rvecIgnoreOb
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
for (auto& rprObject : lstDeferredObjectDestruction)
|
||||
rprObject.first->ptrModule->DestroyObject(rprObject.second);
|
||||
}
|
||||
|
||||
void CRepository::AddObjectDependency(/*in*/ const sdv::u8string& ssObjectName, /*in*/ const sdv::u8string& ssDependsOnObject)
|
||||
{
|
||||
std::unique_lock<std::shared_mutex> lock(m_mtxObjects);
|
||||
auto itService = m_mapServiceObjects.find(ssDependsOnObject);
|
||||
if (itService != m_mapServiceObjects.end())
|
||||
{
|
||||
auto ptrObjectEntry = *itService->second;
|
||||
if (!ptrObjectEntry) return;
|
||||
auto itDependsOn = ptrObjectEntry->setDependentObjects.find(ssObjectName);
|
||||
if (itDependsOn == ptrObjectEntry->setDependentObjects.end())
|
||||
ptrObjectEntry->setDependentObjects.insert(ssObjectName);
|
||||
}
|
||||
}
|
||||
|
||||
void CRepository::RemoveObjectDependency(/*in*/ const sdv::u8string& ssObjectName, /*in*/ const sdv::u8string& ssDependsOnObject)
|
||||
{
|
||||
std::unique_lock<std::shared_mutex> lock(m_mtxObjects);
|
||||
auto itService = m_mapServiceObjects.find(ssDependsOnObject);
|
||||
if (itService != m_mapServiceObjects.end())
|
||||
{
|
||||
auto ptrObjectEntry = *itService->second;
|
||||
if (!ptrObjectEntry) return;
|
||||
ptrObjectEntry->setDependentObjects.erase(ssObjectName);
|
||||
}
|
||||
}
|
||||
|
||||
void CRepository::ResetConfigBaseline()
|
||||
@@ -1100,8 +1365,9 @@ sdv::core::EConfigProcessResult CRepository::StartFromConfig(const CAppConfigFil
|
||||
// Destroy the objects when an error occurred during loading.
|
||||
if (!bAllowPartialLoad && nFail)
|
||||
{
|
||||
TDeferredObjectDestructionList lstDeferredObjectDestruction;
|
||||
for (const std::string& rssName : vecLoadedObjects)
|
||||
DestroyObject(rssName);
|
||||
DestroyObject2(rssName, true, lstDeferredObjectDestruction);
|
||||
return sdv::core::EConfigProcessResult::failed;
|
||||
}
|
||||
|
||||
@@ -1185,11 +1451,14 @@ sdv::core::TObjectID CRepository::CreateIsolatedObject(const sdv::SClassInfo& rs
|
||||
sdv::TInterfaceAccessPtr(GetObject("CommunicationControl")).GetInterface<sdv::com::IConnectionControl>();
|
||||
if (!pConnectionControl) return 0;
|
||||
sdv::u8string ssConnectionString;
|
||||
std::optional<CAccessPermission> optPermissionObject =
|
||||
GetPermissionControl().CreatePermissionObject(sdv::core::EAccessPermission::local_access);
|
||||
sdv::com::TConnectionID tConnection =
|
||||
pConnectionControl->CreateServerConnection(sdv::com::EChannelType::local_channel,
|
||||
sdv::core::GetObject("RepositoryService"), 5000, ssConnectionString);
|
||||
if (!tConnection.uiControl) return 0;
|
||||
if (ssConnectionString.empty()) return 0;
|
||||
optPermissionObject.reset();
|
||||
|
||||
// Create the isolation process configuration
|
||||
std::stringstream sstreamConfig;
|
||||
@@ -1298,7 +1567,10 @@ sdv::core::TObjectID CRepository::InternalCreateObject(const std::shared_ptr<CMo
|
||||
if (!ptrObject)
|
||||
{
|
||||
// Destroy the object again
|
||||
DestroyObject(rssObjectName);
|
||||
TDeferredObjectDestructionList lstDeferredObjectDestruction;
|
||||
DestroyObject2(rssObjectName, true, lstDeferredObjectDestruction);
|
||||
for (auto& rprObject : lstDeferredObjectDestruction)
|
||||
rprObject.first->ptrModule->DestroyObject(rprObject.second);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1306,7 +1578,7 @@ sdv::core::TObjectID CRepository::InternalCreateObject(const std::shared_ptr<CMo
|
||||
auto* pObjectControl = ptrObject.GetInterface<sdv::IObjectControl>();
|
||||
if (pObjectControl)
|
||||
{
|
||||
pObjectControl->Initialize(rssObjectConfig);
|
||||
pObjectControl->Initialize(*ptrObjectEntry);
|
||||
if (pObjectControl->GetObjectState() != sdv::EObjectState::initialized)
|
||||
{
|
||||
// Shutdown the object (even if the initialization didn't work properly).
|
||||
@@ -1315,8 +1587,8 @@ sdv::core::TObjectID CRepository::InternalCreateObject(const std::shared_ptr<CMo
|
||||
// Destroy the object
|
||||
rptrModule->DestroyObject(ptrObject);
|
||||
|
||||
// Destroy the object again
|
||||
DestroyObject(rssObjectName);
|
||||
//// Destroy the object again
|
||||
//DestroyObject2(rssObjectName, true);
|
||||
return 0;
|
||||
}
|
||||
switch (GetAppControl().GetOperationState())
|
||||
@@ -1350,14 +1622,7 @@ sdv::core::TObjectID CRepository::InternalCreateObject(const std::shared_ptr<CMo
|
||||
|
||||
sdv::core::TObjectID CRepository::CreateObjectID()
|
||||
{
|
||||
static sdv::core::TObjectID tCurrent = 0;
|
||||
if (!tCurrent)
|
||||
{
|
||||
std::srand(static_cast<unsigned int>(time(0)));
|
||||
tCurrent = 0;
|
||||
while (!tCurrent) tCurrent = std::rand();
|
||||
}
|
||||
return ++tCurrent;
|
||||
return m_idgen.Generate();
|
||||
}
|
||||
|
||||
std::vector<sdv::core::TObjectID> CRepository::GetDependingObjectInstancesByClass(const std::string& rssClass)
|
||||
@@ -1378,6 +1643,7 @@ std::vector<sdv::core::TObjectID> CRepository::GetDependingObjectInstancesByClas
|
||||
bool CRepositoryService::EnableRepositoryObjectControl()
|
||||
{
|
||||
return GetAppSettings().IsMainApplication() ||
|
||||
GetAppSettings().IsMaintenanceApplication() ||
|
||||
GetAppSettings().IsStandaloneApplication() ||
|
||||
GetAppSettings().IsEssentialApplication() ||
|
||||
GetAppSettings().IsIsolatedApplication();
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "object_lifetime_control.h"
|
||||
#include "iso_monitor.h"
|
||||
#include "app_config_file.h"
|
||||
#include "../../global/unique_id.h"
|
||||
|
||||
/**
|
||||
* @brief repository service providing functionality to load modules, create objects and access exiting objects
|
||||
@@ -34,7 +35,8 @@
|
||||
class CRepository :
|
||||
public sdv::IInterfaceAccess, public sdv::core::IObjectAccess, public sdv::core::IRepositoryUtilityCreate,
|
||||
public sdv::core::IRepositoryMarshallCreate, public sdv::core::IRepositoryControl, public sdv::core::IRegisterForeignObject,
|
||||
public sdv::core::IRepositoryInfo, public IObjectDestroyHandler, public sdv::core::ILinkCoreRepository
|
||||
public sdv::core::IRepositoryInfo, public IObjectDestroyHandler, public sdv::core::ILinkCoreRepository,
|
||||
public sdv::core::IObjectDependency
|
||||
{
|
||||
public:
|
||||
/**
|
||||
@@ -45,6 +47,7 @@ public:
|
||||
// Interface map
|
||||
BEGIN_SDV_INTERFACE_MAP()
|
||||
SDV_INTERFACE_ENTRY(sdv::core::IObjectAccess)
|
||||
SDV_INTERFACE_ENTRY(sdv::core::IObjectDependency)
|
||||
SDV_INTERFACE_ENTRY(sdv::core::IRepositoryMarshallCreate)
|
||||
SDV_INTERFACE_ENTRY(sdv::core::IRepositoryUtilityCreate)
|
||||
END_SDV_INTERFACE_MAP()
|
||||
@@ -102,7 +105,6 @@ public:
|
||||
*/
|
||||
virtual sdv::IInterfaceAccess* CreateStubObject(/*in*/ sdv::interface_id id) override;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Create an object and all its objects it depends on. Overload of Overload of
|
||||
* sdv::core::IRepositoryControl::CreateObject.
|
||||
@@ -127,7 +129,7 @@ protected:
|
||||
*/
|
||||
virtual sdv::core::TObjectID CreateObject(/*in*/ const sdv::u8string& ssClassName, /*in*/ const sdv::u8string& ssObjectName,
|
||||
/*in*/ const sdv::u8string& ssObjectConfig) override;
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Creates an object from a previously loaded module. Provide the module ID to explicitly define what module to
|
||||
* use during object creation. Overload of sdv::core::IRepositoryControl::CreateObjectFromModule.
|
||||
@@ -145,7 +147,6 @@ public:
|
||||
/*in*/ const sdv::u8string& ssClassName, /*in*/ const sdv::u8string& ssObjectName,
|
||||
/*in*/ const sdv::u8string& ssObjectConfig) override;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Destroy a previously created object with the supplied name. Overload of sdv::core::IRepositoryControl::DestroyObject.
|
||||
* @details For standalone and essential applications previously created system, device and service objects can be
|
||||
@@ -156,7 +157,29 @@ protected:
|
||||
*/
|
||||
virtual bool DestroyObject(/*in*/ const sdv::u8string& ssObjectName) override;
|
||||
|
||||
public:
|
||||
private:
|
||||
/**
|
||||
* @brief Object entry
|
||||
* @details The object instance information. Objects that are running remotely are represented by their proxy. Objects can have
|
||||
* multiple references by stubs.
|
||||
*/
|
||||
struct SObjectEntry : sdv::SObjectInfo
|
||||
{
|
||||
sdv::TInterfaceAccessPtr ptrObject; ///< Object interface (could be proxy).
|
||||
std::shared_ptr<CModuleInst> ptrModule; ///< Module instance.
|
||||
bool bControlled = false; ///< When set, the object is controlled.
|
||||
bool bIsolated = false; ///< When set, the object is isolated and running in another process.
|
||||
std::mutex mtxConnect; ///< Mutex used to wait for connection
|
||||
std::condition_variable cvConnect; ///< Condition variable used to wait for connection
|
||||
std::shared_ptr<CIsoMonitor> ptrIsoMon; ///< Object being monitored for shutdown.
|
||||
std::set<std::string> setDependentObjects; ///< List of dependent objects (needed during shutdown).
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief List for collected objects ready for destruction.
|
||||
*/
|
||||
using TDeferredObjectDestructionList = std::list<std::pair<std::shared_ptr<SObjectEntry>, sdv::IInterfaceAccess*>>;
|
||||
|
||||
/**
|
||||
* @brief Create an object and all its objects it depends on. Internal function not accessible through the interface.
|
||||
* @param[in] ssClassName The name of the object class to be created. For the main application, the class string could
|
||||
@@ -166,12 +189,13 @@ public:
|
||||
* the class name. Use the returned object ID to request the name of the object.
|
||||
* @param[in] ssObjectConfig Optional configuration handed over to the object upon creation via IObjectControl. Only
|
||||
* valid for standalone, essential and isolated applications.
|
||||
* @param[in] bIndirect Set when the function was called through a call from GetObject.
|
||||
* @return Returns the object ID when the object creation was successful or 0 when not. On success the object is
|
||||
* available through the IObjectAccess interface. If the object already exists (class and object names are identical),
|
||||
* the object ID of the existing object is returned.
|
||||
*/
|
||||
sdv::core::TObjectID CreateObject2(/*in*/ const sdv::u8string& ssClassName, /*in*/ const sdv::u8string& ssObjectName,
|
||||
/*in*/ const sdv::u8string& ssObjectConfig);
|
||||
sdv::core::TObjectID CreateObject2(const sdv::u8string& ssClassName, const sdv::u8string& ssObjectName,
|
||||
const sdv::u8string& ssObjectConfig, bool bIndirect);
|
||||
|
||||
/**
|
||||
* @brief Destroy a previously created object with the supplied name. Internal function not accessible through the interface.
|
||||
@@ -179,10 +203,14 @@ public:
|
||||
* destroyed. For the main and isolated applications, only the complex service can be destroyed. For isolated
|
||||
* applications a destruction of the object will end the application.
|
||||
* @param[in] ssObjectName The name of the object to destroy.
|
||||
* @param[in] bIndirect Set when the function was not call directly through DestroyObject.
|
||||
* @param[in] rlstDeferredObjectDestruction Reference to the list of objects to be filled with objects to be destroyed.
|
||||
* @return Returns whether the object destruction was successful.
|
||||
*/
|
||||
bool DestroyObject2(/*in*/ const sdv::u8string& ssObjectName);
|
||||
bool DestroyObject2(const sdv::u8string& ssObjectName, bool bIndirect,
|
||||
TDeferredObjectDestructionList& rlstDeferredObjectDestruction);
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Register as foreign object and make it public to the system with the given name. Overload of
|
||||
* sdv::core::IRegisterForeignObject::RegisterObject.
|
||||
@@ -196,15 +224,17 @@ public:
|
||||
/*in*/ const sdv::u8string& ssObjectName) override;
|
||||
|
||||
/**
|
||||
* @brief Register the core repository.
|
||||
* @brief Register the core repository. Overload of sdv::core::ILinkCoreRepository::LinkCoreRepository.
|
||||
* @param[in] pCoreRepository Pointer to the proxy interface of the core repository.
|
||||
* @return Returns a link ID to be used in the Unlink function. Or 0 on failure.
|
||||
*/
|
||||
virtual void LinkCoreRepository(/*in*/ sdv::IInterfaceAccess* pCoreRepository) override;
|
||||
virtual sdv::core::TLinkID LinkCoreRepository(/*in*/ sdv::IInterfaceAccess* pCoreRepository) override;
|
||||
|
||||
/**
|
||||
* @brief Unlink a previously linked core repository.
|
||||
* @brief Unlink a previously linked core repository. Overload of sdv::core::ILinkCoreRepository::UnlinkCoreRepository.
|
||||
* @param[in] tLinkID Link ID of the repository link to remove.
|
||||
*/
|
||||
virtual void UnlinkCoreRepository() override;
|
||||
virtual void UnlinkCoreRepository(sdv::core::TLinkID tLinkID) override;
|
||||
|
||||
/**
|
||||
* @brief Find the class information of an object with the supplied name. Overload of
|
||||
@@ -256,6 +286,20 @@ public:
|
||||
*/
|
||||
void DestroyAllObjects(const std::vector<std::string>&rvecIgnoreObjects, bool bForce = false);
|
||||
|
||||
/**
|
||||
* @brief Add a dependency for an object. Overload of sdv::core::IObjectDependency::AddObjectDependency.
|
||||
* @param[in] ssObjectName Name of the object.
|
||||
* @param[in] ssDependsOnObject Name of the object it depends on.
|
||||
*/
|
||||
void AddObjectDependency(/*in*/ const sdv::u8string& ssObjectName, /*in*/ const sdv::u8string& ssDependsOnObject) override;
|
||||
|
||||
/**
|
||||
* @brief Remove an object dependency. Overload of sdv::core::IObjectDependency::RemoveObjectDependency.
|
||||
* @param[in] ssObjectName Name of the object.
|
||||
* @param[in] ssDependsOnObject Name of the object it depends on.
|
||||
*/
|
||||
void RemoveObjectDependency(/*in*/ const sdv::u8string& ssObjectName, /*in*/ const sdv::u8string& ssDependsOnObject) override;
|
||||
|
||||
/**
|
||||
* @brief Reset the current config baseline.
|
||||
*/
|
||||
@@ -306,7 +350,7 @@ private:
|
||||
* @brief Create a new unique object ID.
|
||||
* @return The created object ID.
|
||||
*/
|
||||
static sdv::core::TObjectID CreateObjectID();
|
||||
sdv::core::TObjectID CreateObjectID();
|
||||
|
||||
/**
|
||||
* @brief Get a list of depending object instances of a specific class.
|
||||
@@ -315,26 +359,6 @@ private:
|
||||
*/
|
||||
std::vector<sdv::core::TObjectID> GetDependingObjectInstancesByClass(const std::string& rssClass);
|
||||
|
||||
/**
|
||||
* @brief Object entry
|
||||
* @details The object instance information. Objects that are running remotely are represented by their proxy. Objects can have
|
||||
* multiple references by stubs.
|
||||
*/
|
||||
struct SObjectEntry
|
||||
{
|
||||
sdv::core::TObjectID tObjectID = 0; ///< Object ID (local to this process).
|
||||
sdv::SClassInfo sClassInfo; ///< Object class name.
|
||||
std::string ssName; ///< Object name (can be zero with local objects).
|
||||
std::string ssConfig; ///< Object configuration.
|
||||
sdv::TInterfaceAccessPtr ptrObject; ///< Object interface (could be proxy).
|
||||
std::shared_ptr<CModuleInst> ptrModule; ///< Module instance.
|
||||
bool bControlled = false; ///< When set, the object is controlled.
|
||||
bool bIsolated = false; ///< When set, the object is isolated and running in another process.
|
||||
std::mutex mtxConnect; ///< Mutex used to wait for connection
|
||||
std::condition_variable cvConnect; ///< Condition variable used to wait for connection
|
||||
std::shared_ptr<CIsoMonitor> ptrIsoMon; ///< Object being monitored for shutdown.
|
||||
};
|
||||
|
||||
using TObjectMap = std::map<sdv::core::TObjectID, std::shared_ptr<SObjectEntry>>;
|
||||
using TOrderedObjectList = std::list<std::shared_ptr<SObjectEntry>>;
|
||||
using TObjectIDList = std::list<std::shared_ptr<SObjectEntry>>;
|
||||
@@ -350,11 +374,11 @@ private:
|
||||
TIsolationMap m_mapIsolatedObjects; ///< Map with isolated objects.
|
||||
TObjectMap m_mapObjects; ///< Map with all objects indexed by the object ID.
|
||||
TConfigSet m_setConfigObjects; ///< Set with the objects for storing in the configuration.
|
||||
sdv::TInterfaceAccessPtr m_ptrCoreRepoAccess; ///< Linked core repository access (proxy interface).
|
||||
std::list<std::pair<sdv::core::TLinkID, sdv::TInterfaceAccessPtr>> m_lstCoreRepoAccess; ///< List to Linked core repositories.
|
||||
bool m_bIsoObjectLoaded = false; ///< When set, the isolated object has loaded. Do not allow
|
||||
///< another object of type complex service or utility to be
|
||||
///< created.
|
||||
///
|
||||
CUniqueID<sdv::core::TObjectID> m_idgen; ///< Unique ID generator.
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "logger_control.h"
|
||||
#include "logger.h"
|
||||
#include "app_config.h"
|
||||
#include "permission_control.h"
|
||||
|
||||
/**
|
||||
* @brief SDV core instance class containing containing the instances for the core services.
|
||||
@@ -47,6 +48,7 @@ public:
|
||||
SDV_INTERFACE_CHAIN_MEMBER(GetMemoryManager())
|
||||
SDV_INTERFACE_CHAIN_MEMBER(GetRepository())
|
||||
SDV_INTERFACE_CHAIN_MEMBER(GetLoggerControl())
|
||||
SDV_INTERFACE_CHAIN_MEMBER(GetPermissionControl())
|
||||
END_SDV_INTERFACE_MAP()
|
||||
|
||||
/**
|
||||
|
||||
@@ -37,7 +37,8 @@ namespace toml_parser
|
||||
TTokenListIterator it = m_lstTokens.begin();
|
||||
bool bCommaAvailable = false;
|
||||
bool bPrintableCharsAvailable = false;
|
||||
bool bLastTokenIsComment = false;
|
||||
bool bLastTokenIsCommentOrForceNewline = false;
|
||||
bool bNewlineAllowed = rContext.NewlineAllowed() || (eMode == EComposeMode::compose_behind && rContext.FinalNewline());
|
||||
while (it != m_lstTokens.end() && it->Category() != ETokenCategory::token_comment)
|
||||
{
|
||||
// Add comma only when needed.
|
||||
@@ -53,12 +54,12 @@ namespace toml_parser
|
||||
bCommaAvailable = true;
|
||||
break;
|
||||
case ETokenCategory::token_comment:
|
||||
bSkip = !rContext.CommentAndNewlineAllowed();
|
||||
if (!bSkip) bLastTokenIsComment = true;
|
||||
bSkip = !bNewlineAllowed;
|
||||
if (!bSkip) bLastTokenIsCommentOrForceNewline = true;
|
||||
break;
|
||||
case ETokenCategory::token_syntax_new_line:
|
||||
bSkip = !rContext.NewlineAllowed();
|
||||
if (!bSkip) bLastTokenIsComment = false;
|
||||
bSkip = !bNewlineAllowed;
|
||||
if (!bSkip) bLastTokenIsCommentOrForceNewline = false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -107,11 +108,11 @@ namespace toml_parser
|
||||
break;
|
||||
case ETokenCategory::token_comment:
|
||||
bSkip = !rContext.CommentAndNewlineAllowed();
|
||||
if (!bSkip) bLastTokenIsComment = true;
|
||||
if (!bSkip) bLastTokenIsCommentOrForceNewline = true;
|
||||
break;
|
||||
case ETokenCategory::token_syntax_new_line:
|
||||
bSkip = !rContext.NewlineAllowed();
|
||||
if (!bSkip) bLastTokenIsComment = false;
|
||||
bSkip = !bNewlineAllowed;
|
||||
if (!bSkip) bLastTokenIsCommentOrForceNewline = false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -210,11 +211,11 @@ namespace toml_parser
|
||||
break;
|
||||
case ETokenCategory::token_comment:
|
||||
bSkip = !rContext.CommentAndNewlineAllowed();
|
||||
if (!bSkip) bLastTokenIsComment = true;
|
||||
if (!bSkip) bLastTokenIsCommentOrForceNewline = true;
|
||||
break;
|
||||
case ETokenCategory::token_syntax_new_line:
|
||||
bSkip = !rContext.NewlineAllowed();
|
||||
if (!bSkip) bLastTokenIsComment = false;
|
||||
bSkip = !bNewlineAllowed;
|
||||
if (!bSkip) bLastTokenIsCommentOrForceNewline = false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -239,7 +240,7 @@ namespace toml_parser
|
||||
}
|
||||
|
||||
// Default newline needed
|
||||
if (rContext.FinalNewline() && ((m_lstTokens.empty() && m_ssComment.empty()) || bLastTokenIsComment))
|
||||
if (rContext.FinalNewline() && ((m_lstTokens.empty() && m_ssComment.empty()) || bLastTokenIsCommentOrForceNewline))
|
||||
sstream << std::endl;
|
||||
break;
|
||||
case EComposeMode::compose_before:
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
#include "exception.h"
|
||||
#include <sstream>
|
||||
#include <limits>
|
||||
#include <support/toml.h>
|
||||
#include "parser_toml.h"
|
||||
|
||||
namespace toml_parser
|
||||
{
|
||||
@@ -560,4 +562,21 @@ namespace toml_parser
|
||||
return sstreamQuotedText.str();
|
||||
}
|
||||
}
|
||||
|
||||
bool CompareEqual(const std::string& rssToml1, const std::string& rssToml2)
|
||||
{
|
||||
try
|
||||
{
|
||||
CParser parser1(rssToml1);
|
||||
CParser parser2(rssToml2);
|
||||
sdv::toml::CNodeCollection collection1(&parser1.Root());
|
||||
sdv::toml::CNodeCollection collection2(&parser2.Root());
|
||||
return sdv::toml::internal::CompareNodes(collection1, collection2) == sdv::toml::ECompareResult::compare_identical;
|
||||
}
|
||||
catch (const toml_parser::XTOMLParseException&)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace toml_parser
|
||||
@@ -108,6 +108,16 @@ namespace toml_parser
|
||||
* @return The quoted key (if applicable); otherwise the key without quotes.
|
||||
*/
|
||||
std::string QuoteText(const std::string& rssText, EQuoteRequest eQuoteRequest = EQuoteRequest::smart_text);
|
||||
|
||||
/**
|
||||
* @brief Compare the content of TOML string with the content of another TOML string. Use internal TOML parser for the
|
||||
* comparison.
|
||||
* @param[in] rssToml1 Reference to the first TOML string to compare with the second TOML string.
|
||||
* @param[in] rssToml2 Reference to the second TOML string to compare with the first TOML string.
|
||||
* @return The comparison result.
|
||||
*/
|
||||
bool CompareEqual(const std::string& rssToml1, const std::string& rssToml2);
|
||||
|
||||
} // namespace toml_parser
|
||||
|
||||
#endif // !defined MISCELLANEOUS_H
|
||||
145
sdv_services/core/toml_parser/parser_node_indexer.cpp
Normal file
145
sdv_services/core/toml_parser/parser_node_indexer.cpp
Normal file
@@ -0,0 +1,145 @@
|
||||
/********************************************************************************
|
||||
* Copyright (c) 2025-2026 ZF Friedrichshafen AG
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Apache License Version 2.0 which is available at
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Contributors:
|
||||
* Erik Verhoeven - initial API and implementation
|
||||
********************************************************************************/
|
||||
|
||||
#include "parser_node_indexer.h"
|
||||
#include <interfaces/toml.h>
|
||||
#include <iostream>
|
||||
|
||||
namespace toml_parser
|
||||
{
|
||||
CNodeIndex::CNodeIndex(CIndexList& rIndexList, CIndexListIterator itPos) : m_ptrPos(std::make_shared<CIteratorWrapper>(rIndexList, itPos))
|
||||
{}
|
||||
|
||||
CNodeIndex::~CNodeIndex()
|
||||
{
|
||||
m_ptrPos.reset();
|
||||
}
|
||||
|
||||
CNodeIndex::CNodeIndex(const CNodeIndex& rIndex) : m_ptrPos(rIndex.m_ptrPos)
|
||||
{}
|
||||
|
||||
CNodeIndex::CNodeIndex(CNodeIndex&& rIndex) : m_ptrPos(std::move(rIndex.m_ptrPos))
|
||||
{}
|
||||
|
||||
CNodeIndex& CNodeIndex::operator=(const CNodeIndex& rIndex)
|
||||
{
|
||||
m_ptrPos = rIndex.m_ptrPos;
|
||||
return *this;
|
||||
}
|
||||
|
||||
CNodeIndex& CNodeIndex::operator=(CNodeIndex&& rIndex)
|
||||
{
|
||||
m_ptrPos = std::move(rIndex.m_ptrPos);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool CNodeIndex::operator==(const CNodeIndex& rIndex) const
|
||||
{
|
||||
return rIndex.m_ptrPos == m_ptrPos;
|
||||
}
|
||||
|
||||
bool CNodeIndex::operator!=(const CNodeIndex& rIndex) const
|
||||
{
|
||||
return !operator==(rIndex);
|
||||
}
|
||||
|
||||
bool CNodeIndex::operator<(const CNodeIndex& rIndex) const
|
||||
{
|
||||
if (!m_ptrPos) return false;
|
||||
if (!rIndex.m_ptrPos) return true;
|
||||
return m_ptrPos->Index() < rIndex.m_ptrPos->Index();
|
||||
}
|
||||
|
||||
bool CNodeIndex::operator<=(const CNodeIndex& rIndex) const
|
||||
{
|
||||
if (!m_ptrPos) return false;
|
||||
if (!rIndex.m_ptrPos) return true;
|
||||
return rIndex.m_ptrPos == m_ptrPos || m_ptrPos->Index() < rIndex.m_ptrPos->Index();
|
||||
}
|
||||
|
||||
bool CNodeIndex::operator>(const CNodeIndex& rIndex) const
|
||||
{
|
||||
if (!m_ptrPos) return true;
|
||||
if (!rIndex.m_ptrPos) return false;
|
||||
return m_ptrPos->Index() > rIndex.m_ptrPos->Index();
|
||||
}
|
||||
|
||||
bool CNodeIndex::operator>=(const CNodeIndex& rIndex) const
|
||||
{
|
||||
if (!m_ptrPos) return true;
|
||||
if (!rIndex.m_ptrPos) return false;
|
||||
return rIndex.m_ptrPos == m_ptrPos || m_ptrPos->Index() > rIndex.m_ptrPos->Index();
|
||||
}
|
||||
|
||||
CNodeIndex::operator bool() const
|
||||
{
|
||||
return m_ptrPos ? true : false;
|
||||
}
|
||||
|
||||
void CNodeIndex::MoveBefore(const CNodeIndex& rIndex)
|
||||
{
|
||||
if (m_ptrPos && rIndex.m_ptrPos)
|
||||
m_ptrPos->MoveBeforeIndex(*rIndex.m_ptrPos);
|
||||
}
|
||||
|
||||
uint32_t CNodeIndex::Index() const
|
||||
{
|
||||
if (!m_ptrPos) return sdv::toml::npos;
|
||||
return m_ptrPos->Index();
|
||||
}
|
||||
|
||||
CNodeIndex::CIteratorWrapper::CIteratorWrapper(CIndexList& rIndexList, CIndexListIterator itPos) :
|
||||
m_rIndexList(rIndexList), m_itPos(itPos)
|
||||
{}
|
||||
|
||||
CNodeIndex::CIteratorWrapper ::~CIteratorWrapper()
|
||||
{
|
||||
m_rIndexList.erase(m_itPos);
|
||||
}
|
||||
|
||||
uint32_t CNodeIndex::CIteratorWrapper::Index() const
|
||||
{
|
||||
return static_cast<uint32_t>(std::distance(m_rIndexList.cbegin(), m_itPos));
|
||||
}
|
||||
|
||||
void CNodeIndex::CIteratorWrapper::MoveBeforeIndex(const CIteratorWrapper& ritTarget)
|
||||
{
|
||||
m_rIndexList.splice(ritTarget.m_itPos, m_rIndexList, m_itPos);
|
||||
}
|
||||
|
||||
// Global index list
|
||||
CIndexList CNodeIndexer::m_lstIndexList;
|
||||
|
||||
CNodeIndexer::CNodeIndexer()
|
||||
{}
|
||||
|
||||
CNodeIndex CNodeIndexer::CreateIndex()
|
||||
{
|
||||
auto itPos = m_lstIndexList.insert(m_lstIndexList.end(), SNodeIndexElement());
|
||||
CNodeIndex index(m_lstIndexList, itPos);
|
||||
return index;
|
||||
}
|
||||
|
||||
CNodeIndex CNodeIndexer::CreateIndex(const CNodeIndex& rInsertBefore)
|
||||
{
|
||||
CNodeIndex node_index = CreateIndex();
|
||||
node_index.MoveBefore(rInsertBefore);
|
||||
return node_index;
|
||||
}
|
||||
|
||||
size_t CNodeIndexer::Count()
|
||||
{
|
||||
return m_lstIndexList.size();
|
||||
}
|
||||
|
||||
} // namespace toml_parser
|
||||
227
sdv_services/core/toml_parser/parser_node_indexer.h
Normal file
227
sdv_services/core/toml_parser/parser_node_indexer.h
Normal file
@@ -0,0 +1,227 @@
|
||||
/********************************************************************************
|
||||
* Copyright (c) 2025-2026 ZF Friedrichshafen AG
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Apache License Version 2.0 which is available at
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Contributors:
|
||||
* Erik Verhoeven - initial API and implementation
|
||||
********************************************************************************/
|
||||
|
||||
#ifndef PARSER_NODE_INDEXER_H
|
||||
#define PARSER_NODE_INDEXER_H
|
||||
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <cstdint>
|
||||
|
||||
namespace toml_parser
|
||||
{
|
||||
class CNodeIndex;
|
||||
class CNodeIndexer;
|
||||
/// Placeholder index element
|
||||
struct SNodeIndexElement {};
|
||||
/// Index list
|
||||
using CIndexList = std::list<SNodeIndexElement>;
|
||||
/// Const iterator to an element in the index list
|
||||
using CIndexListIterator = CIndexList::const_iterator;
|
||||
|
||||
/**
|
||||
* @brief Node index object used to manage the node order.
|
||||
*/
|
||||
class CNodeIndex
|
||||
{
|
||||
friend CNodeIndexer; ///< CNodeIndex uses friend access to create the node index and assign the iterator holding the
|
||||
///< the position within the index list.
|
||||
private:
|
||||
/**
|
||||
* @brief Constructor of the node index object. Use the CNodeIndexer::CreateIndex function to create the object.
|
||||
* @param[in] rIndexList Reference to the index list.
|
||||
* @param[in] itPos Iterator to the position in the index list.
|
||||
*/
|
||||
CNodeIndex(CIndexList& rIndexList, CIndexListIterator itPos);
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
virtual ~CNodeIndex();
|
||||
|
||||
/**
|
||||
* @brief Copy constructor of the node index object.
|
||||
* @param[in] rIndex Reference to the index object to copy from.
|
||||
*/
|
||||
CNodeIndex(const CNodeIndex& rIndex);
|
||||
|
||||
/**
|
||||
* @brief Move constructor of the node index object.
|
||||
* @param[in] rIndex Reference to the index object to move from.
|
||||
*/
|
||||
CNodeIndex(CNodeIndex&& rIndex);
|
||||
|
||||
/**
|
||||
* @brief Copy assignment operator of the node index object.
|
||||
* @param[in] rIndex Reference to the index object to copy from.
|
||||
* @return Returns a reference to this object.
|
||||
*/
|
||||
CNodeIndex& operator=(const CNodeIndex& rIndex);
|
||||
|
||||
/**
|
||||
* @brief Move assignment operator of the node index object.
|
||||
* @param[in] rIndex Reference to the index object to move from.
|
||||
* @return Returns a reference to this object.
|
||||
*/
|
||||
CNodeIndex& operator=(CNodeIndex&& rIndex);
|
||||
|
||||
/**
|
||||
* @brief Compare this node index object to the supplied index object and return whether the position of this object is
|
||||
* identical to the position of the supplied object.
|
||||
* @param[in] rIndex Reference to the node index object to compare with.
|
||||
* @return Returns whether the provided index is identical to this index.
|
||||
*/
|
||||
bool operator==(const CNodeIndex& rIndex) const;
|
||||
|
||||
/**
|
||||
* @brief Compare this node index object to the supplied index object and return whether the position of this object is
|
||||
* not identical to the position of the supplied object.
|
||||
* @param[in] rIndex Reference to the node index object to compare with.
|
||||
* @return Returns whether the provided index is different than this index.
|
||||
*/
|
||||
bool operator!=(const CNodeIndex& rIndex) const;
|
||||
|
||||
/**
|
||||
* @brief Compare this node index object to the supplied index object and return whether the position of this object is
|
||||
* smaller than the position of the supplied object.
|
||||
* @param[in] rIndex Reference to the node index object to compare with.
|
||||
* @return Returns whether the provided index is smaller than this index.
|
||||
*/
|
||||
bool operator<(const CNodeIndex& rIndex) const;
|
||||
|
||||
/**
|
||||
* @brief Compare this node index object to the supplied index object and return whether the position of this object is
|
||||
* smaller than or equal to the position of the supplied object.
|
||||
* @param[in] rIndex Reference to the node index object to compare with.
|
||||
* @return Returns whether the provided index is smaller than or equal to this index.
|
||||
*/
|
||||
bool operator<=(const CNodeIndex& rIndex) const;
|
||||
|
||||
/**
|
||||
* @brief Compare this node index object to the supplied index object and return whether the position of this object is
|
||||
* larger than the position of the supplied object.
|
||||
* @param[in] rIndex Reference to the node index object to compare with.
|
||||
* @return Returns whether the provided index is larger than this index.
|
||||
*/
|
||||
bool operator>(const CNodeIndex& rIndex) const;
|
||||
|
||||
/**
|
||||
* @brief Compare this node index object to the supplied index object and return whether the position of this object is
|
||||
* larger than or equal to the position of the supplied object.
|
||||
* @param[in] rIndex Reference to the node index object to compare with.
|
||||
* @return Returns whether the provided index is larger than or equal to this index.
|
||||
*/
|
||||
bool operator>=(const CNodeIndex& rIndex) const;
|
||||
|
||||
/**
|
||||
* @brief Check for validity.
|
||||
* @return Returns whether the index object contains a valid index.
|
||||
*/
|
||||
operator bool() const;
|
||||
|
||||
/**
|
||||
* @brief Move the node index object before the index object provided as an argument.
|
||||
* @param[in] rIndex Reference to the node index object to move the object before.
|
||||
*/
|
||||
void MoveBefore(const CNodeIndex& rIndex);
|
||||
|
||||
/**
|
||||
* @brief Get the index in the list. Returns sdv::toml::npos if the index is not in the list any more.
|
||||
* @remarks This index os calculated dynamically and can change if indices are added, removed or swapped.
|
||||
* @return The current index in the list.
|
||||
*/
|
||||
uint32_t Index() const;
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Iterator wrapper object with lifetime management.
|
||||
* @remarks The wrapper keeps track of the shared index list as well. It is possible that the parser is deleted before the
|
||||
* nodes are deleted, causing an issue when accessing the index list.
|
||||
*/
|
||||
struct CIteratorWrapper
|
||||
{
|
||||
/**
|
||||
* @brief Constructor
|
||||
* @param[in] rIndexList Reference to the index list.
|
||||
* @param[in] itPos Position of this index in the index list.
|
||||
*/
|
||||
CIteratorWrapper(CIndexList& rIndexList, CIndexListIterator itPos);
|
||||
|
||||
/**
|
||||
* @brief Destructor removing the index object from the index list.
|
||||
*/
|
||||
~CIteratorWrapper();
|
||||
|
||||
/**
|
||||
* @brief Get the index in the list.
|
||||
* @remarks This index os calculated dynamically and can change if indices are added, removed or swapped.
|
||||
* @return The current index in the list.
|
||||
*/
|
||||
uint32_t Index() const;
|
||||
|
||||
/**
|
||||
* @brief Move the index object before the supplied index object.
|
||||
* @param[in] ritTarget Reference to the index object to move the object before.
|
||||
*/
|
||||
void MoveBeforeIndex(const CIteratorWrapper& ritTarget);
|
||||
|
||||
private:
|
||||
CIndexList& m_rIndexList; ///< Reference to the index list.
|
||||
CIndexListIterator m_itPos; ///< Iterator in the list
|
||||
};
|
||||
|
||||
std::shared_ptr<CIteratorWrapper> m_ptrPos; ///< Position in the index list
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Node indexer object managing the node index objects allowing determining and changing the order of nodes.
|
||||
*/
|
||||
class CNodeIndexer
|
||||
{
|
||||
friend CNodeIndex; ///< CNodeIndex has friend relationship allowing access to specific indexing functions using the
|
||||
///< index iterator from the index list.
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Constructor
|
||||
*/
|
||||
CNodeIndexer();
|
||||
|
||||
/**
|
||||
* @brief Create a node index object.
|
||||
* @return The new node index object.
|
||||
*/
|
||||
CNodeIndex CreateIndex();
|
||||
|
||||
/**
|
||||
* @brief Create a new node index object and insert this object before the supplied object.
|
||||
* @attention There is no protection for supplying an invalid iterator or an iterator from a different indexer.
|
||||
* @param[in] rInsertBefore Insert the index object before the supplied iterator position.
|
||||
* @return The new node index object.
|
||||
*/
|
||||
CNodeIndex CreateIndex(const CNodeIndex& rInsertBefore);
|
||||
|
||||
/**
|
||||
* @brief Return the amount of indices allocated.
|
||||
* @return The amount of indices allocated in the index list.
|
||||
*/
|
||||
static size_t Count();
|
||||
|
||||
private:
|
||||
static CIndexList m_lstIndexList; ///< List of all node indices. This list is global to allow nodes from one
|
||||
///< parser to be inserted into the list of another parser.
|
||||
};
|
||||
} // namespace toml_parser
|
||||
|
||||
#endif // !defined PARSER_NODE_INDEXER_H
|
||||
File diff suppressed because it is too large
Load Diff
@@ -26,6 +26,7 @@
|
||||
#include <support/interface_ptr.h>
|
||||
#include "miscellaneous.h"
|
||||
#include "code_snippet.h"
|
||||
#include "parser_node_indexer.h"
|
||||
|
||||
/// The TOML parser namespace
|
||||
namespace toml_parser
|
||||
@@ -72,11 +73,12 @@ namespace toml_parser
|
||||
void InitTopMostNode(const std::shared_ptr<const CNode>& rptrNode);
|
||||
|
||||
/**
|
||||
* @brief Check whether the provided node is a parent of the top most node.
|
||||
* @param[in] rptrNode Reference to the node to use for the checking.
|
||||
* @return Returns true if the node is a parent of the top most node, false otherwise.
|
||||
* @brief A node is part of the view if either it is an inline node, or it is a standard node and the current parent is the
|
||||
* root view.
|
||||
* @param[in] rptrNode Reference to the smart pointer to the node to check for.
|
||||
* @return Returns whether the node is part of the view.
|
||||
*/
|
||||
bool PartOfExcludedParents(const std::shared_ptr<const CNode>& rptrNode) const;
|
||||
bool IsPartOfView(const std::shared_ptr<const CNode>& rptrNode) const;
|
||||
|
||||
/**
|
||||
* @brief Create a copy of the context class with a new key context.
|
||||
@@ -289,10 +291,42 @@ namespace toml_parser
|
||||
END_SDV_INTERFACE_MAP()
|
||||
|
||||
/**
|
||||
* @{
|
||||
* @brief Get a reference to the TOML parser that generated this node.
|
||||
* @return Reference to the TOML parse.
|
||||
*/
|
||||
CParser& Parser();
|
||||
const CParser& Parser() const;
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @{
|
||||
* @brief Return the index object of the node.
|
||||
* @return Reference to the node index object.
|
||||
*/
|
||||
const CNodeIndex& NodeIndex() const;
|
||||
CNodeIndex& NodeIndex();
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Compare the index position of this node with the position of another node and return whether this node occurs
|
||||
* before the other node.
|
||||
* @param[in] rNode Reference to the node to compare the position with.
|
||||
* @return Returns whether this node occurs before the other node.
|
||||
*/
|
||||
bool operator<(const CNode& rNode) const;
|
||||
|
||||
/**
|
||||
* @brief Compare the index position of this node with the position of another node and return whether this node occurs
|
||||
* before the other node.
|
||||
* @param[in] rptrNode Reference to the node to compare the position with.
|
||||
* @return Returns whether this node occurs before the other node.
|
||||
*/
|
||||
bool operator<(const std::shared_ptr<CNode>& rptrNode) const;
|
||||
|
||||
/**
|
||||
* @brief Get the node name (no conversion to a literal or quoted key is made). Overload of sdv::toml::INodeInfo::GetName.
|
||||
@@ -328,9 +362,8 @@ namespace toml_parser
|
||||
virtual sdv::any_t GetValue() const override;
|
||||
|
||||
/**
|
||||
* @brief Get the index of this node within the view collection (either the assigned view or the parent). Overload of
|
||||
* sdv::toml::INodeInfo::GetIndex.
|
||||
* @return The index of the node within the view collection node or npos when no parent is available.
|
||||
* @brief Get the index of this node within the parent collection. Overload of sdv::toml::INodeInfo::GetIndex.
|
||||
* @return The index of the node within the parent collection node or npos when no parent is available.
|
||||
*/
|
||||
virtual uint32_t GetIndex() const override;
|
||||
|
||||
@@ -370,10 +403,10 @@ namespace toml_parser
|
||||
virtual sdv::u8string GetComment(sdv::toml::INodeInfo::ECommentType eType) override;
|
||||
|
||||
/**
|
||||
* @brief Format the node automatically. This will remove the whitespace between the elements within the node. Comments
|
||||
* will not be changed. Overload of sdv::toml::INodeInfo::AutomaticFormat.
|
||||
* @brief Format the node automatically, remove redundant whitespace. Overload of sdv::toml::INodeInfo::AutomaticFormat.
|
||||
* @param[in] bRemoveComments When set, the comments are removed from the node.
|
||||
*/
|
||||
virtual void AutomaticFormat() override;
|
||||
virtual void AutomaticFormat(/*in*/ bool bRemoveComments) override;
|
||||
|
||||
/**
|
||||
* @brief Is the node inline? Overload of sdv::toml::INodeInfo::IsInline.
|
||||
@@ -457,10 +490,11 @@ namespace toml_parser
|
||||
std::shared_ptr<const TNodeType> Cast() const;
|
||||
|
||||
/**
|
||||
* @brief Set the parent node.
|
||||
* @brief Reassign the parent node and if necessary the parser reference.
|
||||
* @details This function allows shifting nodes from one parser to another.
|
||||
* @param[in] rptrParent Reference to the node to assign to this node as a parent.
|
||||
*/
|
||||
void SetParentPtr(const std::shared_ptr<CNodeCollection>& rptrParent);
|
||||
virtual void SetParentPtr(const std::shared_ptr<CNodeCollection>& rptrParent);
|
||||
|
||||
/**
|
||||
* @brief Gets the parent node pointer.
|
||||
@@ -475,29 +509,6 @@ namespace toml_parser
|
||||
*/
|
||||
std::string GetParentPath() const;
|
||||
|
||||
/**
|
||||
* @brief Set the view definition node. The view definition node is a parent or grand parent that presents the node when
|
||||
* generating TOML code. When not set, the parent node is taking over this role.
|
||||
* @param[in] rptrView Reference to the node to assign to this node as a parent or grand parent.
|
||||
*/
|
||||
void SetViewPtr(const std::shared_ptr<CNodeCollection>& rptrView);
|
||||
|
||||
/**
|
||||
* @brief Gets the view definition node pointer.
|
||||
* @return Returns the store view definition node pointer or an empty pointer when no view was assigned.
|
||||
*/
|
||||
std::shared_ptr<CNodeCollection> GetViewPtr() const;
|
||||
|
||||
/**
|
||||
* @brief Checks whether the node is part of the view.
|
||||
* @details The node is part of the view if the supplied pointer is identical to the view definition pointer, when the view
|
||||
* definition pointer is not part of a parent of the topmost node. In all other cases, the node is part of the view.
|
||||
* @param[in] rContext Reference to the context class to use during TOML code generation.
|
||||
* @param[in] rptrNode Reference to the node to check whether it registered for a view.
|
||||
* @return Returns whether this node is part of the view with the supplied pointer.
|
||||
*/
|
||||
bool IsPartOfView(const CGenContext& rContext, const std::shared_ptr<const CNodeCollection>& rptrNode) const;
|
||||
|
||||
/**
|
||||
* @brief Accesses a node by its key in the parse tree.
|
||||
* @details Elements of tables can be accessed and traversed by using '.' to separated the parent name from child name.
|
||||
@@ -507,7 +518,7 @@ namespace toml_parser
|
||||
* @attention Array indexing starts with 0!
|
||||
* @attention For an array, when no indexing is supplied, the latest entry will be returned.
|
||||
* @param[in] rssPath The path of the node to searched for.
|
||||
* @return Returns a shared pointer to the wanted Node if it was found or a node with invalid content if it was not found.
|
||||
* @return Returns a shared pointer to the wanted node if it was found or a node with invalid content if it was not found.
|
||||
*/
|
||||
virtual std::shared_ptr<CNode> Direct(const std::string& rssPath) const = 0;
|
||||
|
||||
@@ -577,13 +588,20 @@ namespace toml_parser
|
||||
*/
|
||||
std::string GetCustomPath(const std::string& rssPrefixKey, const std::string& rssContext) const;
|
||||
|
||||
/**
|
||||
* @brief When the parent changes (e.g. when moving items from one parser to the other), the items and all its sub-items
|
||||
* need to reasign the parser.
|
||||
* @param[in] rParser Reference to the parser to assign.
|
||||
*/
|
||||
virtual void ReassignParser(CParser& rParser);
|
||||
|
||||
private:
|
||||
std::weak_ptr<CNodeCollection> m_ptrParent; ///< Weak pointer to the parent node (if existing).
|
||||
std::weak_ptr<CNodeCollection> m_ptrView; ///< Weak pointer to the view node (if existing and explicitly set).
|
||||
std::string m_ssName; ///< Name of the node.
|
||||
std::string m_ssRawName; ///< Raw name of the node.
|
||||
bool m_bDeleted = false; ///< Enabled when the node was marked for deletion.
|
||||
CParser& m_rParser; ///< Reference to the TOML parser.
|
||||
CNodeIndex m_index; ///< Node index object holding the overall node position.
|
||||
std::weak_ptr<CNodeCollection> m_ptrParent; ///< Weak pointer to the parent node (if existing).
|
||||
std::string m_ssName; ///< Name of the node.
|
||||
std::string m_ssRawName; ///< Raw name of the node.
|
||||
bool m_bDeleted = false; ///< Enabled when the node was marked for deletion.
|
||||
std::reference_wrapper<CParser> m_refParser; ///< Reference to the TOML parser.
|
||||
std::vector<std::map<std::string, CCodeSnippet>> m_vecCodeSnippets; ///< Vector with comments/code snippets.
|
||||
|
||||
public:
|
||||
@@ -596,11 +614,16 @@ namespace toml_parser
|
||||
/**
|
||||
* @brief With some node collections it is possible to switch between inline and normal.
|
||||
* @remarks Additional node composition information will be removed and the order within the parent node might be changed.
|
||||
* @remarks When made inline, all child nodes must be made inline as well. When made standard, only this node is made
|
||||
* standard.
|
||||
* @remarks Making this node a standard node, this is only possible when the parent is not an inline mode.
|
||||
* @param[in] bInline When set, try to switch to inline. Otherwise try to switch to normal.
|
||||
* @param[in] bIncludeChildren When set and bInline is not set, applicable child nodes are converted as well (only tables
|
||||
* and table-arrays can be defined as standard). Making a node inline is always including the children.
|
||||
* @return Returns whether the switch was successful. A switch to the same type (normal to normal or inline to inline is
|
||||
* always successful). When returning false, the switching might not be supported for this type.
|
||||
*/
|
||||
virtual bool Inline(bool bInline) = 0;
|
||||
virtual bool Inline(bool bInline, bool bIncludeChildren = true) = 0;
|
||||
|
||||
/**
|
||||
* @brief Checks whether the table was explicitly defined.
|
||||
@@ -654,10 +677,12 @@ namespace toml_parser
|
||||
* @brief With some node collections it is possible to switch between inline and normal. Overload of CNode::Inline.
|
||||
* @remarks Additional node composition information will be removed and the order within the parent node might be changed.
|
||||
* @param[in] bInline When set, try to switch to inline. Otherwise try to switch to normal.
|
||||
* @param[in] bIncludeChildren When set and bInline is not set, applicable child nodes are converted as well (only tables
|
||||
* and table-arrays can be defined as standard). Making a node inline is always including the children.
|
||||
* @return Returns whether the switch was successful. A switch to the same type (normal to normal or inline to inline is
|
||||
* always successful). When returning false, the switching might not be supported for this type.
|
||||
*/
|
||||
virtual bool Inline(bool bInline) override;
|
||||
virtual bool Inline(bool bInline, bool bIncludeChildren = true) override;
|
||||
|
||||
/**
|
||||
* @brief Accesses a node by its key in the parse tree. Overload of CNode::Direct.
|
||||
@@ -668,7 +693,7 @@ namespace toml_parser
|
||||
* @attention Array indexing starts with 0!
|
||||
* @attention For an array, when no indexing is supplied, the latest entry will be returned.
|
||||
* @param[in] rssPath The path of the node to searched for.
|
||||
* @return Returns a shared pointer to the wanted Node if it was found or a node with invalid content if it was not found.
|
||||
* @return Returns a shared pointer to the wanted node if it was found or a node with invalid content if it was not found.
|
||||
*/
|
||||
virtual std::shared_ptr<CNode> Direct(const std::string& rssPath) const override;
|
||||
|
||||
@@ -943,10 +968,10 @@ namespace toml_parser
|
||||
END_SDV_INTERFACE_MAP()
|
||||
|
||||
/**
|
||||
* @brief Format the node automatically. This will remove the whitespace between the elements within the node. Comments
|
||||
* will not be changed. Overload of sdv::toml::INodeInfo::AutomaticFormat.
|
||||
* @brief Format the node automatically, remove redundant whitespace. Overload of sdv::toml::INodeInfo::AutomaticFormat.
|
||||
* @param[in] bRemoveComments When set, the comments are removed from the node.
|
||||
*/
|
||||
virtual void AutomaticFormat() override;
|
||||
virtual void AutomaticFormat(/*in*/ bool bRemoveComments) override;
|
||||
|
||||
/**
|
||||
* @brief Returns the amount of nodes. Overload of sdv::toml::INodeCollection::GetCount.
|
||||
@@ -966,7 +991,28 @@ namespace toml_parser
|
||||
* @param[in] uiIndex Index of the node to get.
|
||||
* @return Smart pointer to the node object.
|
||||
*/
|
||||
std::shared_ptr<CNode> Get(uint32_t uiIndex) const;
|
||||
virtual std::shared_ptr<CNode> Get(uint32_t uiIndex) const;
|
||||
|
||||
/**
|
||||
* @brief After every insert, deletion and shift, the node order of this and all sub- tables need to be rebuild.
|
||||
* @param[in] bForce Force rebuild, even if locked.
|
||||
*/
|
||||
virtual void RebuildNodeOrder(bool bForce);
|
||||
|
||||
/**
|
||||
* @brief After every insert, deletion and shift, the node order needs to be rebuild.
|
||||
* @details Adding is done iteratively through the node list. In case the top level flag is set, all nodes are added that
|
||||
* are marked explicit or if a node is implicit, the explicit sub-nodes are added. In case this is root view, all sub-nodes
|
||||
* which are standard tables or standard table arrays are added. This flattens the hierarchy. When the root view is not set,
|
||||
* only the tables and table arrays are added that are a direct child of the node collection. At the end of building the
|
||||
* root view, the nodes are sorted using their index. Furthermore, inline nodes are moved to the beginning of the vector and
|
||||
* standard nodes to the end.
|
||||
* @param[in, out] rvecNodes Reference to the vector being filled with the nodes for the view.
|
||||
* @param[in] bRootView Set when the count is top level.
|
||||
* @param[in] bTopLevel Set when this is the top level node for adding sub nodes.
|
||||
*/
|
||||
virtual void FillNodeOrderVector(std::vector<std::shared_ptr<CNode>>& rvecNodes, bool bRootView = true,
|
||||
bool bTopLevel = true);
|
||||
|
||||
/**
|
||||
* @brief Accesses a node by its key in the parse tree. Overload of CNode::Direct.
|
||||
@@ -977,7 +1023,7 @@ namespace toml_parser
|
||||
* @attention Array indexing starts with 0!
|
||||
* @attention For an array, when no indexing is supplied, the latest entry will be returned.
|
||||
* @param[in] rssPath The path of the node to searched for.
|
||||
* @return Returns a shared pointer to the wanted Node if it was found or a node with invalid content if it was not found.
|
||||
* @return Returns a shared pointer to the wanted node if it was found or a node with invalid content if it was not found.
|
||||
*/
|
||||
virtual std::shared_ptr<CNode> Direct(const std::string& rssPath) const override;
|
||||
|
||||
@@ -993,85 +1039,124 @@ namespace toml_parser
|
||||
*/
|
||||
virtual sdv::IInterfaceAccess* GetNodeDirect(/*in*/ const sdv::u8string& ssPath) const override;
|
||||
|
||||
/**
|
||||
* @brief Get or create the parent nodes automotatically from the path.
|
||||
* @details Elements of tables can be accessed and traversed by using '.' to separated the parent name from child name.
|
||||
* E.g. 'parent.child' would access the 'child' element of the 'parent' table. Elements of arrays can be accessed and
|
||||
* traversed by using the index number in brackets. E.g. 'array[3]' would access the fourth element of the array 'array'.
|
||||
* These access conventions can also be chained like 'table.array[2][1].subtable.integerElement'.
|
||||
* @attention Array indexing starts with 0!
|
||||
* @attention For an array, when no indexing or a too large index number is supplied, a new entry will be created and
|
||||
* returned.
|
||||
* @param[in] rssPath The path of the node to searched for.
|
||||
* @param[in] bInsertTableArray Since a table array consists of an array and a table, this is different than the other
|
||||
* insertions that only insert one element. Some special treatment is needed at certain points.
|
||||
* @return Returns a pair with the shared pointer to the parent node and the leftover name. If the creation could not be
|
||||
* done, a NULL pointer is returned.
|
||||
*/
|
||||
virtual std::pair<std::shared_ptr<CNodeCollection>, std::string> SmartParentCreate(const std::string& rssPath,
|
||||
bool bInsertTableArray = false);
|
||||
|
||||
/**
|
||||
* @brief Insert a value into the collection at the location before the supplied index. Overload of
|
||||
* sdv::toml::INodeCollectionInsert::InsertValue.
|
||||
* @param[in] uiIndex The insertion location to insert the node before. Can be npos or any value larger than the
|
||||
* collection count to insert the node at the end of the collection. Value nodes cannot be inserted behind external
|
||||
* tables and table arrays. If the index is referencing a position behind an external table or a table array, the index
|
||||
* is automatically corrected.
|
||||
* @param[in] ssName Name of the node to insert. Will be ignored for an array collection. The name must adhere to the
|
||||
* key names defined by the TOML specification. Defining the key multiple times is not allowed. Quotation of key names
|
||||
* is done automatically; the parser decides itself whether the key is bare-key, a literal key or a quoted key.
|
||||
* @param[in] anyValue The value of the node, being either an integer, floating point number, virtual bool value or a string.
|
||||
* @remarks In TOML, inline nodes are located before standard nodes. Since values are presented as inline node, they
|
||||
* will be inserted before any standard node (table or table array if defined as standard node).
|
||||
* @param[in] ssInsertBefore Name of the node to insert the value before. In case of an array, can be an index between
|
||||
* square brackets. Can be empty, causing the node to be inserted at the end.
|
||||
* @param[in] ssName Name of the node to insert. This name can contain parent nodes, which are automatically created if
|
||||
* not existing. With arrays, since the value in TOML doesn't have a name, the name of the value must be an empty
|
||||
* string and any names provided are considered parent nodes. The name must adhere to the key names defined by the TOML
|
||||
* specification. Defining the key multiple times is not allowed. Quotation of key names is done automatically; the
|
||||
* parser decides itself whether the key is bare-key, a literal key or a quoted key.
|
||||
* @param[in] anyValue The value of the node, being either an integer, floating point number, virtual bool value or a
|
||||
* string.
|
||||
* Conversion is automatically done to int64, double float, bool or u8string.
|
||||
* @return On success the interface to the newly inserted node is returned or NULL otherwise.
|
||||
*/
|
||||
virtual sdv::IInterfaceAccess* InsertValue(uint32_t uiIndex, const sdv::u8string& ssName, sdv::any_t anyValue) override;
|
||||
virtual sdv::IInterfaceAccess* InsertValue(/*in*/ const sdv::u8string& ssInsertBefore, /*in*/ const sdv::u8string& ssName,
|
||||
/*in*/ sdv::any_t anyValue) override;
|
||||
|
||||
/**
|
||||
* @brief Insert an array into the collection at the location before the supplied index. Overload of
|
||||
* sdv::toml::INodeCollectionInsert::InsertArray.
|
||||
* @param[in] uiIndex The insertion location to insert the node before. Can be npos or any value larger than the
|
||||
* collection count to insert the node at the end of the collection. Array nodes cannot be inserted behind external
|
||||
* tables and table arrays. If the index is referencing a position behind an external table or a table array, the index
|
||||
* is automatically corrected.
|
||||
* @param[in] ssName Name of the array node to insert. Will be ignored if the current node is also an array collection.
|
||||
* The name must adhere to the key names defined by the TOML specification. Defining the key multiple times is not
|
||||
* allowed. Quotation of key names is done automatically; the parser decides itself whether the key is bare-key, a
|
||||
* literal key or a quoted key.
|
||||
* @remarks In TOML, inline nodes are located before standard nodes. Since arrays are presented as inline node, they
|
||||
* will be inserted before any standard node (table or table array if defined as standard node).
|
||||
* @param[in] ssInsertBefore Name of the node to insert the value before. In case of an array, can be an index between
|
||||
* square brackets. Can be empty, causing the node to be inserted at the end.
|
||||
* @param[in] ssName Name of the node to insert. This name can contain parent nodes, which are automatically created if
|
||||
* not existing. With arrays, since the value in TOML doesn't have a name, the name of the value must be an empty
|
||||
* string and any names provided are considered parent nodes. The name must adhere to the key names defined by the TOML
|
||||
* specification. Defining the key multiple times is not allowed. Quotation of key names is done automatically; the
|
||||
* parser decides itself whether the key is bare-key, a literal key or a quoted key.
|
||||
* @return On success the interface to the newly inserted node is returned or NULL otherwise.
|
||||
*/
|
||||
virtual sdv::IInterfaceAccess* InsertArray(uint32_t uiIndex, const sdv::u8string& ssName) override;
|
||||
virtual sdv::IInterfaceAccess* InsertArray(/*in*/ const sdv::u8string& ssInsertBefore,
|
||||
/*in*/ const sdv::u8string& ssName) override;
|
||||
|
||||
/**
|
||||
* @brief Insert a table into the collection at the location before the supplied index. Overload of
|
||||
* sdv::toml::INodeCollectionInsert::InsertTable.
|
||||
* @param[in] uiIndex The insertion location to insert the node before. Can be npos or any value larger than the
|
||||
* collection count to insert the node at the end of the collection. Table nodes cannot be inserted before value nodes
|
||||
* or arrays. If the index is referencing a position before a value node or an array, the index is automatically
|
||||
* corrected.
|
||||
* @param[in] ssName Name of the table node to insert. Will be ignored if the current node is an array collection.
|
||||
* The name must adhere to the key names defined by the TOML specification. Defining the key multiple times is not
|
||||
* allowed. Quotation of key names is done automatically; the parser decides itself whether the key is bare-key, a
|
||||
* literal key or a quoted key.
|
||||
* @remarks In TOML, inline nodes are located before standard nodes. Tables can be inserted as inline node, in which
|
||||
* case they will be inserted before any standard node (table or table array if defined as standard node).
|
||||
* @param[in] ssInsertBefore Name of the node to insert the value before. In case of an array, can be an index between
|
||||
* square brackets. Can be empty, causing the node to be inserted at the end.
|
||||
* @param[in] ssName Name of the node to insert. This name can contain parent nodes, which are automatically created if
|
||||
* not existing. With arrays, since the value in TOML doesn't have a name, the name of the value must be an empty
|
||||
* string and any names provided are considered parent nodes. The name must adhere to the key names defined by the TOML
|
||||
* specification. Defining the key multiple times is not allowed. Quotation of key names is done automatically; the
|
||||
* parser decides itself whether the key is bare-key, a literal key or a quoted key.
|
||||
* @param[in] ePreference The preferred form of the node to be inserted.
|
||||
* @return On success the interface to the newly inserted node is returned or NULL otherwise.
|
||||
*/
|
||||
virtual sdv::IInterfaceAccess* InsertTable(uint32_t uiIndex, const sdv::u8string& ssName,
|
||||
sdv::toml::INodeCollectionInsert::EInsertPreference ePreference) override;
|
||||
virtual sdv::IInterfaceAccess* InsertTable(/*in*/ const sdv::u8string& ssInsertBefore, /*in*/ const sdv::u8string& ssName,
|
||||
/*in*/ sdv::toml::EInsertPreference ePreference) override;
|
||||
|
||||
/**
|
||||
* @brief Insert a table array into the collection at the location before the supplied index. Overload of
|
||||
* sdv::toml::INodeCollectionInsert::InsertTableArray.
|
||||
* @param[in] uiIndex The insertion location to insert the node before. Can be npos or any value larger than the
|
||||
* collection count to insert the node at the end of the collection. Table array nodes cannot be inserted before value
|
||||
* nodes or arrays. If the index is referencing a position before a value node or an array, the index is automatically
|
||||
* corrected.
|
||||
* @param[in] ssName Name of the array node to insert. Will be ignored if the current node is also an array collection.
|
||||
* The name must adhere to the key names defined by the TOML specification. Defining the key multiple times is not
|
||||
* allowed. Quotation of key names is done automatically; the parser decides itself whether the key is bare-key, a
|
||||
* literal key or a quoted key.
|
||||
* @remarks In TOML, inline nodes are located before standard nodes. Table arrays can be inserted as inline node, in
|
||||
* which case they will be inserted before any standard node (table or table array if defined as standard node).
|
||||
* @param[in] ssInsertBefore Name of the node to insert the value before. In case of an array, can be an index between
|
||||
* square brackets. Can be empty, causing the node to be inserted at the end.
|
||||
* @param[in] ssName Name of the node to insert. This name can contain parent nodes, which are automatically created if
|
||||
* not existing. With arrays, since the value in TOML doesn't have a name, the name of the value must be an empty
|
||||
* string and any names provided are considered parent nodes. The name must adhere to the key names defined by the TOML
|
||||
* specification. Defining the key multiple times is not allowed. Quotation of key names is done automatically; the
|
||||
* parser decides itself whether the key is bare-key, a literal key or a quoted key.
|
||||
* @param[in] ePreference The preferred form of the node to be inserted.
|
||||
* @return On success the interface to the newly inserted node is returned or NULL otherwise.
|
||||
*/
|
||||
virtual sdv::IInterfaceAccess* InsertTableArray(uint32_t uiIndex, const sdv::u8string& ssName,
|
||||
sdv::toml::INodeCollectionInsert::EInsertPreference ePreference) override;
|
||||
virtual sdv::IInterfaceAccess* InsertTableArray(/*in*/ const sdv::u8string& ssInsertBefore, /*in*/ const sdv::u8string& ssName,
|
||||
/*in*/ sdv::toml::EInsertPreference ePreference) override;
|
||||
|
||||
/**
|
||||
* @brief Insert a TOML string as a child of the current collection node. If the collection is a table, the TOML string
|
||||
* should contain values and inline/external/array-table nodes with names. If the collection is an array, the TOML
|
||||
* string should contain and inline table nodes without names. Overload of sdv::toml::INodeCollectionInsert::InsertTOML.
|
||||
* @param[in] uiIndex The insertion location to insert the node before. Can be npos or any value larger than the
|
||||
* collection count to insert the node at the end of the collection. Table array nodes cannot be inserted before value
|
||||
* nodes or arrays. If the index is referencing a position before a value node or an array, the index is automatically
|
||||
* corrected.
|
||||
* @remarks In TOML, inline nodes are located before standard nodes. Dependable on the nodes defined in the TOML they
|
||||
* might be transferred to inline or they might be inserted at a different location.
|
||||
* @param[in] ssInsertBefore Name of the node to insert the value before. In case of an array, can be an index between
|
||||
* square brackets. Can be empty, causing the node to be inserted at the end.
|
||||
* @param[in] ssTOML The TOML string to insert.
|
||||
* @param[in] bRollbackOnPartly If only part of the nodes could be inserted, no node will be inserted.
|
||||
* @return The result of the insertion.
|
||||
*/
|
||||
virtual sdv::toml::INodeCollectionInsert::EInsertResult InsertTOML(uint32_t uiIndex, const sdv::u8string& ssTOML,
|
||||
bool bRollbackOnPartly) override;
|
||||
virtual sdv::toml::INodeCollectionInsert::EInsertResult InsertTOML(/*in*/ const sdv::u8string& ssInsertBefore,
|
||||
/*in*/ const sdv::u8string& ssTOML, /*in*/ bool bRollbackOnPartly) override;
|
||||
|
||||
/**
|
||||
* @brief Insert a TOML string as a child of the current collection node. If the collection is a table, the TOML string
|
||||
* should contain values and inline/external/array-table nodes with names. If the collection is an array, the TOML
|
||||
* string should contain and inline table nodes without names.
|
||||
* @param[in] rptrInsertBefore The node to insert the TOML nodes before (if possible). Can be NULL, causing the TOML nodes
|
||||
* to be inserted at the end.
|
||||
* @param[in] ssTOML The TOML string to insert.
|
||||
* @param[in] bRollbackOnPartly If only part of the nodes could be inserted, no node will be inserted.
|
||||
* @return A pair structure with the result of the insertion and a vector of all the inserted nodes.
|
||||
*/
|
||||
std::pair<sdv::toml::INodeCollectionInsert::EInsertResult, std::vector<std::shared_ptr<CNode>>> InsertTOML(
|
||||
const std::shared_ptr<CNode>& rptrInsertBefore, const sdv::u8string& ssTOML, bool bRollbackOnPartly);
|
||||
|
||||
/**
|
||||
* @brief Delete the current node. Overload of sdv::toml::INodeUpdate::DeleteNode.
|
||||
@@ -1080,6 +1165,23 @@ namespace toml_parser
|
||||
*/
|
||||
virtual bool DeleteNode() override;
|
||||
|
||||
/**
|
||||
* @brief The derived class from the node collection can be inline or not. Overload of CNode::Inline.
|
||||
* @return Returns whether the node is an inline node.
|
||||
*/
|
||||
virtual bool Inline() const override;
|
||||
|
||||
/**
|
||||
* @brief With some node collections it is possible to switch between inline and normal. Overload of CNode::Inline.
|
||||
* @remarks Additional node composition information will be removed and the order within the parent node might be changed.
|
||||
* @param[in] bInline When set, try to switch to inline. Otherwise try to switch to normal.
|
||||
* @param[in] bIncludeChildren When set and bInline is not set, applicable child nodes are converted as well (only tables
|
||||
* and table-arrays can be defined as standard). Making a node inline is always including the children.
|
||||
* @return Returns whether the switch was successful. A switch to the same type (normal to normal or inline to inline is
|
||||
* always successful). When returning false, the switching might not be supported for this type.
|
||||
*/
|
||||
virtual bool Inline(bool bInline, bool bIncludeChildren = true) override;
|
||||
|
||||
/**
|
||||
* @brief Can the node convert to an inline definition? Overload of sdv::toml::INodeCollectionConvert::CanMakeInline.
|
||||
* @return Returns whether the conversion to inline is possible. Returns 'true' when the node is already inline.
|
||||
@@ -1101,10 +1203,12 @@ namespace toml_parser
|
||||
|
||||
/**
|
||||
* @brief Convert the node to a standard node. Overload of sdv::toml::INodeCollectionConvert::MakeStandard.
|
||||
* @param[in] bIncludeChildren When set, applicable child nodes are made are converted to standard nodes as well (only
|
||||
* tables and table-arrays can be defined as standard).
|
||||
* @return Returns whether the conversion was successful. Returns 'true' when the node was already defined as standard
|
||||
* node.
|
||||
*/
|
||||
virtual bool MakeStandard() override;
|
||||
virtual bool MakeStandard(/*in*/ bool bIncludeChildren) override;
|
||||
|
||||
/**
|
||||
* @brief Delete a node from the collection.
|
||||
@@ -1114,28 +1218,6 @@ namespace toml_parser
|
||||
*/
|
||||
bool DeleteNode(const std::shared_ptr<CNode>& rptrNode);
|
||||
|
||||
/**
|
||||
* @brief Insert the node into the view.
|
||||
* @remarks The node must be a descendant (direct child or an indirect child) of the node.
|
||||
* @details Insert the node into the vector (and remove it from any previous vector if still assigned). Inline nodes can be
|
||||
* assigned to stadard and inline nodes. Standard nodes can only be assigned to standard nodes. In the vector, first the
|
||||
* inline nodes and then the standard nodes are located. This means that an inline node will be placed before standard nodes
|
||||
* and standard nodes behind inline nodes, regardless of the index provided.
|
||||
* @param[in] uiIndex Location within the vector to insert the node. Could be sdv::toml::npos to insert as last node.
|
||||
* @param[in] rptrNode Reference to the smart pointer to the node to set the view for. If the node is not a direct child
|
||||
* node, the view pointer of the node will be set.
|
||||
* @return Returns true when the insertion was successful, false when node (likely because the node to insert is a standard
|
||||
* node, whereas this node is an inline node.
|
||||
*/
|
||||
bool InsertIntoView(uint32_t uiIndex, const std::shared_ptr<CNode>& rptrNode);
|
||||
|
||||
/**
|
||||
* @brief Remove a node from a view.
|
||||
* @param[in] rptrNode Reference to the smart pointer pointing to the node to remove.
|
||||
* @return Returns whether the removal was successful.
|
||||
*/
|
||||
bool RemoveFromView(const std::shared_ptr<CNode>& rptrNode);
|
||||
|
||||
/**
|
||||
* @brief Find the index belonging to the provided node.
|
||||
* @param[in] rptrNode Reference to the smart pointer holding the node to return the index for.
|
||||
@@ -1151,7 +1233,7 @@ namespace toml_parser
|
||||
bool IsDescendant(const std::shared_ptr<CNode>& rptrNode) const;
|
||||
|
||||
/**
|
||||
* @brief Generic inserting function for nodes.
|
||||
* @brief Generic add function for nodes.
|
||||
* @details Elements of tables can be accessed and traversed by using '.' to separated the parent name from child name.
|
||||
* E.g. 'parent.child' would access the 'child' element of the 'parent' table. Elements of arrays can be accessed and
|
||||
* traversed by using the index number in brackets. E.g. 'array[3]' would access the fourth element of the array 'array'.
|
||||
@@ -1160,10 +1242,6 @@ namespace toml_parser
|
||||
* @attention For an array, when no indexing is supplied, the latest entry will be returned.
|
||||
* @remarks If the node to insert exists already, but is marked implicit, the node will be returned and made explicit. In
|
||||
* all other cases the an error will occur that the node already exists.
|
||||
* @param[in] uiIndex The insertion location to insert the node before. Can be npos or any value larger than the
|
||||
* collection count to insert the node at the end of the collection. Table array nodes cannot be inserted before value
|
||||
* nodes or arrays. If the index is referencing a position before a value node or an array, the index is automatically
|
||||
* corrected.
|
||||
* @param[in] rrangeKeyPath Reference to the token range containing the path to the node to insert.
|
||||
* @param[in] rtArgs Zero or more references to arguments passed to the constructor of the node classes being created by
|
||||
* this function.
|
||||
@@ -1171,7 +1249,7 @@ namespace toml_parser
|
||||
* there the returned node is a table within the table array.
|
||||
*/
|
||||
template <typename TNodeType, typename... TArgs>
|
||||
std::shared_ptr<CNode> Insert(uint32_t uiIndex, const CTokenRange& rrangeKeyPath, const TArgs&... rtArgs);
|
||||
std::shared_ptr<CNode> AddNodeFromRange(const CTokenRange& rrangeKeyPath, const TArgs&... rtArgs);
|
||||
|
||||
/**
|
||||
* @brief Combine the collection with the provided content (mathematical union).
|
||||
@@ -1191,6 +1269,13 @@ namespace toml_parser
|
||||
*/
|
||||
virtual bool Reduce(const std::shared_ptr<CNodeCollection>& rptrCollection) = 0;
|
||||
|
||||
/**
|
||||
* @brief When the parent changes (e.g. when moving items from one parser to the other), the items and all its sub-items
|
||||
* need to reasign the parser. Overload of CNode::ReassignParser.
|
||||
* @param[in] rParser Reference to the parser to assign.
|
||||
*/
|
||||
virtual void ReassignParser(CParser& rParser) override;
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief When set, the child nodes need grouping (values following each other, tables and table arrays at the end).
|
||||
@@ -1228,8 +1313,6 @@ namespace toml_parser
|
||||
// The child node uses the parent node pointer to indicate which node holds node.
|
||||
// The child node uses the view node pointer to indicate which node displays the node content.
|
||||
|
||||
std::vector<std::shared_ptr<CNode>> m_vecNodeOrder; ///< Vector holding the child nodes (could contain grand children as
|
||||
///< well).
|
||||
std::list<std::shared_ptr<CNode>> m_lstNodes; ///< List holding the direct child nodes.
|
||||
std::list<std::shared_ptr<CNode>> m_lstRecycleBin; ///< List holding the child elements that were deleted. This will
|
||||
///< prevent destruction of the node class, which would otherwise
|
||||
@@ -1283,6 +1366,26 @@ namespace toml_parser
|
||||
*/
|
||||
virtual sdv::toml::ENodeType GetType() const override;
|
||||
|
||||
/**
|
||||
* @brief Returns the amount of nodes. Overload of sdv::toml::INodeCollection::GetCount.
|
||||
* @return The amount of nodes.
|
||||
*/
|
||||
virtual uint32_t GetCount() const override;
|
||||
|
||||
/**
|
||||
* @brief Get the node. Overload of CNodeCollection::Get.
|
||||
* @param[in] uiIndex Index of the node to get.
|
||||
* @return Smart pointer to the node object.
|
||||
*/
|
||||
virtual std::shared_ptr<CNode> Get(uint32_t uiIndex) const override;
|
||||
|
||||
/**
|
||||
* @brief Delete the current node. Overload of sdv::toml::INodeUpdate::DeleteNode.
|
||||
* @attention A successful deletion will cause all interfaces to the current node to become inoperable.
|
||||
* @return Returns whether the deletion was successful.
|
||||
*/
|
||||
virtual bool DeleteNode() override;
|
||||
|
||||
/**
|
||||
* @brief Create the TOML text based on the content using an optional prefix node. Overload of CNode::GenerateTOML.
|
||||
* @param[in] rContext Reference to the context class to use during TOML code generation.
|
||||
@@ -1308,10 +1411,12 @@ namespace toml_parser
|
||||
* table doesn't have a name.
|
||||
* @remarks Additional node composition information will be removed and the order within the parent node might be changed.
|
||||
* @param[in] bInline When set, try to switch to inline. Otherwise try to switch to normal.
|
||||
* @param[in] bIncludeChildren When set and bInline is not set, applicable child nodes are converted as well (only tables
|
||||
* and table-arrays can be defined as standard). Making a node inline is always including the children.
|
||||
* @return Returns whether the switch was successful. A switch to the same type (normal to normal or inline to inline is
|
||||
* always successful). When returning false, the switching might not be supported for this type.
|
||||
*/
|
||||
virtual bool Inline(bool bInline) override;
|
||||
virtual bool Inline(bool bInline, bool bIncludeChildren = true) override;
|
||||
|
||||
/**
|
||||
* @brief Checks whether the table was explicitly defined. Overload of CNodeCollection::ExplicitlyDefined.
|
||||
@@ -1342,9 +1447,17 @@ namespace toml_parser
|
||||
*/
|
||||
virtual bool Reduce(const std::shared_ptr<CNodeCollection>& rptrCollection) override;
|
||||
|
||||
/**
|
||||
* @brief After every insert, deletion and shift, the node order of this and all sub- tables need to be rebuild.
|
||||
* @param[in] bForce Force rebuild, even if locked.
|
||||
*/
|
||||
virtual void RebuildNodeOrder(bool bForce) override;
|
||||
|
||||
private:
|
||||
bool m_bDefinedExplicitly = true; ///< When set, the table is defined explicitly.
|
||||
bool m_bInline = false; ///< Flag determining whether the table is inline or not.
|
||||
bool m_bDefinedExplicitly = true; ///< When set, the table is defined explicitly.
|
||||
bool m_bInline = false; ///< Flag determining whether the table is inline or not.
|
||||
std::vector<std::shared_ptr<CNode>> m_vecNodeOrder; ///< Vector holding the child nodes (could contain grand children as
|
||||
///< well).
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1426,10 +1539,28 @@ namespace toml_parser
|
||||
* @attention Array element access indices starts with 0!
|
||||
* @attention For an array element inserting, when no indexing is supplied, the latest entry will be returned.
|
||||
* @param[in] rssPath Reference to the path of the node to searched for.
|
||||
* @return Returns a shared pointer to the wanted Node if it was found or a node with invalid content if it was not found.
|
||||
* @return Returns a shared pointer to the wanted node if it was found or a node with invalid content if it was not found.
|
||||
*/
|
||||
virtual std::shared_ptr<CNode> Direct(const std::string& rssPath) const override;
|
||||
|
||||
/**
|
||||
* @brief Get or create the parent nodes automotatically from the path. Overload of CNodeCollection::SmartParentCreate.
|
||||
* @details Elements of tables can be accessed and traversed by using '.' to separated the parent name from child name.
|
||||
* E.g. 'parent.child' would access the 'child' element of the 'parent' table. Elements of arrays can be accessed and
|
||||
* traversed by using the index number in brackets. E.g. 'array[3]' would access the fourth element of the array 'array'.
|
||||
* These access conventions can also be chained like 'table.array[2][1].subtable.integerElement'.
|
||||
* @attention Array indexing starts with 0!
|
||||
* @attention For an array, when no indexing or a too large index number is supplied, a new entry will be created and
|
||||
* returned.
|
||||
* @param[in] rssPath The path of the node to searched for.
|
||||
* @param[in] bInsertTableArray Since a table array consists of an array and a table, this is different than the other
|
||||
* insertions that only insert one element. Some special treatment is needed at certain points.
|
||||
* @return Returns a pair with the shared pointer to the parent node and the leftover name. If the creation could not be
|
||||
* done, a NULL pointer is returned.
|
||||
*/
|
||||
virtual std::pair<std::shared_ptr<CNodeCollection>, std::string> SmartParentCreate(const std::string& rssPath,
|
||||
bool bInsertTableArray = false) override;
|
||||
|
||||
/**
|
||||
* @brief Create the TOML text based on the content using an optional prefix node. Overload of CNode::GenerateTOML.
|
||||
* @param[in] rContext Reference to the context class to use during TOML code generation.
|
||||
@@ -1450,13 +1581,6 @@ namespace toml_parser
|
||||
*/
|
||||
bool TableArray() const;
|
||||
|
||||
/**
|
||||
* @brief Can the node convert to a standard definition? Overload of sdv::toml::INodeCollectionConvert::CanMakeStandard.
|
||||
* @return Returns whether the conversion to standard is possible. Returns 'true' when the node is already defined as
|
||||
* standard node.
|
||||
*/
|
||||
virtual bool CanMakeStandard() const override;
|
||||
|
||||
/**
|
||||
* @brief The derived class from the node collection can be inline or not. Overload of CNode::Inline.
|
||||
* @return Returns whether the node is an inline node.
|
||||
@@ -1472,10 +1596,19 @@ namespace toml_parser
|
||||
* doesn't have a name that can be used to define the explicit table array.
|
||||
* @remarks Additional node composition information will be removed and the order within the parent node might be changed.
|
||||
* @param[in] bInline When set, try to switch to inline. Otherwise try to switch to normal.
|
||||
* @param[in] bIncludeChildren When set and bInline is not set, applicable child nodes are converted as well (only tables
|
||||
* and table-arrays can be defined as standard). Making a node inline is always including the children.
|
||||
* @return Returns whether the switch was successful. A switch to the same type (normal to normal or inline to inline is
|
||||
* always successful). When returning false, the switching might not be supported for this type.
|
||||
*/
|
||||
virtual bool Inline(bool bInline) override;
|
||||
virtual bool Inline(bool bInline, bool bIncludeChildren = true) override;
|
||||
|
||||
/**
|
||||
* @brief Can the node convert to a standard definition? Overload of sdv::toml::INodeCollectionConvert::CanMakeStandard.
|
||||
* @return Returns whether the conversion to standard is possible. Returns 'true' when the node is already defined as
|
||||
* standard node.
|
||||
*/
|
||||
virtual bool CanMakeStandard() const override;
|
||||
|
||||
/**
|
||||
* @brief Does the last child node need a comma following the node?
|
||||
@@ -1534,7 +1667,7 @@ namespace toml_parser
|
||||
* @brief Constructor
|
||||
* @param[in] rparser Reference to the TOML parser.
|
||||
*/
|
||||
CRootTable(CParser& rparser) : CTable(rparser, "root", "", false)
|
||||
CRootTable(CParser& rparser) : CTable(rparser, "root", "", false, true)
|
||||
{}
|
||||
|
||||
/**
|
||||
@@ -1550,20 +1683,25 @@ namespace toml_parser
|
||||
*/
|
||||
virtual bool Inline() const override
|
||||
{
|
||||
// The root node can never be inline.
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief With some node collections it is possible to switch between inline and normal. Overload of
|
||||
* CNodeCollection::Inline.
|
||||
* @brief Switch between inline and explicit table definition. Overload of CNodeCollection::Inline.
|
||||
* @attention It is not possible to switch to an explicit table definition if the table is part of an array, since the
|
||||
* table doesn't have a name.
|
||||
* @remarks Additional node composition information will be removed and the order within the parent node might be changed.
|
||||
* @param[in] bInline When set, try to switch to inline. Otherwise try to switch to normal.
|
||||
* @param[in] bIncludeChildren When set and bInline is not set, applicable child nodes are converted as well (only tables
|
||||
* and table-arrays can be defined as standard). Making a node inline is always including the children.
|
||||
* @return Returns whether the switch was successful. A switch to the same type (normal to normal or inline to inline is
|
||||
* always successful). When returning false, the switching might not be supported for this type.
|
||||
*/
|
||||
virtual bool Inline(bool bInline) override
|
||||
virtual bool Inline(bool bInline, bool bIncludeChildren = true) override
|
||||
{
|
||||
return bInline == false;
|
||||
// Pass call to table implementation.
|
||||
return CTable::Inline(bInline, bIncludeChildren);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1580,7 +1718,7 @@ namespace toml_parser
|
||||
}
|
||||
|
||||
template <typename TNodeType, typename... TArgs>
|
||||
inline std::shared_ptr<CNode> CNodeCollection::Insert(uint32_t uiIndex, const CTokenRange& rrangeKeyPath, const TArgs&... rtArgs)
|
||||
inline std::shared_ptr<CNode> CNodeCollection::AddNodeFromRange(const CTokenRange& rrangeKeyPath, const TArgs&... rtArgs)
|
||||
{
|
||||
// Get the first part of the node
|
||||
auto prKey = SplitNodeKey(rrangeKeyPath);
|
||||
@@ -1613,9 +1751,8 @@ namespace toml_parser
|
||||
"' exists already, but is not a table array.");
|
||||
|
||||
// Create the table.
|
||||
ptrNode = ptrTableArray->Insert<CTable>(sdv::toml::npos, CTokenRange(prKey.first, prKey.first.get().Next()),
|
||||
ptrNode = ptrTableArray->AddNodeFromRange<CTable>(CTokenRange(prKey.first, prKey.first.get().Next()),
|
||||
false, true);
|
||||
ptrNode->SetViewPtr(Cast<CNodeCollection>());
|
||||
} else if (!Cast<CArray>() && itNode != m_lstNodes.end())
|
||||
{
|
||||
// If existing... this might be a duplicate if not explicitly defined before.
|
||||
@@ -1644,7 +1781,7 @@ namespace toml_parser
|
||||
// If the current node is implicit, take over the inline flag (this determines whether a sub-table definition is
|
||||
// allowed or not).
|
||||
if (!ExplicitlyDefined())
|
||||
Inline(ptrNode->Inline());
|
||||
Inline(ptrNode->Inline(), false);
|
||||
}
|
||||
}
|
||||
else // Intermediate node
|
||||
@@ -1663,6 +1800,9 @@ namespace toml_parser
|
||||
auto prNextKey = SplitNodeKey(prKey.second);
|
||||
if (prNextKey.first.get().Category() != ETokenCategory::token_integer)
|
||||
{
|
||||
// The table array should have an ordered vector. This is not the case yet. Rebuild the table array.
|
||||
ptrNode->Cast<CTableArray>()->RebuildNodeOrder(true);
|
||||
|
||||
// Get the last table
|
||||
if (!ptrNode->Cast<CTableArray>()->GetCount())
|
||||
throw XTOMLParseException("The parent table array node '" + prKey.first.get().StringValue() +
|
||||
@@ -1700,23 +1840,14 @@ namespace toml_parser
|
||||
std::shared_ptr<CNodeCollection> ptrNodeCollection = ptrNode->Cast<CNodeCollection>();
|
||||
if (!ptrNodeCollection)
|
||||
throw XTOMLParseException("Parent node is not an array or table '" + ptrNode->GetPath(true) + "'.");
|
||||
ptrNode = ptrNodeCollection->Insert<TNodeType>(sdv::toml::npos, prKey.second, rtArgs...);
|
||||
ptrNode = ptrNodeCollection->AddNodeFromRange<TNodeType>(prKey.second, rtArgs...);
|
||||
if (!ptrNode)
|
||||
throw XTOMLParseException("Could not create the node '" + prKey.first.get().StringValue() + "'.");
|
||||
}
|
||||
|
||||
// Insert the node at the requested location if this node is inline or this is the view of the node.
|
||||
auto itPos = (static_cast<size_t>(uiIndex) >= m_vecNodeOrder.size()) ? m_vecNodeOrder.end() :
|
||||
m_vecNodeOrder.begin() + static_cast<size_t>(uiIndex);
|
||||
m_vecNodeOrder.insert(itPos, ptrNode);
|
||||
if (ptrNode->GetParentPtr() != Cast<CNodeCollection>())
|
||||
ptrNode->SetViewPtr(Cast<CNodeCollection>());
|
||||
|
||||
// Return the result
|
||||
return ptrNode;
|
||||
}
|
||||
|
||||
|
||||
} // namespace toml_parser
|
||||
|
||||
#endif // !defined PARSER_NODE_TOML_H
|
||||
@@ -54,6 +54,9 @@ namespace toml_parser
|
||||
|
||||
try
|
||||
{
|
||||
// Lock the rebuild of the node order
|
||||
auto lock = CreateRebuildLockObject();
|
||||
|
||||
// Run through all tokens of the lexer and process the tokens.
|
||||
bool bEOF = false; // Explicit test, since the peek could return EOF, but the cursor might not be at the end yet.
|
||||
while (!bEOF && !m_lexer.IsEnd())
|
||||
@@ -95,7 +98,7 @@ namespace toml_parser
|
||||
}
|
||||
catch (const sdv::toml::XTOMLParseException& e)
|
||||
{
|
||||
std::cout << e.what() << '\n';
|
||||
std::cerr << e.what() << std::endl;
|
||||
throw;
|
||||
}
|
||||
|
||||
@@ -115,6 +118,11 @@ namespace toml_parser
|
||||
return m_lexer;
|
||||
}
|
||||
|
||||
CNodeIndexer& CParser::Indexer()
|
||||
{
|
||||
return m_indexer;
|
||||
}
|
||||
|
||||
const CNodeCollection& CParser::Root() const
|
||||
{
|
||||
auto ptrCollection = m_ptrRoot->Cast<CTable>();
|
||||
@@ -139,6 +147,49 @@ namespace toml_parser
|
||||
return m_ptrRoot->GenerateTOML(rssPrefixKey);
|
||||
}
|
||||
|
||||
CParser::CLockRebuild::CLockRebuild(CParser& rParser) : m_rParser(rParser)
|
||||
{
|
||||
rParser.IncrRebuildLockCnt();
|
||||
}
|
||||
|
||||
CParser::CLockRebuild::CLockRebuild(const CLockRebuild& rLockRebuild) : m_rParser(rLockRebuild.m_rParser)
|
||||
{
|
||||
m_rParser.IncrRebuildLockCnt();
|
||||
}
|
||||
|
||||
CParser::CLockRebuild::CLockRebuild(CLockRebuild&& rLockRebuild) : m_rParser(rLockRebuild.m_rParser)
|
||||
{
|
||||
m_rParser.IncrRebuildLockCnt();
|
||||
}
|
||||
|
||||
CParser::CLockRebuild::~CLockRebuild()
|
||||
{
|
||||
m_rParser.DecrRebuildLockCnt();
|
||||
}
|
||||
|
||||
CParser::CLockRebuild CParser::CreateRebuildLockObject()
|
||||
{
|
||||
return CLockRebuild(*this);
|
||||
}
|
||||
|
||||
bool CParser::RebuildLocked() const
|
||||
{
|
||||
return m_nRebuildLockCnt ? true : false;
|
||||
}
|
||||
|
||||
void CParser::IncrRebuildLockCnt()
|
||||
{
|
||||
++m_nRebuildLockCnt;
|
||||
}
|
||||
|
||||
void CParser::DecrRebuildLockCnt()
|
||||
{
|
||||
if (!m_nRebuildLockCnt)
|
||||
return; // Should not occur
|
||||
--m_nRebuildLockCnt;
|
||||
if (!m_nRebuildLockCnt) Root().RebuildNodeOrder(false);
|
||||
}
|
||||
|
||||
void CParser::ProcessTable(CNodeTokenRange& rNodeRange)
|
||||
{
|
||||
// Get the table path (table name preceded by parent tables separated with dots).
|
||||
@@ -154,7 +205,7 @@ namespace toml_parser
|
||||
m_lexer.SmartExtendNodeRange(rNodeRange);
|
||||
|
||||
// Add the table to the root
|
||||
auto ptrTable = m_ptrRoot->Insert<CTable>(sdv::toml::npos, rangeKeyPath, false);
|
||||
auto ptrTable = m_ptrRoot->AddNodeFromRange<CTable>(rangeKeyPath, false);
|
||||
if (ptrTable)
|
||||
{
|
||||
m_ptrCurrentCollection = ptrTable->Cast<CTable>();
|
||||
@@ -176,7 +227,7 @@ namespace toml_parser
|
||||
m_lexer.SmartExtendNodeRange(rNodeRange);
|
||||
|
||||
// Add the table array to the root
|
||||
auto ptrTableArray = m_ptrRoot->Insert<CTableArray>(sdv::toml::npos, rangeKeyPath);
|
||||
auto ptrTableArray = m_ptrRoot->AddNodeFromRange<CTableArray>(rangeKeyPath);
|
||||
if (ptrTableArray)
|
||||
{
|
||||
m_ptrCurrentCollection = ptrTableArray->Cast<CNodeCollection>();
|
||||
@@ -226,46 +277,43 @@ namespace toml_parser
|
||||
switch (rAssignmentValue.Category())
|
||||
{
|
||||
case ETokenCategory::token_boolean:
|
||||
ptrNode = m_ptrCurrentCollection->Insert<CBooleanNode>(sdv::toml::npos, rrangeKeyPath, rAssignmentValue.BooleanValue(),
|
||||
ptrNode = m_ptrCurrentCollection->AddNodeFromRange<CBooleanNode>(rrangeKeyPath, rAssignmentValue.BooleanValue(),
|
||||
rAssignmentValue.RawString());
|
||||
break;
|
||||
case ETokenCategory::token_integer:
|
||||
ptrNode = m_ptrCurrentCollection->Insert<CIntegerNode>(sdv::toml::npos, rrangeKeyPath, rAssignmentValue.IntegerValue(),
|
||||
ptrNode = m_ptrCurrentCollection->AddNodeFromRange<CIntegerNode>(rrangeKeyPath, rAssignmentValue.IntegerValue(),
|
||||
rAssignmentValue.RawString());
|
||||
break;
|
||||
case ETokenCategory::token_float:
|
||||
ptrNode = m_ptrCurrentCollection->Insert<CFloatingPointNode>(sdv::toml::npos, rrangeKeyPath, rAssignmentValue.FloatValue(),
|
||||
ptrNode = m_ptrCurrentCollection->AddNodeFromRange<CFloatingPointNode>(rrangeKeyPath, rAssignmentValue.FloatValue(),
|
||||
rAssignmentValue.RawString());
|
||||
break;
|
||||
case ETokenCategory::token_string:
|
||||
switch (rAssignmentValue.StringType())
|
||||
{
|
||||
case ETokenStringType::literal_string:
|
||||
ptrNode = m_ptrCurrentCollection->Insert<CStringNode>(sdv::toml::npos, rrangeKeyPath, rAssignmentValue.StringValue(),
|
||||
ptrNode = m_ptrCurrentCollection->AddNodeFromRange<CStringNode>(rrangeKeyPath, rAssignmentValue.StringValue(),
|
||||
CStringNode::EQuotationType::literal_string, rAssignmentValue.RawString());
|
||||
break;
|
||||
case ETokenStringType::multi_line_literal:
|
||||
ptrNode = m_ptrCurrentCollection->Insert<CStringNode>(
|
||||
sdv::toml::npos, rrangeKeyPath, rAssignmentValue.StringValue(), CStringNode::EQuotationType::multi_line_literal,
|
||||
rAssignmentValue.RawString());
|
||||
ptrNode = m_ptrCurrentCollection->AddNodeFromRange<CStringNode>(rrangeKeyPath, rAssignmentValue.StringValue(),
|
||||
CStringNode::EQuotationType::multi_line_literal, rAssignmentValue.RawString());
|
||||
break;
|
||||
case ETokenStringType::multi_line_quoted:
|
||||
ptrNode = m_ptrCurrentCollection->Insert<CStringNode>(
|
||||
sdv::toml::npos, rrangeKeyPath, rAssignmentValue.StringValue(), CStringNode::EQuotationType::multi_line_quoted,
|
||||
rAssignmentValue.RawString());
|
||||
ptrNode = m_ptrCurrentCollection->AddNodeFromRange<CStringNode>(rrangeKeyPath, rAssignmentValue.StringValue(),
|
||||
CStringNode::EQuotationType::multi_line_quoted, rAssignmentValue.RawString());
|
||||
break;
|
||||
case ETokenStringType::quoted_string:
|
||||
default:
|
||||
ptrNode = m_ptrCurrentCollection->Insert<CStringNode>(
|
||||
sdv::toml::npos, rrangeKeyPath, rAssignmentValue.StringValue(), CStringNode::EQuotationType::quoted_string,
|
||||
rAssignmentValue.RawString());
|
||||
ptrNode = m_ptrCurrentCollection->AddNodeFromRange<CStringNode>(rrangeKeyPath, rAssignmentValue.StringValue(),
|
||||
CStringNode::EQuotationType::quoted_string, rAssignmentValue.RawString());
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case ETokenCategory::token_syntax_array_open:
|
||||
{
|
||||
auto ptrCurrentCollectionStored = m_ptrCurrentCollection;
|
||||
ptrNode = m_ptrCurrentCollection->Insert<CArray>(sdv::toml::npos, rrangeKeyPath);
|
||||
ptrNode = m_ptrCurrentCollection->AddNodeFromRange<CArray>(rrangeKeyPath);
|
||||
m_ptrCurrentCollection = ptrNode->Cast<CNodeCollection>();
|
||||
m_stackEnvironment.push(EEnvironment::array);
|
||||
ProcessArray(rNodeRange);
|
||||
@@ -276,7 +324,7 @@ namespace toml_parser
|
||||
case ETokenCategory::token_syntax_inline_table_open:
|
||||
{
|
||||
auto ptrCurrentCollectionStored = m_ptrCurrentCollection;
|
||||
ptrNode = m_ptrCurrentCollection->Insert<CTable>(sdv::toml::npos, rrangeKeyPath, true);
|
||||
ptrNode = m_ptrCurrentCollection->AddNodeFromRange<CTable>(rrangeKeyPath, true);
|
||||
m_ptrCurrentCollection = ptrNode->Cast<CNodeCollection>();
|
||||
m_stackEnvironment.push(EEnvironment::inline_table);
|
||||
ProcessInlineTable(rNodeRange);
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "lexer_toml.h"
|
||||
#include "parser_node_toml.h"
|
||||
#include "miscellaneous.h"
|
||||
#include "parser_node_indexer.h"
|
||||
#include <stack>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
@@ -25,7 +26,9 @@
|
||||
/// The TOML parser namespace
|
||||
namespace toml_parser
|
||||
{
|
||||
// Forward declarations
|
||||
class CNode;
|
||||
class CNodeCollection;
|
||||
|
||||
/**
|
||||
* @brief Creates a tree structure from input of UTF-8 encoded TOML source data
|
||||
@@ -33,6 +36,10 @@ namespace toml_parser
|
||||
class CParser : public sdv::IInterfaceAccess, public sdv::toml::ITOMLParser
|
||||
{
|
||||
public:
|
||||
// Forward declaration
|
||||
class CLockRebuild;
|
||||
friend CLockRebuild; ///< Friend class can trigger manage rebuild lock counter.
|
||||
|
||||
/**
|
||||
* @brief Construct a new Parser object
|
||||
* @param[in] rssString UTF-8 encoded data of a TOML source
|
||||
@@ -66,6 +73,12 @@ namespace toml_parser
|
||||
*/
|
||||
CLexer& Lexer();
|
||||
|
||||
/**
|
||||
* @brief Get the indexer object managing the overall order of the nodes.
|
||||
* @return Reference to the index object.
|
||||
*/
|
||||
CNodeIndexer& Indexer();
|
||||
|
||||
/**
|
||||
* @{
|
||||
* @brief Return the root node.
|
||||
@@ -85,7 +98,65 @@ namespace toml_parser
|
||||
*/
|
||||
std::string GenerateTOML(const std::string& rssPrefixKey = std::string()) const;
|
||||
|
||||
/**
|
||||
* @brief Lock rebuild object preventing rebuilding the node order of all the tables.
|
||||
*/
|
||||
class CLockRebuild
|
||||
{
|
||||
friend CParser; ///< Parser can access the constructor
|
||||
|
||||
/**
|
||||
* @brief Constructor
|
||||
* @param[in] rParser Reference to the parser object
|
||||
*/
|
||||
CLockRebuild(CParser& rParser);
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Copy constructor
|
||||
* @param[in] rLockRebuild Reference to another rebuild lock object.
|
||||
*/
|
||||
CLockRebuild(const CLockRebuild& rLockRebuild);
|
||||
|
||||
/**
|
||||
* @brief Move constructor
|
||||
* @param[in] rLockRebuild Reference to another rebuild lock object.
|
||||
*/
|
||||
CLockRebuild(CLockRebuild&& rLockRebuild);
|
||||
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
~CLockRebuild();
|
||||
|
||||
private:
|
||||
CParser& m_rParser; ///< Reference to the parser object
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Create a rebuild lock object. During the lifetime of the object rebuilding the node order is locked using the lock
|
||||
* counter method.
|
||||
* @return An instance of the rebuild lock object.
|
||||
*/
|
||||
CLockRebuild CreateRebuildLockObject();
|
||||
|
||||
/**
|
||||
* @brief Returns whether rebuild is locked at the moment.
|
||||
* @return Set when rebuild is locked.
|
||||
*/
|
||||
bool RebuildLocked() const;
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Increase the rebuild lock counter. A lock count larger than 0 will prevent a rebuild.
|
||||
*/
|
||||
void IncrRebuildLockCnt();
|
||||
|
||||
/**
|
||||
* @brief Decrease the rebuild lock counter. A lock count of 0 will trigger the rebuild.
|
||||
*/
|
||||
void DecrRebuildLockCnt();
|
||||
|
||||
/**
|
||||
* @brief Process a table declaration.
|
||||
* @param[in, out] rNodeRange Reference to the extended token range of the node.
|
||||
@@ -139,10 +210,13 @@ namespace toml_parser
|
||||
inline_table ///< Environment for a table
|
||||
};
|
||||
|
||||
enum_stack<EEnvironment, EEnvironment::none> m_stackEnvironment; ///< Tracking of environments in nested structures.
|
||||
std::shared_ptr<CRootTable> m_ptrRoot; ///< The one root node.
|
||||
std::shared_ptr<CNodeCollection> m_ptrCurrentCollection; ///< The current collection node.
|
||||
CLexer m_lexer; ///< Lexer.
|
||||
enum_stack<EEnvironment, EEnvironment::none> m_stackEnvironment; ///< Tracking of environments in nested structures.
|
||||
std::shared_ptr<CRootTable> m_ptrRoot; ///< The one root node.
|
||||
std::shared_ptr<CNodeCollection> m_ptrCurrentCollection; ///< The current collection node.
|
||||
CLexer m_lexer; ///< Lexer object user for lexing the TOML code.
|
||||
CNodeIndexer m_indexer; ///< Indexer managing the oberall order of the nodes.
|
||||
size_t m_nRebuildLockCnt = 0; ///< A lock counter > 0 will prevent the node order
|
||||
///< rebuild.
|
||||
};
|
||||
} // namespace toml_parser
|
||||
|
||||
|
||||
@@ -25,8 +25,6 @@ add_library(ipc_com SHARED
|
||||
"com_channel.cpp"
|
||||
"marshall_object.h"
|
||||
"marshall_object.cpp"
|
||||
|
||||
#"scheduler.cpp"
|
||||
)
|
||||
if(UNIX)
|
||||
target_link_libraries(ipc_com rt ${CMAKE_DL_LIBS} ${CMAKE_THREAD_LIBS_INIT})
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
#include <support/serdes.h>
|
||||
#include <support/local_service_access.h>
|
||||
#include <interfaces/serdes/core_ps_serdes.h>
|
||||
#include "../../global/scheduler/scheduler.cpp"
|
||||
#include "../../global/scheduler/scheduler.h"
|
||||
|
||||
CChannelConnector::CChannelConnector(CCommunicationControl& rcontrol, uint32_t uiIndex, sdv::IInterfaceAccess* pChannelEndpoint) :
|
||||
m_rcontrol(rcontrol), m_ptrChannelEndpoint(pChannelEndpoint),
|
||||
|
||||
@@ -95,8 +95,8 @@ public:
|
||||
* @brief Sends data consisting of multiple data chunks via the IPC connection.
|
||||
* @param[in] tProxyID Marshall ID of the proxy (source).
|
||||
* @param[in] tStubID Marshall ID of the stub (target).
|
||||
* @param[in] rseqInputData Sequence of data buffers to be sent. May be altered during processing to add/change the sequence content
|
||||
* without having to copy the data.
|
||||
* @param[in] rseqInputData Sequence of data buffers to be sent. May be altered during processing to add/change the sequence
|
||||
* content without having to copy the data.
|
||||
* @return Returns the results of the call or throws a marshall exception.
|
||||
*/
|
||||
sdv::sequence<sdv::pointer<uint8_t>> MakeCall(sdv::ps::TMarshallID tProxyID, sdv::ps::TMarshallID tStubID,
|
||||
@@ -155,7 +155,9 @@ private:
|
||||
sdv::ipc::IDataSend* m_pDataSend = nullptr; ///< Pointer to the send interface.
|
||||
std::mutex m_mtxCalls; ///< Call map protection.
|
||||
std::map<uint64_t, SCallEntry&> m_mapCalls; ///< call map.
|
||||
CTaskScheduler m_scheduler; ///< Scheduler to process incoming calls.
|
||||
CTaskScheduler<sdv::core::secure_thread> m_scheduler; ///< Scheduler to process incoming calls.
|
||||
sdv::core::IPermissionControl* m_pPermissionControl = nullptr; ///< Pointer to the permission control interface of the
|
||||
///< target system.
|
||||
};
|
||||
|
||||
#endif // !defined COM_CHANNEL_H
|
||||
@@ -16,6 +16,8 @@
|
||||
#include <support/toml.h>
|
||||
#include "com_channel.h"
|
||||
#include "marshall_object.h"
|
||||
#include <support/app_control.h>
|
||||
#include <support/any.h>
|
||||
|
||||
thread_local CChannelConnector* CCommunicationControl::m_pConnectorContext = nullptr;
|
||||
|
||||
@@ -40,7 +42,7 @@ void CCommunicationControl::OnShutdown()
|
||||
std::unique_lock<std::mutex> lock(m_mtxChannels);
|
||||
auto vecInitialConnectMon = std::move(m_vecInitialConnectMon);
|
||||
lock.unlock();
|
||||
for (std::thread& rthread : vecInitialConnectMon)
|
||||
for (sdv::core::secure_thread& rthread : vecInitialConnectMon)
|
||||
{
|
||||
if (rthread.joinable())
|
||||
rthread.join();
|
||||
@@ -63,10 +65,11 @@ sdv::com::TConnectionID CCommunicationControl::CreateServerConnection(/*in*/ sdv
|
||||
switch (eChannelType)
|
||||
{
|
||||
case sdv::com::EChannelType::local_channel:
|
||||
ssChannelServer = "LocalChannelControl";
|
||||
ssChannelServer = static_cast<std::string>(sdv::app::GetAppSettingsAttribute("Communication.DefaultProvider"));
|
||||
break;
|
||||
case sdv::com::EChannelType::remote_channel:
|
||||
ssChannelServer = "RemoteChannelControl";
|
||||
// Currently not supported
|
||||
ssChannelServer = "UnknownComProvider";
|
||||
break;
|
||||
default:
|
||||
return {};
|
||||
|
||||
@@ -196,7 +196,7 @@ public:
|
||||
private:
|
||||
std::mutex m_mtxChannels; ///< Protect the channel map.
|
||||
std::vector<std::shared_ptr<CChannelConnector>> m_vecChannels; ///< Channel vector.
|
||||
std::vector<std::thread> m_vecInitialConnectMon; ///< Initial connection monitor.
|
||||
std::vector<sdv::core::secure_thread> m_vecInitialConnectMon; ///< Initial connection monitor.
|
||||
std::recursive_mutex m_mtxObjects; ///< Protect object vectors.
|
||||
std::vector<std::weak_ptr<CMarshallObject>> m_vecMarshallObjects; ///< Vector with marshall objects; lifetime is handled by channel.
|
||||
std::map<sdv::interface_t, std::shared_ptr<CMarshallObject>> m_mapStubObjects; ///< Map of interfaces to stub objects
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
|
||||
/********************************************************************************
|
||||
* Copyright (c) 2025-2026 ZF Friedrichshafen AG
|
||||
*
|
||||
@@ -18,7 +19,7 @@
|
||||
#include <support/pssup.h>
|
||||
#include <interfaces/app.h>
|
||||
|
||||
CRepositoryProxy::CRepositoryProxy(CClient& rClient, sdv::com::TConnectionID tConnection,
|
||||
CRepositoryProxy::CRepositoryProxy(CClientConnect& rClient, sdv::com::TConnectionID tConnection,
|
||||
sdv::IInterfaceAccess* pRepositoryProxy) :
|
||||
m_rClient(rClient), m_tConnection(tConnection), m_ptrRepositoryProxy(pRepositoryProxy)
|
||||
{}
|
||||
@@ -26,120 +27,131 @@ CRepositoryProxy::CRepositoryProxy(CClient& rClient, sdv::com::TConnectionID tCo
|
||||
void CRepositoryProxy::DestroyObject()
|
||||
{
|
||||
// Call the client to disconnect the connection and destroy the object.
|
||||
m_rClient.Disconnect(m_tConnection);
|
||||
m_rClient.Disconnect();
|
||||
}
|
||||
|
||||
bool CClient::OnInitialize()
|
||||
sdv::com::TConnectionID CRepositoryProxy::GetConnectionID() const
|
||||
{
|
||||
return true;
|
||||
return m_tConnection;
|
||||
}
|
||||
|
||||
void CClient::OnShutdown()
|
||||
{
|
||||
sdv::com::IConnectionControl* pConnectionControl = sdv::core::GetObject<sdv::com::IConnectionControl>("CommunicationControl");
|
||||
if (!pConnectionControl)
|
||||
SDV_LOG_ERROR("Failed to get communication control!");
|
||||
|
||||
// Disconnect from all repositories
|
||||
std::unique_lock<std::mutex> lock(m_mtxRepositoryProxies);
|
||||
auto mapRepositoryProxiesCopy = std::move(m_mapRepositoryProxies);
|
||||
lock.unlock();
|
||||
if (pConnectionControl)
|
||||
{
|
||||
for (const auto& rvtRepository : mapRepositoryProxiesCopy)
|
||||
pConnectionControl->RemoveConnection(rvtRepository.first);
|
||||
}
|
||||
}
|
||||
|
||||
sdv::IInterfaceAccess* CClient::Connect(const sdv::u8string& ssConnectString)
|
||||
bool CClientConnect::OnInitialize()
|
||||
{
|
||||
const sdv::app::IAppContext* pContext = sdv::core::GetCore<sdv::app::IAppContext>();
|
||||
if (!pContext)
|
||||
{
|
||||
SDV_LOG_ERROR("Failed to get application context!");
|
||||
return nullptr;
|
||||
return false;
|
||||
}
|
||||
sdv::com::IConnectionControl* pConnectionControl = sdv::core::GetObject<sdv::com::IConnectionControl>("CommunicationControl");
|
||||
if (!pConnectionControl)
|
||||
{
|
||||
SDV_LOG_ERROR("Failed to get communication control!");
|
||||
return nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
sdv::ipc::IChannelAccess* pChannelAccess = nullptr;
|
||||
std::string ssConfig;
|
||||
try
|
||||
// Check for a provider. Without provider there is no object to use for listening.
|
||||
if (m_ssProvider.empty())
|
||||
{
|
||||
// Determine whether the service is running as server or as client.
|
||||
sdv::toml::CTOMLParser config(ssConnectString);
|
||||
std::string ssType = config.GetDirect("Client.Type").GetValue();
|
||||
if (ssType.empty()) ssType = "Local";
|
||||
if (ssType == "Local")
|
||||
{
|
||||
uint32_t uiInstanceID = config.GetDirect("Client.Instance").GetValue();
|
||||
pChannelAccess = sdv::core::GetObject<sdv::ipc::IChannelAccess>("LocalChannelControl");
|
||||
if (!pChannelAccess)
|
||||
{
|
||||
SDV_LOG_ERROR("No local channel control or channel control not configured as client!");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ssConfig = std::string(R"code([IpcChannel]
|
||||
Name = "LISTENER_)code") + std::to_string(uiInstanceID ? uiInstanceID : pContext->GetInstanceID()) + R"code("
|
||||
)code";
|
||||
|
||||
}
|
||||
else if (ssType == "Remote")
|
||||
{
|
||||
std::string ssInterface = config.GetDirect("Client.Interface").GetValue();
|
||||
uint32_t uiPort = config.GetDirect("Client.Interface").GetValue();
|
||||
if (ssInterface.empty() || !uiPort)
|
||||
{
|
||||
SDV_LOG_ERROR("Missing interface or port number to initialize a remote client!");
|
||||
return nullptr;
|
||||
}
|
||||
pChannelAccess = sdv::core::GetObject<sdv::ipc::IChannelAccess>("RemoteChannelControl");
|
||||
if (!pChannelAccess)
|
||||
{
|
||||
SDV_LOG_ERROR("No remote channel control or channel control not configured as client!");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ssConfig = R"code([IpcChannel]
|
||||
Interface = ")code" + ssInterface + R"code(
|
||||
Port = ")code" + std::to_string(uiPort) + R"code(
|
||||
)code";
|
||||
}
|
||||
else
|
||||
{
|
||||
SDV_LOG_ERROR("Invalid or missing listener configuration for listener service!");
|
||||
SetObjectIntoConfigErrorState();
|
||||
return nullptr;
|
||||
}
|
||||
SDV_LOG_ERROR("Missing provider name for creating a client object!");
|
||||
return false;
|
||||
}
|
||||
catch (const sdv::toml::XTOMLParseException& rexcept)
|
||||
sdv::ipc::IChannelAccess* pChannelAccess = sdv::core::GetObject<sdv::ipc::IChannelAccess>(m_ssProvider);
|
||||
if (!pChannelAccess)
|
||||
{
|
||||
SDV_LOG_ERROR("Invalid service configuration for listener service: ", rexcept.what(), "!");
|
||||
SetObjectIntoConfigErrorState();
|
||||
return nullptr;
|
||||
SDV_LOG_ERROR("Cannot instantiate provider '", m_ssProvider, "' for the creation of a client object!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// The connection will be established in the Connect function.
|
||||
return true;
|
||||
}
|
||||
|
||||
void CClientConnect::OnShutdown()
|
||||
{
|
||||
sdv::com::IConnectionControl* pConnectionControl = sdv::core::GetObject<sdv::com::IConnectionControl>("CommunicationControl");
|
||||
if (!pConnectionControl)
|
||||
SDV_LOG_ERROR("Failed to get communication control!");
|
||||
|
||||
// Disconnect
|
||||
Disconnect();
|
||||
}
|
||||
|
||||
bool CClientConnect::Connect()
|
||||
{
|
||||
sdv::u8string ssProvider;
|
||||
sdv::u8string ssConfig;
|
||||
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_mtx);
|
||||
ssProvider = m_ssProvider;
|
||||
ssConfig = BuildObjectConfig();
|
||||
}
|
||||
|
||||
const sdv::app::IAppContext* pContext = sdv::core::GetCore<sdv::app::IAppContext>();
|
||||
if (!pContext)
|
||||
{
|
||||
SDV_LOG_ERROR("Failed to get application context!");
|
||||
return false;
|
||||
}
|
||||
sdv::com::IConnectionControl* pConnectionControl = sdv::core::GetObject<sdv::com::IConnectionControl>("CommunicationControl");
|
||||
if (!pConnectionControl)
|
||||
{
|
||||
SDV_LOG_ERROR("Failed to get communication control!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for a provider. Without provider there is no object to use for listening.
|
||||
if (ssProvider.empty())
|
||||
{
|
||||
SDV_LOG_ERROR("Missing provider name for creating a client object!");
|
||||
return false;
|
||||
}
|
||||
|
||||
sdv::ipc::IChannelAccess* pChannelAccess = sdv::core::GetObject<sdv::ipc::IChannelAccess>(ssProvider);
|
||||
if (!pChannelAccess)
|
||||
{
|
||||
SDV_LOG_ERROR("Cannot instantiate provider '", m_ssProvider, "' for the creation of a client object!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the repository
|
||||
sdv::TInterfaceAccessPtr ptrRespository = sdv::core::GetObject("RepositoryService");
|
||||
if (!ptrRespository)
|
||||
{
|
||||
SDV_LOG_ERROR("Failed to get repository service!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add a dependency of the provider to this service (to prevent the provider to be terminated while the service is still
|
||||
// running).
|
||||
sdv::core::IObjectDependency* pObjectDependency = ptrRespository.GetInterface<sdv::core::IObjectDependency>();
|
||||
if (!pObjectDependency)
|
||||
{
|
||||
SDV_LOG_ERROR("Failed to get the object dependency interface!");
|
||||
return false;
|
||||
}
|
||||
|
||||
pObjectDependency->AddObjectDependency(Self().ssName, ssProvider);
|
||||
|
||||
|
||||
// First access the listener channel. This allows us to access the channel creation interface.
|
||||
|
||||
// TODO: Use named mutex to prevent multiple connections at the same time.
|
||||
// Connect to the channel.
|
||||
sdv::TObjectPtr ptrListenerEndpoint = pChannelAccess->Access(ssConfig);
|
||||
|
||||
|
||||
// Assign the endpoint to the communication service.
|
||||
sdv::IInterfaceAccess* pListenerProxy = nullptr;
|
||||
sdv::com::TConnectionID tListenerConnection = pConnectionControl->AssignClientEndpoint(ptrListenerEndpoint, 5000,
|
||||
pListenerProxy);
|
||||
sdv::com::TConnectionID tListenerConnection =
|
||||
pConnectionControl->AssignClientEndpoint(ptrListenerEndpoint, 5000, pListenerProxy);
|
||||
ptrListenerEndpoint.Clear(); // Lifetime has been taken over by communication control.
|
||||
if (!tListenerConnection || !pListenerProxy)
|
||||
{
|
||||
SDV_LOG_ERROR("Could not assign the client endpoint!");
|
||||
if (tListenerConnection != sdv::com::TConnectionID{}) pConnectionControl->RemoveConnection(tListenerConnection);
|
||||
return nullptr;
|
||||
return false;
|
||||
}
|
||||
sdv::TInterfaceAccessPtr ptrListenerProxy(pListenerProxy);
|
||||
|
||||
@@ -149,9 +161,26 @@ Port = ")code" + std::to_string(uiPort) + R"code(
|
||||
{
|
||||
SDV_LOG_ERROR("Could not get the channel creation interface!");
|
||||
if (tListenerConnection != sdv::com::TConnectionID{}) pConnectionControl->RemoveConnection(tListenerConnection);
|
||||
return nullptr;
|
||||
return false;
|
||||
}
|
||||
sdv::u8string ssConnectionString = pRequestChannel->RequestChannel("");
|
||||
|
||||
sdv::u8string ssRequestConfig;
|
||||
|
||||
// Tunnel providers need the full object config so the private channel
|
||||
// can preserve provider-specific fields such as the tunnel name.
|
||||
// For shared memory we keep the legacy behavior and let the provider
|
||||
// create its own private channel details.
|
||||
if (ssProvider == "unix_domain_sockets_tunnel")
|
||||
{
|
||||
ssRequestConfig = ssConfig;
|
||||
}
|
||||
else
|
||||
{
|
||||
ssRequestConfig.clear();
|
||||
}
|
||||
|
||||
sdv::u8string ssConnectionString = pRequestChannel->RequestChannel(ssRequestConfig);
|
||||
|
||||
|
||||
// Disconnect from the listener
|
||||
if (tListenerConnection != sdv::com::TConnectionID{}) pConnectionControl->RemoveConnection(tListenerConnection);
|
||||
@@ -159,7 +188,7 @@ Port = ")code" + std::to_string(uiPort) + R"code(
|
||||
if (ssConnectionString.empty())
|
||||
{
|
||||
SDV_LOG_ERROR("Could not get the private channel connection information!");
|
||||
return nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: Use named mutex to prevent multiple connections at the same time.
|
||||
@@ -174,30 +203,64 @@ Port = ")code" + std::to_string(uiPort) + R"code(
|
||||
{
|
||||
SDV_LOG_ERROR("Could not assign the client endpoint to the private channel!");
|
||||
if (tPrivateConnection != sdv::com::TConnectionID{}) pConnectionControl->RemoveConnection(tPrivateConnection);
|
||||
return nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create a remote repository object
|
||||
std::unique_lock<std::mutex> lock(m_mtxRepositoryProxies);
|
||||
m_mapRepositoryProxies.try_emplace(tPrivateConnection, *this, tPrivateConnection, pPrivateProxy);
|
||||
m_ptrRemoteRepo = std::make_shared<CRepositoryProxy>(*this, tPrivateConnection, pPrivateProxy);
|
||||
|
||||
return pPrivateProxy;
|
||||
return true;
|
||||
}
|
||||
|
||||
void CClient::Disconnect(sdv::com::TConnectionID tConnectionID)
|
||||
bool CClientConnect::Disconnect()
|
||||
{
|
||||
// Find the connection, disconnect and remove the connection from the repository list.
|
||||
std::unique_lock<std::mutex> lock(m_mtxRepositoryProxies);
|
||||
auto itRepository = m_mapRepositoryProxies.find(tConnectionID);
|
||||
if (itRepository == m_mapRepositoryProxies.end()) return;
|
||||
std::unique_lock<std::mutex> lock(m_mtx);
|
||||
|
||||
if (!m_ptrRemoteRepo) return false;
|
||||
|
||||
// Whatever happens, the connection will be removed
|
||||
std::shared_ptr<CRepositoryProxy> ptrRemoteRepoLocal = std::move(m_ptrRemoteRepo);
|
||||
|
||||
// Disconnect
|
||||
sdv::com::IConnectionControl* pConnectionControl = sdv::core::GetObject<sdv::com::IConnectionControl>("CommunicationControl");
|
||||
if (!pConnectionControl)
|
||||
{
|
||||
SDV_LOG_ERROR("Failed to get communication control!");
|
||||
else
|
||||
pConnectionControl->RemoveConnection(itRepository->first);
|
||||
return false;
|
||||
}
|
||||
pConnectionControl->RemoveConnection(ptrRemoteRepoLocal->GetConnectionID());
|
||||
|
||||
// Remove entry
|
||||
m_mapRepositoryProxies.erase(itRepository);
|
||||
// Get the repository
|
||||
sdv::TInterfaceAccessPtr ptrRespository = sdv::core::GetObject("RepositoryService");
|
||||
if (!ptrRespository)
|
||||
{
|
||||
SDV_LOG_ERROR("Failed to get repository service!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add a dependency of the provider to this service (to prevent the provider to be terminated while the service is still
|
||||
// running).
|
||||
sdv::core::IObjectDependency* pObjectDependency = ptrRespository.GetInterface<sdv::core::IObjectDependency>();
|
||||
if (!pObjectDependency)
|
||||
{
|
||||
SDV_LOG_ERROR("Failed to get the object dependency interface!");
|
||||
return false;
|
||||
}
|
||||
pObjectDependency->RemoveObjectDependency("ClientConnectService", m_ssProvider);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CClientConnect::IsConnected() const
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_mtx);
|
||||
|
||||
return m_ptrRemoteRepo ? true : false;
|
||||
}
|
||||
|
||||
sdv::IInterfaceAccess* CClientConnect::GetRemoteRepository()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_mtx);
|
||||
|
||||
return m_ptrRemoteRepo ? m_ptrRemoteRepo.get() : nullptr;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
#include <interfaces/com.h>
|
||||
|
||||
// Forward declaration.
|
||||
class CClient;
|
||||
class CClientConnect;
|
||||
|
||||
/**
|
||||
* @brief Class managing the connection and providing access to the server repository through a proxy.
|
||||
@@ -33,7 +33,7 @@ public:
|
||||
* @param[in] tConnection The connection ID to the server.
|
||||
* @param[in] pRepositoryProxy Proxy to the server repository.
|
||||
*/
|
||||
CRepositoryProxy(CClient& rClient, sdv::com::TConnectionID tConnection, sdv::IInterfaceAccess* pRepositoryProxy);
|
||||
CRepositoryProxy(CClientConnect& rClient, sdv::com::TConnectionID tConnection, sdv::IInterfaceAccess* pRepositoryProxy);
|
||||
|
||||
/**
|
||||
* @brief Do not allow a copy constructor.
|
||||
@@ -59,8 +59,14 @@ public:
|
||||
*/
|
||||
virtual void DestroyObject() override;
|
||||
|
||||
/**
|
||||
* @brief Get the connection ID for this connection.
|
||||
* @return The connection ID.
|
||||
*/
|
||||
sdv::com::TConnectionID GetConnectionID() const;
|
||||
|
||||
private:
|
||||
CClient& m_rClient; ///< Reference to the client object.
|
||||
CClientConnect& m_rClient; ///< Reference to the client object.
|
||||
sdv::com::TConnectionID m_tConnection = {}; ///< Connection ID.
|
||||
sdv::TInterfaceAccessPtr m_ptrRepositoryProxy; ///< Smart pointer to the remote repository.
|
||||
};
|
||||
@@ -68,7 +74,7 @@ private:
|
||||
/**
|
||||
* @brief Client object
|
||||
*/
|
||||
class CClient : public sdv::CSdvObject, public sdv::com::IClientConnect
|
||||
class CClientConnect : public sdv::CSdvObject, public sdv::com::IClientConnect
|
||||
{
|
||||
public:
|
||||
// Interface map
|
||||
@@ -78,8 +84,15 @@ public:
|
||||
|
||||
// Object declaration
|
||||
DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::system_object)
|
||||
DECLARE_OBJECT_CLASS_NAME("ConnectionService")
|
||||
DECLARE_OBJECT_SINGLETON()
|
||||
DECLARE_OBJECT_CLASS_NAME("ClientConnectService")
|
||||
DECLARE_OBJECT_DEPENDENCIES("CommunicationControl")
|
||||
|
||||
// Parameter map
|
||||
BEGIN_SDV_PARAM_MAP()
|
||||
SDV_PARAM_ENABLE_LOCKING()
|
||||
SDV_PARAM_GROUP("Provider")
|
||||
SDV_PARAM_ENTRY(m_ssProvider, "Name", "", "", "Provider name to create a connection for.")
|
||||
END_SDV_PARAM_MAP()
|
||||
|
||||
/**
|
||||
* @brief Initialization event, called after object configuration was loaded. Overload of sdv::CSdvObject::OnInitialize.
|
||||
@@ -93,40 +106,40 @@ public:
|
||||
virtual void OnShutdown() override;
|
||||
|
||||
/**
|
||||
* @brief Connect to a remote system using the connection string to contact the system. Overload of
|
||||
* sdv::com::IClientConnect::Connect.
|
||||
* @remarks After a successful connection, the ConnectClient utility is not needed any more.
|
||||
* @param[in] ssConnectString Optional connection string to use for connection. If not provided, the connection will
|
||||
* automatically get the connection ID from the app-control service (default). The connection string for a local
|
||||
* connection can be of the form:
|
||||
* @code
|
||||
* [Client]
|
||||
* Type = "Local"
|
||||
* Instance = 1234 # Optional: only use when connecting to a system with a different instance ID.
|
||||
* @endcode
|
||||
* And the following can be used for a remote connection:
|
||||
* @code
|
||||
* [Client]
|
||||
* Type = "Remote"
|
||||
* Interface = "127.0.0.1"
|
||||
* Port = 2000
|
||||
* @endcode
|
||||
* @return Returns an interface to the repository of the remote system or a NULL pointer if not found.
|
||||
* @brief Connect to a remote system. Overload of IClientConnect::Connect.
|
||||
* @return Returns whether connect was successful.
|
||||
*/
|
||||
virtual sdv::IInterfaceAccess* Connect(const sdv::u8string& ssConnectString) override;
|
||||
virtual bool Connect() override;
|
||||
|
||||
/**
|
||||
* @brief Disconnect and remove the remote repository object.
|
||||
* @param[in] tConnectionID The ID of the connection.
|
||||
* @brief Disconnect from a connected system. Overload of IClientConnect::Disconnect.
|
||||
* @return Returns whether disconnect was successful.
|
||||
*/
|
||||
void Disconnect(sdv::com::TConnectionID tConnectionID);
|
||||
bool Disconnect() override;
|
||||
|
||||
/**
|
||||
* @brief State of the current connection. Overload of IClientConnect::IsConnected.
|
||||
* @return Returns whether an active connection exists.
|
||||
* @return The connect state.
|
||||
*/
|
||||
bool IsConnected() const override;
|
||||
|
||||
/**
|
||||
* @brief Get the remote repository that is available after connection. Overload of IClientConnect::GetRemoteRepository.
|
||||
* @remarks For main, isolated and external applications, the remote repository will be automatically linked to the
|
||||
* local repository. Hence a requests for the repository is not needed. For all other applications, access must be
|
||||
* explicitly acquired through this interface.
|
||||
* @return Interface to the remote repository if a successful connection is established. The interface is valid until
|
||||
* disconnect is called or the client connection service is terminated.
|
||||
*/
|
||||
sdv::IInterfaceAccess* GetRemoteRepository() override;
|
||||
|
||||
private:
|
||||
std::mutex m_mtxRepositoryProxies; ///< Protect access to the remnote repository map.
|
||||
std::map<sdv::com::TConnectionID, CRepositoryProxy> m_mapRepositoryProxies; ///< map of remote repositories.
|
||||
mutable std::mutex m_mtx; ///< Protect against multiple parallel connection activities.
|
||||
sdv::u8string m_ssProvider; ///< Name of the provider to use for the listening.
|
||||
std::shared_ptr<CRepositoryProxy> m_ptrRemoteRepo; ///< Interface to the remote repository.
|
||||
};
|
||||
|
||||
DEFINE_SDV_OBJECT(CClient)
|
||||
|
||||
DEFINE_SDV_OBJECT(CClientConnect)
|
||||
|
||||
#endif // !defined CLIENT_H
|
||||
@@ -16,11 +16,12 @@
|
||||
#include <interfaces/com.h>
|
||||
#include <interfaces/app.h>
|
||||
#include <support/pssup.h>
|
||||
#include <support/local_service_access.h>
|
||||
|
||||
CChannelBroker::CChannelBroker(CListener& rListener) : m_rListener(rListener)
|
||||
{}
|
||||
|
||||
sdv::u8string CChannelBroker::RequestChannel(/*in*/ const sdv::u8string& /*ssConfig*/)
|
||||
sdv::u8string CChannelBroker::RequestChannel(/*in*/ const sdv::u8string& ssConfig)
|
||||
{
|
||||
// Get the communication control
|
||||
sdv::com::IConnectionControl* pConnectionControl = sdv::core::GetObject<sdv::com::IConnectionControl>("CommunicationControl");
|
||||
@@ -39,26 +40,30 @@ sdv::u8string CChannelBroker::RequestChannel(/*in*/ const sdv::u8string& /*ssCon
|
||||
}
|
||||
|
||||
// Get the channel control.
|
||||
sdv::ipc::ICreateEndpoint* pEndpoint = nullptr;
|
||||
if (m_rListener.IsLocalListener())
|
||||
pEndpoint = sdv::core::GetObject<sdv::ipc::ICreateEndpoint>("LocalChannelControl");
|
||||
else
|
||||
pEndpoint = sdv::core::GetObject<sdv::ipc::ICreateEndpoint>("RemoteChannelControl");
|
||||
sdv::ipc::ICreateEndpoint* pEndpoint = sdv::core::GetObject<sdv::ipc::ICreateEndpoint>(m_rListener.GetProviderName());
|
||||
if (!pEndpoint)
|
||||
{
|
||||
SDV_LOG_ERROR("No local channel control!");
|
||||
return {};
|
||||
}
|
||||
|
||||
// Create the endpoint
|
||||
sdv::ipc::SChannelEndpoint sEndpoint = pEndpoint->CreateEndpoint(sdv::u8string());
|
||||
// Forward protocol-specific configuration to the provider when creating the private channel.
|
||||
SDV_LOG_INFO("[IPC_CONNECT][Listener] RequestChannel input config:\n", ssConfig);
|
||||
|
||||
sdv::ipc::SChannelEndpoint sEndpoint = pEndpoint->CreateEndpoint(ssConfig);
|
||||
if (!sEndpoint.pConnection)
|
||||
{
|
||||
SDV_LOG_ERROR("Could not create the endpoint for channel request!");
|
||||
return sdv::u8string();
|
||||
return {};
|
||||
}
|
||||
|
||||
SDV_LOG_INFO("[IPC_CONNECT][Listener] RequestChannel produced connect string: ", sEndpoint.ssConnectString);
|
||||
|
||||
sdv::TObjectPtr ptrEndpoint(sEndpoint.pConnection); // Does automatic destruction if failure happens.
|
||||
|
||||
// Restrict access permissions
|
||||
sdv::core::CAccessPermission permission = sdv::core::RestrictAccessPermission(sdv::core::EAccessPermission::local_access);
|
||||
|
||||
// Assign the endpoint to the communication service.
|
||||
sdv::com::TConnectionID tConnection = pConnectionControl->AssignServerEndpoint(ptrEndpoint, ptrRespository, 100, false);
|
||||
ptrEndpoint.Clear(); // Lifetime taken over by communication control.
|
||||
@@ -93,49 +98,21 @@ bool CListener::OnInitialize()
|
||||
return false;
|
||||
}
|
||||
|
||||
sdv::ipc::ICreateEndpoint* pEndpoint = nullptr;
|
||||
std::string ssConfig;
|
||||
if (m_ssType == "Local")
|
||||
// Check for a provider. Without provider there is no object to use for listening.
|
||||
if (m_ssProvider.empty())
|
||||
{
|
||||
m_bLocalListener = true;
|
||||
pEndpoint = sdv::core::GetObject<sdv::ipc::ICreateEndpoint>("LocalChannelControl");
|
||||
if (!pEndpoint)
|
||||
{
|
||||
SDV_LOG_ERROR("No local channel control!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Request the instance ID from the app control
|
||||
ssConfig = std::string(R"code([IpcChannel]
|
||||
Name = "LISTENER_)code") + std::to_string(m_uiInstanceID ? m_uiInstanceID : pContext->GetInstanceID()) + R"code("
|
||||
Size = 2048
|
||||
)code";
|
||||
}
|
||||
else if (m_ssType == "Remote")
|
||||
{
|
||||
m_bLocalListener = false;
|
||||
if (m_ssInterface.empty() || !m_uiPort)
|
||||
{
|
||||
SDV_LOG_ERROR("Missing interface or port number to initialize a remote listener!");
|
||||
return false;
|
||||
}
|
||||
pEndpoint = sdv::core::GetObject<sdv::ipc::ICreateEndpoint>("RemoteChannelControl");
|
||||
if (!pEndpoint)
|
||||
{
|
||||
SDV_LOG_ERROR("No remote channel control!");
|
||||
return false;
|
||||
}
|
||||
|
||||
ssConfig = R"code([IpcChannel]
|
||||
Interface = ")code" + m_ssInterface + R"code(
|
||||
Port = ")code" + std::to_string(m_uiPort) + R"code(
|
||||
)code";
|
||||
}
|
||||
else
|
||||
{
|
||||
SDV_LOG_ERROR("Invalid or missing listener configuration for listener service!");
|
||||
SDV_LOG_ERROR("Missing provider name for creating a listener object!");
|
||||
return false;
|
||||
}
|
||||
sdv::ipc::ICreateEndpoint* pEndpoint = sdv::core::GetObject<sdv::ipc::ICreateEndpoint>(m_ssProvider);
|
||||
if (!pEndpoint)
|
||||
{
|
||||
SDV_LOG_ERROR("Cannot instantiate provider '", m_ssProvider, "' for the creation of a listener object!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the IpcChannel information from the the configuration
|
||||
auto ssConfig = BuildObjectConfig();
|
||||
|
||||
// Create the endpoint
|
||||
sdv::ipc::SChannelEndpoint sEndpoint = pEndpoint->CreateEndpoint(ssConfig);
|
||||
@@ -146,6 +123,24 @@ Port = ")code" + std::to_string(m_uiPort) + R"code(
|
||||
}
|
||||
sdv::TObjectPtr ptrEndpoint(sEndpoint.pConnection); // Does automatic destruction if failure happens.
|
||||
|
||||
// Get the repository
|
||||
sdv::TInterfaceAccessPtr ptrRespository = sdv::core::GetObject("RepositoryService");
|
||||
if (!ptrRespository)
|
||||
{
|
||||
SDV_LOG_ERROR("Failed to get repository service!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add a dependency of the provider to this service (to prevent the provider to be terminated while the service is still
|
||||
// running).
|
||||
sdv::core::IObjectDependency* pObjectDependency = ptrRespository.GetInterface<sdv::core::IObjectDependency>();
|
||||
if (!pObjectDependency)
|
||||
{
|
||||
SDV_LOG_ERROR("Failed to get the object dependency interface!");
|
||||
return false;
|
||||
}
|
||||
pObjectDependency->AddObjectDependency(Self().ssName, m_ssProvider);
|
||||
|
||||
// Assign the endpoint to the communication service.
|
||||
m_tConnection = pConnectionControl->AssignServerEndpoint(ptrEndpoint, &m_broker, 100, true);
|
||||
ptrEndpoint.Clear(); // Lifetime taken over by communication control.
|
||||
@@ -173,9 +168,8 @@ void CListener::OnShutdown()
|
||||
m_ptrConnection.Clear();
|
||||
}
|
||||
|
||||
bool CListener::IsLocalListener() const
|
||||
|
||||
const sdv::u8string& CListener::GetProviderName()
|
||||
{
|
||||
return m_bLocalListener;
|
||||
return m_ssProvider;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -52,6 +52,25 @@ private:
|
||||
|
||||
/**
|
||||
* @brief Listener object
|
||||
* @details the lister is instantiated using the following parameter information
|
||||
* @code
|
||||
* # Provider to use for listening
|
||||
* [Provider]
|
||||
* Name = ""
|
||||
*
|
||||
* # Additional channel information for the listener (needed for unique listener identification)
|
||||
* [IpcChannel]
|
||||
* xyz = ""
|
||||
* @endcode
|
||||
*
|
||||
* For example for shared memory:
|
||||
* @code
|
||||
* [Provider]
|
||||
* Name = "DefaultSharedMemory"
|
||||
* [IpcChannel]
|
||||
* Name = "LISTENER_1234"
|
||||
* Size = 10240
|
||||
* @endcode
|
||||
*/
|
||||
class CListener : public sdv::CSdvObject
|
||||
{
|
||||
@@ -63,34 +82,18 @@ public:
|
||||
|
||||
// Object declaration
|
||||
DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::system_object)
|
||||
DECLARE_OBJECT_CLASS_NAME("ConnectionListenerService")
|
||||
DECLARE_OBJECT_CLASS_NAME("ListenerConnectService")
|
||||
DECLARE_OBJECT_DEPENDENCIES("CommunicationControl")
|
||||
|
||||
// Parameter map
|
||||
BEGIN_SDV_PARAM_MAP()
|
||||
SDV_PARAM_ENABLE_LOCKING()
|
||||
SDV_PARAM_GROUP("Listener")
|
||||
SDV_PARAM_ENTRY(m_ssType, "Type", "Local", "", "The type of listener \"Local\" or \"Remote\".")
|
||||
SDV_PARAM_ENTRY(m_uiInstanceID, "Instance", 0, "", "The instance ID to listen for.")
|
||||
SDV_PARAM_ENTRY(m_ssInterface, "Interface", "", "", "Interface identification.")
|
||||
SDV_PARAM_ENTRY(m_uiPort, "Port", 0, "", "Port number for connection.")
|
||||
SDV_PARAM_GROUP("Provider")
|
||||
SDV_PARAM_ENTRY(m_ssProvider, "Name", "", "", "Provider name to create a listener for.")
|
||||
END_SDV_PARAM_MAP()
|
||||
|
||||
/**
|
||||
* @brief Initialization event, called after object configuration was loaded. Overload of sdv::CSdvObject::OnInitialize.
|
||||
* @details The object configuration contains the information needed to start the listener. The following configuration is
|
||||
* available for the local listener:
|
||||
* @code
|
||||
* [Listener]
|
||||
* Type = "Local"
|
||||
* Instance = 1000 # Normally not used; system instance ID is used automatically.
|
||||
* @endcode
|
||||
* And the following is available for a remote listener:
|
||||
* @code
|
||||
* [Listener]
|
||||
* Type = "Remote"
|
||||
* Interface = "127.0.0.1"
|
||||
* Port = 2000
|
||||
* @endcode
|
||||
* @return Returns 'true' when the initialization was successful, 'false' when not.
|
||||
*/
|
||||
virtual bool OnInitialize() override;
|
||||
@@ -101,19 +104,15 @@ public:
|
||||
virtual void OnShutdown() override;
|
||||
|
||||
/**
|
||||
* @brief When set, the listener is configured to be a local listener. Otherwise the listerner is configured as remote listener.
|
||||
* @return Boolean set when local lostener.
|
||||
* @brief Get the provider name used by this listener.
|
||||
* @return Reference to the provider name.
|
||||
*/
|
||||
bool IsLocalListener() const;
|
||||
const sdv::u8string& GetProviderName();
|
||||
|
||||
private:
|
||||
sdv::u8string m_ssType; ///< Listener type: "Local" or "Remote"
|
||||
uint32_t m_uiInstanceID = 0; ///< Instance ID to listen for.
|
||||
std::string m_ssInterface; ///< Interface string for remote listener.
|
||||
uint32_t m_uiPort = 0; ///< Port for remote listener.
|
||||
sdv::u8string m_ssProvider; ///< Name of the provider to use for the listening.
|
||||
sdv::TObjectPtr m_ptrConnection; ///< The connection object.
|
||||
CChannelBroker m_broker; ///< Channel broker, used to request new channels
|
||||
bool m_bLocalListener = true; ///< When set, the listener is a local listener; otherwise a remote listener.
|
||||
sdv::com::TConnectionID m_tConnection = {}; ///< Channel connection ID.
|
||||
};
|
||||
|
||||
|
||||
@@ -56,15 +56,15 @@ sdv::IInterfaceAccess* CSharedMemChannelMgnt::Access(const sdv::u8string& ssConn
|
||||
sdv::toml::CTOMLParser parser(ssConnectString);
|
||||
if (!parser.IsValid()) return nullptr;
|
||||
|
||||
// Is this a configuration provided by the endpoint (uses a "Provider" key), then this is a connection string. Use this
|
||||
// Is this a configuration provided by the endpoint (uses a "ConnectParam" key), then this is a connection string. Use this
|
||||
// to connect to the shared memory.
|
||||
std::shared_ptr<CConnection> ptrConnection;
|
||||
if (parser.GetDirect("Provider").IsValid())
|
||||
if (parser.GetDirect("ConnectParam").IsValid())
|
||||
ptrConnection = std::make_shared<CConnection>(m_watchdog, ssConnectString.c_str());
|
||||
else
|
||||
{
|
||||
std::string ssName = static_cast<std::string>(parser.GetDirect("IpcChannel.Name").GetValue());
|
||||
ptrConnection = std::make_shared<CConnection>(m_watchdog, 0,ssName, false);
|
||||
ptrConnection = std::make_shared<CConnection>(m_watchdog, 0, ssName, false);
|
||||
}
|
||||
if (!ptrConnection) return {};
|
||||
m_watchdog.AddConnection(ptrConnection);
|
||||
|
||||
@@ -43,9 +43,7 @@ public:
|
||||
|
||||
// Object declarations
|
||||
DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::system_object)
|
||||
DECLARE_OBJECT_CLASS_NAME("DefaultSharedMemoryChannelControl")
|
||||
DECLARE_OBJECT_CLASS_ALIAS("LocalChannelControl")
|
||||
DECLARE_DEFAULT_OBJECT_NAME("LocalChannelControl")
|
||||
DECLARE_OBJECT_CLASS_NAME("DefaultSharedMemory")
|
||||
DECLARE_OBJECT_SINGLETON()
|
||||
|
||||
/**
|
||||
|
||||
@@ -117,7 +117,7 @@ std::string CConnection::GetConnectionString()
|
||||
{
|
||||
// The connection string is of the form:
|
||||
// [Provider]
|
||||
// Name = "DefaultSharedMemoryChannelControl"
|
||||
// Name = "DefaultSharedMemory"
|
||||
//
|
||||
// [[ConnectParam]]
|
||||
// Type = "shared_mem"
|
||||
@@ -132,9 +132,9 @@ std::string CConnection::GetConnectionString()
|
||||
// SyncTx = "SDV_TX_SYNC_REQUEST_1234"
|
||||
// SyncRx = "SDV_RX_SYNC_REQUEST_1234"
|
||||
// Direction = "request"
|
||||
std::string ssConnectionString = R"code([Provider]
|
||||
Name = "LocalChannelControl"
|
||||
)code" + std::string("\n") + m_sender.GetConnectionString() + "\n" + m_receiver.GetConnectionString();
|
||||
std::string ssConnectionString = R"toml([Provider]
|
||||
Name = "DefaultSharedMemory"
|
||||
)toml" + std::string("\n") + m_sender.GetConnectionString() + "\n" + m_receiver.GetConnectionString();
|
||||
return ssConnectionString;
|
||||
}
|
||||
|
||||
@@ -352,7 +352,7 @@ bool CConnection::AsyncConnect(sdv::IInterfaceAccess* pReceiver)
|
||||
std::unique_lock<std::mutex> lock(m_mtxConnect);
|
||||
|
||||
// Allowed to connect?
|
||||
if (m_eConnectState != sdv::ipc::EConnectState::uninitialized)
|
||||
if (m_eConnectState != sdv::ipc::EConnectState::uninitialized && m_eConnectState != sdv::ipc::EConnectState::disconnected)
|
||||
{
|
||||
for (auto& rprEventCallback : m_lstEventCallbacks)
|
||||
if (rprEventCallback.pCallback && rprEventCallback.uiCookie)
|
||||
@@ -377,9 +377,9 @@ bool CConnection::AsyncConnect(sdv::IInterfaceAccess* pReceiver)
|
||||
SetConnectState(sdv::ipc::EConnectState::initialized);
|
||||
|
||||
// Start the receiving thread (wait until started).
|
||||
m_threadReceive = std::thread(&CConnection::ReceiveMessages, this);
|
||||
m_threadReceive = sdv::core::secure_thread(&CConnection::ReceiveMessages, this);
|
||||
#if ENABLE_DECOUPLING > 0
|
||||
m_threadDecoupleReceive = std::thread(&CConnection::DecoupleReceive, this);
|
||||
m_threadDecoupleReceive = sdv::core::secure_thread(&CConnection::DecoupleReceive, this);
|
||||
#endif
|
||||
if (!m_bStarted)
|
||||
m_cvStartConnect.wait_for(lock, std::chrono::milliseconds(1000));
|
||||
@@ -602,9 +602,10 @@ void CConnection::ReceiveMessages()
|
||||
auto optPacket = m_receiver.TryRead();
|
||||
if (!optPacket)
|
||||
{
|
||||
// TODO EVE: Also allow synchronization take place when disconnected.
|
||||
// Start communication, but only if connection is client based. Server based should not start the communication. If
|
||||
// there is no client, the server would otherwise fill its send-buffer. Repeat sending every 500ms.
|
||||
if (!m_bServer && (/*m_eConnectState == sdv::ipc::EConnectState::disconnected ||*/ m_eConnectState == sdv::ipc::EConnectState::initialized))
|
||||
if (!m_bServer && (m_eConnectState == sdv::ipc::EConnectState::disconnected || m_eConnectState == sdv::ipc::EConnectState::initialized))
|
||||
{
|
||||
// Send request
|
||||
auto tpNow = std::chrono::high_resolution_clock::now();
|
||||
@@ -1001,9 +1002,10 @@ void CConnection::ReceiveConnectTerm(CMessage& /*rMessage*/)
|
||||
m_sender.CancelSend();
|
||||
m_sender.ResetRx();
|
||||
|
||||
// Send sync request (do not wait until next round in case a very short connection <100ms took place).
|
||||
if (m_bServer)
|
||||
Send(SMsgHdr{ SDVFrameworkInterfaceVersion, EMsgType::sync_request });
|
||||
// TODO EVE: Disabled, since state of client straight goes into "connecting" after being disconnected.
|
||||
//// Send sync request (do not wait until next round in case a very short connection <100ms took place).
|
||||
//if (m_bServer)
|
||||
// Send(SMsgHdr{ SDVFrameworkInterfaceVersion, EMsgType::sync_request });
|
||||
}
|
||||
|
||||
void CConnection::ReceiveDataMessage(CMessage& rMessage, SDataContext& rsDataCtxt)
|
||||
|
||||
@@ -257,7 +257,7 @@ private:
|
||||
sdv::CLifetimeCookie m_cookie = sdv::CreateLifetimeCookie(); ///< Lifetime cookie to manage module lifetime.
|
||||
CSharedMemBufferTx m_sender; ///< Shared buffer for sending.
|
||||
CSharedMemBufferRx m_receiver; ///< Shared buffer for receiving.
|
||||
std::thread m_threadReceive; ///< Thread which receives data from the socket.
|
||||
sdv::core::secure_thread m_threadReceive; ///< Thread which receives data from the socket.
|
||||
std::atomic<sdv::ipc::EConnectState> m_eConnectState = sdv::ipc::EConnectState::uninitialized; ///< the state of the connection
|
||||
sdv::ipc::IDataReceiveCallback* m_pReceiver = nullptr; ///< Receiver to pass the messages to if available
|
||||
std::shared_mutex m_mtxEventCallbacks; ///< Protect access to callback list. Only locking when
|
||||
@@ -275,7 +275,7 @@ private:
|
||||
#if ENABLE_DECOUPLING > 0
|
||||
std::mutex m_mtxReceive; ///< Protect receive queue.
|
||||
std::queue<sdv::sequence<sdv::pointer<uint8_t>>> m_queueReceive; ///< Receive queue to decouple receiving and processing.
|
||||
std::thread m_threadDecoupleReceive; ///< Decoupled receive thread.
|
||||
sdv::core::secure_thread m_threadDecoupleReceive; ///< Decoupled receive thread.
|
||||
std::condition_variable m_cvReceiveAvailable; ///< Condition variable synchronizing the processing.
|
||||
std::condition_variable m_cvReceiveProcessed; ///< Condition variable synchronizing the processing.
|
||||
#endif
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
CWatchDog::CWatchDog()
|
||||
{
|
||||
m_threadScheduledConnectionDestructions = std::thread(&CWatchDog::HandleScheduledConnectionDestructions, this);
|
||||
m_threadScheduledConnectionDestructions = sdv::core::secure_thread(&CWatchDog::HandleScheduledConnectionDestructions, this);
|
||||
}
|
||||
|
||||
CWatchDog::~CWatchDog()
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
|
||||
#include <interfaces/process.h>
|
||||
#include <support/interface_ptr.h>
|
||||
#include <support/local_service_access.h>
|
||||
#include <mutex>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
@@ -127,7 +128,7 @@ private:
|
||||
std::condition_variable m_cvTriggerConnectionDestruction; ///< Condition variable used to trigger when a
|
||||
///< connection is scheduled for destruction.
|
||||
std::queue<std::shared_ptr<CConnection>> m_queueScheduledConnectionDestructions; ///< Scheduled connection for destruction.
|
||||
std::thread m_threadScheduledConnectionDestructions; ///< Thread processing the scheduled destructions.
|
||||
sdv::core::secure_thread m_threadScheduledConnectionDestructions; ///< Thread processing the scheduled destructions.
|
||||
std::atomic_bool m_bShutdown = false; ///< Set when shutting down the watchdog
|
||||
};
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ CProcessControl::~CProcessControl()
|
||||
bool CProcessControl::OnInitialize()
|
||||
{
|
||||
// Without monitor no trigger...
|
||||
m_threadMonitor = std::thread(&CProcessControl::MonitorThread, this);
|
||||
m_threadMonitor = sdv::core::secure_thread(&CProcessControl::MonitorThread, this);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -76,6 +76,7 @@ void CProcessControl::OnShutdown()
|
||||
|
||||
bool CProcessControl::AllowProcessControl() const
|
||||
{
|
||||
if (m_bEnableBypass) return true;
|
||||
const sdv::app::IAppContext* pAppContext = sdv::core::GetCore<sdv::app::IAppContext>();
|
||||
return pAppContext && (pAppContext->GetContextType() == sdv::app::EAppContext::main ||
|
||||
pAppContext->GetContextType() == sdv::app::EAppContext::maintenance ||
|
||||
@@ -574,3 +575,7 @@ void CProcessControl::MonitorThread()
|
||||
}
|
||||
}
|
||||
|
||||
void CProcessControl::EnableProcessControlAccessBypass()
|
||||
{
|
||||
m_bEnableBypass = true;
|
||||
}
|
||||
|
||||
@@ -118,7 +118,12 @@ public:
|
||||
*/
|
||||
virtual bool Terminate(/*in*/ sdv::process::TProcessID tProcessID) override;
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Enable process control access (explicitly bypass application check for testing).
|
||||
*/
|
||||
void EnableProcessControlAccessBypass();
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Monitor thread function.
|
||||
*/
|
||||
@@ -151,12 +156,13 @@ public:
|
||||
#else
|
||||
#error OS is not supported!
|
||||
#endif
|
||||
mutable std::mutex m_mtxProcesses; ///< Access control for monitor map.
|
||||
mutable std::mutex m_mtxProcesses; ///< Access control for monitor map.
|
||||
std::map<sdv::process::TProcessID, std::shared_ptr<SProcessHelper>> m_mapProcesses; ///< Monitor map
|
||||
uint32_t m_uiNextMonCookie = 1; ///< Next monitor cookie
|
||||
uint32_t m_uiNextMonCookie = 1; ///< Next monitor cookie
|
||||
std::map<uint32_t, std::shared_ptr<SProcessHelper>> m_mapMonitors; ///< Map with monitors.
|
||||
std::atomic_bool m_bShutdown = false; ///< Set to shutdown the monitor thread.
|
||||
std::thread m_threadMonitor; ///< Monitor thread.
|
||||
std::atomic_bool m_bShutdown = false; ///< Set to shutdown the monitor thread.
|
||||
sdv::core::secure_thread m_threadMonitor; ///< Monitor thread.
|
||||
bool m_bEnableBypass = false; ///< When set, enables process control regardless of application mode.
|
||||
};
|
||||
DEFINE_SDV_OBJECT(CProcessControl)
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
#include <functional>
|
||||
|
||||
CTimer::CTimer(CTaskTimerService& rtimersvc, uint32_t uiPeriod, sdv::core::ITaskExecute* pExecute) :
|
||||
m_rtimersvc(rtimersvc), m_pExecute(pExecute)
|
||||
m_rtimersvc(rtimersvc), m_pExecute(pExecute), m_tPermissionTransferID(sdv::core::TransferCurrentPermission())
|
||||
{
|
||||
if (!pExecute) return;
|
||||
|
||||
@@ -35,6 +35,11 @@ CTimer::CTimer(CTaskTimerService& rtimersvc, uint32_t uiPeriod, sdv::core::ITask
|
||||
sev.sigev_notify_function = [](sigval sv)
|
||||
{
|
||||
CTimer* pTimer = reinterpret_cast<CTimer*>(sv.sival_ptr);
|
||||
if (pTimer->m_tPermissionTransferID)
|
||||
{
|
||||
sdv::core::SetAccessPermission(pTimer->m_tPermissionTransferID);
|
||||
pTimer->m_tPermissionTransferID = 0u;
|
||||
}
|
||||
std::unique_lock<std::mutex> lock(pTimer->m_mtxExecution);
|
||||
if (!pTimer->m_bRunning) return;
|
||||
sdv::core::ITaskExecute* pExecuteLocal = reinterpret_cast<sdv::core::ITaskExecute*>(pTimer->m_pExecute);
|
||||
@@ -111,6 +116,11 @@ void CTimer::ExecuteCallback()
|
||||
{
|
||||
if (m_rtimersvc.GetObjectState() != sdv::EObjectState::running) return;
|
||||
if (!m_pExecute) return;
|
||||
if (m_tPermissionTransferID)
|
||||
{
|
||||
sdv::core::SetAccessPermission(m_tPermissionTransferID);
|
||||
m_tPermissionTransferID = 0u;
|
||||
}
|
||||
if (!m_bPrioritySet)
|
||||
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
|
||||
m_bPrioritySet = true;
|
||||
|
||||
@@ -102,6 +102,8 @@ private:
|
||||
std::atomic_bool m_bRunning = false; ///< When set, the timer is running.
|
||||
std::mutex m_mtxExecution; ///< Prevent killing the timer when in execution.
|
||||
#endif
|
||||
sdv::core::TPermissionTransferID m_tPermissionTransferID = 0u; ///< The transfer ID to transfer permissions between the
|
||||
///< timer creator and the execution thread.
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,20 +12,55 @@ if(UNIX)
|
||||
# Define project
|
||||
project(uds_unix_sockets VERSION 1.0 LANGUAGES CXX)
|
||||
|
||||
# Define target
|
||||
add_library(uds_unix_sockets STATIC
|
||||
# Build sources once and expose them in two forms:
|
||||
# - uds_unix_sockets: runtime-loadable .sdv module for ModuleControl
|
||||
# - uds_unix_sockets_static: static link target for unit tests / static consumers
|
||||
set(UDS_UNIX_SOCKETS_SOURCES
|
||||
channel_mgnt.cpp
|
||||
connection.cpp
|
||||
)
|
||||
watchdog.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(uds_unix_sockets rt ${CMAKE_DL_LIBS} ${CMAKE_THREAD_LIBS_INIT})
|
||||
add_library(uds_unix_sockets SHARED
|
||||
${UDS_UNIX_SOCKETS_SOURCES}
|
||||
)
|
||||
|
||||
add_library(uds_unix_sockets_static STATIC
|
||||
${UDS_UNIX_SOCKETS_SOURCES}
|
||||
)
|
||||
|
||||
target_include_directories(uds_unix_sockets
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
./include/
|
||||
)
|
||||
|
||||
target_include_directories(uds_unix_sockets_static
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
./include/
|
||||
)
|
||||
|
||||
target_link_libraries(uds_unix_sockets
|
||||
PUBLIC
|
||||
rt
|
||||
${CMAKE_DL_LIBS}
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
)
|
||||
|
||||
target_link_libraries(uds_unix_sockets_static
|
||||
PUBLIC
|
||||
rt
|
||||
${CMAKE_DL_LIBS}
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
)
|
||||
|
||||
target_include_directories(uds_unix_sockets PRIVATE ./include/)
|
||||
set_target_properties(uds_unix_sockets PROPERTIES PREFIX "")
|
||||
set_target_properties(uds_unix_sockets PROPERTIES SUFFIX ".sdv")
|
||||
|
||||
# Build dependencies
|
||||
add_dependencies(uds_unix_sockets CompileCoreIDL)
|
||||
add_dependencies(uds_unix_sockets_static CompileCoreIDL)
|
||||
|
||||
# Appending the service in the service list
|
||||
set(SDV_Service_List ${SDV_Service_List} uds_unix_sockets PARENT_SCOPE)
|
||||
|
||||
@@ -81,19 +81,19 @@ namespace
|
||||
// Directory selection (/run/user/<uid>/sdv or /tmp/sdv)
|
||||
std::string CUnixDomainSocketsChannelMgnt::MakeUserRuntimeDir()
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << "/run/user/" << ::getuid();
|
||||
const std::string path = "/run/ipc/sdv";
|
||||
|
||||
struct stat st{};
|
||||
if (::stat(oss.str().c_str(), &st) == 0)
|
||||
if (::stat(path.c_str(), &st) == 0)
|
||||
{
|
||||
std::string path = oss.str() + "/sdv";
|
||||
::mkdir(path.c_str(), 0770);
|
||||
return path;
|
||||
}
|
||||
|
||||
::mkdir("/tmp/sdv", 0770);
|
||||
return "/tmp/sdv";
|
||||
// fallback if /run/ipc/sdv is not available
|
||||
const std::string fallback = "/tmp/sdv";
|
||||
::mkdir(fallback.c_str(), 0770);
|
||||
|
||||
return fallback;
|
||||
}
|
||||
|
||||
bool CUnixDomainSocketsChannelMgnt::OnInitialize()
|
||||
@@ -102,7 +102,11 @@ bool CUnixDomainSocketsChannelMgnt::OnInitialize()
|
||||
}
|
||||
|
||||
void CUnixDomainSocketsChannelMgnt::OnShutdown()
|
||||
{}
|
||||
|
||||
void CUnixDomainSocketsChannelMgnt::OnDestroy()
|
||||
{
|
||||
m_watchdog.Clear();
|
||||
}
|
||||
|
||||
// Endpoint creation (server)
|
||||
@@ -129,33 +133,133 @@ sdv::ipc::SChannelEndpoint CUnixDomainSocketsChannelMgnt::CreateEndpoint(const s
|
||||
|
||||
path = ClampSunPath(path);
|
||||
|
||||
// Use a shared_ptr and store it to keep the server connection alive
|
||||
auto server = std::make_shared<CUnixSocketConnection>(-1, true, path);
|
||||
m_ServerConnections.push_back(server);
|
||||
// Keep lifetime consistent with shared_mem: watchdog owns active connections.
|
||||
std::shared_ptr<CUnixSocketConnection> server = std::make_shared<CUnixSocketConnection>(-1, true, path);
|
||||
// Ignore cppcheck warning; if construction failed, an exception is expected first.
|
||||
// cppcheck-suppress knownConditionTrueFalse
|
||||
if (!server)
|
||||
return {};
|
||||
server->SetWatchDogRemoveCallback([this](const void* connection)
|
||||
{
|
||||
m_watchdog.RemoveConnection(connection);
|
||||
});
|
||||
m_watchdog.AddConnection(server);
|
||||
|
||||
sdv::ipc::SChannelEndpoint ep{};
|
||||
ep.pConnection = static_cast<IInterfaceAccess*>(server.get());
|
||||
ep.ssConnectString = server->GetConnectionString();
|
||||
|
||||
// Publish a Provider-wrapped connect string for compatibility with
|
||||
// CCommunicationControl::CreateClientConnection() flows.
|
||||
const std::string udsConnectString = server->GetConnectionString();
|
||||
ep.ssConnectString = std::string("[Provider]\n") +
|
||||
"Name = \"unix_domain_sockets\"\n" +
|
||||
"ConnectString = \"" + udsConnectString + "\"\n";
|
||||
return ep;
|
||||
}
|
||||
|
||||
// Access existing endpoint (server or client)
|
||||
sdv::IInterfaceAccess* CUnixDomainSocketsChannelMgnt::Access(const sdv::u8string& ssConnectString)
|
||||
{
|
||||
const auto kv = ParseKV(static_cast<std::string>(ssConnectString));
|
||||
const bool isServer = (kv.count("role") && kv.at("role") == "server");
|
||||
const std::string path = kv.count("path") ? kv.at("path") : (MakeUserRuntimeDir() + "/UDS_auto.sock");
|
||||
const std::string input = static_cast<std::string>(ssConnectString);
|
||||
|
||||
if (isServer)
|
||||
bool isServer = false;
|
||||
bool parsed = false;
|
||||
std::string path;
|
||||
|
||||
// Parse structured TOML forms first, but only for non-raw inputs.
|
||||
// Raw connect strings (proto=uds;...) are intentionally not TOML and
|
||||
// would produce noisy parser diagnostics like "Missing value".
|
||||
if (input.rfind("proto=uds", 0) != 0)
|
||||
{
|
||||
auto server = std::make_shared<CUnixSocketConnection>(-1, true, path);
|
||||
m_ServerConnections.push_back(server);
|
||||
return static_cast<IInterfaceAccess*>(server.get());
|
||||
sdv::toml::CTOMLParser parser(input);
|
||||
if (!parser.IsValid())
|
||||
return nullptr;
|
||||
|
||||
const std::string providerName = parser.GetDirect("Provider.Name").GetValue();
|
||||
if (!providerName.empty())
|
||||
{
|
||||
if (providerName != "unix_domain_sockets" &&
|
||||
providerName != "UnixSocketsChannelControl" &&
|
||||
providerName != "UnixDomainSocketsChannelControl")
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const std::string nested = parser.GetDirect("Provider.ConnectString").GetValue();
|
||||
if (!nested.empty())
|
||||
{
|
||||
const auto kv = ParseKV(nested);
|
||||
if (kv.count("path"))
|
||||
path = kv.at("path");
|
||||
}
|
||||
|
||||
// Provider descriptions are consumed as client endpoints.
|
||||
parsed = true;
|
||||
isServer = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Shared-memory style callers pass [IpcChannel] config directly to Access().
|
||||
parsed = true;
|
||||
isServer = false;
|
||||
}
|
||||
|
||||
if (path.empty())
|
||||
{
|
||||
auto pathNode = parser.GetDirect("IpcChannel.Path");
|
||||
if (pathNode.GetType() == sdv::toml::ENodeType::node_string)
|
||||
{
|
||||
path = static_cast<std::string>(pathNode.GetValue());
|
||||
}
|
||||
else
|
||||
{
|
||||
auto nameNode = parser.GetDirect("IpcChannel.Name");
|
||||
if (nameNode.GetType() == sdv::toml::ENodeType::node_string)
|
||||
{
|
||||
const std::string name = static_cast<std::string>(nameNode.GetValue());
|
||||
if (!name.empty())
|
||||
path = MakeUserRuntimeDir() + "/" + name + ".sock";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Client: allocated raw pointer (expected to be managed by SDV framework)
|
||||
auto* client = new CUnixSocketConnection(-1, false, path);
|
||||
return static_cast<IInterfaceAccess*>(client);
|
||||
// Raw KV fallback (legacy/tests): proto=uds;role=...;path=...;
|
||||
if (!parsed)
|
||||
{
|
||||
// Only treat as raw format when it actually starts with proto=uds.
|
||||
if (input.rfind("proto=uds", 0) != 0)
|
||||
return nullptr;
|
||||
|
||||
const auto kv = ParseKV(input);
|
||||
parsed = true;
|
||||
isServer = (kv.count("role") && kv.at("role") == "server");
|
||||
if (kv.count("path"))
|
||||
path = kv.at("path");
|
||||
}
|
||||
|
||||
if (!parsed)
|
||||
return nullptr;
|
||||
|
||||
if (path.empty())
|
||||
path = MakeUserRuntimeDir() + "/UDS_auto.sock";
|
||||
|
||||
path = ClampSunPath(path);
|
||||
|
||||
std::shared_ptr<CUnixSocketConnection> connection =
|
||||
std::make_shared<CUnixSocketConnection>(-1, isServer, path);
|
||||
// Ignore cppcheck warning; if construction failed, an exception is expected first.
|
||||
// cppcheck-suppress knownConditionTrueFalse
|
||||
if (!connection)
|
||||
return nullptr;
|
||||
|
||||
connection->SetWatchDogRemoveCallback([this](const void* instance)
|
||||
{
|
||||
m_watchdog.RemoveConnection(instance);
|
||||
});
|
||||
|
||||
m_watchdog.AddConnection(connection);
|
||||
return static_cast<IInterfaceAccess*>(connection.get());
|
||||
}
|
||||
|
||||
#endif // defined(__unix__)
|
||||
@@ -17,6 +17,8 @@
|
||||
|
||||
#include <support/component_impl.h>
|
||||
#include <interfaces/ipc.h>
|
||||
#include "watchdog.h"
|
||||
#include <algorithm>
|
||||
|
||||
class CUnixSocketConnection;
|
||||
|
||||
@@ -37,9 +39,9 @@ public:
|
||||
|
||||
// Object declarations
|
||||
DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::system_object)
|
||||
DECLARE_OBJECT_CLASS_NAME("UnixDomainSocketsChannelControl")
|
||||
DECLARE_OBJECT_CLASS_ALIAS("LocalChannelControl")
|
||||
DECLARE_DEFAULT_OBJECT_NAME("LocalChannelControl")
|
||||
DECLARE_OBJECT_CLASS_NAME("UnixSocketsChannelControl")
|
||||
DECLARE_OBJECT_CLASS_ALIAS("unix_domain_sockets")
|
||||
DECLARE_DEFAULT_OBJECT_NAME("unix_domain_sockets")
|
||||
DECLARE_OBJECT_SINGLETON()
|
||||
|
||||
/**
|
||||
@@ -53,6 +55,11 @@ public:
|
||||
*/
|
||||
virtual void OnShutdown() override;
|
||||
|
||||
/**
|
||||
* @brief Last function called before destruction. Overload of sdv::CSdvObject::OnDestroy.
|
||||
*/
|
||||
virtual void OnDestroy() override;
|
||||
|
||||
/**
|
||||
* @brief Create IPC connection object and return the endpoint information. Overload of
|
||||
* sdv::ipc::ICreateEndpoint::CreateEndpoint.
|
||||
@@ -80,8 +87,7 @@ private:
|
||||
|
||||
// Helper: choose runtime dir (/run/user/<uid>/sdv) or fallback (/tmp/sdv)
|
||||
static std::string MakeUserRuntimeDir();
|
||||
|
||||
std::vector<std::shared_ptr<CUnixSocketConnection>> m_ServerConnections;
|
||||
CUnixSocketsConnectionWatchDog m_watchdog;
|
||||
|
||||
};
|
||||
DEFINE_SDV_OBJECT(CUnixDomainSocketsChannelMgnt)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -22,6 +22,8 @@
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <condition_variable>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
@@ -98,6 +100,9 @@ public:
|
||||
/** @brief Destroy object (IObjectDestroy). */
|
||||
void DestroyObject() override;
|
||||
|
||||
/** @brief Register callback used to remove this connection from manager watchdog storage. */
|
||||
void SetWatchDogRemoveCallback(std::function<void(const void*)> callback);
|
||||
|
||||
/** @brief Set state and notify listeners (callback-safe). */
|
||||
void SetConnectState(sdv::ipc::EConnectState eConnectState);
|
||||
|
||||
@@ -153,21 +158,27 @@ public:
|
||||
SMsgHdr GetMsgHdr() const
|
||||
{
|
||||
if (GetSize() < sizeof(SMsgHdr)) return SMsgHdr{0, EMsgType::connect_term};
|
||||
return *reinterpret_cast<const SMsgHdr*>(GetData());
|
||||
SMsgHdr hdr{};
|
||||
std::memcpy(&hdr, GetData(), sizeof(SMsgHdr));
|
||||
return hdr;
|
||||
}
|
||||
|
||||
/** @return SDV connect header (or default if undersized). */
|
||||
SConnectMsg GetConnectHdr() const
|
||||
{
|
||||
if (GetSize() < sizeof(SConnectMsg)) return SConnectMsg{};
|
||||
return *reinterpret_cast<const SConnectMsg*>(GetData());
|
||||
SConnectMsg hdr{};
|
||||
std::memcpy(&hdr, GetData(), sizeof(SConnectMsg));
|
||||
return hdr;
|
||||
}
|
||||
|
||||
/** @return SDV fragmented header (or default if undersized). */
|
||||
SFragmentedMsgHdr GetFragmentedHdr() const
|
||||
{
|
||||
if (GetSize() < sizeof(SFragmentedMsgHdr)) return SFragmentedMsgHdr{};
|
||||
return *reinterpret_cast<const SFragmentedMsgHdr*>(GetData());
|
||||
SFragmentedMsgHdr hdr{};
|
||||
std::memcpy(&hdr, GetData(), sizeof(SFragmentedMsgHdr));
|
||||
return hdr;
|
||||
}
|
||||
|
||||
/** @return true if the SDV envelope is well-formed. */
|
||||
@@ -253,6 +264,10 @@ public:
|
||||
*/
|
||||
void ReceiveMessages();
|
||||
|
||||
bool SendConnectMessage(EMsgType type);
|
||||
|
||||
bool SendControlMessage(EMsgType type);
|
||||
|
||||
/**
|
||||
* @brief Handle an incoming sync_request message.
|
||||
*
|
||||
@@ -360,6 +375,10 @@ public:
|
||||
*/
|
||||
void StopThreadsAndCloseSockets(bool unlinkPath);
|
||||
|
||||
void WaitForClientEnd();
|
||||
|
||||
void CloseClientSocketOnly();
|
||||
|
||||
private:
|
||||
//Transport state
|
||||
int m_Fd { -1 }; ///< Active connection FD.
|
||||
@@ -370,19 +389,24 @@ private:
|
||||
//Threads & control
|
||||
std::atomic<bool> m_StopReceiveThread { false };
|
||||
std::atomic<bool> m_StopConnectThread { false };
|
||||
std::thread m_ReceiveThread;
|
||||
std::thread m_ConnectThread;
|
||||
sdv::core::secure_thread m_ReceiveThread;
|
||||
sdv::core::secure_thread m_ConnectThread;
|
||||
|
||||
//State & synchronization
|
||||
std::condition_variable m_StateCv;
|
||||
std::atomic<sdv::ipc::EConnectState> m_eConnectState { sdv::ipc::EConnectState::uninitialized };
|
||||
std::atomic<bool> m_bConnectedOnce { false }; ///< Latched true once a connection was established.
|
||||
sdv::ipc::IDataReceiveCallback* m_pReceiver { nullptr };
|
||||
sdv::ipc::IConnectEventCallback* m_pEvent { nullptr };
|
||||
std::mutex m_MtxConnect;
|
||||
std::condition_variable m_CvConnect;
|
||||
std::condition_variable m_cvConnect;
|
||||
std::mutex m_StateMtx; ///< Protects receiver/event assignment.
|
||||
std::list<SEventCallback> m_lstEventCallbacks; ///< List containing event callbacks
|
||||
std::shared_mutex m_mtxEventCallbacks; ///< Protect access to callback list
|
||||
std::mutex m_WatchdogMtx;
|
||||
std::function<void(const void*)> m_WatchdogRemoveCallback;
|
||||
std::atomic<bool> m_DestroyObjectCalled { false };
|
||||
std::atomic<bool> m_CancelWait { false };
|
||||
|
||||
//TX synchronization
|
||||
std::mutex m_SendMtx;
|
||||
|
||||
58
sdv_services/uds_unix_sockets/watchdog.cpp
Normal file
58
sdv_services/uds_unix_sockets/watchdog.cpp
Normal file
@@ -0,0 +1,58 @@
|
||||
/********************************************************************************
|
||||
* Copyright (c) 2025-2026 ZF Friedrichshafen AG
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Apache License Version 2.0 which is available at
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Contributors:
|
||||
* Denisa Ros - initial API and implementation
|
||||
********************************************************************************/
|
||||
|
||||
#if defined(__unix__)
|
||||
|
||||
#include "watchdog.h"
|
||||
|
||||
void CUnixSocketsConnectionWatchDog::AddConnectionImpl(const std::shared_ptr<void>& connection)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_Mutex);
|
||||
m_Connections[connection.get()] = connection;
|
||||
}
|
||||
|
||||
void CUnixSocketsConnectionWatchDog::RemoveConnection(const void* connection)
|
||||
{
|
||||
if (!connection)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::shared_ptr<void> removed;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_Mutex);
|
||||
auto it = m_Connections.find(connection);
|
||||
if (it == m_Connections.end())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
removed = std::move(it->second);
|
||||
m_Connections.erase(it);
|
||||
}
|
||||
|
||||
removed.reset();
|
||||
}
|
||||
|
||||
void CUnixSocketsConnectionWatchDog::Clear()
|
||||
{
|
||||
std::map<const void*, std::shared_ptr<void>> localConnections;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_Mutex);
|
||||
localConnections.swap(m_Connections);
|
||||
}
|
||||
|
||||
localConnections.clear();
|
||||
}
|
||||
|
||||
#endif // defined(__unix__)
|
||||
47
sdv_services/uds_unix_sockets/watchdog.h
Normal file
47
sdv_services/uds_unix_sockets/watchdog.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/********************************************************************************
|
||||
* Copyright (c) 2025-2026 ZF Friedrichshafen AG
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Apache License Version 2.0 which is available at
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Contributors:
|
||||
* Denisa Ros - initial API and implementation
|
||||
********************************************************************************/
|
||||
|
||||
#if defined(__unix__)
|
||||
#ifndef UDS_UNIX_SOCKETS_WATCHDOG_H
|
||||
#define UDS_UNIX_SOCKETS_WATCHDOG_H
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
class CUnixSocketsConnectionWatchDog
|
||||
{
|
||||
public:
|
||||
template<typename T>
|
||||
void AddConnection(const std::shared_ptr<T>& connection)
|
||||
{
|
||||
if (!connection)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AddConnectionImpl(std::static_pointer_cast<void>(connection));
|
||||
}
|
||||
|
||||
void RemoveConnection(const void* connection);
|
||||
void Clear();
|
||||
|
||||
private:
|
||||
void AddConnectionImpl(const std::shared_ptr<void>& connection);
|
||||
|
||||
std::mutex m_Mutex;
|
||||
std::map<const void*, std::shared_ptr<void>> m_Connections;
|
||||
};
|
||||
|
||||
#endif // UDS_UNIX_SOCKETS_WATCHDOG_H
|
||||
#endif // defined(__unix__)
|
||||
@@ -12,22 +12,57 @@ if(UNIX)
|
||||
# Define project
|
||||
project(uds_unix_tunnel VERSION 1.0 LANGUAGES CXX)
|
||||
|
||||
# Define target
|
||||
add_library(uds_unix_tunnel STATIC
|
||||
set(UDS_UNIX_TUNNEL_SOURCES
|
||||
channel_mgnt.cpp
|
||||
connection.cpp
|
||||
watchdog.cpp
|
||||
)
|
||||
|
||||
# Define targets
|
||||
add_library(uds_unix_tunnel STATIC
|
||||
${UDS_UNIX_TUNNEL_SOURCES}
|
||||
)
|
||||
|
||||
target_link_libraries(uds_unix_tunnel rt ${CMAKE_DL_LIBS} ${CMAKE_THREAD_LIBS_INIT})
|
||||
add_library(uds_unix_tunnel_module SHARED
|
||||
${UDS_UNIX_TUNNEL_SOURCES}
|
||||
)
|
||||
|
||||
target_include_directories(uds_unix_tunnel PRIVATE ./include/)
|
||||
set_target_properties(uds_unix_tunnel PROPERTIES PREFIX "")
|
||||
set_target_properties(uds_unix_tunnel PROPERTIES SUFFIX ".sdv")
|
||||
target_link_libraries(uds_unix_tunnel
|
||||
PRIVATE
|
||||
uds_unix_sockets_static
|
||||
rt
|
||||
${CMAKE_DL_LIBS}
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
)
|
||||
|
||||
target_link_libraries(uds_unix_tunnel_module
|
||||
PRIVATE
|
||||
uds_unix_sockets_static
|
||||
rt
|
||||
${CMAKE_DL_LIBS}
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
)
|
||||
|
||||
target_include_directories(uds_unix_tunnel
|
||||
PRIVATE
|
||||
./include/
|
||||
../uds_unix_sockets/
|
||||
)
|
||||
|
||||
target_include_directories(uds_unix_tunnel_module
|
||||
PRIVATE
|
||||
./include/
|
||||
../uds_unix_sockets/
|
||||
)
|
||||
set_target_properties(uds_unix_tunnel_module PROPERTIES PREFIX "")
|
||||
set_target_properties(uds_unix_tunnel_module PROPERTIES SUFFIX ".sdv")
|
||||
set_target_properties(uds_unix_tunnel_module PROPERTIES OUTPUT_NAME "uds_unix_tunnel")
|
||||
|
||||
# Build dependencies
|
||||
add_dependencies(uds_unix_tunnel CompileCoreIDL)
|
||||
add_dependencies(uds_unix_tunnel_module CompileCoreIDL)
|
||||
|
||||
# Appending the service in the service list
|
||||
set(SDV_Service_List ${SDV_Service_List} uds_unix_tunnel PARENT_SCOPE)
|
||||
set(SDV_Service_List ${SDV_Service_List} uds_unix_tunnel_module PARENT_SCOPE)
|
||||
|
||||
endif()
|
||||
@@ -1,3 +1,15 @@
|
||||
/********************************************************************************
|
||||
* Copyright (c) 2025-2026 ZF Friedrichshafen AG
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Apache License Version 2.0 which is available at
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Contributors:
|
||||
* Denisa Ros - initial API and implementation
|
||||
********************************************************************************/
|
||||
#if defined(__unix__)
|
||||
|
||||
#include "channel_mgnt.h"
|
||||
@@ -15,6 +27,7 @@
|
||||
|
||||
namespace
|
||||
{
|
||||
static std::atomic<uint32_t> g_nextChannelId{1};
|
||||
|
||||
/**
|
||||
* @brief Parses a semicolon-separated list of key=value pairs into a map.
|
||||
@@ -52,23 +65,113 @@ static std::string ClampSunPath(const std::string& p)
|
||||
return (p.size() < MaxLen) ? p : p.substr(0, MaxLen - 1);
|
||||
}
|
||||
|
||||
static std::string GetUserRuntimeDir()
|
||||
{
|
||||
const std::string path = "/run/ipc/sdv";
|
||||
|
||||
struct stat st{};
|
||||
if (::stat(path.c_str(), &st) == 0)
|
||||
{
|
||||
return path;
|
||||
}
|
||||
|
||||
// fallback if /run/ipc/sdv is not available
|
||||
const std::string fallback = "/tmp/sdv";
|
||||
::mkdir(fallback.c_str(), 0770);
|
||||
|
||||
return fallback;
|
||||
}
|
||||
|
||||
|
||||
static std::string BuildTunnelPath(const std::string& baseDir,
|
||||
const std::string& tunnel,
|
||||
uint32_t instanceId,
|
||||
uint32_t pid)
|
||||
{
|
||||
std::string dir = baseDir + "/" + tunnel;
|
||||
|
||||
if (::mkdir(dir.c_str(), 0770) != 0 && errno != EEXIST)
|
||||
{
|
||||
SDV_LOG_WARNING("Failed to create tunnel directory");
|
||||
}
|
||||
|
||||
return dir + "/vapi_" +
|
||||
std::to_string(instanceId) + "_" +
|
||||
std::to_string(pid) + ".sock";
|
||||
}
|
||||
|
||||
static bool ExtractTunnelPathFromConfig(const std::string& input, std::string& outPath)
|
||||
{
|
||||
const auto kv = ParseKV(input);
|
||||
if (kv.count("proto"))
|
||||
{
|
||||
if (kv.at("proto") != "tunnel")
|
||||
return false;
|
||||
|
||||
auto it = kv.find("path");
|
||||
outPath = (it != kv.end()) ? it->second : std::string{};
|
||||
return true;
|
||||
}
|
||||
|
||||
sdv::toml::CTOMLParser parser(input);
|
||||
if (!parser.IsValid())
|
||||
return false;
|
||||
|
||||
const std::string providerName = parser.GetDirect("Provider.Name").GetValue();
|
||||
if (!providerName.empty() &&
|
||||
providerName != "unix_domain_sockets_tunnel" &&
|
||||
providerName != "UnixTunnelChannelControl" &&
|
||||
providerName != "WinTunnelChannelControl")
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::string nested = parser.GetDirect("Provider.ConnectString").GetValue();
|
||||
if (!nested.empty())
|
||||
{
|
||||
return ExtractTunnelPathFromConfig(nested, outPath);
|
||||
}
|
||||
|
||||
auto pathNode = parser.GetDirect("IpcChannel.Path");
|
||||
if (pathNode.GetType() == sdv::toml::ENodeType::node_string)
|
||||
{
|
||||
outPath = static_cast<std::string>(pathNode.GetValue());
|
||||
return true;
|
||||
}
|
||||
|
||||
auto nameNode = parser.GetDirect("IpcChannel.Name");
|
||||
if (nameNode.GetType() == sdv::toml::ENodeType::node_string)
|
||||
{
|
||||
const std::string name = static_cast<std::string>(nameNode.GetValue());
|
||||
if (!name.empty())
|
||||
{
|
||||
outPath = GetUserRuntimeDir() + "/" + name + ".sock";
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
outPath.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
static std::string ExtractTunnelName(const std::string& input)
|
||||
{
|
||||
const auto kv = ParseKV(input);
|
||||
if (kv.count("tunnel"))
|
||||
{
|
||||
return kv.at("tunnel");
|
||||
}
|
||||
|
||||
// Fallback default
|
||||
return "default";
|
||||
}
|
||||
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
std::string CUnixTunnelChannelMgnt::MakeUserRuntimeDir()
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << "/run/user/" << ::getuid();
|
||||
|
||||
struct stat st{};
|
||||
if (::stat(oss.str().c_str(), &st) == 0)
|
||||
{
|
||||
std::string path = oss.str() + "/sdv";
|
||||
::mkdir(path.c_str(), 0770);
|
||||
return path;
|
||||
}
|
||||
|
||||
::mkdir("/tmp/sdv", 0770);
|
||||
return "/tmp/sdv";
|
||||
return GetUserRuntimeDir();
|
||||
}
|
||||
|
||||
bool CUnixTunnelChannelMgnt::OnInitialize()
|
||||
@@ -77,9 +180,11 @@ bool CUnixTunnelChannelMgnt::OnInitialize()
|
||||
}
|
||||
|
||||
void CUnixTunnelChannelMgnt::OnShutdown()
|
||||
{}
|
||||
|
||||
void CUnixTunnelChannelMgnt::OnDestroy()
|
||||
{
|
||||
// Actual cleanup is handled by destructors of CUnixTunnelConnection
|
||||
// and CUnixSocketConnection (shared_ptr).
|
||||
m_watchdog.Clear();
|
||||
}
|
||||
|
||||
sdv::ipc::SChannelEndpoint CUnixTunnelChannelMgnt::CreateEndpoint(
|
||||
@@ -88,91 +193,270 @@ sdv::ipc::SChannelEndpoint CUnixTunnelChannelMgnt::CreateEndpoint(
|
||||
sdv::ipc::SChannelEndpoint endpoint{};
|
||||
|
||||
const std::string baseDir = MakeUserRuntimeDir();
|
||||
std::string name = "TUNNEL_" + std::to_string(::getpid());
|
||||
std::string path = baseDir + "/" + name + ".sock";
|
||||
uint32_t instanceId = 1000;
|
||||
const sdv::app::IAppContext* pCtx = sdv::core::GetCore<sdv::app::IAppContext>();
|
||||
if (pCtx && pCtx->GetInstanceID() != 0)
|
||||
{
|
||||
instanceId = pCtx->GetInstanceID();
|
||||
}
|
||||
|
||||
// Extract tunnel name from config
|
||||
std::string input = static_cast<std::string>(ssChannelConfig);
|
||||
std::string tunnel = ExtractTunnelName(input);
|
||||
|
||||
// Build safe path
|
||||
std::string path = BuildTunnelPath(baseDir, tunnel, instanceId, static_cast<uint32_t>(::getpid()));
|
||||
|
||||
// Parse optional TOML config for custom name/path
|
||||
if (!ssChannelConfig.empty())
|
||||
{
|
||||
sdv::toml::CTOMLParser cfg(ssChannelConfig.c_str());
|
||||
auto nameNode = cfg.GetDirect("IpcChannel.Name");
|
||||
if (nameNode.GetType() == sdv::toml::ENodeType::node_string)
|
||||
name = static_cast<std::string>(nameNode.GetValue());
|
||||
std::string configuredPath;
|
||||
|
||||
auto pathNode = cfg.GetDirect("IpcChannel.Path");
|
||||
if (pathNode.GetType() == sdv::toml::ENodeType::node_string)
|
||||
path = static_cast<std::string>(pathNode.GetValue());
|
||||
if (ExtractTunnelPathFromConfig(input, configuredPath) && !configuredPath.empty())
|
||||
{
|
||||
std::string baseName = configuredPath;
|
||||
auto pos = baseName.find_last_of('/');
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
baseName = baseName.substr(pos + 1);
|
||||
}
|
||||
path = baseDir + "/" + tunnel + "/" + baseName;
|
||||
SDV_LOG_WARNING("Using custom path without tunnel isolation");
|
||||
}
|
||||
else
|
||||
path = baseDir + "/" + name + ".sock";
|
||||
{
|
||||
// fallback - still use tunnel-based path
|
||||
tunnel = ExtractTunnelName(input);
|
||||
instanceId = 1000;
|
||||
|
||||
if (pCtx && pCtx->GetInstanceID() != 0)
|
||||
{
|
||||
instanceId = pCtx->GetInstanceID();
|
||||
}
|
||||
|
||||
path = BuildTunnelPath(baseDir, tunnel, instanceId, static_cast<uint32_t>(::getpid()));
|
||||
}
|
||||
}
|
||||
|
||||
path = ClampSunPath(path);
|
||||
|
||||
// Create underlying UDS server transport
|
||||
auto udsServer = std::make_shared<CUnixSocketConnection>(
|
||||
-1,
|
||||
/*acceptConnectionRequired*/ true,
|
||||
path);
|
||||
auto udsServer = std::make_shared<CUnixSocketConnection>(-1, /*acceptConnectionRequired*/ true, path);
|
||||
|
||||
uint32_t chId = g_nextChannelId++;
|
||||
// Create tunnel wrapper on top of UDS
|
||||
auto tunnelServer = std::make_shared<CUnixTunnelConnection>(
|
||||
udsServer,
|
||||
/*channelId*/ 0u);
|
||||
auto tunnelServer = std::make_shared<CUnixTunnelConnection>(udsServer, /*channelId*/ chId);
|
||||
|
||||
m_ServerTunnels.push_back(tunnelServer);
|
||||
// Ignore cppcheck warning; if construction failed, an exception is expected first.
|
||||
// cppcheck-suppress knownConditionTrueFalse
|
||||
if (!tunnelServer)
|
||||
return {};
|
||||
|
||||
tunnelServer->SetWatchDogRemoveCallback([this](const void* connection)
|
||||
{
|
||||
m_watchdog.RemoveConnection(connection);
|
||||
});
|
||||
m_watchdog.AddConnection(tunnelServer);
|
||||
|
||||
endpoint.pConnection = static_cast<sdv::IInterfaceAccess*>(tunnelServer.get());
|
||||
endpoint.ssConnectString = "proto=tunnel;role=server;path=" + path + ";";
|
||||
const std::string tunnelConnectString = "proto=tunnel;path=" + path + ";tunnel=" + tunnel + ";";
|
||||
endpoint.ssConnectString = std::string("[Provider]\n") +
|
||||
"Name = \"unix_domain_sockets_tunnel\"\n" +
|
||||
"ConnectString = \"" + tunnelConnectString + "\"\n";
|
||||
|
||||
return endpoint;
|
||||
}
|
||||
|
||||
sdv::IInterfaceAccess* CUnixTunnelChannelMgnt::Access(
|
||||
const sdv::u8string& ssConnectString)
|
||||
sdv::IInterfaceAccess* CUnixTunnelChannelMgnt::Access(const sdv::u8string& ssConnectString)
|
||||
{
|
||||
const auto kv = ParseKV(static_cast<std::string>(ssConnectString));
|
||||
const std::string input = static_cast<std::string>(ssConnectString);
|
||||
std::string tunnel = ExtractTunnelName(input);
|
||||
bool parsed = false;
|
||||
bool isServer = false;
|
||||
std::string path;
|
||||
|
||||
// Only handle proto=tunnel
|
||||
if (!kv.count("proto") || kv.at("proto") != "tunnel")
|
||||
// Parse structured TOML forms first; raw connect strings are handled below.
|
||||
if (input.rfind("proto=tunnel", 0) != 0)
|
||||
{
|
||||
|
||||
sdv::toml::CTOMLParser parser(input);
|
||||
if (!parser.IsValid())
|
||||
{
|
||||
SDV_LOG_WARNING("[TUNNEL][Access] TOML parse failed. Trying text fallback.");
|
||||
|
||||
// Fallback for malformed TOML: try to recover an embedded proto=tunnel connect string.
|
||||
const auto protoPos = input.find("proto=tunnel");
|
||||
if (protoPos != std::string::npos)
|
||||
{
|
||||
std::string fallbackCs = input.substr(protoPos);
|
||||
|
||||
const auto quotePos = fallbackCs.find('"');
|
||||
if (quotePos != std::string::npos)
|
||||
fallbackCs = fallbackCs.substr(0, quotePos);
|
||||
|
||||
const auto newlinePos = fallbackCs.find('\n');
|
||||
if (newlinePos != std::string::npos)
|
||||
fallbackCs = fallbackCs.substr(0, newlinePos);
|
||||
|
||||
const auto kv = ParseKV(fallbackCs);
|
||||
if (kv.count("proto") && kv.at("proto") == "tunnel")
|
||||
{
|
||||
parsed = true;
|
||||
isServer = (kv.count("role") && kv.at("role") == "server");
|
||||
if (kv.count("path"))
|
||||
path = kv.at("path");
|
||||
}
|
||||
}
|
||||
|
||||
if (!parsed)
|
||||
{
|
||||
SDV_LOG_WARNING("[TUNNEL][Access] Fallback parse failed. Returning nullptr");
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (!parsed)
|
||||
{
|
||||
const std::string providerName = parser.GetDirect("Provider.Name").GetValue();
|
||||
if (!providerName.empty())
|
||||
{
|
||||
if (providerName != "unix_domain_sockets_tunnel" &&
|
||||
providerName != "UnixTunnelChannelControl" &&
|
||||
providerName != "WinTunnelChannelControl")
|
||||
{
|
||||
SDV_LOG_WARNING("[TUNNEL][Access] Unsupported provider. Returning nullptr");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const std::string nested = parser.GetDirect("Provider.ConnectString").GetValue();
|
||||
if (!nested.empty())
|
||||
{
|
||||
const auto nestedKv = ParseKV(nested);
|
||||
if (nestedKv.count("tunnel"))
|
||||
{
|
||||
tunnel = nestedKv.at("tunnel");
|
||||
}
|
||||
|
||||
if (nestedKv.count("proto") && nestedKv.at("proto") != "tunnel")
|
||||
{
|
||||
SDV_LOG_WARNING("[TUNNEL][Access] Nested proto is not tunnel. Returning nullptr");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (nestedKv.count("path"))
|
||||
{
|
||||
std::string baseName = nestedKv.at("path");
|
||||
auto pos = baseName.find_last_of('/');
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
baseName = baseName.substr(pos + 1);
|
||||
}
|
||||
|
||||
path = MakeUserRuntimeDir() + "/" + tunnel + "/" + baseName;
|
||||
}
|
||||
}
|
||||
|
||||
parsed = true;
|
||||
isServer = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Callers can pass [IpcChannel] directly to Access().
|
||||
parsed = true;
|
||||
isServer = false;
|
||||
}
|
||||
|
||||
if (path.empty())
|
||||
{
|
||||
auto pathNode = parser.GetDirect("IpcChannel.Path");
|
||||
if (pathNode.GetType() == sdv::toml::ENodeType::node_string)
|
||||
{
|
||||
std::string baseName = static_cast<std::string>(pathNode.GetValue());
|
||||
|
||||
auto pos = baseName.find_last_of('/');
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
baseName = baseName.substr(pos + 1);
|
||||
}
|
||||
|
||||
path = MakeUserRuntimeDir() + "/" + tunnel + "/" + baseName;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto nameNode = parser.GetDirect("IpcChannel.Name");
|
||||
if (nameNode.GetType() == sdv::toml::ENodeType::node_string)
|
||||
{
|
||||
const std::string name = static_cast<std::string>(nameNode.GetValue());
|
||||
if (!name.empty())
|
||||
{
|
||||
path = MakeUserRuntimeDir() + "/" + tunnel + "/" + name + ".sock";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Raw KV fallback: proto=tunnel;role=...;path=...;
|
||||
if (!parsed)
|
||||
{
|
||||
if (input.rfind("proto=tunnel", 0) != 0)
|
||||
{
|
||||
SDV_LOG_WARNING("[TUNNEL][Access] Raw KV parse rejected: input does not start with proto=tunnel. Returning nullptr");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const auto kv = ParseKV(input);
|
||||
if (!kv.count("proto") || kv.at("proto") != "tunnel")
|
||||
{
|
||||
SDV_LOG_WARNING("[TUNNEL][Access] Raw KV parse rejected: missing/invalid proto. Returning nullptr ");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
parsed = true;
|
||||
isServer = (kv.count("role") && kv.at("role") == "server");
|
||||
if (kv.count("path"))
|
||||
{
|
||||
path = kv.at("path");
|
||||
}
|
||||
}
|
||||
|
||||
if (!parsed)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const bool isServer =
|
||||
(kv.count("role") && kv.at("role") == "server");
|
||||
|
||||
const std::string path =
|
||||
kv.count("path")
|
||||
? kv.at("path")
|
||||
: (MakeUserRuntimeDir() + "/TUNNEL_auto.sock");
|
||||
|
||||
if (isServer)
|
||||
if (path.empty())
|
||||
{
|
||||
// For simplicity, create a new server tunnel instance for each Access().
|
||||
// The SDV framework is expected to call Access(serverCS) only once in normal cases.
|
||||
auto udsServer = std::make_shared<CUnixSocketConnection>(
|
||||
-1,
|
||||
/*acceptConnectionRequired*/ true,
|
||||
path);
|
||||
tunnel = ExtractTunnelName(input);
|
||||
|
||||
auto tunnelServer = std::make_shared<CUnixTunnelConnection>(
|
||||
udsServer,
|
||||
/*channelId*/ 0u);
|
||||
uint32_t instanceId = 1000;
|
||||
const sdv::app::IAppContext* pCtx = sdv::core::GetCore<sdv::app::IAppContext>();
|
||||
if (pCtx && pCtx->GetInstanceID() != 0)
|
||||
{
|
||||
instanceId = pCtx->GetInstanceID();
|
||||
}
|
||||
|
||||
m_ServerTunnels.push_back(tunnelServer);
|
||||
return static_cast<sdv::IInterfaceAccess*>(tunnelServer.get());
|
||||
path = BuildTunnelPath(MakeUserRuntimeDir(), tunnel, instanceId, static_cast<uint32_t>(::getpid()));
|
||||
}
|
||||
|
||||
// Client: allocate raw pointer (expected to be managed by SDV framework via IObjectDestroy)
|
||||
auto udsClient = std::make_shared<CUnixSocketConnection>(
|
||||
-1,
|
||||
/*acceptConnectionRequired*/ false,
|
||||
path);
|
||||
path = ClampSunPath(path);
|
||||
|
||||
auto* tunnelClient =
|
||||
new CUnixTunnelConnection(udsClient, /*channelId*/ 0u);
|
||||
std::shared_ptr<CUnixSocketConnection> transport = std::make_shared<CUnixSocketConnection>(-1, /*acceptConnectionRequired*/ isServer, path);
|
||||
uint32_t chId = g_nextChannelId++;
|
||||
std::shared_ptr<CUnixTunnelConnection> connection = std::make_shared<CUnixTunnelConnection>(transport, /*channelId*/ chId);
|
||||
|
||||
return static_cast<sdv::IInterfaceAccess*>(tunnelClient);
|
||||
// Ignore cppcheck warning; if construction failed, an exception is expected first.
|
||||
// cppcheck-suppress knownConditionTrueFalse
|
||||
if (!connection)
|
||||
return nullptr;
|
||||
|
||||
connection->SetWatchDogRemoveCallback([this](const void* instance)
|
||||
{
|
||||
m_watchdog.RemoveConnection(instance);
|
||||
});
|
||||
|
||||
m_watchdog.AddConnection(connection);
|
||||
return static_cast<sdv::IInterfaceAccess*>(connection.get());
|
||||
}
|
||||
|
||||
#endif // defined(__unix__)
|
||||
@@ -16,43 +16,11 @@
|
||||
|
||||
#include <support/component_impl.h>
|
||||
#include <interfaces/ipc.h>
|
||||
#include "../sdv_services/uds_unix_sockets/channel_mgnt.h" // existing UDS transport
|
||||
#include "watchdog.h"
|
||||
#include <algorithm>
|
||||
|
||||
class CUnixTunnelConnection;
|
||||
/**
|
||||
* @brief Initialize WinSock on Windows (idempotent).
|
||||
*
|
||||
* This helper ensures WSAStartup() is called only once in the process.
|
||||
* On non-Windows platforms, this is a no-op and always returns success.
|
||||
*
|
||||
* @return 0 on success, otherwise a WinSock error code (Windows only).
|
||||
*/
|
||||
inline int StartUpWinSock()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
static bool isInitialized = false;
|
||||
if (isInitialized)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
WSADATA wsaData {};
|
||||
const int error = WSAStartup(MAKEWORD(2, 2), &wsaData);
|
||||
if (error != 0)
|
||||
{
|
||||
SDV_LOG_ERROR("WSAStartup failed with error: ", std::to_string(error));
|
||||
}
|
||||
else
|
||||
{
|
||||
SDV_LOG_INFO("WSAStartup initialized");
|
||||
isInitialized = true;
|
||||
}
|
||||
return error;
|
||||
#else
|
||||
// Non-Windows: nothing to do. Return success for symmetry
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
/**
|
||||
* @class CUnixTunnelChannelMgnt
|
||||
* @brief IPC channel management class for Unix Domain Socket tunnel communication.
|
||||
@@ -76,8 +44,8 @@ public:
|
||||
// Object declarations
|
||||
DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::system_object)
|
||||
DECLARE_OBJECT_CLASS_NAME("UnixTunnelChannelControl")
|
||||
DECLARE_OBJECT_CLASS_ALIAS("TunnelChannelControl")
|
||||
DECLARE_DEFAULT_OBJECT_NAME("TunnelChannelControl")
|
||||
DECLARE_OBJECT_CLASS_ALIAS("unix_domain_sockets_tunnel")
|
||||
DECLARE_DEFAULT_OBJECT_NAME("unix_domain_sockets_tunnel")
|
||||
DECLARE_OBJECT_SINGLETON()
|
||||
|
||||
/**
|
||||
@@ -96,6 +64,11 @@ public:
|
||||
*/
|
||||
virtual void OnShutdown() override;
|
||||
|
||||
/**
|
||||
* @brief Last function called before destruction. Overload of sdv::CSdvObject::OnDestroy.
|
||||
*/
|
||||
virtual void OnDestroy() override;
|
||||
|
||||
/**
|
||||
* @brief Creates a tunnel endpoint (server side) and returns endpoint info.
|
||||
*
|
||||
@@ -126,12 +99,7 @@ private:
|
||||
*/
|
||||
static std::string MakeUserRuntimeDir();
|
||||
|
||||
/**
|
||||
* @brief Keeps server-side tunnel connections alive for the lifetime of the manager.
|
||||
*
|
||||
* This ensures that server tunnel objects are not destroyed while the manager is active.
|
||||
*/
|
||||
std::vector<std::shared_ptr<CUnixTunnelConnection>> m_ServerTunnels;
|
||||
CUnixTunnelConnectionWatchDog m_watchdog;
|
||||
};
|
||||
|
||||
DEFINE_SDV_OBJECT(CUnixTunnelChannelMgnt)
|
||||
|
||||
@@ -28,6 +28,12 @@ CUnixTunnelConnection::CUnixTunnelConnection(
|
||||
// No additional initialization required; acts as a thin wrapper.
|
||||
}
|
||||
|
||||
void CUnixTunnelConnection::SetWatchDogRemoveCallback(std::function<void(const void*)> callback)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_WatchdogMtx);
|
||||
m_WatchdogRemoveCallback = std::move(callback);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Prepends a tunnel header and forwards the data to the underlying transport.
|
||||
@@ -155,11 +161,38 @@ sdv::ipc::EConnectState CUnixTunnelConnection::GetConnectState() const
|
||||
|
||||
void CUnixTunnelConnection::DestroyObject()
|
||||
{
|
||||
bool expected = false;
|
||||
if (!m_DestroyObjectCalled.compare_exchange_strong(expected, true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Stop forwarding to upper layer before transport teardown.
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_CallbackMtx);
|
||||
m_pUpperReceiver = nullptr;
|
||||
m_pUpperEvent = nullptr;
|
||||
}
|
||||
|
||||
SetConnectState(sdv::ipc::EConnectState::terminating);
|
||||
|
||||
// Disconnect underlying transport and clear callbacks.
|
||||
Disconnect();
|
||||
|
||||
std::lock_guard<std::mutex> lock(m_CallbackMtx);
|
||||
m_Transport.reset();
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_CallbackMtx);
|
||||
m_Transport.reset();
|
||||
}
|
||||
|
||||
std::function<void(const void*)> removeCallback;
|
||||
{
|
||||
std::lock_guard<std::mutex> watchdogLock(m_WatchdogMtx);
|
||||
removeCallback = std::move(m_WatchdogRemoveCallback);
|
||||
}
|
||||
if (removeCallback)
|
||||
{
|
||||
removeCallback(this);
|
||||
}
|
||||
}
|
||||
|
||||
void CUnixTunnelConnection::ReceiveData(/*inout*/ sdv::sequence<sdv::pointer<uint8_t>>& seqData)
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
@@ -179,12 +180,20 @@ public:
|
||||
*/
|
||||
uint16_t GetChannelId() const noexcept { return m_ChannelId; }
|
||||
|
||||
/**
|
||||
* @brief Register callback used to remove this connection from manager watchdog storage.
|
||||
*/
|
||||
void SetWatchDogRemoveCallback(std::function<void(const void*)> callback);
|
||||
|
||||
private:
|
||||
std::shared_ptr<CUnixSocketConnection> m_Transport; ///< shared physical tunnel port
|
||||
uint16_t m_ChannelId {0}; ///< default logical channel id
|
||||
sdv::ipc::IDataReceiveCallback* m_pUpperReceiver {nullptr}; ///< Callback to upper layer (data receive)
|
||||
sdv::ipc::IConnectEventCallback* m_pUpperEvent {nullptr}; ///< Callback to upper layer (state event)
|
||||
mutable std::mutex m_CallbackMtx; ///< Mutex to guard callback access
|
||||
std::mutex m_WatchdogMtx;
|
||||
std::function<void(const void*)> m_WatchdogRemoveCallback;
|
||||
std::atomic<bool> m_DestroyObjectCalled { false };
|
||||
};
|
||||
|
||||
#endif // UNIX_SOCKET_TUNNEL_CONNECTION_H
|
||||
|
||||
58
sdv_services/uds_unix_tunnel/watchdog.cpp
Normal file
58
sdv_services/uds_unix_tunnel/watchdog.cpp
Normal file
@@ -0,0 +1,58 @@
|
||||
/********************************************************************************
|
||||
* Copyright (c) 2025-2026 ZF Friedrichshafen AG
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Apache License Version 2.0 which is available at
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Contributors:
|
||||
* Denisa Ros - initial API and implementation
|
||||
********************************************************************************/
|
||||
|
||||
#if defined(__unix__)
|
||||
|
||||
#include "watchdog.h"
|
||||
|
||||
void CUnixTunnelConnectionWatchDog::AddConnectionImpl(const std::shared_ptr<void>& connection)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_Mutex);
|
||||
m_Connections[connection.get()] = connection;
|
||||
}
|
||||
|
||||
void CUnixTunnelConnectionWatchDog::RemoveConnection(const void* connection)
|
||||
{
|
||||
if (!connection)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::shared_ptr<void> removed;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_Mutex);
|
||||
auto it = m_Connections.find(connection);
|
||||
if (it == m_Connections.end())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
removed = std::move(it->second);
|
||||
m_Connections.erase(it);
|
||||
}
|
||||
|
||||
removed.reset();
|
||||
}
|
||||
|
||||
void CUnixTunnelConnectionWatchDog::Clear()
|
||||
{
|
||||
std::map<const void*, std::shared_ptr<void>> localConnections;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_Mutex);
|
||||
localConnections.swap(m_Connections);
|
||||
}
|
||||
|
||||
localConnections.clear();
|
||||
}
|
||||
|
||||
#endif // defined(__unix__)
|
||||
47
sdv_services/uds_unix_tunnel/watchdog.h
Normal file
47
sdv_services/uds_unix_tunnel/watchdog.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/********************************************************************************
|
||||
* Copyright (c) 2025-2026 ZF Friedrichshafen AG
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Apache License Version 2.0 which is available at
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Contributors:
|
||||
* Denisa Ros - initial API and implementation
|
||||
********************************************************************************/
|
||||
|
||||
#if defined(__unix__)
|
||||
#ifndef UDS_UNIX_TUNNEL_WATCHDOG_H
|
||||
#define UDS_UNIX_TUNNEL_WATCHDOG_H
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
class CUnixTunnelConnectionWatchDog
|
||||
{
|
||||
public:
|
||||
template<typename T>
|
||||
void AddConnection(const std::shared_ptr<T>& connection)
|
||||
{
|
||||
if (!connection)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AddConnectionImpl(std::static_pointer_cast<void>(connection));
|
||||
}
|
||||
|
||||
void RemoveConnection(const void* connection);
|
||||
void Clear();
|
||||
|
||||
private:
|
||||
void AddConnectionImpl(const std::shared_ptr<void>& connection);
|
||||
|
||||
std::mutex m_Mutex;
|
||||
std::map<const void*, std::shared_ptr<void>> m_Connections;
|
||||
};
|
||||
|
||||
#endif // UDS_UNIX_TUNNEL_WATCHDOG_H
|
||||
#endif // defined(__unix__)
|
||||
@@ -12,31 +12,59 @@ if(WIN32)
|
||||
# Define project
|
||||
project(uds_win_sockets VERSION 1.0 LANGUAGES CXX)
|
||||
|
||||
# Define target
|
||||
add_library(uds_win_sockets STATIC
|
||||
# Build sources once and expose them in two forms:
|
||||
# - uds_win_sockets: static link target for tests / other local libraries on MinGW
|
||||
# - uds_win_sockets_module: runtime-loadable .sdv module for ModuleControl
|
||||
set(UDS_WIN_SOCKETS_SOURCES
|
||||
channel_mgnt.cpp
|
||||
connection.cpp
|
||||
watchdog.cpp
|
||||
)
|
||||
|
||||
target_include_directories(uds_win_sockets
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
./include/
|
||||
add_library(uds_win_sockets STATIC
|
||||
${UDS_WIN_SOCKETS_SOURCES}
|
||||
)
|
||||
|
||||
add_library(uds_win_sockets_module SHARED
|
||||
${UDS_WIN_SOCKETS_SOURCES}
|
||||
)
|
||||
|
||||
target_include_directories(uds_win_sockets
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
./include/
|
||||
)
|
||||
|
||||
target_include_directories(uds_win_sockets_module
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
./include/
|
||||
)
|
||||
|
||||
target_link_libraries(uds_win_sockets
|
||||
PUBLIC
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
Ws2_32.lib
|
||||
)
|
||||
|
||||
set_target_properties(uds_win_sockets PROPERTIES PREFIX "")
|
||||
set_target_properties(uds_win_sockets PROPERTIES SUFFIX ".sdv")
|
||||
target_link_libraries(uds_win_sockets_module
|
||||
PUBLIC
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
Ws2_32.lib
|
||||
)
|
||||
|
||||
set_target_properties(uds_win_sockets_module PROPERTIES
|
||||
PREFIX ""
|
||||
SUFFIX ".sdv"
|
||||
OUTPUT_NAME "uds_win_sockets"
|
||||
ARCHIVE_OUTPUT_NAME "uds_win_sockets_module"
|
||||
)
|
||||
|
||||
# Build dependencies
|
||||
add_dependencies(uds_win_sockets CompileCoreIDL)
|
||||
add_dependencies(uds_win_sockets_module CompileCoreIDL)
|
||||
|
||||
# Appending the service in the service list
|
||||
set(SDV_Service_List ${SDV_Service_List} uds_win_sockets PARENT_SCOPE)
|
||||
set(SDV_Service_List ${SDV_Service_List} uds_win_sockets_module PARENT_SCOPE)
|
||||
|
||||
endif()
|
||||
|
||||
@@ -11,15 +11,20 @@
|
||||
* Denisa Ros - initial API and implementation
|
||||
********************************************************************************/
|
||||
#ifdef _WIN32
|
||||
|
||||
#include "channel_mgnt.h"
|
||||
#include "connection.h"
|
||||
|
||||
#include "../../global/base64.h"
|
||||
#include <support/toml.h>
|
||||
#include <interfaces/process.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <future>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <cstring>
|
||||
|
||||
#include "../../global/base64.h"
|
||||
#include <interfaces/app.h>
|
||||
#include <interfaces/process.h>
|
||||
#include <support/local_service_access.h>
|
||||
#include <support/toml.h>
|
||||
|
||||
#pragma push_macro("interface")
|
||||
#undef interface
|
||||
@@ -44,53 +49,23 @@
|
||||
namespace
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Parse a UDS connect/config string and extract the path
|
||||
*
|
||||
* Expected format (substring-based, not strict):
|
||||
* "proto=uds;path=<something>;"
|
||||
*
|
||||
* Behavior:
|
||||
* - If "proto=uds" is missing -> returns false (not a UDS config)
|
||||
* - If "path=" is missing -> returns true and outPath is cleared
|
||||
* - If "path=" is present -> extracts the substring until ';' or end
|
||||
*
|
||||
* @param cs Input configuration / connect string
|
||||
* @param outPath Output: extracted path (possibly empty)
|
||||
* @return true if this looks like a UDS string, false otherwise
|
||||
*/
|
||||
static bool ParseUdsPath(const std::string& cs, std::string& outPath)
|
||||
static bool EnsureWSAInitialized()
|
||||
{
|
||||
constexpr const char* protoKey = "proto=uds";
|
||||
constexpr const char* pathKey = "path=";
|
||||
static std::once_flag s_once;
|
||||
static bool s_ok = false;
|
||||
|
||||
// Must contain "proto=uds" to be considered UDS
|
||||
if (cs.find(protoKey) == std::string::npos)
|
||||
std::call_once(s_once, []()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
WSADATA wsa{};
|
||||
const int rc = WSAStartup(MAKEWORD(2, 2), &wsa);
|
||||
s_ok = (rc == 0);
|
||||
if (!s_ok)
|
||||
{
|
||||
SDV_LOG_ERROR("[AF_UNIX] WSAStartup failed, rc=", rc);
|
||||
}
|
||||
});
|
||||
|
||||
const auto p = cs.find(pathKey);
|
||||
if (p == std::string::npos)
|
||||
{
|
||||
// No path given, but proto=uds is present -> use default later
|
||||
outPath.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto start = p + std::strlen(pathKey);
|
||||
const auto end = cs.find(';', start);
|
||||
|
||||
if (end == std::string::npos)
|
||||
{
|
||||
outPath = cs.substr(start);
|
||||
}
|
||||
else
|
||||
{
|
||||
outPath = cs.substr(start, end - start);
|
||||
}
|
||||
|
||||
return true;
|
||||
return s_ok;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -105,6 +80,7 @@ static bool ParseUdsPath(const std::string& cs, std::string& outPath)
|
||||
*
|
||||
* @return Expanded string, or the original input on failure
|
||||
*/
|
||||
|
||||
static std::string ExpandEnvVars(const std::string& in)
|
||||
{
|
||||
if (in.find('%') == std::string::npos)
|
||||
@@ -112,9 +88,8 @@ static std::string ExpandEnvVars(const std::string& in)
|
||||
return in;
|
||||
}
|
||||
|
||||
char buf[4096] = {};
|
||||
DWORD n = ExpandEnvironmentStringsA(in.c_str(), buf, static_cast<DWORD>(sizeof(buf)));
|
||||
|
||||
char buf[4096] = {};
|
||||
DWORD n = ExpandEnvironmentStringsA(in.c_str(), buf, static_cast<DWORD>(sizeof(buf)));
|
||||
if (n > 0 && n < sizeof(buf))
|
||||
{
|
||||
return std::string(buf);
|
||||
@@ -135,19 +110,181 @@ static std::string ExpandEnvVars(const std::string& in)
|
||||
*
|
||||
* @return A safe pathname guaranteed to fit into sun_path
|
||||
*/
|
||||
|
||||
static std::string ClampUdsPath(const std::string& p)
|
||||
{
|
||||
SOCKADDR_UN tmp{};
|
||||
SOCKADDR_UN tmp{};
|
||||
constexpr auto kMax = sizeof(tmp.sun_path) - 1;
|
||||
|
||||
if (p.size() <= kMax)
|
||||
{
|
||||
return p;
|
||||
}
|
||||
|
||||
return p.substr(0, kMax);
|
||||
}
|
||||
|
||||
/*static std::string BuildFinalUdsPath(const std::string& rawPath)
|
||||
{
|
||||
std::string full = ExpandEnvVars(rawPath);
|
||||
|
||||
auto pos = full.find_last_of("\\/");
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
const std::string dir = full.substr(0, pos);
|
||||
CreateDirectoryA(dir.c_str(), nullptr);
|
||||
}
|
||||
|
||||
return ClampUdsPath(full);
|
||||
}*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Parse a UDS connect/config string and extract the path
|
||||
*
|
||||
* Expected format (substring-based, not strict):
|
||||
* "proto=uds;path=<something>;"
|
||||
*
|
||||
* Behavior:
|
||||
* - If "proto=uds" is missing -> returns false (not a UDS config)
|
||||
* - If "path=" is missing -> returns true and outPath is cleared
|
||||
* - If "path=" is present -> extracts the substring until ';' or end
|
||||
*
|
||||
* @param cs Input configuration / connect string
|
||||
* @param outPath Output: extracted path (possibly empty)
|
||||
* @return true if this looks like a UDS string, false otherwise
|
||||
*/
|
||||
static bool ParseUdsPath(const std::string& cs, std::string& outPath)
|
||||
{
|
||||
constexpr const char* protoKey = "proto=uds";
|
||||
constexpr const char* pathKey = "path=";
|
||||
|
||||
// Strict raw connect string only
|
||||
if (cs.rfind(protoKey, 0) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto p = cs.find(pathKey);
|
||||
if (p == std::string::npos)
|
||||
{
|
||||
outPath.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto start = p + std::strlen(pathKey);
|
||||
const auto end = cs.find(';', start);
|
||||
if (end == std::string::npos)
|
||||
{
|
||||
outPath = cs.substr(start);
|
||||
}
|
||||
else
|
||||
{
|
||||
outPath = cs.substr(start, end - start);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static std::string SanitizeUdsName(std::string name)
|
||||
{
|
||||
for (char& ch : name)
|
||||
{
|
||||
const bool isAlphaNum = (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9');
|
||||
if (!isAlphaNum && ch != '_' && ch != '-')
|
||||
{
|
||||
ch = '_';
|
||||
}
|
||||
}
|
||||
|
||||
if (name.empty())
|
||||
{
|
||||
name = "sdv";
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Build a named UDS raw path using the channel name
|
||||
*
|
||||
* @param[in] channelName The name of the channel
|
||||
*
|
||||
* @return A raw UDS path suitable for further processing
|
||||
*/
|
||||
static std::string BuildNamedUdsRawPath(const std::string& channelName)
|
||||
{
|
||||
return "%LOCALAPPDATA%/sdv/" + SanitizeUdsName(channelName) + ".sock";
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Extract a UDS connect string from either raw UDS format or Provider TOML.
|
||||
*
|
||||
* Supported inputs:
|
||||
* - "proto=uds;path=<...>;"
|
||||
* - [Provider]\nName="unix_domain_sockets"\nConnectString="proto=uds;path=<...>;"
|
||||
*/
|
||||
static bool ExtractUdsConnectString(const std::string& in,
|
||||
std::string& outUdsConnectString)
|
||||
{
|
||||
std::string path;
|
||||
|
||||
// Case 1: strict raw UDS connect string
|
||||
if (ParseUdsPath(in, path))
|
||||
{
|
||||
outUdsConnectString = in;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Do not run the TOML parser on obviously non-TOML garbage
|
||||
//if (!LooksLikeToml(in))
|
||||
//{
|
||||
// return false;
|
||||
//}
|
||||
|
||||
// Case 2: structured TOML
|
||||
sdv::toml::CTOMLParser parser(in);
|
||||
if (!parser.IsValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::string providerName = parser.GetDirect("Provider.Name").GetValue();
|
||||
if (!providerName.empty() &&
|
||||
providerName != "unix_domain_sockets" &&
|
||||
providerName != "WinSocketsChannelControl")
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::string nested = parser.GetDirect("Provider.ConnectString").GetValue();
|
||||
if (!nested.empty())
|
||||
{
|
||||
if (ParseUdsPath(nested, path))
|
||||
{
|
||||
outUdsConnectString = nested;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::string cfgPath = parser.GetDirect("IpcChannel.Path").GetValue();
|
||||
if (!cfgPath.empty())
|
||||
{
|
||||
outUdsConnectString = "proto=uds;path=" + cfgPath + ";";
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::string cfgName = parser.GetDirect("IpcChannel.Name").GetValue();
|
||||
if (!cfgName.empty())
|
||||
{
|
||||
outUdsConnectString = "proto=uds;path=" + BuildNamedUdsRawPath(cfgName) + ";";
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Normalize a UDS path for display/logging purposes
|
||||
*
|
||||
@@ -166,20 +303,18 @@ static std::string ClampUdsPath(const std::string& p)
|
||||
static std::string NormalizeUdsPathForWindows(const std::string& raw)
|
||||
{
|
||||
std::string p = ExpandEnvVars(raw);
|
||||
|
||||
const size_t pos = p.find_last_of("/\\");
|
||||
std::string base = (pos == std::string::npos) ? p : p.substr(pos + 1);
|
||||
|
||||
const size_t pos = p.find_last_of("/\\");
|
||||
std::string base = (pos == std::string::npos) ? p : p.substr(pos + 1);
|
||||
if (base.empty())
|
||||
{
|
||||
base = "sdv.sock";
|
||||
}
|
||||
|
||||
SDV_LOG_INFO("[AF_UNIX] Normalize raw='", raw, "' -> base='", base, "'");
|
||||
|
||||
return ClampUdsPath(base);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Build a short absolute Win32 path suitable for AF_UNIX `sun_path`
|
||||
*
|
||||
@@ -224,6 +359,41 @@ static std::string MakeShortWinUdsPath(const std::string& raw)
|
||||
return ClampUdsPath(full);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Build the default UDS path using the current SDV instance ID.
|
||||
*
|
||||
* Using a single global socket filename makes independently running test
|
||||
* processes trample each other when the build executes multiple post-build
|
||||
* tests in parallel. Namespace the implicit endpoint per instance instead.
|
||||
*/
|
||||
static std::string GetDefaultUdsRawPath()
|
||||
{
|
||||
uint32_t instanceId = 1000u;
|
||||
const sdv::app::IAppContext* pAppContext = sdv::core::GetCore<sdv::app::IAppContext>();
|
||||
if (pAppContext && pAppContext->GetInstanceID() != 0u)
|
||||
{
|
||||
instanceId = pAppContext->GetInstanceID();
|
||||
}
|
||||
|
||||
return "%LOCALAPPDATA%/sdv/vapi_" + std::to_string(instanceId) + ".sock";
|
||||
}
|
||||
|
||||
static std::string GetUniqueEndpointUdsRawPath()
|
||||
{
|
||||
static std::atomic<uint32_t> nextEndpointId { 0u };
|
||||
|
||||
uint32_t instanceId = 1000u;
|
||||
const sdv::app::IAppContext* pAppContext = sdv::core::GetCore<sdv::app::IAppContext>();
|
||||
if (pAppContext && pAppContext->GetInstanceID() != 0u)
|
||||
{
|
||||
instanceId = pAppContext->GetInstanceID();
|
||||
}
|
||||
|
||||
const uint32_t endpointId = nextEndpointId.fetch_add(1u, std::memory_order_relaxed);
|
||||
return "%LOCALAPPDATA%/sdv/vapi_" + std::to_string(instanceId) + "_" +
|
||||
std::to_string(static_cast<uint32_t>(GetCurrentProcessId())) + "_" + std::to_string(endpointId) + ".sock";
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Create a listening AF_UNIX socket on Windows
|
||||
*
|
||||
@@ -239,6 +409,9 @@ static std::string MakeShortWinUdsPath(const std::string& raw)
|
||||
*/
|
||||
static SOCKET CreateUnixListenSocket(const std::string& rawPath)
|
||||
{
|
||||
if (!EnsureWSAInitialized())
|
||||
return INVALID_SOCKET;
|
||||
|
||||
SOCKET s = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (s == INVALID_SOCKET)
|
||||
{
|
||||
@@ -285,118 +458,99 @@ static SOCKET CreateUnixListenSocket(const std::string& rawPath)
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Connect to a Windows AF_UNIX server socket with retry logic
|
||||
*
|
||||
* Repeatedly attempts to connect to the server's UDS path until either:
|
||||
* - connection succeeds, or
|
||||
* - total timeout is exceeded
|
||||
*
|
||||
* On each attempt:
|
||||
* - a new socket() is created
|
||||
* - connect() is attempted
|
||||
* - on failure the socket is closed and retried
|
||||
*
|
||||
* This mirrors Linux AF_UNIX behavior where the client waits for the
|
||||
* server's socket file to appear/become ready
|
||||
*
|
||||
* @param[in] rawPath Raw UDS path from configuration
|
||||
* @param[in] totalTimeoutMs Maximum total wait time in milliseconds
|
||||
* @param[in] retryDelayMs Delay between retries in milliseconds
|
||||
*
|
||||
* @return Connected SOCKET on success, INVALID_SOCKET on timeout or error
|
||||
*/
|
||||
static SOCKET ConnectUnixSocket(
|
||||
const std::string& rawPath,
|
||||
uint32_t totalTimeoutMs,
|
||||
uint32_t retryDelayMs)
|
||||
/*static SOCKET ConnectUnixSocket(const std::string& rawPath,
|
||||
uint32_t totalTimeoutMs,
|
||||
uint32_t retryDelayMs)
|
||||
{
|
||||
const std::string udsPath = MakeShortWinUdsPath(rawPath);
|
||||
if (!EnsureWSAInitialized())
|
||||
{
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
|
||||
const std::string udsPath = BuildFinalUdsPath(rawPath);
|
||||
|
||||
SOCKADDR_UN addr{};
|
||||
addr.sun_family = AF_UNIX;
|
||||
strcpy_s(addr.sun_path, sizeof(addr.sun_path), udsPath.c_str());
|
||||
|
||||
const int addrlen = static_cast<int>(
|
||||
offsetof(SOCKADDR_UN, sun_path) + std::strlen(addr.sun_path) + 1
|
||||
);
|
||||
offsetof(SOCKADDR_UN, sun_path) + std::strlen(addr.sun_path) + 1);
|
||||
|
||||
const auto deadline = std::chrono::steady_clock::now() +
|
||||
std::chrono::milliseconds(totalTimeoutMs);
|
||||
auto deadline =
|
||||
std::chrono::steady_clock::now() + std::chrono::milliseconds(totalTimeoutMs);
|
||||
|
||||
while (true)
|
||||
{
|
||||
SOCKET s = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (s == INVALID_SOCKET)
|
||||
{
|
||||
SDV_LOG_ERROR("[AF_UNIX] socket() FAIL (client), WSA=", WSAGetLastError());
|
||||
SDV_LOG_ERROR("[AF_UNIX] socket FAIL (client), WSA=", WSAGetLastError());
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
|
||||
if (connect(s, reinterpret_cast<const sockaddr*>(&addr), addrlen) == 0)
|
||||
{
|
||||
SDV_LOG_INFO("[AF_UNIX] connect OK (pathname), path='", udsPath, "'");
|
||||
SDV_LOG_INFO("[AF_UNIX] connect OK: ", udsPath);
|
||||
return s;
|
||||
}
|
||||
|
||||
int lastError = WSAGetLastError();
|
||||
const int err = WSAGetLastError();
|
||||
closesocket(s);
|
||||
|
||||
if (std::chrono::steady_clock::now() >= deadline)
|
||||
{
|
||||
SDV_LOG_ERROR(
|
||||
"[AF_UNIX] connect TIMEOUT (pathname), last WSA=",
|
||||
lastError, ", path='", udsPath, "'"
|
||||
);
|
||||
SDV_LOG_ERROR("[AF_UNIX] connect TIMEOUT, WSA=", err, ", path=", udsPath);
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(retryDelayMs));
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
bool CSocketsChannelMgnt::OnInitialize()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void CSocketsChannelMgnt::OnServerClosed(const std::string& udsPath, CWinsockConnection* ptr)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_udsMtx);
|
||||
|
||||
auto it = m_udsServers.find(udsPath);
|
||||
if (it != m_udsServers.end() && it->second.get() == ptr)
|
||||
{
|
||||
// Remove the server entry only if it matches the pointer we know
|
||||
m_udsServers.erase(it);
|
||||
}
|
||||
|
||||
// Mark this UDS path as no longer claimed
|
||||
m_udsServerClaimed.erase(udsPath);
|
||||
return EnsureWSAInitialized();
|
||||
}
|
||||
|
||||
void CSocketsChannelMgnt::OnShutdown()
|
||||
{}
|
||||
{
|
||||
}
|
||||
|
||||
void CSocketsChannelMgnt::OnDestroy()
|
||||
{
|
||||
m_watchdog.Clear();
|
||||
}
|
||||
|
||||
sdv::ipc::SChannelEndpoint CSocketsChannelMgnt::CreateEndpoint(const sdv::u8string& cfgStr)
|
||||
{
|
||||
// Ensure WinSock is initialized on Windows
|
||||
if (StartUpWinSock() != 0)
|
||||
{
|
||||
// If WinSock cannot be initialized, we cannot create an endpoint
|
||||
return {};
|
||||
}
|
||||
|
||||
// Parse UDS path from config. If proto!=uds, we still default to UDS
|
||||
std::string udsRaw;
|
||||
bool udsRequested = ParseUdsPath(cfgStr, udsRaw);
|
||||
|
||||
if (!udsRequested || udsRaw.empty())
|
||||
{
|
||||
// Default path if not provided or not UDS-specific
|
||||
udsRaw = "%LOCALAPPDATA%/sdv/vapi.sock";
|
||||
sdv::toml::CTOMLParser parser(cfgStr);
|
||||
const std::string cfgPath = parser.GetDirect("IpcChannel.Path").GetValue();
|
||||
const std::string cfgName = parser.GetDirect("IpcChannel.Name").GetValue();
|
||||
|
||||
if (!cfgPath.empty())
|
||||
{
|
||||
udsRaw = cfgPath;
|
||||
}
|
||||
else if (!cfgName.empty())
|
||||
{
|
||||
udsRaw = BuildNamedUdsRawPath(cfgName);
|
||||
}
|
||||
else if (cfgStr.empty())
|
||||
{
|
||||
udsRaw = GetUniqueEndpointUdsRawPath();
|
||||
}
|
||||
else
|
||||
{
|
||||
udsRaw = GetDefaultUdsRawPath();
|
||||
}
|
||||
}
|
||||
|
||||
std::string udsPath = NormalizeUdsPathForWindows(udsRaw);
|
||||
@@ -411,75 +565,81 @@ sdv::ipc::SChannelEndpoint CSocketsChannelMgnt::CreateEndpoint(const sdv::u8stri
|
||||
|
||||
// Server-side CWinsockConnection, it will accept() a client on first use
|
||||
auto server = std::make_shared<CWinsockConnection>(listenSocket, /*acceptRequired*/ true);
|
||||
|
||||
// Ignore cppcheck warning; if construction failed, an exception is expected first.
|
||||
// cppcheck-suppress knownConditionTrueFalse
|
||||
if (!server)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_udsMtx);
|
||||
m_udsServers[udsPath] = server;
|
||||
m_udsServerClaimed.erase(udsPath);
|
||||
return {};
|
||||
}
|
||||
server->SetWatchDogRemoveCallback([this](const void* connection)
|
||||
{
|
||||
m_watchdog.RemoveConnection(connection);
|
||||
});
|
||||
m_watchdog.AddConnection(server);
|
||||
|
||||
sdv::ipc::SChannelEndpoint ep{};
|
||||
ep.pConnection = static_cast<IInterfaceAccess*>(server.get());
|
||||
ep.ssConnectString = "proto=uds;path=" + udsPath + ";";
|
||||
ep.pConnection = static_cast<IInterfaceAccess*>(server.get());
|
||||
|
||||
// Keep compatibility with CCommunicationControl::CreateClientConnection,
|
||||
// which expects a Provider TOML section with Provider.Name.
|
||||
const std::string udsConnectString = "proto=uds;path=" + udsPath + ";";
|
||||
ep.ssConnectString = udsConnectString;
|
||||
|
||||
return ep;
|
||||
}
|
||||
|
||||
sdv::IInterfaceAccess* CSocketsChannelMgnt::Access(const sdv::u8string& cs)
|
||||
{
|
||||
// Ensure WinSock is initialized
|
||||
if (StartUpWinSock() != 0)
|
||||
std::string udsConnectString;
|
||||
if (!ExtractUdsConnectString(cs, udsConnectString))
|
||||
{
|
||||
// Not a UDS connect string / provider description
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string udsRaw;
|
||||
if (!ParseUdsPath(cs, udsRaw))
|
||||
{
|
||||
// Not a UDS connect string
|
||||
return nullptr;
|
||||
}
|
||||
ParseUdsPath(udsConnectString, udsRaw);
|
||||
|
||||
if (udsRaw.empty())
|
||||
{
|
||||
udsRaw = "%LOCALAPPDATA%/sdv/vapi.sock";
|
||||
udsRaw = GetDefaultUdsRawPath();
|
||||
}
|
||||
|
||||
std::string udsPath = NormalizeUdsPathForWindows(udsRaw);
|
||||
SDV_LOG_INFO("[AF_UNIX] Access udsPath=", udsPath);
|
||||
|
||||
const bool isServer = (udsConnectString.find("role=server") != std::string::npos);
|
||||
std::shared_ptr<CWinsockConnection> connection;
|
||||
if (isServer)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_udsMtx);
|
||||
|
||||
auto it = m_udsServers.find(udsPath);
|
||||
if (it != m_udsServers.end() && it->second != nullptr)
|
||||
SOCKET listenSocket = CreateUnixListenSocket(udsPath);
|
||||
if (listenSocket == INVALID_SOCKET)
|
||||
{
|
||||
// Return the server-side object only once for this UDS path
|
||||
if (!m_udsServerClaimed.count(udsPath))
|
||||
{
|
||||
m_udsServerClaimed.insert(udsPath);
|
||||
SDV_LOG_INFO("[AF_UNIX] Access -> RETURN SERVER for ", udsPath);
|
||||
return it->second.get(); // server object (acceptRequired=true)
|
||||
}
|
||||
// Otherwise, later calls will create a client socket below
|
||||
return nullptr;
|
||||
}
|
||||
connection = std::make_shared<CWinsockConnection>(listenSocket, /*acceptRequired*/ true);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Client-side endpoint object. The actual socket connect is deferred to AsyncConnect,
|
||||
// matching the semantic contract used by ipc_com and shared memory.
|
||||
connection = std::make_shared<CWinsockConnection>(udsPath);
|
||||
}
|
||||
|
||||
// CLIENT: create a socket connected to the same udsPath
|
||||
SOCKET s = ConnectUnixSocket(udsPath,
|
||||
/*totalTimeoutMs*/ 5000,
|
||||
/*retryDelayMs*/ 50);
|
||||
|
||||
if (s == INVALID_SOCKET)
|
||||
// Ignore cppcheck warning; if construction failed, an exception is expected first.
|
||||
// cppcheck-suppress knownConditionTrueFalse
|
||||
if (!connection)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SDV_LOG_INFO("[AF_UNIX] Access -> CREATE CLIENT for ", udsPath);
|
||||
connection->SetWatchDogRemoveCallback([this](const void* instance)
|
||||
{
|
||||
m_watchdog.RemoveConnection(instance);
|
||||
});
|
||||
|
||||
// Client-side connection object (acceptRequired=false)
|
||||
// Ownership is transferred to the caller (VAPI runtime)
|
||||
return new CWinsockConnection(s, /*acceptRequired*/ false);
|
||||
m_watchdog.AddConnection(connection);
|
||||
return static_cast<IInterfaceAccess*>(connection.get());
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -18,16 +18,14 @@
|
||||
#include <support/component_impl.h>
|
||||
#include <interfaces/ipc.h>
|
||||
#include "connection.h"
|
||||
#include "watchdog.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <map>
|
||||
#include <unordered_set>
|
||||
#include <mutex>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#ifdef _WIN32
|
||||
// Winsock headers are required for SOCKET / AF_UNIX / WSAStartup
|
||||
// NOTE: The actual initialization is done via StartUpWinSock()
|
||||
# include <ws2tcpip.h>
|
||||
#endif
|
||||
|
||||
@@ -77,40 +75,6 @@ struct CAddrInfo
|
||||
addrinfo* AddressInfo { nullptr };
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Initialize WinSock on Windows (idempotent)
|
||||
*
|
||||
* This helper ensures WSAStartup() is called only once in the process
|
||||
*
|
||||
* @return 0 on success, otherwise a WinSock error code
|
||||
*/
|
||||
inline int StartUpWinSock()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
static bool isInitialized = false;
|
||||
if (isInitialized)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
WSADATA wsaData {};
|
||||
const int error = WSAStartup(MAKEWORD(2, 2), &wsaData);
|
||||
if (error != 0)
|
||||
{
|
||||
SDV_LOG_ERROR("WSAStartup failed with error: ", std::to_string(error));
|
||||
}
|
||||
else
|
||||
{
|
||||
SDV_LOG_INFO("WSAStartup initialized");
|
||||
isInitialized = true;
|
||||
}
|
||||
return error;
|
||||
#else
|
||||
// Non-Windows: nothing to do. Return success for symmetry
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Simple pair of sockets used to connect two child processes
|
||||
*
|
||||
@@ -147,8 +111,8 @@ public:
|
||||
// Object declarations
|
||||
DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::system_object)
|
||||
DECLARE_OBJECT_CLASS_NAME("WinSocketsChannelControl")
|
||||
DECLARE_OBJECT_CLASS_ALIAS("LocalChannelControl")
|
||||
DECLARE_DEFAULT_OBJECT_NAME("LocalChannelControl")
|
||||
DECLARE_OBJECT_CLASS_ALIAS("unix_domain_sockets")
|
||||
DECLARE_DEFAULT_OBJECT_NAME("unix_domain_sockets")
|
||||
DECLARE_OBJECT_SINGLETON()
|
||||
|
||||
virtual ~CSocketsChannelMgnt() = default;
|
||||
@@ -164,6 +128,11 @@ public:
|
||||
*/
|
||||
virtual void OnShutdown() override;
|
||||
|
||||
/**
|
||||
* @brief Last function called before destruction. Overload of sdv::CSdvObject::OnDestroy.
|
||||
*/
|
||||
virtual void OnDestroy() override;
|
||||
|
||||
/**
|
||||
* @brief Create an IPC endpoint and return its connection information
|
||||
*
|
||||
@@ -208,29 +177,8 @@ public:
|
||||
*/
|
||||
sdv::IInterfaceAccess* Access(const sdv::u8string& ssConnectString) override;
|
||||
|
||||
/**
|
||||
* @brief Called by a CWinsockConnection instance when the server side is closed
|
||||
*
|
||||
* Used to clean up internal registries for a given UDS path
|
||||
*
|
||||
* @param ptr Pointer to the CWinsockConnection instance that was closed
|
||||
*/
|
||||
void OnServerClosed(const std::string& udsPath, CWinsockConnection* ptr);
|
||||
|
||||
private:
|
||||
/// @brief Registry of AF_UNIX server connections keyed by normalized UDS path
|
||||
std::map<std::string, std::shared_ptr<CWinsockConnection>> m_udsServers;
|
||||
|
||||
/**
|
||||
* @brief Set of UDS paths that already returned their server-side
|
||||
* connection once via Access()
|
||||
*
|
||||
* This prevents returning the same server object multiple times
|
||||
*/
|
||||
std::unordered_set<std::string> m_udsServerClaimed;
|
||||
|
||||
/// @brief Mutex protecting m_udsServers and m_udsServerClaimed
|
||||
std::mutex m_udsMtx;
|
||||
CWinSocketsConnectionWatchDog m_watchdog;
|
||||
};
|
||||
|
||||
// SDV object factory macro
|
||||
|
||||
@@ -17,11 +17,92 @@
|
||||
#include <WS2tcpip.h>
|
||||
#include <cstring>
|
||||
#include <chrono>
|
||||
#include <afunix.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
static std::string ExpandEnvVarsLocal(const std::string& in)
|
||||
{
|
||||
if (in.find('%') == std::string::npos)
|
||||
{
|
||||
return in;
|
||||
}
|
||||
|
||||
char buf[4096] = {};
|
||||
DWORD n = ExpandEnvironmentStringsA(in.c_str(), buf, static_cast<DWORD>(sizeof(buf)));
|
||||
if (n > 0 && n < sizeof(buf))
|
||||
{
|
||||
return std::string(buf);
|
||||
}
|
||||
|
||||
return in;
|
||||
}
|
||||
|
||||
static std::string ClampUdsPathLocal(const std::string& path)
|
||||
{
|
||||
sockaddr_un tmp{};
|
||||
constexpr auto kMax = sizeof(tmp.sun_path) - 1;
|
||||
return path.size() <= kMax ? path : path.substr(0, kMax);
|
||||
}
|
||||
|
||||
static std::string MakeShortWinUdsPathLocal(const std::string& raw)
|
||||
{
|
||||
std::string path = ExpandEnvVarsLocal(raw);
|
||||
const size_t pos = path.find_last_of("/\\");
|
||||
std::string base = (pos == std::string::npos) ? path : path.substr(pos + 1);
|
||||
|
||||
if (base.empty())
|
||||
{
|
||||
base = "sdv.sock";
|
||||
}
|
||||
|
||||
std::string dir = ExpandEnvVarsLocal("%TEMP%\\sdv\\");
|
||||
CreateDirectoryA(dir.c_str(), nullptr);
|
||||
return ClampUdsPathLocal(dir + base);
|
||||
}
|
||||
|
||||
static SOCKET ConnectUnixSocketLocal(const std::string& rawPath, uint32_t totalTimeoutMs, uint32_t retryDelayMs)
|
||||
{
|
||||
const std::string udsPath = MakeShortWinUdsPathLocal(rawPath);
|
||||
|
||||
sockaddr_un addr{};
|
||||
addr.sun_family = AF_UNIX;
|
||||
strcpy_s(addr.sun_path, sizeof(addr.sun_path), udsPath.c_str());
|
||||
|
||||
const int addrlen = static_cast<int>(offsetof(sockaddr_un, sun_path) + std::strlen(addr.sun_path) + 1);
|
||||
const auto deadline = std::chrono::steady_clock::now() + std::chrono::milliseconds(totalTimeoutMs);
|
||||
|
||||
while (true)
|
||||
{
|
||||
SOCKET s = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (s == INVALID_SOCKET)
|
||||
{
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
|
||||
if (connect(s, reinterpret_cast<const sockaddr*>(&addr), addrlen) == 0)
|
||||
{
|
||||
return s;
|
||||
}
|
||||
|
||||
const int lastError = WSAGetLastError();
|
||||
closesocket(s);
|
||||
|
||||
if (std::chrono::steady_clock::now() >= deadline)
|
||||
{
|
||||
SDV_LOG_ERROR("[AF_UNIX] lazy connect TIMEOUT, last WSA=", lastError, ", path='", udsPath, "'");
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(retryDelayMs));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CWinsockConnection::CWinsockConnection(unsigned long long preconfiguredSocket, bool acceptConnectionRequired)
|
||||
: m_ConnectionState(sdv::ipc::EConnectState::uninitialized)
|
||||
, m_AcceptConnectionRequired(acceptConnectionRequired)
|
||||
, m_CancelWait(false)
|
||||
, m_UdsPath()
|
||||
{
|
||||
// Initialize legacy buffers with zero (kept for potential compatibility)
|
||||
std::fill(std::begin(m_SendBuffer), std::end(m_SendBuffer), '\0');
|
||||
@@ -41,6 +122,17 @@ CWinsockConnection::CWinsockConnection(unsigned long long preconfiguredSocket, b
|
||||
}
|
||||
}
|
||||
|
||||
CWinsockConnection::CWinsockConnection(const std::string& udsPath)
|
||||
: m_ConnectionState(sdv::ipc::EConnectState::uninitialized)
|
||||
, m_AcceptConnectionRequired(false)
|
||||
, m_UdsPath(udsPath)
|
||||
{
|
||||
std::fill(std::begin(m_SendBuffer), std::end(m_SendBuffer), '\0');
|
||||
std::fill(std::begin(m_ReceiveBuffer), std::end(m_ReceiveBuffer), '\0');
|
||||
m_ListenSocket = INVALID_SOCKET;
|
||||
m_ConnectionSocket = INVALID_SOCKET;
|
||||
}
|
||||
|
||||
/*CWinsockConnection::CWinsockConnection()
|
||||
: m_ConnectionState(sdv::ipc::EConnectState::uninitialized)
|
||||
, m_AcceptConnectionRequired(false)
|
||||
@@ -65,28 +157,47 @@ CWinsockConnection::~CWinsockConnection()
|
||||
}
|
||||
}
|
||||
|
||||
void CWinsockConnection::SetWatchDogRemoveCallback(std::function<void(const void*)> callback)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_WatchdogMtx);
|
||||
m_WatchdogRemoveCallback = std::move(callback);
|
||||
}
|
||||
|
||||
void CWinsockConnection::SetConnectState(sdv::ipc::EConnectState state)
|
||||
{
|
||||
sdv::ipc::IConnectEventCallback* pEventLocal = nullptr;
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(m_MtxConnect);
|
||||
m_ConnectionState.store(state, std::memory_order_release);
|
||||
|
||||
// Main receiver has priority over registered state listener
|
||||
if (m_pMainEvent)
|
||||
{
|
||||
pEventLocal = m_pMainEvent;
|
||||
}
|
||||
else
|
||||
{
|
||||
pEventLocal = m_pRegisteredEvent;
|
||||
}
|
||||
}
|
||||
|
||||
// Wake up any waiter
|
||||
m_CvConnect.notify_all();
|
||||
|
||||
// Notify event callback if registered
|
||||
if (m_pEvent)
|
||||
if (pEventLocal)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_pEvent->SetConnectState(state);
|
||||
pEventLocal->SetConnectState(state);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// Ignore callbacks throwing exceptions
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int32_t CWinsockConnection::Send(const char* data, int32_t dataLength)
|
||||
@@ -177,7 +288,7 @@ bool CWinsockConnection::SendData(/*inout*/ sdv::sequence<sdv::pointer<uint8_t>>
|
||||
(kMaxUdsPacketSize - static_cast<uint32_t>(sizeof(SFragmentedMsgHdr))) :
|
||||
0;
|
||||
|
||||
if (maxPayloadFrag == 0)
|
||||
if constexpr (maxPayloadFrag == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -318,9 +429,18 @@ SOCKET CWinsockConnection::AcceptConnection()
|
||||
tv.tv_usec = 50 * 1000; // 50 ms
|
||||
|
||||
const int sr = ::select(0, &rfds, nullptr, nullptr, &tv);
|
||||
|
||||
if (sr == SOCKET_ERROR)
|
||||
{
|
||||
SDV_LOG_ERROR("[AF_UNIX] select(listen) FAIL, WSA=", WSAGetLastError());
|
||||
const int err = WSAGetLastError();
|
||||
|
||||
if (m_StopConnectThread.load() || m_ListenSocket == INVALID_SOCKET)
|
||||
{
|
||||
SDV_LOG_INFO("[AF_UNIX] select(listen) canceled during shutdown, WSA=", err);
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
|
||||
SDV_LOG_ERROR("[AF_UNIX] select(listen) FAIL, WSA=", err);
|
||||
SetConnectState(sdv::ipc::EConnectState::connection_error);
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
@@ -331,9 +451,17 @@ SOCKET CWinsockConnection::AcceptConnection()
|
||||
}
|
||||
|
||||
SOCKET c = ::accept(m_ListenSocket, nullptr, nullptr);
|
||||
|
||||
if (c == INVALID_SOCKET)
|
||||
{
|
||||
const int err = WSAGetLastError();
|
||||
|
||||
if (m_StopConnectThread.load() || m_ListenSocket == INVALID_SOCKET || err == WSAENOTSOCK)
|
||||
{
|
||||
SDV_LOG_INFO("[AF_UNIX] accept canceled during shutdown, WSA=", err);
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
|
||||
if (err == WSAEINTR || err == WSAEWOULDBLOCK)
|
||||
{
|
||||
continue;
|
||||
@@ -355,23 +483,53 @@ SOCKET CWinsockConnection::AcceptConnection()
|
||||
|
||||
bool CWinsockConnection::AsyncConnect(sdv::IInterfaceAccess* pReceiver)
|
||||
{
|
||||
auto expectedState = m_ConnectionState.load(std::memory_order_acquire);
|
||||
while (true)
|
||||
{
|
||||
if (expectedState == sdv::ipc::EConnectState::connected)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (expectedState == sdv::ipc::EConnectState::initializing)
|
||||
{
|
||||
SDV_LOG_WARNING("[AF_UNIX] AsyncConnect ignored: connect worker already running");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_ConnectionState.compare_exchange_weak(
|
||||
expectedState,
|
||||
sdv::ipc::EConnectState::initializing,
|
||||
std::memory_order_acq_rel,
|
||||
std::memory_order_acquire))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Store callbacks
|
||||
m_pReceiver = sdv::TInterfaceAccessPtr(pReceiver).GetInterface<sdv::ipc::IDataReceiveCallback>();
|
||||
m_pEvent = sdv::TInterfaceAccessPtr(pReceiver).GetInterface<sdv::ipc::IConnectEventCallback>();
|
||||
auto* pReceiverIfc = sdv::TInterfaceAccessPtr(pReceiver).GetInterface<sdv::ipc::IDataReceiveCallback>();
|
||||
auto* pEventIfc = sdv::TInterfaceAccessPtr(pReceiver).GetInterface<sdv::ipc::IConnectEventCallback>();
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(m_MtxConnect);
|
||||
m_pReceiver = pReceiverIfc;
|
||||
m_pMainEvent = pEventIfc;
|
||||
}
|
||||
|
||||
// Reset stop flags
|
||||
m_StopReceiveThread.store(false);
|
||||
m_StopConnectThread.store(false);
|
||||
m_CancelWait.store(false);
|
||||
|
||||
// Join old threads if any
|
||||
// Join old workers only after they completed.
|
||||
// Joining an active worker here can block forever when a previous connect is still pending.
|
||||
if (m_ReceiveThread.joinable())
|
||||
m_ReceiveThread.join();
|
||||
if (m_ConnectThread.joinable())
|
||||
m_ConnectThread.join();
|
||||
|
||||
// Start the connect worker
|
||||
m_ConnectThread = std::thread(&CWinsockConnection::ConnectWorker, this);
|
||||
m_ConnectThread = sdv::core::secure_thread(&CWinsockConnection::ConnectWorker, this);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -428,11 +586,16 @@ void CWinsockConnection::Disconnect()
|
||||
|
||||
uint64_t CWinsockConnection::RegisterStateEventCallback(/*in*/ sdv::IInterfaceAccess* pEventCallback)
|
||||
{
|
||||
auto* pEventIfc = sdv::TInterfaceAccessPtr(pEventCallback).GetInterface<sdv::ipc::IConnectEventCallback>();
|
||||
|
||||
// Extract IConnectEventCallback interface
|
||||
m_pEvent = sdv::TInterfaceAccessPtr(pEventCallback).GetInterface<sdv::ipc::IConnectEventCallback>();
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(m_MtxConnect);
|
||||
m_pRegisteredEvent = pEventIfc;
|
||||
}
|
||||
|
||||
// Only one callback is supported; cookie 1 = valid
|
||||
return (m_pEvent != nullptr) ? 1ULL : 0ULL;
|
||||
return (pEventIfc != nullptr) ? 1ULL : 0ULL;
|
||||
}
|
||||
|
||||
void CWinsockConnection::UnregisterStateEventCallback(/*in*/ uint64_t uiCookie)
|
||||
@@ -440,7 +603,9 @@ void CWinsockConnection::UnregisterStateEventCallback(/*in*/ uint64_t uiCookie)
|
||||
// Only one callback supported -> cookie value is 1
|
||||
if (uiCookie == 1ULL)
|
||||
{
|
||||
m_pEvent = nullptr;
|
||||
std::lock_guard<std::mutex> lk(m_MtxConnect);
|
||||
m_pMainEvent = nullptr;
|
||||
m_pRegisteredEvent = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -451,11 +616,38 @@ sdv::ipc::EConnectState CWinsockConnection::GetConnectState() const
|
||||
|
||||
void CWinsockConnection::DestroyObject()
|
||||
{
|
||||
m_StopReceiveThread = true;
|
||||
m_StopConnectThread = true;
|
||||
|
||||
StopThreadsAndCloseSockets();
|
||||
m_ConnectionState = sdv::ipc::EConnectState::disconnected;
|
||||
bool expected = false;
|
||||
if (!m_DestroyObjectCalled.compare_exchange_strong(expected, true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_StopReceiveThread.store(true);
|
||||
m_StopConnectThread.store(true);
|
||||
|
||||
// Optional: notify terminating before releasing callbacks
|
||||
SetConnectState(sdv::ipc::EConnectState::terminating);
|
||||
|
||||
Disconnect();
|
||||
|
||||
// Release callbacks only during final teardown
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(m_MtxConnect);
|
||||
m_pReceiver = nullptr;
|
||||
m_pMainEvent = nullptr;
|
||||
m_pRegisteredEvent = nullptr;
|
||||
}
|
||||
|
||||
std::function<void(const void*)> removeCallback;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_WatchdogMtx);
|
||||
removeCallback = std::move(m_WatchdogRemoveCallback);
|
||||
}
|
||||
if (removeCallback)
|
||||
{
|
||||
removeCallback(this);
|
||||
}
|
||||
}
|
||||
|
||||
void CWinsockConnection::ConnectWorker()
|
||||
@@ -482,16 +674,6 @@ void CWinsockConnection::ConnectWorker()
|
||||
|
||||
if (c == INVALID_SOCKET)
|
||||
{
|
||||
if (m_pEvent)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_pEvent->SetConnectState(m_ConnectionState);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -505,8 +687,22 @@ void CWinsockConnection::ConnectWorker()
|
||||
// CLIENT SIDE
|
||||
if (m_ConnectionSocket == INVALID_SOCKET)
|
||||
{
|
||||
SetConnectState(sdv::ipc::EConnectState::connection_error);
|
||||
return;
|
||||
SetConnectState(sdv::ipc::EConnectState::initializing);
|
||||
|
||||
if (m_UdsPath.empty())
|
||||
{
|
||||
SetConnectState(sdv::ipc::EConnectState::connection_error);
|
||||
return;
|
||||
}
|
||||
|
||||
SOCKET s = ConnectUnixSocketLocal(m_UdsPath, 5000, 50);
|
||||
if (s == INVALID_SOCKET)
|
||||
{
|
||||
SetConnectState(sdv::ipc::EConnectState::connection_error);
|
||||
return;
|
||||
}
|
||||
|
||||
m_ConnectionSocket = s;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -528,7 +724,7 @@ void CWinsockConnection::StartReceiveThread_Unsafe()
|
||||
}
|
||||
|
||||
m_StopReceiveThread.store(false);
|
||||
m_ReceiveThread = std::thread(&CWinsockConnection::ReceiveMessages, this);
|
||||
m_ReceiveThread = sdv::core::secure_thread(&CWinsockConnection::ReceiveMessages, this);
|
||||
}
|
||||
|
||||
void CWinsockConnection::StopThreadsAndCloseSockets()
|
||||
@@ -583,6 +779,7 @@ void CWinsockConnection::StopThreadsAndCloseSockets()
|
||||
SDV_LOG_INFO("[AF_UNIX] StopThreadsAndCloseSockets: closing listen=%llu conn=%llu",
|
||||
static_cast<uint64_t>(l),
|
||||
static_cast<uint64_t>(s));
|
||||
|
||||
}
|
||||
|
||||
bool CWinsockConnection::ReadNumberOfBytes(char* buffer, uint32_t length)
|
||||
@@ -608,7 +805,7 @@ bool CWinsockConnection::ReadNumberOfBytes(char* buffer, uint32_t length)
|
||||
continue;
|
||||
}
|
||||
|
||||
SDV_LOG_WARNING("[UDS][RX] recv() error: ", std::strerror(err));
|
||||
SDV_LOG_WARNING("[UDS][RX] recv() FAIL, WSA=", err);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -742,8 +939,13 @@ bool CWinsockConnection::ReadDataChunk(const CMessage& message, uint32_t offset,
|
||||
#if ENABLE_DECOUPLING > 0
|
||||
// optional queueing path...
|
||||
#else
|
||||
if (m_pReceiver)
|
||||
m_pReceiver->ReceiveData(dataCtx.seqDataChunks);
|
||||
sdv::ipc::IDataReceiveCallback* pReceiverLocal = nullptr;
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(m_MtxConnect);
|
||||
pReceiverLocal = m_pReceiver;
|
||||
}
|
||||
if (pReceiverLocal)
|
||||
pReceiverLocal->ReceiveData(dataCtx.seqDataChunks);
|
||||
dataCtx = SDataContext(); // reset context
|
||||
#endif
|
||||
break;
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <WinSock2.h>
|
||||
@@ -70,11 +72,12 @@ struct SMsgHeader
|
||||
* - sdv::ipc::IConnect : async connect / wait / state / events
|
||||
* - sdv::IObjectDestroy : explicit destruction hook for SDV runtime
|
||||
*/
|
||||
class CWinsockConnection
|
||||
: public sdv::IInterfaceAccess
|
||||
, public sdv::ipc::IDataSend
|
||||
, public sdv::ipc::IConnect
|
||||
, public sdv::IObjectDestroy
|
||||
class CWinsockConnection :
|
||||
//public sdv::CSharedLifetimeControlImpl<CWinsockConnection>, public sdv::ipc::IDataSend, public sdv::ipc::IConnect
|
||||
public sdv::IInterfaceAccess,
|
||||
public sdv::ipc::IDataSend,
|
||||
public sdv::ipc::IConnect,
|
||||
public sdv::IObjectDestroy
|
||||
{
|
||||
public:
|
||||
/**
|
||||
@@ -89,6 +92,12 @@ public:
|
||||
*/
|
||||
CWinsockConnection(unsigned long long preconfiguredSocket, bool acceptConnectionRequired);
|
||||
|
||||
/**
|
||||
* @brief Create a client endpoint that connects lazily in AsyncConnect.
|
||||
* @param[in] udsPath Normalized path for AF_UNIX connection.
|
||||
*/
|
||||
explicit CWinsockConnection(const std::string& udsPath);
|
||||
|
||||
/**
|
||||
* @brief Virtual destructor needed for "delete this;"
|
||||
*/
|
||||
@@ -98,6 +107,7 @@ public:
|
||||
SDV_INTERFACE_ENTRY(sdv::ipc::IDataSend)
|
||||
SDV_INTERFACE_ENTRY(sdv::ipc::IConnect)
|
||||
SDV_INTERFACE_ENTRY(sdv::IObjectDestroy)
|
||||
//SDV_INTERFACE_CHAIN_BASE(sdv::CSharedLifetimeControlImpl<CWinsockConnection>)
|
||||
END_SDV_INTERFACE_MAP()
|
||||
|
||||
/**
|
||||
@@ -188,23 +198,28 @@ public:
|
||||
*/
|
||||
void DestroyObject() override;
|
||||
|
||||
/** @brief Register callback used to remove this connection from manager watchdog storage. */
|
||||
void SetWatchDogRemoveCallback(std::function<void(const void*)> callback);
|
||||
|
||||
private:
|
||||
|
||||
std::mutex m_MtxConnect;
|
||||
std::condition_variable m_CvConnect;
|
||||
std::thread m_ReceiveThread; ///< Thread which receives data from the socket
|
||||
std::thread m_ConnectThread;
|
||||
sdv::core::secure_thread m_ReceiveThread; ///< Thread which receives data from the socket
|
||||
sdv::core::secure_thread m_ConnectThread;
|
||||
std::atomic<bool> m_StopReceiveThread{false}; ///< bool variable to stop thread
|
||||
std::atomic<bool> m_StopConnectThread{false};
|
||||
std::atomic<sdv::ipc::EConnectState> m_ConnectionState; ///< the state of the connection
|
||||
|
||||
sdv::ipc::IDataReceiveCallback* m_pReceiver = nullptr; ///< Receiver to pass the messages if available
|
||||
sdv::ipc::IConnectEventCallback* m_pEvent = nullptr; ///< Event receiver
|
||||
sdv::ipc::IDataReceiveCallback* m_pReceiver{nullptr}; ///< Receiver to pass the messages if available
|
||||
sdv::ipc::IConnectEventCallback* m_pMainEvent{nullptr}; ///< Event receiver
|
||||
sdv::ipc::IConnectEventCallback* m_pRegisteredEvent{nullptr};
|
||||
bool m_AcceptConnectionRequired; ///< if true connection has to be accepted before receive thread can be started
|
||||
mutable std::recursive_mutex m_SendMutex; ///< Synchronize all packages to be send
|
||||
|
||||
SOCKET m_ListenSocket{INVALID_SOCKET}; ///< Server-side listening socket
|
||||
SOCKET m_ConnectionSocket{INVALID_SOCKET}; ///< Active connected socket (client <-> server)
|
||||
std::string m_UdsPath; ///< Client-side AF_UNIX path (lazy connect)
|
||||
|
||||
static constexpr uint32_t m_SendMessageSize{ 1024 }; ///< size for the message to be send
|
||||
static constexpr uint32_t m_SendBufferSize = sizeof(SMsgHeader) + m_SendMessageSize; ///< Initial size of the send buffer
|
||||
@@ -213,6 +228,9 @@ private:
|
||||
uint32_t m_ReceiveBufferLength = sizeof(SMsgHeader); ///< receive buffer length
|
||||
|
||||
std::atomic<bool> m_CancelWait{false};
|
||||
std::mutex m_WatchdogMtx;
|
||||
std::function<void(const void*)> m_WatchdogRemoveCallback;
|
||||
std::atomic<bool> m_DestroyObjectCalled{false};
|
||||
|
||||
/// @brief Server accept loop / client connect confirmation
|
||||
void ConnectWorker();
|
||||
|
||||
58
sdv_services/uds_win_sockets/watchdog.cpp
Normal file
58
sdv_services/uds_win_sockets/watchdog.cpp
Normal file
@@ -0,0 +1,58 @@
|
||||
/********************************************************************************
|
||||
* Copyright (c) 2025-2026 ZF Friedrichshafen AG
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Apache License Version 2.0 which is available at
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Contributors:
|
||||
* Denisa Ros - initial API and implementation
|
||||
********************************************************************************/
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#include "watchdog.h"
|
||||
|
||||
void CWinSocketsConnectionWatchDog::AddConnectionImpl(const std::shared_ptr<void>& connection)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_Mutex);
|
||||
m_Connections[connection.get()] = connection;
|
||||
}
|
||||
|
||||
void CWinSocketsConnectionWatchDog::RemoveConnection(const void* connection)
|
||||
{
|
||||
if (!connection)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::shared_ptr<void> removed;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_Mutex);
|
||||
auto it = m_Connections.find(connection);
|
||||
if (it == m_Connections.end())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
removed = std::move(it->second);
|
||||
m_Connections.erase(it);
|
||||
}
|
||||
|
||||
removed.reset();
|
||||
}
|
||||
|
||||
void CWinSocketsConnectionWatchDog::Clear()
|
||||
{
|
||||
std::map<const void*, std::shared_ptr<void>> localConnections;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_Mutex);
|
||||
localConnections.swap(m_Connections);
|
||||
}
|
||||
|
||||
localConnections.clear();
|
||||
}
|
||||
|
||||
#endif // _WIN32
|
||||
47
sdv_services/uds_win_sockets/watchdog.h
Normal file
47
sdv_services/uds_win_sockets/watchdog.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/********************************************************************************
|
||||
* Copyright (c) 2025-2026 ZF Friedrichshafen AG
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Apache License Version 2.0 which is available at
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Contributors:
|
||||
* Denisa Ros - initial API and implementation
|
||||
********************************************************************************/
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifndef UDS_WIN_SOCKETS_WATCHDOG_H
|
||||
#define UDS_WIN_SOCKETS_WATCHDOG_H
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
class CWinSocketsConnectionWatchDog
|
||||
{
|
||||
public:
|
||||
template<typename T>
|
||||
void AddConnection(const std::shared_ptr<T>& connection)
|
||||
{
|
||||
if (!connection)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AddConnectionImpl(std::static_pointer_cast<void>(connection));
|
||||
}
|
||||
|
||||
void RemoveConnection(const void* connection);
|
||||
void Clear();
|
||||
|
||||
private:
|
||||
void AddConnectionImpl(const std::shared_ptr<void>& connection);
|
||||
|
||||
std::mutex m_Mutex;
|
||||
std::map<const void*, std::shared_ptr<void>> m_Connections;
|
||||
};
|
||||
|
||||
#endif // UDS_WIN_SOCKETS_WATCHDOG_H
|
||||
#endif // _WIN32
|
||||
@@ -8,33 +8,64 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#*******************************************************************************
|
||||
|
||||
if(WIN32)
|
||||
|
||||
# Define project
|
||||
project(uds_win_tunnel VERSION 1.0 LANGUAGES CXX)
|
||||
|
||||
# Define target
|
||||
add_library(uds_win_tunnel STATIC
|
||||
set(UDS_WIN_TUNNEL_SOURCES
|
||||
channel_mgnt.cpp
|
||||
connection.cpp
|
||||
watchdog.cpp
|
||||
)
|
||||
|
||||
# Define targets
|
||||
add_library(uds_win_tunnel STATIC
|
||||
${UDS_WIN_TUNNEL_SOURCES}
|
||||
)
|
||||
|
||||
add_library(uds_win_tunnel_module SHARED
|
||||
${UDS_WIN_TUNNEL_SOURCES}
|
||||
)
|
||||
|
||||
target_link_libraries(uds_win_tunnel
|
||||
PRIVATE
|
||||
uds_win_sockets
|
||||
Ws2_32.lib
|
||||
Ws2_32.lib
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
)
|
||||
|
||||
target_include_directories(uds_win_tunnel
|
||||
PRIVATE
|
||||
target_link_libraries(uds_win_tunnel_module
|
||||
PRIVATE
|
||||
uds_win_sockets
|
||||
Ws2_32.lib
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
)
|
||||
|
||||
target_include_directories(uds_win_tunnel
|
||||
PRIVATE
|
||||
./include/
|
||||
../uds_win_sockets/
|
||||
)
|
||||
|
||||
set_target_properties(uds_win_tunnel PROPERTIES PREFIX "")
|
||||
set_target_properties(uds_win_tunnel PROPERTIES SUFFIX ".sdv")
|
||||
target_include_directories(uds_win_tunnel_module
|
||||
PRIVATE
|
||||
./include/
|
||||
../uds_win_sockets/
|
||||
)
|
||||
|
||||
set_target_properties(uds_win_tunnel_module PROPERTIES
|
||||
PREFIX ""
|
||||
SUFFIX ".sdv"
|
||||
OUTPUT_NAME "uds_win_tunnel"
|
||||
ARCHIVE_OUTPUT_NAME "uds_win_tunnel_module"
|
||||
)
|
||||
|
||||
# Build dependencies
|
||||
add_dependencies(uds_win_tunnel CompileCoreIDL)
|
||||
add_dependencies(uds_win_tunnel_module CompileCoreIDL)
|
||||
|
||||
# Appending the service in the service list
|
||||
set(SDV_Service_List ${SDV_Service_List} uds_win_tunnel PARENT_SCOPE)
|
||||
set(SDV_Service_List ${SDV_Service_List} uds_win_tunnel_module PARENT_SCOPE)
|
||||
|
||||
endif()
|
||||
|
||||
@@ -13,12 +13,14 @@
|
||||
#ifdef _WIN32
|
||||
#include "channel_mgnt.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <future>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include "../../global/base64.h"
|
||||
#include <support/toml.h>
|
||||
#include <interfaces/process.h>
|
||||
|
||||
#include <future>
|
||||
|
||||
#pragma push_macro("interface")
|
||||
#undef interface
|
||||
|
||||
@@ -39,298 +41,498 @@
|
||||
#pragma pop_macro("GetObject")
|
||||
#pragma pop_macro("interface")
|
||||
|
||||
extern int StartUpWinSock();
|
||||
//#include "../sdv_services/uds_win_sockets/channel_mgnt.cpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
/**
|
||||
* @brief Parse a tunnel connect/config string and extract the path.
|
||||
*
|
||||
* Expected format:
|
||||
* "proto=tunnel;path=<something>;"
|
||||
*
|
||||
* Behavior:
|
||||
* - If "proto=tunnel" missing -> false
|
||||
* - If "path=" missing -> true and outPath.clear()
|
||||
*
|
||||
* @param[in] cs The connect/config string to parse.
|
||||
* @param[out] outPath The extracted path, or empty if not found.
|
||||
* @return true if parsing succeeded, false otherwise.
|
||||
*/
|
||||
static bool ParseTunnelPath(const std::string& cs, std::string& outPath)
|
||||
{
|
||||
constexpr const char* protoKey = "proto=tunnel";
|
||||
constexpr const char* pathKey = "path=";
|
||||
|
||||
if (cs.find(protoKey) == std::string::npos)
|
||||
static std::atomic<uint32_t> g_nextChannelId{1};
|
||||
|
||||
static bool EnsureWSAInitialized()
|
||||
{
|
||||
return false;
|
||||
static std::once_flag s_once;
|
||||
static bool s_ok = false;
|
||||
|
||||
std::call_once(s_once, []()
|
||||
{
|
||||
WSADATA wsa{};
|
||||
const int rc = WSAStartup(MAKEWORD(2, 2), &wsa);
|
||||
s_ok = (rc == 0);
|
||||
if (!s_ok)
|
||||
{
|
||||
SDV_LOG_ERROR("[AF_UNIX] WSAStartup failed, rc=", rc);
|
||||
}
|
||||
});
|
||||
|
||||
return s_ok;
|
||||
}
|
||||
|
||||
const auto p = cs.find(pathKey);
|
||||
if (p == std::string::npos)
|
||||
static std::string SanitizeUdsName(std::string name)
|
||||
{
|
||||
outPath.clear();
|
||||
for (char& ch : name)
|
||||
{
|
||||
const bool isAlphaNum = (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9');
|
||||
if (!isAlphaNum && ch != '_' && ch != '-')
|
||||
{
|
||||
ch = '_';
|
||||
}
|
||||
}
|
||||
|
||||
if (name.empty())
|
||||
{
|
||||
name = "sdv";
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
static std::string BuildNamedUdsRawPath(const std::string& channelName)
|
||||
{
|
||||
return "%LOCALAPPDATA%/sdv/" + SanitizeUdsName(channelName) + ".sock";
|
||||
}
|
||||
|
||||
static std::string GetDefaultUdsRawPath()
|
||||
{
|
||||
uint32_t instanceId = 1000u;
|
||||
const sdv::app::IAppContext* pAppContext = sdv::core::GetCore<sdv::app::IAppContext>();
|
||||
if (pAppContext && pAppContext->GetInstanceID() != 0u)
|
||||
{
|
||||
instanceId = pAppContext->GetInstanceID();
|
||||
}
|
||||
|
||||
return "%LOCALAPPDATA%/sdv/vapi_" + std::to_string(instanceId) + ".sock";
|
||||
}
|
||||
|
||||
static std::string GetUniqueEndpointUdsRawPath()
|
||||
{
|
||||
static std::atomic<uint32_t> nextEndpointId { 0u };
|
||||
|
||||
uint32_t instanceId = 1000u;
|
||||
const sdv::app::IAppContext* pAppContext = sdv::core::GetCore<sdv::app::IAppContext>();
|
||||
if (pAppContext && pAppContext->GetInstanceID() != 0u)
|
||||
{
|
||||
instanceId = pAppContext->GetInstanceID();
|
||||
}
|
||||
|
||||
const uint32_t endpointId = nextEndpointId.fetch_add(1u, std::memory_order_relaxed);
|
||||
return "%LOCALAPPDATA%/sdv/vapi_" + std::to_string(instanceId) + "_" +
|
||||
std::to_string(static_cast<uint32_t>(GetCurrentProcessId())) + "_" + std::to_string(endpointId) + ".sock";
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Parse a tunnel connect/config string and extract the path.
|
||||
*
|
||||
* Expected format:
|
||||
* "proto=tunnel;path=<something>;"
|
||||
*
|
||||
* Behavior:
|
||||
* - If "proto=tunnel" missing -> false
|
||||
* - If "path=" missing -> true and outPath.clear()
|
||||
*
|
||||
* @param[in] cs The connect/config string to parse.
|
||||
* @param[out] outPath The extracted path, or empty if not found.
|
||||
* @return true if parsing succeeded, false otherwise.
|
||||
*/
|
||||
static bool ParseTunnelPath(const std::string& cs, std::string& outPath)
|
||||
{
|
||||
constexpr const char* protoKey = "proto=tunnel";
|
||||
constexpr const char* pathKey = "path=";
|
||||
|
||||
// Strict raw connect string only
|
||||
if (cs.rfind(protoKey, 0) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto p = cs.find(pathKey);
|
||||
if (p == std::string::npos)
|
||||
{
|
||||
outPath.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto start = p + std::strlen(pathKey);
|
||||
const auto end = cs.find(';', start);
|
||||
if (end == std::string::npos)
|
||||
{
|
||||
outPath = cs.substr(start);
|
||||
}
|
||||
else
|
||||
{
|
||||
outPath = cs.substr(start, end - start);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto start = p + std::strlen(pathKey);
|
||||
const auto end = cs.find(';', start);
|
||||
if (end == std::string::npos)
|
||||
static std::string ExtractTunnelNameFromToml(sdv::toml::CTOMLParser& parser)
|
||||
{
|
||||
outPath = cs.substr(start);
|
||||
}
|
||||
else
|
||||
{
|
||||
outPath = cs.substr(start, end - start);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
std::string tunnel;
|
||||
|
||||
/**
|
||||
* @brief Expands Windows environment variables in a string (e.g., %TEMP%).
|
||||
* @param[in] in Input string possibly containing environment variables.
|
||||
* @return String with environment variables expanded, or original if expansion fails.
|
||||
*/
|
||||
static std::string ExpandEnvVars(const std::string& in)
|
||||
{
|
||||
if (in.find('%') == std::string::npos)
|
||||
auto ipcTunnelNode = parser.GetDirect("IpcChannel.Tunnel");
|
||||
if (ipcTunnelNode.GetType() == sdv::toml::ENodeType::node_string)
|
||||
{
|
||||
tunnel = static_cast<std::string>(ipcTunnelNode.GetValue());
|
||||
}
|
||||
|
||||
if (tunnel.empty())
|
||||
{
|
||||
auto providerTunnelNode = parser.GetDirect("Provider.Tunnel");
|
||||
if (providerTunnelNode.GetType() == sdv::toml::ENodeType::node_string)
|
||||
{
|
||||
tunnel = static_cast<std::string>(providerTunnelNode.GetValue());
|
||||
}
|
||||
}
|
||||
|
||||
return tunnel;
|
||||
}
|
||||
|
||||
static bool ExtractTunnelConnectString(const std::string& in,
|
||||
std::string& outTunnelConnectString)
|
||||
{
|
||||
std::string path;
|
||||
|
||||
// Case 1: strict raw tunnel connect string
|
||||
if (ParseTunnelPath(in, path))
|
||||
{
|
||||
outTunnelConnectString = in;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Case 2: structured TOML
|
||||
sdv::toml::CTOMLParser parser(in);
|
||||
if (!parser.IsValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::string providerName = parser.GetDirect("Provider.Name").GetValue();
|
||||
if (!providerName.empty() &&
|
||||
providerName != "unix_domain_sockets_tunnel" &&
|
||||
providerName != "WinTunnelChannelControl" &&
|
||||
providerName != "UnixTunnelChannelControl")
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Provider.ConnectString = "proto=tunnel;path=...;tunnel=...;"
|
||||
const std::string nested = parser.GetDirect("Provider.ConnectString").GetValue();
|
||||
if (!nested.empty())
|
||||
{
|
||||
if (ParseTunnelPath(nested, path))
|
||||
{
|
||||
outTunnelConnectString = nested;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Build a tunnel connect string from [IpcChannel]
|
||||
const std::string tunnel = ExtractTunnelNameFromToml(parser);
|
||||
|
||||
const std::string cfgPath = parser.GetDirect("IpcChannel.Path").GetValue();
|
||||
if (!cfgPath.empty())
|
||||
{
|
||||
outTunnelConnectString = "proto=tunnel;path=" + cfgPath + ";";
|
||||
if (!tunnel.empty())
|
||||
{
|
||||
outTunnelConnectString += "tunnel=" + tunnel + ";";
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::string cfgName = parser.GetDirect("IpcChannel.Name").GetValue();
|
||||
if (!cfgName.empty())
|
||||
{
|
||||
outTunnelConnectString = "proto=tunnel;path=" + BuildNamedUdsRawPath(cfgName) + ";";
|
||||
if (!tunnel.empty())
|
||||
{
|
||||
outTunnelConnectString += "tunnel=" + tunnel + ";";
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Expands Windows environment variables in a string (e.g., %TEMP%).
|
||||
* @param[in] in Input string possibly containing environment variables.
|
||||
* @return String with environment variables expanded, or original if expansion fails.
|
||||
*/
|
||||
static std::string ExpandEnvVars(const std::string& in)
|
||||
{
|
||||
if (in.find('%') == std::string::npos)
|
||||
{
|
||||
return in;
|
||||
}
|
||||
char buf[4096] = {};
|
||||
DWORD n = ExpandEnvironmentStringsA(in.c_str(), buf, static_cast<DWORD>(sizeof(buf)));
|
||||
if (n > 0 && n < sizeof(buf))
|
||||
{
|
||||
return std::string(buf);
|
||||
}
|
||||
return in;
|
||||
}
|
||||
char buf[4096] = {};
|
||||
DWORD n = ExpandEnvironmentStringsA(in.c_str(), buf, static_cast<DWORD>(sizeof(buf)));
|
||||
if (n > 0 && n < sizeof(buf))
|
||||
{
|
||||
return std::string(buf);
|
||||
}
|
||||
return in;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Clamps a UDS path to the maximum allowed by SOCKADDR_UN.
|
||||
* @param[in] p The input path.
|
||||
* @return The clamped path.
|
||||
*/
|
||||
static std::string ClampUdsPath(const std::string& p)
|
||||
{
|
||||
SOCKADDR_UN tmp{};
|
||||
constexpr auto kMax = sizeof(tmp.sun_path) - 1;
|
||||
if (p.size() <= kMax)
|
||||
/**
|
||||
* @brief Clamps a UDS path to the maximum allowed by SOCKADDR_UN.
|
||||
* @param[in] p The input path.
|
||||
* @return The clamped path.
|
||||
*/
|
||||
static std::string ClampUdsPath(const std::string& p)
|
||||
{
|
||||
return p;
|
||||
}
|
||||
return p.substr(0, kMax);
|
||||
}
|
||||
|
||||
// Only for logging – basename
|
||||
/**
|
||||
* @brief Normalizes a raw UDS path for Windows, extracting the basename and ensuring a default if empty.
|
||||
* @param[in] raw The raw path string.
|
||||
* @return The normalized basename, clamped to max length.
|
||||
*/
|
||||
static std::string NormalizeUdsPathForWindows(const std::string& raw)
|
||||
{
|
||||
std::string p = ExpandEnvVars(raw);
|
||||
const size_t pos = p.find_last_of("/\\");
|
||||
std::string base = (pos == std::string::npos) ? p : p.substr(pos + 1);
|
||||
if (base.empty())
|
||||
{
|
||||
base = "sdv_tunnel.sock";
|
||||
}
|
||||
SDV_LOG_INFO("[AF_UNIX][Tunnel] Normalize raw='", raw, "' -> base='", base, "'");
|
||||
return ClampUdsPath(base);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Creates a short, safe UDS path in the Windows temp directory.
|
||||
* @param[in] raw The raw path string.
|
||||
* @return The full path in %TEMP%\sdv\, clamped to max length.
|
||||
*/
|
||||
static std::string MakeShortWinUdsPath(const std::string& raw)
|
||||
{
|
||||
std::string p = ExpandEnvVars(raw);
|
||||
const size_t pos = p.find_last_of("/\\");
|
||||
std::string base = (pos == std::string::npos) ? p : p.substr(pos + 1);
|
||||
if (base.empty())
|
||||
{
|
||||
base = "sdv_tunnel.sock";
|
||||
SOCKADDR_UN tmp{};
|
||||
constexpr auto kMax = sizeof(tmp.sun_path) - 1;
|
||||
if (p.size() <= kMax)
|
||||
{
|
||||
return p;
|
||||
}
|
||||
return p.substr(0, kMax);
|
||||
}
|
||||
|
||||
std::string dir = ExpandEnvVars("%TEMP%\\sdv\\");
|
||||
CreateDirectoryA(dir.c_str(), nullptr);
|
||||
const std::string full = dir + base;
|
||||
|
||||
return ClampUdsPath(full);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Creates an AF_UNIX listen socket at the specified path.
|
||||
* @param[in] rawPath The raw path for the socket.
|
||||
* @return The created socket handle, or INVALID_SOCKET on failure.
|
||||
*/
|
||||
static SOCKET CreateUnixListenSocket(const std::string& rawPath)
|
||||
{
|
||||
SOCKET s = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (s == INVALID_SOCKET)
|
||||
static std::string BuildFinalUdsPath(const std::string& rawPath)
|
||||
{
|
||||
SDV_LOG_ERROR("[AF_UNIX][Tunnel] socket() FAIL (listen), WSA=", WSAGetLastError());
|
||||
return INVALID_SOCKET;
|
||||
// Expand environment variables
|
||||
std::string full = ExpandEnvVars(rawPath);
|
||||
|
||||
// Ensure directory exists (parent folder)
|
||||
auto pos = full.find_last_of("\\/");
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
std::string dir = full.substr(0, pos);
|
||||
|
||||
// Ensure immediate parent exists (base/tunnel directory is created earlier)
|
||||
CreateDirectoryA(dir.c_str(), nullptr);
|
||||
}
|
||||
|
||||
// Clamp to AF_UNIX limit
|
||||
return ClampUdsPath(full);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Normalizes a raw UDS path for Windows, extracting the basename and ensuring a default if empty.
|
||||
* @param[in] raw The raw path string.
|
||||
* @return The normalized basename, clamped to max length.
|
||||
*/
|
||||
static std::string NormalizeUdsPathForWindows(const std::string& raw)
|
||||
{
|
||||
std::string p = ExpandEnvVars(raw);
|
||||
const size_t pos = p.find_last_of("/\\");
|
||||
std::string base = (pos == std::string::npos) ? p : p.substr(pos + 1);
|
||||
if (base.empty())
|
||||
{
|
||||
base = "sdv_tunnel.sock";
|
||||
}
|
||||
SDV_LOG_INFO("[AF_UNIX][Tunnel] Normalize raw='", raw, "' -> base='", base, "'");
|
||||
return ClampUdsPath(base);
|
||||
}
|
||||
|
||||
std::string udsPath = MakeShortWinUdsPath(rawPath);
|
||||
SOCKADDR_UN addr{};
|
||||
addr.sun_family = AF_UNIX;
|
||||
strcpy_s(addr.sun_path, sizeof(addr.sun_path), udsPath.c_str());
|
||||
|
||||
const int addrlen = static_cast<int>(
|
||||
offsetof(SOCKADDR_UN, sun_path) + std::strlen(addr.sun_path) + 1);
|
||||
|
||||
::remove(udsPath.c_str());
|
||||
if (bind(s, reinterpret_cast<sockaddr*>(&addr), addrlen) == SOCKET_ERROR)
|
||||
/**
|
||||
* @brief Creates an AF_UNIX listen socket at the specified path.
|
||||
* @param[in] rawPath The raw path for the socket.
|
||||
* @return The created socket handle, or INVALID_SOCKET on failure.
|
||||
*/
|
||||
static SOCKET CreateUnixListenSocket(const std::string& rawPath)
|
||||
{
|
||||
SDV_LOG_ERROR("[AF_UNIX][Tunnel] bind FAIL, WSA=",
|
||||
WSAGetLastError(), ", path='", udsPath, "'");
|
||||
closesocket(s);
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
if (listen(s, SOMAXCONN) == SOCKET_ERROR)
|
||||
{
|
||||
SDV_LOG_ERROR("[AF_UNIX][Tunnel] listen FAIL, WSA=",
|
||||
WSAGetLastError(), ", path='", udsPath, "'");
|
||||
closesocket(s);
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
|
||||
if (!EnsureWSAInitialized())
|
||||
return INVALID_SOCKET;
|
||||
|
||||
SDV_LOG_INFO("[AF_UNIX][Tunnel] bind+listen OK, path='", udsPath, "'");
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Connects to an AF_UNIX socket at the specified path, retrying until timeout.
|
||||
* @param[in] rawPath The raw path to connect to.
|
||||
* @param[in] totalTimeoutMs Total timeout in milliseconds.
|
||||
* @param[in] retryDelayMs Delay between retries in milliseconds.
|
||||
* @return The connected socket handle, or INVALID_SOCKET on failure.
|
||||
*/
|
||||
static SOCKET ConnectUnixSocket(
|
||||
const std::string& rawPath,
|
||||
uint32_t totalTimeoutMs,
|
||||
uint32_t retryDelayMs)
|
||||
{
|
||||
const std::string udsPath = MakeShortWinUdsPath(rawPath);
|
||||
SOCKADDR_UN addr{};
|
||||
addr.sun_family = AF_UNIX;
|
||||
strcpy_s(addr.sun_path, sizeof(addr.sun_path), udsPath.c_str());
|
||||
|
||||
const int addrlen = static_cast<int>(
|
||||
offsetof(SOCKADDR_UN, sun_path) + std::strlen(addr.sun_path) + 1);
|
||||
|
||||
const auto deadline = std::chrono::steady_clock::now() +
|
||||
std::chrono::milliseconds(totalTimeoutMs);
|
||||
|
||||
int lastError = 0;
|
||||
while (true)
|
||||
{
|
||||
SOCKET s = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (s == INVALID_SOCKET)
|
||||
{
|
||||
lastError = WSAGetLastError();
|
||||
SDV_LOG_ERROR("[AF_UNIX][Tunnel] socket() FAIL (client), WSA=", lastError);
|
||||
SDV_LOG_ERROR("[AF_UNIX] socket FAIL (listen), WSA=", WSAGetLastError());
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
|
||||
if (connect(s, reinterpret_cast<const sockaddr*>(&addr), addrlen) == 0)
|
||||
{
|
||||
SDV_LOG_INFO("[AF_UNIX][Tunnel] connect OK, path='", udsPath, "'");
|
||||
return s;
|
||||
}
|
||||
//bulletproof path handling
|
||||
std::string udsPath = BuildFinalUdsPath(rawPath);
|
||||
|
||||
lastError = WSAGetLastError();
|
||||
closesocket(s);
|
||||
SOCKADDR_UN addr{};
|
||||
addr.sun_family = AF_UNIX;
|
||||
strcpy_s(addr.sun_path, sizeof(addr.sun_path), udsPath.c_str());
|
||||
|
||||
if (std::chrono::steady_clock::now() >= deadline)
|
||||
const int addrlen = static_cast<int>(
|
||||
offsetof(SOCKADDR_UN, sun_path) + std::strlen(addr.sun_path) + 1);
|
||||
|
||||
::remove(udsPath.c_str());
|
||||
|
||||
if (bind(s, reinterpret_cast<sockaddr*>(&addr), addrlen) == SOCKET_ERROR)
|
||||
{
|
||||
SDV_LOG_ERROR("[AF_UNIX][Tunnel] connect TIMEOUT, last WSA=",
|
||||
lastError, ", path='", udsPath, "'");
|
||||
int err = WSAGetLastError();
|
||||
SDV_LOG_ERROR("[AF_UNIX] bind FAIL, WSA=", err, ", path=", udsPath);
|
||||
closesocket(s);
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(retryDelayMs));
|
||||
if (listen(s, SOMAXCONN) == SOCKET_ERROR)
|
||||
{
|
||||
int err = WSAGetLastError();
|
||||
SDV_LOG_ERROR("[AF_UNIX] listen FAIL, WSA=", err, ", path=", udsPath);
|
||||
closesocket(s);
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
|
||||
SDV_LOG_INFO("[AF_UNIX] bind+listen OK: ", udsPath);
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
static bool ExtractTunnelName(const std::string& in, std::string& outTunnel)
|
||||
{
|
||||
auto pos = in.find("tunnel=");
|
||||
if (pos == std::string::npos)
|
||||
{
|
||||
outTunnel.clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
pos += 7;
|
||||
auto end = in.find(';', pos);
|
||||
outTunnel = (end == std::string::npos) ? in.substr(pos) : in.substr(pos, end - pos);
|
||||
return !outTunnel.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Connects to an AF_UNIX socket at the specified path, retrying until timeout.
|
||||
* @param[in] rawPath The raw path to connect to.CreateUnixListenSocket
|
||||
* @param[in] totalTimeoutMs Total timeout in milliseconds.
|
||||
* @param[in] retryDelayMs Delay between retries in milliseconds.
|
||||
* @return The connected socket handle, or INVALID_SOCKET on failure.
|
||||
*/
|
||||
static SOCKET ConnectUnixSocket(
|
||||
const std::string& rawPath,
|
||||
uint32_t totalTimeoutMs,
|
||||
uint32_t retryDelayMs)
|
||||
{
|
||||
if (!EnsureWSAInitialized())
|
||||
return INVALID_SOCKET;
|
||||
|
||||
//SAME path logic ca server
|
||||
const std::string udsPath = BuildFinalUdsPath(rawPath);
|
||||
SDV_LOG_INFO("[AF_UNIX][Tunnel] Attempting to connect to ", udsPath, "with rawPath=", rawPath);
|
||||
SOCKADDR_UN addr{};
|
||||
addr.sun_family = AF_UNIX;
|
||||
strcpy_s(addr.sun_path, sizeof(addr.sun_path), udsPath.c_str());
|
||||
|
||||
const int addrlen = static_cast<int>(
|
||||
offsetof(SOCKADDR_UN, sun_path) + std::strlen(addr.sun_path) + 1);
|
||||
|
||||
auto deadline = std::chrono::steady_clock::now() +
|
||||
std::chrono::milliseconds(totalTimeoutMs);
|
||||
|
||||
while (true)
|
||||
{
|
||||
SOCKET s = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (s == INVALID_SOCKET)
|
||||
{
|
||||
SDV_LOG_ERROR("[AF_UNIX] socket FAIL (client), WSA=", WSAGetLastError());
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
|
||||
if (connect(s, reinterpret_cast<const sockaddr*>(&addr), addrlen) == 0)
|
||||
{
|
||||
SDV_LOG_INFO("[AF_UNIX] connect OK: ", udsPath);
|
||||
return s;
|
||||
}
|
||||
|
||||
int err = WSAGetLastError();
|
||||
closesocket(s);
|
||||
|
||||
if (std::chrono::steady_clock::now() >= deadline)
|
||||
{
|
||||
SDV_LOG_ERROR("[AF_UNIX] connect TIMEOUT, WSA=", err, ", path=", udsPath);
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(retryDelayMs));
|
||||
}
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
||||
bool CSocketsTunnelChannelMgnt::OnInitialize()
|
||||
{
|
||||
return true;
|
||||
return EnsureWSAInitialized();
|
||||
}
|
||||
|
||||
void CSocketsTunnelChannelMgnt::OnShutdown()
|
||||
{}
|
||||
|
||||
// -------- Server bookkeeping (optional) --------
|
||||
void CSocketsTunnelChannelMgnt::OnServerClosed(const std::string& udsPath, CWinTunnelConnection* ptr)
|
||||
void CSocketsTunnelChannelMgnt::OnDestroy()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_udsMtx);
|
||||
auto it = m_udsServers.find(udsPath);
|
||||
if (it != m_udsServers.end() && it->second.get() == ptr)
|
||||
{
|
||||
m_udsServers.erase(it);
|
||||
}
|
||||
m_udsServerClaimed.erase(udsPath);
|
||||
m_watchdog.Clear();
|
||||
}
|
||||
|
||||
// -------- ICreateEndpoint --------
|
||||
|
||||
// Note:
|
||||
// tunnel name is required by the current Windows tunnel design for namespace isolation.
|
||||
// [IpcChannel.Path]/[IpcChannel.Name] alone are not sufficient unless Tunnel is also provided.
|
||||
sdv::ipc::SChannelEndpoint CSocketsTunnelChannelMgnt::CreateEndpoint(const sdv::u8string& cfgStr)
|
||||
{
|
||||
sdv::ipc::SChannelEndpoint ep{};
|
||||
|
||||
if (StartUpWinSock() != 0)
|
||||
{
|
||||
SDV_LOG_ERROR("[AF_UNIX][Tunnel] WinSock startup failed in CreateEndpoint");
|
||||
return ep;
|
||||
}
|
||||
|
||||
// Optional TOML config: [IpcChannel] Path = "..."
|
||||
std::string udsRaw;
|
||||
if (!cfgStr.empty())
|
||||
{
|
||||
//for toml file
|
||||
bool isTOML = cfgStr.find('=') == std::string::npos;
|
||||
if(isTOML)
|
||||
{
|
||||
sdv::toml::CTOMLParser cfg(cfgStr.c_str());
|
||||
auto pathNode = cfg.GetDirect("IpcChannel.Path");
|
||||
if (pathNode.GetType() == sdv::toml::ENodeType::node_string)
|
||||
{
|
||||
udsRaw = static_cast<std::string>(pathNode.GetValue());
|
||||
}
|
||||
}
|
||||
std::string tunnelConnectString;
|
||||
|
||||
//for connect string
|
||||
if (udsRaw.empty())
|
||||
if (ExtractTunnelConnectString(cfgStr, tunnelConnectString))
|
||||
{
|
||||
ParseTunnelPath(tunnelConnectString, udsRaw);
|
||||
}
|
||||
else if (!cfgStr.empty())
|
||||
{
|
||||
sdv::toml::CTOMLParser parser(cfgStr);
|
||||
const std::string cfgPath = parser.GetDirect("IpcChannel.Path").GetValue();
|
||||
const std::string cfgName = parser.GetDirect("IpcChannel.Name").GetValue();
|
||||
|
||||
if (!cfgPath.empty())
|
||||
{
|
||||
const std::string s(cfgStr);
|
||||
const std::string key = "path=";
|
||||
auto pos = s.find(key);
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
auto end = s.find(';', pos + key.size());
|
||||
if (end == std::string::npos)
|
||||
udsRaw = s.substr(pos + key.size());
|
||||
else
|
||||
udsRaw = s.substr(pos + key.size(), end - pos - key.size());
|
||||
}
|
||||
udsRaw = cfgPath;
|
||||
}
|
||||
else if (!cfgName.empty())
|
||||
{
|
||||
udsRaw = BuildNamedUdsRawPath(cfgName);
|
||||
}
|
||||
else
|
||||
{
|
||||
udsRaw = GetDefaultUdsRawPath();
|
||||
}
|
||||
}
|
||||
if (udsRaw.empty())
|
||||
{
|
||||
udsRaw = "%LOCALAPPDATA%/sdv/tunnel.sock";
|
||||
udsRaw = cfgStr.empty() ? GetUniqueEndpointUdsRawPath() : GetDefaultUdsRawPath();
|
||||
}
|
||||
|
||||
if (tunnelConnectString.empty())
|
||||
{
|
||||
tunnelConnectString = cfgStr;
|
||||
}
|
||||
|
||||
std::string tunnel;
|
||||
if (!ExtractTunnelName(tunnelConnectString, tunnel))
|
||||
{
|
||||
// fallback for AppConnect (config minimal)
|
||||
sdv::toml::CTOMLParser parser(cfgStr);
|
||||
const std::string cfgName = parser.GetDirect("IpcChannel.Name").GetValue();
|
||||
|
||||
if (!cfgName.empty())
|
||||
{
|
||||
tunnel = cfgName;
|
||||
SDV_LOG_INFO("[AF_UNIX][Tunnel] Using channel name as tunnel name: ", tunnel);
|
||||
}
|
||||
else
|
||||
{
|
||||
// fallback
|
||||
tunnel = "default";
|
||||
SDV_LOG_WARNING("[AF_UNIX][Tunnel] Missing tunnel and channel name, using default");
|
||||
}
|
||||
}
|
||||
|
||||
std::string udsPathBase = NormalizeUdsPathForWindows(udsRaw);
|
||||
std::string base = ExpandEnvVars("%TEMP%\\sdv\\");
|
||||
CreateDirectoryA(base.c_str(), nullptr);
|
||||
|
||||
std::string dir = base + tunnel + "\\";
|
||||
CreateDirectoryA(dir.c_str(), nullptr);
|
||||
|
||||
std::string udsPathBase = dir + NormalizeUdsPathForWindows(udsRaw);
|
||||
|
||||
SDV_LOG_INFO("[AF_UNIX][Tunnel] endpoint udsPath=", udsPathBase);
|
||||
|
||||
SOCKET listenSocket = CreateUnixListenSocket(udsPathBase);
|
||||
@@ -340,75 +542,177 @@ sdv::ipc::SChannelEndpoint CSocketsTunnelChannelMgnt::CreateEndpoint(const sdv::
|
||||
return ep;
|
||||
}
|
||||
|
||||
auto serverTransport = std::make_shared<CWinsockConnection>( static_cast<unsigned long long>(listenSocket), true);
|
||||
auto serverTunnel = std::make_shared<CWinTunnelConnection>(serverTransport, /*channelId*/ static_cast<uint16_t>(0u));
|
||||
|
||||
auto serverTransport = std::make_shared<CWinsockConnection>(static_cast<unsigned long long>(listenSocket), true);
|
||||
uint32_t chId = g_nextChannelId++;
|
||||
auto serverTunnel = std::make_shared<CWinTunnelConnection>(serverTransport, /*channelId*/ static_cast<uint16_t>(chId));
|
||||
// Ignore cppcheck warning; if construction failed, an exception is expected first.
|
||||
// cppcheck-suppress knownConditionTrueFalse
|
||||
if (!serverTunnel)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_udsMtx);
|
||||
m_udsServers[udsPathBase] = serverTunnel;
|
||||
m_udsServerClaimed.erase(udsPathBase);
|
||||
return ep;
|
||||
}
|
||||
// Retain shared_ptr to keep object alive for the duration of the connection.
|
||||
// This ensures shared_from_this() works correctly in DestroyObject().
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_ConnectionsMutex);
|
||||
m_ptrConnections[serverTunnel.get()] = serverTunnel;
|
||||
}
|
||||
|
||||
serverTunnel->SetWatchDogRemoveCallback([this](const void* connection)
|
||||
{
|
||||
m_watchdog.RemoveConnection(connection);
|
||||
// Remove the retained shared_ptr when connection is destroyed
|
||||
std::lock_guard<std::mutex> lock(m_ConnectionsMutex);
|
||||
m_ptrConnections.erase(connection);
|
||||
});
|
||||
|
||||
m_watchdog.AddConnection(serverTunnel);
|
||||
|
||||
ep.pConnection = static_cast<sdv::IInterfaceAccess*>(serverTunnel.get());
|
||||
ep.ssConnectString = "proto=tunnel;role=server;path=" + udsPathBase + ";";
|
||||
|
||||
const std::string clientConnectString = "proto=tunnel;path=" + udsPathBase + ";tunnel=" + tunnel + ";";
|
||||
|
||||
// Publish raw connect string (cleaner and avoids TOML escaping issues on Windows)
|
||||
ep.ssConnectString = clientConnectString;
|
||||
|
||||
return ep;
|
||||
}
|
||||
|
||||
sdv::IInterfaceAccess* CSocketsTunnelChannelMgnt::Access(const sdv::u8string& cs)
|
||||
{
|
||||
if (StartUpWinSock() != 0)
|
||||
std::string tunnelConnectString;
|
||||
const std::string input = static_cast<std::string>(cs);
|
||||
|
||||
// Operational metadata, not part of the tunnel connect string itself
|
||||
const bool isServer = (input.find("role=server") != std::string::npos);
|
||||
|
||||
// Parse only the real connect/config part
|
||||
std::string parseInput = input;
|
||||
const auto rolePos = parseInput.find(";role=");
|
||||
if (rolePos != std::string::npos)
|
||||
{
|
||||
SDV_LOG_ERROR("[AF_UNIX][Tunnel] WinSock startup failed in Access()" );
|
||||
const auto roleEnd = parseInput.find(';', rolePos + 1);
|
||||
if (roleEnd != std::string::npos)
|
||||
{
|
||||
parseInput.erase(rolePos, roleEnd - rolePos + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
parseInput.erase(rolePos);
|
||||
}
|
||||
}
|
||||
|
||||
if (!ExtractTunnelConnectString(parseInput, tunnelConnectString))
|
||||
{
|
||||
SDV_LOG_ERROR("[AF_UNIX][Tunnel] Invalid tunnel connect/config string");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string connectStr = static_cast<std::string>(cs);
|
||||
std::string udsRaw;
|
||||
if (!ParseTunnelPath(connectStr, udsRaw))
|
||||
{
|
||||
SDV_LOG_ERROR("[AF_UNIX][Tunnel] Invalid tunnel connect string: ", connectStr);
|
||||
return nullptr;
|
||||
}
|
||||
ParseTunnelPath(tunnelConnectString, udsRaw);
|
||||
SDV_LOG_INFO("[AF_UNIX][Tunnel] Access requested with connect string: ",
|
||||
tunnelConnectString, ", extracted path: ", udsRaw);
|
||||
|
||||
if (udsRaw.empty())
|
||||
{
|
||||
udsRaw = "%LOCALAPPDATA%/sdv/tunnel.sock";
|
||||
udsRaw = GetDefaultUdsRawPath();
|
||||
SDV_LOG_INFO("[AF_UNIX][Tunnel] No path specified, using default: ", udsRaw);
|
||||
}
|
||||
|
||||
std::string udsPathBase = NormalizeUdsPathForWindows(udsRaw);
|
||||
SDV_LOG_INFO("[AF_UNIX][Tunnel] Access udsPath=", udsPathBase);
|
||||
|
||||
const bool isServer =
|
||||
(connectStr.find("role=server") != std::string::npos);
|
||||
std::string tunnel;
|
||||
|
||||
if (!ExtractTunnelName(tunnelConnectString, tunnel))
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_udsMtx);
|
||||
auto it = m_udsServers.find(udsPathBase);
|
||||
if (isServer && it != m_udsServers.end() && it->second != nullptr)
|
||||
// fallback: derive from filename
|
||||
std::string path;
|
||||
ParseTunnelPath(tunnelConnectString, path);
|
||||
|
||||
// extract filename
|
||||
auto pos = path.find_last_of("/\\");
|
||||
std::string filename = (pos != std::string::npos) ? path.substr(pos + 1) : path;
|
||||
|
||||
// remove ".sock"
|
||||
auto dot = filename.rfind(".sock");
|
||||
if (dot != std::string::npos)
|
||||
{
|
||||
if (!m_udsServerClaimed.count(udsPathBase))
|
||||
{
|
||||
m_udsServerClaimed.insert(udsPathBase);
|
||||
SDV_LOG_INFO("[AF_UNIX][Tunnel] Access -> RETURN SERVER for ", udsPathBase);
|
||||
return it->second.get(); // Ownership: managed by m_udsServers (do not delete)
|
||||
}
|
||||
tunnel = filename.substr(0, dot);
|
||||
}
|
||||
else
|
||||
{
|
||||
tunnel = filename;
|
||||
}
|
||||
|
||||
if (!tunnel.empty())
|
||||
{
|
||||
SDV_LOG_INFO("[AF_UNIX][Tunnel] Derived tunnel from filename: ", tunnel);
|
||||
}
|
||||
}
|
||||
|
||||
// CLIENT: create AF_UNIX client socket and wrap it in a tunnel
|
||||
SOCKET s = ConnectUnixSocket(udsPathBase, /*totalTimeoutMs*/ 5000, /*retryDelayMs*/ 50);
|
||||
if (s == INVALID_SOCKET)
|
||||
if (tunnel.empty())
|
||||
{
|
||||
SDV_LOG_ERROR("[AF_UNIX][Tunnel] Failed to connect client socket for ", udsPathBase);
|
||||
SDV_LOG_ERROR("[AF_UNIX][Tunnel] Missing required tunnel name");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SDV_LOG_INFO("[AF_UNIX][Tunnel] Access -> CREATE CLIENT for ", udsPathBase);
|
||||
auto clientTransport = std::make_shared<CWinsockConnection>(s, /*acceptRequired*/ false);
|
||||
// Ownership: The returned pointer must be managed and deleted by the SDV framework via IObjectDestroy
|
||||
auto* tunnelClient = new CWinTunnelConnection(clientTransport, /*channelId*/ 0u);
|
||||
std::string base = ExpandEnvVars("%TEMP%\\sdv\\");
|
||||
CreateDirectoryA(base.c_str(), nullptr);
|
||||
|
||||
return static_cast<sdv::IInterfaceAccess*>(tunnelClient);
|
||||
std::string dir = base + tunnel + "\\";
|
||||
CreateDirectoryA(dir.c_str(), nullptr);
|
||||
|
||||
std::string udsPathBase = dir + NormalizeUdsPathForWindows(udsRaw);
|
||||
SDV_LOG_INFO("[AF_UNIX][Tunnel] Access udsPath=", udsPathBase);
|
||||
|
||||
std::shared_ptr<CWinTunnelConnection> connection;
|
||||
if (isServer)
|
||||
{
|
||||
SOCKET listenSocket = CreateUnixListenSocket(udsPathBase);
|
||||
if (listenSocket == INVALID_SOCKET)
|
||||
{
|
||||
SDV_LOG_ERROR("[AF_UNIX][Tunnel] Failed to create server socket for ", udsPathBase);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto serverTransport = std::make_shared<CWinsockConnection>(
|
||||
static_cast<unsigned long long>(listenSocket), true);
|
||||
uint32_t chId = g_nextChannelId++;
|
||||
connection = std::make_shared<CWinTunnelConnection>(
|
||||
serverTransport, static_cast<uint16_t>(chId));
|
||||
}
|
||||
else
|
||||
{
|
||||
SOCKET s = ConnectUnixSocket(udsPathBase, 5000, 50);
|
||||
if (s == INVALID_SOCKET)
|
||||
{
|
||||
SDV_LOG_ERROR("[AF_UNIX][Tunnel] Failed to connect client socket for ", udsPathBase);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SDV_LOG_INFO("[AF_UNIX][Tunnel] Access -> CREATE CLIENT for ", udsPathBase);
|
||||
auto clientTransport = std::make_shared<CWinsockConnection>(s, false);
|
||||
uint32_t chId = g_nextChannelId++;
|
||||
connection = std::make_shared<CWinTunnelConnection>(
|
||||
clientTransport, static_cast<uint16_t>(chId));
|
||||
}
|
||||
|
||||
if (!connection)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_ConnectionsMutex);
|
||||
m_ptrConnections[connection.get()] = connection;
|
||||
}
|
||||
|
||||
connection->SetWatchDogRemoveCallback([this](const void* instance)
|
||||
{
|
||||
m_watchdog.RemoveConnection(instance);
|
||||
std::lock_guard<std::mutex> lock(m_ConnectionsMutex);
|
||||
m_ptrConnections.erase(instance);
|
||||
});
|
||||
|
||||
m_watchdog.AddConnection(connection);
|
||||
return static_cast<sdv::IInterfaceAccess*>(connection.get());
|
||||
}
|
||||
#endif
|
||||
@@ -16,18 +16,15 @@
|
||||
|
||||
#include <support/component_impl.h>
|
||||
#include <interfaces/ipc.h>
|
||||
#include "../sdv_services/uds_win_sockets/channel_mgnt.h"
|
||||
#include "../sdv_services/uds_win_sockets/connection.h"
|
||||
#include "connection.h"
|
||||
#include "watchdog.h"
|
||||
|
||||
#include <mutex>
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
||||
// Winsock headers are required for SOCKET / AF_UNIX / WSAStartup
|
||||
// NOTE: The actual initialization is done via StartUpWinSock()
|
||||
#include <ws2tcpip.h>
|
||||
|
||||
class CWinTunnelConnection;
|
||||
@@ -56,8 +53,8 @@ public:
|
||||
|
||||
DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::system_object)
|
||||
DECLARE_OBJECT_CLASS_NAME("WinTunnelChannelControl")
|
||||
DECLARE_OBJECT_CLASS_ALIAS("LocalChannelControl")
|
||||
DECLARE_DEFAULT_OBJECT_NAME("LocalChannelControl")
|
||||
DECLARE_OBJECT_CLASS_ALIAS("unix_domain_sockets_tunnel")
|
||||
DECLARE_DEFAULT_OBJECT_NAME("unix_domain_sockets_tunnel")
|
||||
DECLARE_OBJECT_SINGLETON()
|
||||
|
||||
virtual ~CSocketsTunnelChannelMgnt() = default;
|
||||
@@ -73,6 +70,11 @@ public:
|
||||
*/
|
||||
virtual void OnShutdown() override;
|
||||
|
||||
/**
|
||||
* @brief Last function called before destruction. Overload of sdv::CSdvObject::OnDestroy.
|
||||
*/
|
||||
virtual void OnDestroy() override;
|
||||
|
||||
/**
|
||||
* @brief Creates a tunnel endpoint (server side) and returns endpoint info.
|
||||
* @param[in] cfgStr Optional config string (TOML or connect string).
|
||||
@@ -87,17 +89,10 @@ public:
|
||||
*/
|
||||
sdv::IInterfaceAccess* Access(const sdv::u8string& cs) override;
|
||||
|
||||
/**
|
||||
* @brief Called by server tunnel when closing (bookkeeping).
|
||||
* @param[in] udsPath The UDS path for the server.
|
||||
* @param[in] ptr Pointer to the tunnel connection being closed.
|
||||
*/
|
||||
void OnServerClosed(const std::string& udsPath, CWinTunnelConnection* ptr);
|
||||
|
||||
private:
|
||||
std::mutex m_udsMtx;
|
||||
std::map<std::string, std::shared_ptr<CWinTunnelConnection>> m_udsServers;
|
||||
std::set<std::string> m_udsServerClaimed;
|
||||
CWinTunnelConnectionWatchDog m_watchdog;
|
||||
std::map<const void*, std::shared_ptr<CWinTunnelConnection>> m_ptrConnections; ///< Retain shared_ptr for all connections
|
||||
mutable std::mutex m_ConnectionsMutex; ///< Protect concurrent access to m_ptrConnections
|
||||
};
|
||||
|
||||
DEFINE_SDV_OBJECT(CSocketsTunnelChannelMgnt)
|
||||
|
||||
@@ -24,26 +24,35 @@ CWinTunnelConnection::CWinTunnelConnection(
|
||||
// No additional initialization required; acts as a thin wrapper.
|
||||
}
|
||||
|
||||
bool CWinTunnelConnection::SendData(
|
||||
/*inout*/ sdv::sequence<sdv::pointer<uint8_t>>& seqData)
|
||||
CWinTunnelConnection::~CWinTunnelConnection()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void CWinTunnelConnection::SetWatchDogRemoveCallback(std::function<void(const void*)> callback)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_WatchdogMtx);
|
||||
m_WatchdogRemoveCallback = std::move(callback);
|
||||
}
|
||||
|
||||
bool CWinTunnelConnection::SendData( /*inout*/ sdv::sequence<sdv::pointer<uint8_t>>& seqData)
|
||||
{
|
||||
|
||||
if (!m_Transport)
|
||||
{
|
||||
SDV_LOG_ERROR("[WinTunnel] SendData failed: transport is null");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Build tunnel header buffer
|
||||
sdv::pointer<uint8_t> hdrBuf;
|
||||
hdrBuf.resize(sizeof(STunnelHeader));
|
||||
|
||||
STunnelHeader hdr{};
|
||||
hdr.uiChannelId = m_ChannelId; // Logical channel for this connection
|
||||
hdr.uiFlags = 0; // Reserved for future use
|
||||
hdr.uiChannelId = m_ChannelId;
|
||||
hdr.uiFlags = 0;
|
||||
|
||||
std::memcpy(hdrBuf.get(), &hdr, sizeof(STunnelHeader));
|
||||
|
||||
// Compose new sequence: [header] + original payload chunks
|
||||
sdv::sequence<sdv::pointer<uint8_t>> seqWithHdr;
|
||||
seqWithHdr.push_back(hdrBuf);
|
||||
for (auto& chunk : seqData)
|
||||
@@ -51,11 +60,13 @@ bool CWinTunnelConnection::SendData(
|
||||
seqWithHdr.push_back(chunk);
|
||||
}
|
||||
|
||||
bool result = m_Transport->SendData(seqWithHdr);
|
||||
if (!result) {
|
||||
const bool result = m_Transport->SendData(seqWithHdr);
|
||||
if (!result)
|
||||
{
|
||||
SDV_LOG_ERROR("[WinTunnel] SendData failed in underlying transport");
|
||||
}
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
bool CWinTunnelConnection::AsyncConnect(/*in*/ sdv::IInterfaceAccess* pReceiver)
|
||||
@@ -70,7 +81,7 @@ bool CWinTunnelConnection::AsyncConnect(/*in*/ sdv::IInterfaceAccess* pReceiver)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_CallbackMtx);
|
||||
sdv::TInterfaceAccessPtr acc(pReceiver);
|
||||
m_pUpperReceiver = acc.GetInterface<sdv::ipc::IDataReceiveCallback>();
|
||||
m_pUpperRecv = acc.GetInterface<sdv::ipc::IDataReceiveCallback>();
|
||||
m_pUpperEvent = acc.GetInterface<sdv::ipc::IConnectEventCallback>();
|
||||
}
|
||||
|
||||
@@ -110,14 +121,8 @@ void CWinTunnelConnection::Disconnect()
|
||||
return;
|
||||
}
|
||||
|
||||
// Keep upper callbacks on plain Disconnect(); they are released in DestroyObject().
|
||||
m_Transport->Disconnect();
|
||||
|
||||
// Clear upper-layer callbacks (thread-safe)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_CallbackMtx);
|
||||
m_pUpperReceiver = nullptr;
|
||||
m_pUpperEvent = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t CWinTunnelConnection::RegisterStateEventCallback(
|
||||
@@ -156,55 +161,91 @@ sdv::ipc::EConnectState CWinTunnelConnection::GetConnectState() const
|
||||
|
||||
void CWinTunnelConnection::DestroyObject()
|
||||
{
|
||||
bool expected = false;
|
||||
if (!m_DestroyObjectCalled.compare_exchange_strong(expected, true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Stop forwarding to upper layer before transport teardown.
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_CallbackMtx);
|
||||
m_pUpperRecv = nullptr;
|
||||
m_pUpperEvent = nullptr;
|
||||
}
|
||||
|
||||
// Do not notify upper layer during destruction teardown to avoid races.
|
||||
SetConnectState(sdv::ipc::EConnectState::terminating);
|
||||
|
||||
Disconnect();
|
||||
std::lock_guard<std::mutex> lock(m_CallbackMtx);
|
||||
m_Transport.reset();
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_CallbackMtx);
|
||||
m_Transport.reset();
|
||||
}
|
||||
|
||||
std::function<void(const void*)> removeCallback;
|
||||
{
|
||||
std::lock_guard<std::mutex> watchdogLock(m_WatchdogMtx);
|
||||
removeCallback = std::move(m_WatchdogRemoveCallback);
|
||||
}
|
||||
if (removeCallback)
|
||||
{
|
||||
removeCallback(this);
|
||||
}
|
||||
}
|
||||
|
||||
void CWinTunnelConnection::ReceiveData(
|
||||
/*inout*/ sdv::sequence<sdv::pointer<uint8_t>>& seqData)
|
||||
void CWinTunnelConnection::ReceiveData(/*inout*/ sdv::sequence<sdv::pointer<uint8_t>>& seqData)
|
||||
{
|
||||
// Expect at least one chunk (the tunnel header)
|
||||
|
||||
if (seqData.empty())
|
||||
{
|
||||
SDV_LOG_ERROR("[WinTunnel] ReceiveData: empty sequence");
|
||||
return; // nothing to do
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& hdrChunk = seqData[0];
|
||||
if (hdrChunk.size() < sizeof(STunnelHeader))
|
||||
{
|
||||
SDV_LOG_ERROR("[WinTunnel] ReceiveData: invalid tunnel header size");
|
||||
// Invalid tunnel frame; drop it for now (could set communication_error)
|
||||
return;
|
||||
}
|
||||
|
||||
STunnelHeader hdr{};
|
||||
std::memcpy(&hdr, hdrChunk.get(), sizeof(STunnelHeader));
|
||||
|
||||
// Future demux point:
|
||||
// if (hdr.uiChannelId != m_ChannelId) { ... }
|
||||
|
||||
// TODO: use channelId for multiplexing later
|
||||
|
||||
// Build payload-only sequence: drop header chunk, keep others
|
||||
sdv::sequence<sdv::pointer<uint8_t>> payload;
|
||||
for (size_t i = 1; i < seqData.size(); ++i)
|
||||
{
|
||||
payload.push_back(seqData[i]);
|
||||
}
|
||||
|
||||
if (m_pUpperReceiver)
|
||||
sdv::ipc::IDataReceiveCallback* upper = nullptr;
|
||||
{
|
||||
try {
|
||||
m_pUpperReceiver->ReceiveData(payload); // header stripped
|
||||
} catch (...) {
|
||||
SDV_LOG_ERROR("[WinTunnel] Exception in upper receiver's ReceiveData");
|
||||
std::lock_guard<std::mutex> lock(m_CallbackMtx);
|
||||
upper = m_pUpperRecv;
|
||||
}
|
||||
|
||||
if (upper)
|
||||
{
|
||||
try
|
||||
{
|
||||
upper->ReceiveData(payload);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
SDV_LOG_ERROR("[WinTunnel] Exception in upper receiver callback");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void CWinTunnelConnection::SetConnectState(sdv::ipc::EConnectState state)
|
||||
{
|
||||
sdv::ipc::IConnectEventCallback* upper = nullptr;
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_CallbackMtx);
|
||||
upper = m_pUpperEvent;
|
||||
@@ -218,8 +259,7 @@ void CWinTunnelConnection::SetConnectState(sdv::ipc::EConnectState state)
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
SDV_LOG_ERROR("[WinTunnel] Exception in upper event callback's SetConnectState");
|
||||
// Never let user callback crash the transport.
|
||||
SDV_LOG_ERROR("[WinTunnel] Exception in upper event callback");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,10 +21,12 @@
|
||||
#include <WinSock2.h>
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#include <cstring>
|
||||
|
||||
#include "../sdv_services/uds_win_sockets/connection.h" // existing AF_UNIX transport: CWinsockConnection
|
||||
|
||||
@@ -69,7 +71,7 @@ public:
|
||||
/**
|
||||
* @brief Destructor.
|
||||
*/
|
||||
virtual ~CWinTunnelConnection() = default;
|
||||
virtual ~CWinTunnelConnection();
|
||||
|
||||
BEGIN_SDV_INTERFACE_MAP()
|
||||
SDV_INTERFACE_ENTRY(sdv::ipc::IDataSend)
|
||||
@@ -153,15 +155,19 @@ public:
|
||||
// Helpers
|
||||
void SetChannelId(uint16_t channelId) { m_ChannelId = channelId; }
|
||||
uint16_t GetChannelId() const noexcept { return m_ChannelId; }
|
||||
void SetWatchDogRemoveCallback(std::function<void(const void*)> callback);
|
||||
|
||||
private:
|
||||
std::shared_ptr<CWinsockConnection> m_Transport; ///< shared physical tunnel port
|
||||
uint16_t m_ChannelId{0}; ///< default logical channel id
|
||||
|
||||
// Upper layer callbacks (original VAPI receiver)
|
||||
sdv::ipc::IDataReceiveCallback* m_pUpperReceiver{nullptr};
|
||||
sdv::ipc::IDataReceiveCallback* m_pUpperRecv{nullptr};
|
||||
sdv::ipc::IConnectEventCallback* m_pUpperEvent{nullptr};
|
||||
mutable std::mutex m_CallbackMtx;
|
||||
std::mutex m_WatchdogMtx;
|
||||
std::function<void(const void*)> m_WatchdogRemoveCallback;
|
||||
std::atomic<bool> m_DestroyObjectCalled{false};
|
||||
};
|
||||
|
||||
#endif // UDS_WIN_TUNNEL_CONNECTION_H
|
||||
|
||||
58
sdv_services/uds_win_tunnel/watchdog.cpp
Normal file
58
sdv_services/uds_win_tunnel/watchdog.cpp
Normal file
@@ -0,0 +1,58 @@
|
||||
/********************************************************************************
|
||||
* Copyright (c) 2025-2026 ZF Friedrichshafen AG
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Apache License Version 2.0 which is available at
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Contributors:
|
||||
* Denisa Ros - initial API and implementation
|
||||
********************************************************************************/
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#include "watchdog.h"
|
||||
|
||||
void CWinTunnelConnectionWatchDog::AddConnectionImpl(const std::shared_ptr<void>& connection)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_Mutex);
|
||||
m_Connections[connection.get()] = connection;
|
||||
}
|
||||
|
||||
void CWinTunnelConnectionWatchDog::RemoveConnection(const void* connection)
|
||||
{
|
||||
if (!connection)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::shared_ptr<void> removed;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_Mutex);
|
||||
auto it = m_Connections.find(connection);
|
||||
if (it == m_Connections.end())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
removed = std::move(it->second);
|
||||
m_Connections.erase(it);
|
||||
}
|
||||
|
||||
removed.reset();
|
||||
}
|
||||
|
||||
void CWinTunnelConnectionWatchDog::Clear()
|
||||
{
|
||||
std::map<const void*, std::shared_ptr<void>> localConnections;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_Mutex);
|
||||
localConnections.swap(m_Connections);
|
||||
}
|
||||
|
||||
localConnections.clear();
|
||||
}
|
||||
|
||||
#endif // _WIN32
|
||||
47
sdv_services/uds_win_tunnel/watchdog.h
Normal file
47
sdv_services/uds_win_tunnel/watchdog.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/********************************************************************************
|
||||
* Copyright (c) 2025-2026 ZF Friedrichshafen AG
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Apache License Version 2.0 which is available at
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Contributors:
|
||||
* Denisa Ros - initial API and implementation
|
||||
********************************************************************************/
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifndef UDS_WIN_TUNNEL_WATCHDOG_H
|
||||
#define UDS_WIN_TUNNEL_WATCHDOG_H
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
class CWinTunnelConnectionWatchDog
|
||||
{
|
||||
public:
|
||||
template<typename T>
|
||||
void AddConnection(const std::shared_ptr<T>& connection)
|
||||
{
|
||||
if (!connection)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AddConnectionImpl(std::static_pointer_cast<void>(connection));
|
||||
}
|
||||
|
||||
void RemoveConnection(const void* connection);
|
||||
void Clear();
|
||||
|
||||
private:
|
||||
void AddConnectionImpl(const std::shared_ptr<void>& connection);
|
||||
|
||||
std::mutex m_Mutex;
|
||||
std::map<const void*, std::shared_ptr<void>> m_Connections;
|
||||
};
|
||||
|
||||
#endif // UDS_WIN_TUNNEL_WATCHDOG_H
|
||||
#endif // _WIN32
|
||||
Reference in New Issue
Block a user