2026-03-27 14:12:49 +01:00
|
|
|
/********************************************************************************
|
|
|
|
|
* 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
|
|
|
|
|
********************************************************************************/
|
|
|
|
|
|
2025-11-04 13:28:06 +01:00
|
|
|
#include "app_config.h"
|
|
|
|
|
#include "../../global/exec_dir_helper.h"
|
|
|
|
|
#include <interfaces/com.h>
|
|
|
|
|
#include <interfaces/process.h>
|
|
|
|
|
#include <interfaces/ipc.h>
|
|
|
|
|
#include <support/crc.h>
|
2026-03-27 14:12:49 +01:00
|
|
|
#include "toml_parser/parser_toml.h"
|
|
|
|
|
#include <support/toml.h>
|
|
|
|
|
#include "module_control.h"
|
|
|
|
|
#include "repository.h"
|
|
|
|
|
#include "app_settings.h"
|
2025-11-04 13:28:06 +01:00
|
|
|
|
|
|
|
|
#if __unix__
|
|
|
|
|
#include <utime.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
2026-03-27 14:12:49 +01:00
|
|
|
CAppConfig& GetAppConfig()
|
|
|
|
|
{
|
|
|
|
|
static CAppConfig app_config;
|
|
|
|
|
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
|
|
|
|
|
|
2025-11-04 13:28:06 +01:00
|
|
|
bool CAppConfig::LoadInstallationManifests()
|
|
|
|
|
{
|
2026-03-27 14:12:49 +01:00
|
|
|
// Check for allowance
|
|
|
|
|
bool bServerApp = false;
|
|
|
|
|
switch (GetAppSettings().GetContextType())
|
|
|
|
|
{
|
|
|
|
|
case sdv::app::EAppContext::main:
|
|
|
|
|
case sdv::app::EAppContext::isolated:
|
|
|
|
|
case sdv::app::EAppContext::maintenance:
|
|
|
|
|
bServerApp = true;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
2025-11-04 13:28:06 +01:00
|
|
|
|
|
|
|
|
std::filesystem::path pathCore = GetCoreDirectory();
|
|
|
|
|
std::filesystem::path pathExe = GetExecDirectory();
|
2026-03-27 14:12:49 +01:00
|
|
|
std::filesystem::path pathInstall = GetAppSettings().GetInstallDir();
|
2025-11-04 13:28:06 +01:00
|
|
|
|
|
|
|
|
// Load the core manifest (should work in any case).
|
|
|
|
|
m_manifestCore = CInstallManifest();
|
|
|
|
|
if (!m_manifestCore.Load(pathCore)) return false;
|
|
|
|
|
|
|
|
|
|
// Check whether the exe path is identical to the core. If so, skip the manifest. If there is no manifest, this is not an error.
|
|
|
|
|
if (pathCore != pathExe)
|
|
|
|
|
{
|
|
|
|
|
m_manifestExe = CInstallManifest();
|
|
|
|
|
m_manifestExe.Load(pathExe);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
m_manifestExe.Clear();
|
|
|
|
|
|
2026-03-27 14:12:49 +01:00
|
|
|
// Done when not server application.
|
|
|
|
|
if (!bServerApp) return true;
|
|
|
|
|
|
2025-11-04 13:28:06 +01:00
|
|
|
// Get the user manifests. If there is no manifest, this is not an error.
|
2026-03-27 14:12:49 +01:00
|
|
|
if (!pathInstall.empty())
|
2025-11-04 13:28:06 +01:00
|
|
|
{
|
2026-03-27 14:12:49 +01:00
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
for (auto const& dir_entry : std::filesystem::directory_iterator{pathInstall})
|
|
|
|
|
{
|
|
|
|
|
if (!dir_entry.is_directory())
|
|
|
|
|
continue;
|
|
|
|
|
if (!std::filesystem::exists(dir_entry.path() / "install_manifest.toml"))
|
|
|
|
|
continue;
|
|
|
|
|
CInstallManifest manifestUser;
|
|
|
|
|
if (manifestUser.Load(dir_entry.path(), false))
|
|
|
|
|
m_vecUserManifests.push_back(manifestUser);
|
|
|
|
|
}
|
|
|
|
|
} catch (const std::filesystem::filesystem_error& /*rexcept*/)
|
|
|
|
|
{
|
|
|
|
|
SDV_LOG_ERROR("Failed to iterate installation directory: '", pathInstall.generic_u8string(), "'.");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2025-11-04 13:28:06 +01:00
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CAppConfig::UnloadInstallatonManifests()
|
|
|
|
|
{
|
|
|
|
|
m_vecUserManifests.clear();
|
|
|
|
|
m_manifestExe.Clear();
|
|
|
|
|
m_manifestCore.Clear();
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-27 14:12:49 +01:00
|
|
|
bool CAppConfig::LoadAppConfigs()
|
2025-11-04 13:28:06 +01:00
|
|
|
{
|
2026-03-27 14:12:49 +01:00
|
|
|
// Isolated applications do not load configurations
|
|
|
|
|
if (GetAppSettings().IsIsolatedApplication()) return false;
|
2025-11-04 13:28:06 +01:00
|
|
|
|
2026-03-27 14:12:49 +01:00
|
|
|
// When running as server application, load the system configurations
|
|
|
|
|
if (GetAppSettings().IsMainApplication() || GetAppSettings().IsMaintenanceApplication())
|
2025-11-04 13:28:06 +01:00
|
|
|
{
|
2026-03-27 14:12:49 +01:00
|
|
|
m_vecSysConfigs.clear();
|
2025-11-04 13:28:06 +01:00
|
|
|
|
2026-03-27 14:12:49 +01:00
|
|
|
auto vecSysConfigs = GetAppSettings().GetSystemConfigPaths();
|
|
|
|
|
for (const std::filesystem::path& rpathConfig : vecSysConfigs)
|
2025-11-04 13:28:06 +01:00
|
|
|
{
|
2026-03-27 14:12:49 +01:00
|
|
|
CAppConfigFile config(rpathConfig);
|
|
|
|
|
if (!config.IsLoaded())
|
2025-11-04 13:28:06 +01:00
|
|
|
{
|
2026-03-27 14:12:49 +01:00
|
|
|
SDV_LOG_ERROR("Failed to load system configuration '", rpathConfig.generic_u8string(), "'.");
|
|
|
|
|
return false;
|
2025-11-04 13:28:06 +01:00
|
|
|
}
|
|
|
|
|
|
2026-03-27 14:12:49 +01:00
|
|
|
// Start the configuration by the repository
|
|
|
|
|
// cppcheck warns that the following condition is always true. This is incorrect. Suppress the warning.
|
|
|
|
|
// cppcheck-suppress knownConditionTrueFalse
|
|
|
|
|
if (GetRepository().StartFromConfig(config, false) == sdv::core::EConfigProcessResult::failed)
|
2025-11-04 13:28:06 +01:00
|
|
|
{
|
2026-03-27 14:12:49 +01:00
|
|
|
SDV_LOG_ERROR("Failed to start the system configuration '", rpathConfig.generic_u8string(), "'.");
|
|
|
|
|
return false;
|
2025-11-04 13:28:06 +01:00
|
|
|
}
|
2026-03-27 14:12:49 +01:00
|
|
|
|
|
|
|
|
// Loading was successful.
|
|
|
|
|
m_vecSysConfigs.push_back(std::move(config));
|
2025-11-04 13:28:06 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-27 14:12:49 +01:00
|
|
|
// Reset config baseline (all changes from here are logged).
|
|
|
|
|
ResetConfigBaseline();
|
|
|
|
|
|
|
|
|
|
// Load the user configuration
|
|
|
|
|
std::filesystem::path pathUserConfig = GetAppSettings().GetUserConfigPath();
|
|
|
|
|
if (pathUserConfig.empty())
|
|
|
|
|
m_configUserConfig.Clear();
|
|
|
|
|
else
|
2025-11-04 13:28:06 +01:00
|
|
|
{
|
2026-03-27 14:12:49 +01:00
|
|
|
CAppConfigFile config(pathUserConfig);
|
|
|
|
|
if (!config.IsLoaded())
|
2025-11-04 13:28:06 +01:00
|
|
|
{
|
2026-03-27 14:12:49 +01:00
|
|
|
SDV_LOG_ERROR("Failed to load user configuration '", pathUserConfig.generic_u8string(), "'.");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2025-11-04 13:28:06 +01:00
|
|
|
|
2026-03-27 14:12:49 +01:00
|
|
|
// Load the configuration by the repository
|
|
|
|
|
// cppcheck warns that the following condition is always true. This is incorrect. Suppress the warning.
|
|
|
|
|
// cppcheck-suppress knownConditionTrueFalse
|
|
|
|
|
if (GetRepository().StartFromConfig(config, false) == sdv::core::EConfigProcessResult::failed)
|
|
|
|
|
{
|
|
|
|
|
SDV_LOG_ERROR("Failed to start the system configuration '", pathUserConfig.generic_u8string(), "'.");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2025-11-04 13:28:06 +01:00
|
|
|
|
2026-03-27 14:12:49 +01:00
|
|
|
// Loading was successful.
|
|
|
|
|
m_configUserConfig = std::move(config);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CAppConfig::SaveAppConfigs()
|
|
|
|
|
{
|
|
|
|
|
// User configuration is always saved when changed.
|
|
|
|
|
if (m_configUserConfig.IsLoaded() && !m_configUserConfig.SaveConfigFile())
|
|
|
|
|
SDV_LOG_ERROR("Failed to save the user configuration '", m_configUserConfig.ConfigPath(), "'.");
|
|
|
|
|
|
|
|
|
|
// System configurations are only saved when running as maintenance application.
|
|
|
|
|
if (GetAppSettings().IsMaintenanceApplication())
|
|
|
|
|
{
|
|
|
|
|
for (const CAppConfigFile& rconfig : m_vecSysConfigs)
|
|
|
|
|
{
|
|
|
|
|
if (!rconfig.IsLoaded() && !rconfig.SaveConfigFile())
|
|
|
|
|
SDV_LOG_ERROR("Failed to save system configuration '", rconfig.ConfigPath(), "'.");
|
2025-11-04 13:28:06 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-27 14:12:49 +01:00
|
|
|
sdv::core::EConfigProcessResult CAppConfig::ProcessConfig(/*in*/ const sdv::u8string& /*ssContent*/)
|
2025-11-04 13:28:06 +01:00
|
|
|
{
|
2026-03-27 14:12:49 +01:00
|
|
|
// Check for allowance
|
|
|
|
|
switch (GetAppSettings().GetContextType())
|
|
|
|
|
{
|
|
|
|
|
case sdv::app::EAppContext::main:
|
|
|
|
|
case sdv::app::EAppContext::isolated:
|
|
|
|
|
case sdv::app::EAppContext::maintenance:
|
2025-11-04 13:28:06 +01:00
|
|
|
return sdv::core::EConfigProcessResult::failed;
|
2026-03-27 14:12:49 +01:00
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (GetAppControl().GetOperationState() != sdv::app::EAppOperationState::configuring)
|
2025-11-04 13:28:06 +01:00
|
|
|
return sdv::core::EConfigProcessResult::failed;
|
|
|
|
|
|
2026-03-27 14:12:49 +01:00
|
|
|
//// Reset the current baseline
|
|
|
|
|
//ResetConfigBaseline();
|
|
|
|
|
|
|
|
|
|
//toml_parser::CParser parser(ssContent);
|
|
|
|
|
|
|
|
|
|
//// Check for config file compatibility
|
|
|
|
|
//auto ptrConfigVersion = parser.Root().Direct("Configuration.Version");
|
|
|
|
|
//if (!ptrConfigVersion)
|
|
|
|
|
//{
|
|
|
|
|
// SDV_LOG_ERROR("Configuration version must be present in the configuration file.");
|
|
|
|
|
// return sdv::core::EConfigProcessResult::failed;
|
|
|
|
|
//}
|
|
|
|
|
//if (ptrConfigVersion->GetValue() != SDVFrameworkInterfaceVersion)
|
|
|
|
|
//{
|
|
|
|
|
// SDV_LOG_ERROR("Incompatible configuration file version.");
|
|
|
|
|
// return sdv::core::EConfigProcessResult::failed;
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
|
|
//// If this is not a main, isolated or maintenance application, load all modules in the component and module sections.
|
|
|
|
|
//std::map<std::filesystem::path, sdv::core::TModuleID> mapModules;
|
|
|
|
|
//size_t nLoadable = 0;
|
|
|
|
|
//size_t nNotLoadable = 0;
|
|
|
|
|
//if (!GetAppSettings().IsMainApplication() && !GetAppSettings().IsIsolatedApplication() &&
|
|
|
|
|
// !GetAppSettings().IsMaintenanceApplication())
|
|
|
|
|
//{
|
|
|
|
|
// // Load all modules in the component section
|
|
|
|
|
// auto ptrComponents = parser.Root().Direct("Component");
|
|
|
|
|
// if (ptrComponents && ptrComponents->Cast<toml_parser::CArray>())
|
|
|
|
|
// {
|
|
|
|
|
// for (uint32_t uiIndex = 0; uiIndex < ptrComponents->Cast<toml_parser::CArray>()->GetCount(); uiIndex++)
|
|
|
|
|
// {
|
|
|
|
|
// auto ptrComponent = ptrComponents->Cast<toml_parser::CArray>()->Get(uiIndex);
|
|
|
|
|
// if (ptrComponent)
|
|
|
|
|
// {
|
|
|
|
|
// // Get the information from the component.
|
|
|
|
|
// std::filesystem::path pathModule;
|
|
|
|
|
// auto ptrModule = ptrComponent->Direct("Path");
|
|
|
|
|
// if (ptrModule) pathModule = static_cast<std::string>(ptrModule->GetValue());
|
|
|
|
|
|
|
|
|
|
// // If there is no path, this is allowed... skip this module
|
|
|
|
|
// if (pathModule.empty()) continue;
|
|
|
|
|
|
|
|
|
|
// // Load the module.
|
|
|
|
|
// sdv::core::TModuleID tModuleID = GetModuleControl().Load(pathModule.generic_u8string());
|
|
|
|
|
// if (tModuleID)
|
|
|
|
|
// {
|
|
|
|
|
// mapModules[pathModule] = tModuleID;
|
|
|
|
|
// nLoadable++;
|
|
|
|
|
// }
|
|
|
|
|
// else
|
|
|
|
|
// {
|
|
|
|
|
// SDV_LOG_ERROR("Failed to load module: ", pathModule);
|
|
|
|
|
// nNotLoadable++;
|
|
|
|
|
// continue;
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// // Load all modules from the module section.
|
|
|
|
|
// auto ptrModules = parser.Root().Direct("Module");
|
|
|
|
|
// if (ptrModules && ptrModules->Cast<toml_parser::CArray>())
|
|
|
|
|
// {
|
|
|
|
|
// for (uint32_t uiIndex = 0; uiIndex < ptrModules->Cast<toml_parser::CArray>()->GetCount(); uiIndex++)
|
|
|
|
|
// {
|
|
|
|
|
// auto ptrModule = ptrModules->Cast<toml_parser::CArray>()->Get(uiIndex);
|
|
|
|
|
// if (ptrModule)
|
|
|
|
|
// {
|
|
|
|
|
// // Get the information from the component.
|
|
|
|
|
// std::filesystem::path pathModule;
|
|
|
|
|
// auto ptrModulePath = ptrModule->Direct("Path");
|
|
|
|
|
// if (ptrModulePath) pathModule = static_cast<std::string>(ptrModulePath->GetValue());
|
|
|
|
|
|
|
|
|
|
// if (pathModule.empty())
|
|
|
|
|
// {
|
|
|
|
|
// SDV_LOG_ERROR("Missing module path for component configuration.");
|
|
|
|
|
// nNotLoadable++;
|
|
|
|
|
// continue;
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// // Load the module.
|
|
|
|
|
// sdv::core::TModuleID tModuleID = GetModuleControl().Load(pathModule.generic_u8string());
|
|
|
|
|
// if (tModuleID)
|
|
|
|
|
// {
|
|
|
|
|
// mapModules[pathModule] = tModuleID;
|
|
|
|
|
// nLoadable++;
|
|
|
|
|
// }
|
|
|
|
|
// else
|
|
|
|
|
// {
|
|
|
|
|
// SDV_LOG_ERROR("Failed to load module: ", pathModule);
|
|
|
|
|
// nNotLoadable++;
|
|
|
|
|
// continue;
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
|
|
//// Start all components from the component section
|
|
|
|
|
//std::filesystem::path pathLastModule;
|
|
|
|
|
//auto ptrComponents = parser.Root().Direct("Component");
|
|
|
|
|
//if (ptrComponents && ptrComponents->Cast<toml_parser::CArray>())
|
|
|
|
|
//{
|
|
|
|
|
// for (uint32_t uiIndex = 0; uiIndex < ptrComponents->Cast<toml_parser::CArray>()->GetCount(); uiIndex++)
|
|
|
|
|
// {
|
|
|
|
|
// auto ptrComponent = ptrComponents->Cast<toml_parser::CArray>()->Get(uiIndex);
|
|
|
|
|
// if (ptrComponent)
|
|
|
|
|
// {
|
|
|
|
|
// // Get the information from the component.
|
|
|
|
|
// std::filesystem::path pathModule;
|
|
|
|
|
// auto ptrModule = ptrComponent->Direct("Path");
|
|
|
|
|
// if (ptrModule) pathModule = static_cast<std::string>(ptrModule->GetValue());
|
|
|
|
|
// std::string ssClass;
|
|
|
|
|
// auto ptrClass = ptrComponent->Direct("Class");
|
|
|
|
|
// if (ptrClass) ssClass = static_cast<std::string>(ptrClass->GetValue());
|
|
|
|
|
// std::string ssName;
|
|
|
|
|
// auto ptrName = ptrComponent->Direct("Name");
|
|
|
|
|
// if (ptrName) ssName = static_cast<std::string>(ptrName->GetValue());
|
|
|
|
|
|
|
|
|
|
// // If there is a path, store it. If there is none, take the last stored
|
|
|
|
|
// if (!pathModule.empty())
|
|
|
|
|
// pathLastModule = pathModule;
|
|
|
|
|
// else
|
|
|
|
|
// pathModule = pathLastModule;
|
|
|
|
|
|
|
|
|
|
// if (pathModule.empty())
|
|
|
|
|
// {
|
|
|
|
|
// SDV_LOG_ERROR("Missing module path for component configuration.");
|
|
|
|
|
// nNotLoadable++;
|
|
|
|
|
// continue;
|
|
|
|
|
// }
|
|
|
|
|
// if (ssClass.empty())
|
|
|
|
|
// {
|
|
|
|
|
// SDV_LOG_ERROR("Missing component class name in the configuration.");
|
|
|
|
|
// nNotLoadable++;
|
|
|
|
|
// continue;
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// // In case of a main, isolated or maintenance application, ignore the module. In all other cases, the module was
|
|
|
|
|
// // loaded; get the module ID.
|
|
|
|
|
// sdv::core::TObjectID tObjectID = 0;
|
|
|
|
|
// if (!GetAppSettings().IsMainApplication() && !GetAppSettings().IsIsolatedApplication() &&
|
|
|
|
|
// !GetAppSettings().IsMaintenanceApplication())
|
|
|
|
|
// {
|
|
|
|
|
// auto itModule = mapModules.find(pathModule);
|
|
|
|
|
// if (itModule == mapModules.end()) continue; // Module was not loaded before...
|
|
|
|
|
// tObjectID = GetRepository().CreateObjectFromModule(itModule->second, ssClass, ssName, ptrComponent->GenerateTOML());
|
|
|
|
|
// }
|
|
|
|
|
// else
|
|
|
|
|
// tObjectID = GetRepository().CreateObject2(ssClass, ssName, ptrComponent->GenerateTOML());
|
|
|
|
|
|
|
|
|
|
// if (!tObjectID)
|
|
|
|
|
// {
|
|
|
|
|
// SDV_LOG_ERROR("Failed to load component: ", ssClass);
|
|
|
|
|
// nNotLoadable++;
|
|
|
|
|
// continue;
|
|
|
|
|
// }
|
|
|
|
|
// else
|
|
|
|
|
// nLoadable++;
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
//}
|
|
|
|
|
//if (!nNotLoadable)
|
|
|
|
|
// return sdv::core::EConfigProcessResult::successful;
|
|
|
|
|
//if (!nLoadable)
|
|
|
|
|
// return sdv::core::EConfigProcessResult::failed;
|
|
|
|
|
return sdv::core::EConfigProcessResult::partially_successful;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sdv::core::EConfigProcessResult CAppConfig::LoadConfig(/*in*/ const sdv::u8string& ssConfigPath)
|
|
|
|
|
{
|
|
|
|
|
// Even though a server based application is not allowed to call this function, it is called by the startup function. The
|
|
|
|
|
// prevention of access to this function is done through the interface not being available.
|
|
|
|
|
bool bServerApp = false;
|
|
|
|
|
switch (GetAppSettings().GetContextType())
|
2025-11-04 13:28:06 +01:00
|
|
|
{
|
2026-03-27 14:12:49 +01:00
|
|
|
case sdv::app::EAppContext::main:
|
|
|
|
|
case sdv::app::EAppContext::isolated:
|
|
|
|
|
case sdv::app::EAppContext::maintenance:
|
|
|
|
|
bServerApp = true;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
2025-11-04 13:28:06 +01:00
|
|
|
}
|
|
|
|
|
|
2026-03-27 14:12:49 +01:00
|
|
|
std::filesystem::path pathConfig;
|
|
|
|
|
if (!bServerApp)
|
|
|
|
|
{
|
|
|
|
|
// Close the configuration before
|
|
|
|
|
CloseConfig();
|
2025-11-04 13:28:06 +01:00
|
|
|
|
2026-03-27 14:12:49 +01:00
|
|
|
// Add the current path to the directory search path list.
|
|
|
|
|
AddCurrentPath();
|
2025-11-04 13:28:06 +01:00
|
|
|
|
2026-03-27 14:12:49 +01:00
|
|
|
// Determine the absolute path.
|
|
|
|
|
pathConfig = ComposeConfigPathStandaloneApp(static_cast<std::string>(ssConfigPath));
|
|
|
|
|
if (pathConfig.empty())
|
2025-11-04 13:28:06 +01:00
|
|
|
{
|
2026-03-27 14:12:49 +01:00
|
|
|
SDV_LOG_ERROR("Configuration file was not found \"", ssConfigPath, "\"");
|
2025-11-04 13:28:06 +01:00
|
|
|
return sdv::core::EConfigProcessResult::failed;
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-03-27 14:12:49 +01:00
|
|
|
else
|
|
|
|
|
pathConfig = std::filesystem::u8path(static_cast<std::string>(ssConfigPath));
|
|
|
|
|
|
|
|
|
|
CAppConfigFile config(pathConfig);
|
|
|
|
|
if (!config.IsLoaded())
|
|
|
|
|
{
|
|
|
|
|
SDV_LOG_ERROR("Failed to load user configuration '", pathConfig.generic_u8string(), "'.");
|
|
|
|
|
return sdv::core::EConfigProcessResult::failed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Load the configuration by the repository
|
|
|
|
|
sdv::core::EConfigProcessResult eResult = GetRepository().StartFromConfig(config, true);
|
|
|
|
|
// cppcheck warns that the following condition is always true. This is incorrect. Suppress the warning.
|
|
|
|
|
// cppcheck-suppress knownConditionTrueFalse
|
|
|
|
|
if (eResult == sdv::core::EConfigProcessResult::failed)
|
2025-11-04 13:28:06 +01:00
|
|
|
{
|
2026-03-27 14:12:49 +01:00
|
|
|
SDV_LOG_ERROR("Failed to start the system configuration '", pathConfig.generic_u8string(), "'.");
|
2025-11-04 13:28:06 +01:00
|
|
|
return sdv::core::EConfigProcessResult::failed;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-27 14:12:49 +01:00
|
|
|
// Loading was successful.
|
|
|
|
|
m_configUserConfig = std::move(config);
|
|
|
|
|
|
|
|
|
|
return eResult;
|
2025-11-04 13:28:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool CAppConfig::SaveConfig(/*in*/ const sdv::u8string& ssConfigPath) const
|
|
|
|
|
{
|
2026-03-27 14:12:49 +01:00
|
|
|
// Check for allowance
|
|
|
|
|
switch (GetAppSettings().GetContextType())
|
2025-11-04 13:28:06 +01:00
|
|
|
{
|
2026-03-27 14:12:49 +01:00
|
|
|
case sdv::app::EAppContext::main:
|
|
|
|
|
case sdv::app::EAppContext::isolated:
|
|
|
|
|
case sdv::app::EAppContext::maintenance:
|
|
|
|
|
return false;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
2025-11-04 13:28:06 +01:00
|
|
|
}
|
|
|
|
|
|
2026-03-27 14:12:49 +01:00
|
|
|
// TODO... synchronize the configuration from the repository to the configuration file.
|
|
|
|
|
return m_configUserConfig.SaveConfigFile(static_cast<std::string>(ssConfigPath));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sdv::u8string CAppConfig::GenerateConfigString() const
|
|
|
|
|
{
|
|
|
|
|
// Check for allowance
|
|
|
|
|
switch (GetAppSettings().GetContextType())
|
2025-11-04 13:28:06 +01:00
|
|
|
{
|
2026-03-27 14:12:49 +01:00
|
|
|
case sdv::app::EAppContext::main:
|
|
|
|
|
case sdv::app::EAppContext::isolated:
|
|
|
|
|
case sdv::app::EAppContext::maintenance:
|
|
|
|
|
return {};
|
|
|
|
|
default:
|
|
|
|
|
break;
|
2025-11-04 13:28:06 +01:00
|
|
|
}
|
|
|
|
|
|
2026-03-27 14:12:49 +01:00
|
|
|
// Get the configuration string
|
|
|
|
|
std::string ssConfig;
|
|
|
|
|
bool bChanged = false;
|
|
|
|
|
if (!m_configUserConfig.UpdateConfigString(ssConfig, bChanged))
|
|
|
|
|
return {};
|
|
|
|
|
return ssConfig;
|
|
|
|
|
}
|
2025-11-04 13:28:06 +01:00
|
|
|
|
2026-03-27 14:12:49 +01:00
|
|
|
void CAppConfig::CloseConfig()
|
|
|
|
|
{
|
|
|
|
|
// Check for allowance
|
|
|
|
|
switch (GetAppSettings().GetContextType())
|
|
|
|
|
{
|
|
|
|
|
case sdv::app::EAppContext::main:
|
|
|
|
|
case sdv::app::EAppContext::isolated:
|
|
|
|
|
case sdv::app::EAppContext::maintenance:
|
|
|
|
|
return;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
2025-11-04 13:28:06 +01:00
|
|
|
|
2026-03-27 14:12:49 +01:00
|
|
|
// TODO... remove all components from baseline
|
2025-11-04 13:28:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool CAppConfig::AddConfigSearchDir(/*in*/ const sdv::u8string& ssDir)
|
|
|
|
|
{
|
2026-03-27 14:12:49 +01:00
|
|
|
// Check for allowance
|
|
|
|
|
switch (GetAppSettings().GetContextType())
|
|
|
|
|
{
|
|
|
|
|
case sdv::app::EAppContext::main:
|
|
|
|
|
case sdv::app::EAppContext::isolated:
|
|
|
|
|
case sdv::app::EAppContext::maintenance:
|
|
|
|
|
return false;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (GetAppControl().GetOperationState() != sdv::app::EAppOperationState::configuring)
|
|
|
|
|
return false;
|
2025-11-04 13:28:06 +01:00
|
|
|
|
|
|
|
|
// Add initial paths if not done so already
|
|
|
|
|
AddCurrentPath();
|
|
|
|
|
|
|
|
|
|
std::unique_lock<std::mutex> lock(m_mtxSearchPaths);
|
|
|
|
|
|
|
|
|
|
std::filesystem::path pathDir(ssDir.c_str());
|
|
|
|
|
|
|
|
|
|
// Relative paths are always relative of the executable
|
|
|
|
|
if (pathDir.is_relative())
|
|
|
|
|
pathDir = GetExecDirectory() / ssDir.c_str();
|
|
|
|
|
|
|
|
|
|
// Remove any indirections
|
|
|
|
|
pathDir = pathDir.lexically_normal();
|
|
|
|
|
|
|
|
|
|
// If the current path is not a directory, it cannot be added
|
|
|
|
|
if (!std::filesystem::is_directory(pathDir)) return false;
|
|
|
|
|
|
|
|
|
|
// Check whether the path is already in the list
|
|
|
|
|
if (std::find(m_lstSearchPaths.begin(), m_lstSearchPaths.end(), pathDir) != m_lstSearchPaths.end())
|
|
|
|
|
return true; // This is not an error
|
|
|
|
|
|
|
|
|
|
// Add the path
|
|
|
|
|
m_lstSearchPaths.push_back(pathDir);
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CAppConfig::ResetConfigBaseline()
|
|
|
|
|
{
|
2026-03-27 14:12:49 +01:00
|
|
|
//// Check for allowance
|
|
|
|
|
//switch (GetAppSettings().GetContextType())
|
|
|
|
|
//{
|
|
|
|
|
//case sdv::app::EAppContext::main:
|
|
|
|
|
//case sdv::app::EAppContext::isolated:
|
|
|
|
|
//case sdv::app::EAppContext::maintenance:
|
|
|
|
|
// return;
|
|
|
|
|
//default:
|
|
|
|
|
// break;
|
|
|
|
|
//}
|
|
|
|
|
|
2025-11-04 13:28:06 +01:00
|
|
|
GetRepository().ResetConfigBaseline();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef _MSC_VER
|
|
|
|
|
// Prevent bogus warning about uninitialized memory for the variable *hFile.
|
|
|
|
|
#pragma warning(push)
|
|
|
|
|
#pragma warning(disable : 6001)
|
|
|
|
|
#endif
|
|
|
|
|
|
2026-03-27 14:12:49 +01:00
|
|
|
bool CAppConfig::Install(/*in*/ const sdv::u8string& /*ssInstallName*/, /*in*/ const sdv::sequence<sdv::installation::SFileDesc>& /*seqFiles*/)
|
2025-11-04 13:28:06 +01:00
|
|
|
{
|
2026-03-27 14:12:49 +01:00
|
|
|
// // Installations can only be done in the main application.
|
|
|
|
|
// if (!GetAppSettings().IsMainApplication()) return false;
|
|
|
|
|
//
|
|
|
|
|
// std::unique_lock<std::mutex> lock(m_mtxInstallations);
|
|
|
|
|
//
|
|
|
|
|
// // Check whether the installation already exists. If so, cancel the installation.
|
|
|
|
|
// if (m_mapInstallations.find(ssInstallName) != m_mapInstallations.end())
|
|
|
|
|
// {
|
|
|
|
|
// throw sdv::installation::XDuplicateInstall();
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// // Rollback class - automatically reverses the loading.
|
|
|
|
|
// class CRollback
|
|
|
|
|
// {
|
|
|
|
|
// public:
|
|
|
|
|
// ~CRollback()
|
|
|
|
|
// {
|
|
|
|
|
// bool bSuccess = true;
|
|
|
|
|
// for (sdv::core::TModuleID tModuleID : m_vecLoadedModules)
|
|
|
|
|
// {
|
|
|
|
|
// try
|
|
|
|
|
// {
|
|
|
|
|
// bSuccess &= GetModuleControl().ContextUnload(tModuleID, true);
|
|
|
|
|
// }
|
|
|
|
|
// catch (const sdv::XSysExcept&)
|
|
|
|
|
// {
|
|
|
|
|
// }
|
|
|
|
|
// catch (const std::exception&)
|
|
|
|
|
// {
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
// if (!bSuccess)
|
|
|
|
|
// {
|
|
|
|
|
// SDV_LOG_ERROR("Failed to rollback \"", m_pathInstallRoot, "\".");
|
|
|
|
|
// return;
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// if (!m_pathInstallRoot.empty())
|
|
|
|
|
// {
|
|
|
|
|
// try
|
|
|
|
|
// {
|
|
|
|
|
// std::filesystem::remove_all(m_pathInstallRoot);
|
|
|
|
|
// }
|
|
|
|
|
// catch (const std::filesystem::filesystem_error& rexcept)
|
|
|
|
|
// {
|
|
|
|
|
// SDV_LOG_ERROR("Failed to rollback \"", m_pathInstallRoot, "\": ", rexcept.what());
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
// void Commit()
|
|
|
|
|
// {
|
|
|
|
|
// // Do not roll back.
|
|
|
|
|
// m_pathInstallRoot.clear();
|
|
|
|
|
// m_vecLoadedModules.clear();
|
|
|
|
|
// }
|
|
|
|
|
// void SetInstallPath(const std::filesystem::path& rPathInstallDir) { m_pathInstallRoot = rPathInstallDir; }
|
|
|
|
|
// void AddLoadedModule(sdv::core::TModuleID tModuleID) { m_vecLoadedModules.push_back(tModuleID); }
|
|
|
|
|
// private:
|
|
|
|
|
// std::filesystem::path m_pathInstallRoot; ///< Installation root directory
|
|
|
|
|
// std::vector<sdv::core::TModuleID> m_vecLoadedModules; ///< loaded modules
|
|
|
|
|
// } rollback;
|
|
|
|
|
//
|
|
|
|
|
// std::filesystem::path pathInstallation = GetAppSettings().GetInstallDir() / static_cast<std::string>(ssInstallName);
|
|
|
|
|
// std::vector<std::filesystem::path> vecComponents;
|
|
|
|
|
// try
|
|
|
|
|
// {
|
|
|
|
|
// // A sub-directory with the name must not already exist, if so, delete it.
|
|
|
|
|
// if (std::filesystem::exists(pathInstallation))
|
|
|
|
|
// std::filesystem::remove_all(pathInstallation);
|
|
|
|
|
//
|
|
|
|
|
// // Create a directory for the installation
|
|
|
|
|
// std::filesystem::create_directories(pathInstallation);
|
|
|
|
|
// rollback.SetInstallPath(pathInstallation);
|
|
|
|
|
//
|
|
|
|
|
// // Add each file
|
|
|
|
|
// for (const sdv::installation::SFileDesc& rsFileDesc : seqFiles)
|
|
|
|
|
// {
|
|
|
|
|
// // File path
|
|
|
|
|
// std::filesystem::path pathFileDir = pathInstallation;
|
|
|
|
|
// //if (!rsFileDesc.ssRelativeDir.empty())
|
|
|
|
|
// //{
|
|
|
|
|
// // std::filesystem::path pathTemp = static_cast<std::string>(rsFileDesc.ssRelativeDir);
|
|
|
|
|
// // if (pathTemp.is_absolute() || !pathTemp.is_relative())
|
|
|
|
|
// // throw sdv::installation::XIncorrectPath();
|
|
|
|
|
// // pathFileDir /= pathTemp;
|
|
|
|
|
// // if (!std::filesystem::exists(pathFileDir))
|
|
|
|
|
// // std::filesystem::create_directories(pathFileDir);
|
|
|
|
|
// //}
|
|
|
|
|
// std::filesystem::path pathFile = pathFileDir;
|
|
|
|
|
// if (std::filesystem::exists(pathFile))
|
|
|
|
|
// throw sdv::installation::XIncorrectPath();
|
|
|
|
|
//
|
|
|
|
|
// // TODO EVE
|
|
|
|
|
// //// Check the file CRC
|
|
|
|
|
// //sdv::crcCRC32C crc;
|
|
|
|
|
// //uint32_t uiCRC = crc.calc_checksum(rsFileDesc.ptrContent.get(), rsFileDesc.ptrContent.size());
|
|
|
|
|
// //if (uiCRC != rsFileDesc.uiCRC32c)
|
|
|
|
|
// // throw sdv::installation::XIncorrectCRC();
|
|
|
|
|
//
|
|
|
|
|
// // Create the file
|
|
|
|
|
// std::ofstream fstream(pathFile, std::ios_base::out | std::ios_base::binary);
|
|
|
|
|
// if (!fstream.is_open()) throw sdv::installation::XIncorrectPath();
|
|
|
|
|
// if (rsFileDesc.ptrContent.size())
|
|
|
|
|
// fstream.write(reinterpret_cast<const char*>(rsFileDesc.ptrContent.get()), rsFileDesc.ptrContent.size());
|
|
|
|
|
// fstream.close();
|
|
|
|
|
//
|
|
|
|
|
// // Set file times
|
|
|
|
|
// // Note: use dedicated OS functions. C++11 filesystem library doesn't define a proper way to convert between UNIX time
|
|
|
|
|
// // and the time defined by the C++11 functions. This deficit has been solved in C++20.
|
|
|
|
|
//
|
|
|
|
|
//#ifdef _WIN32
|
|
|
|
|
// // Windows uses file access for this.
|
|
|
|
|
// HANDLE hFile = CreateFile(pathFile.native().c_str(), FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
|
|
|
// NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
|
|
|
// if (hFile == nullptr || hFile == INVALID_HANDLE_VALUE)
|
|
|
|
|
// {
|
|
|
|
|
// SDV_LOG_ERROR("Cannot create files or directories in \"", pathInstallation, "\"");
|
|
|
|
|
// throw sdv::installation::XIncorrectPath();
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// // Set the filetime on the file.
|
|
|
|
|
// // The filetime starts counting from 1st of January 1601. The resolution is 100ns.
|
|
|
|
|
// // The UNIX time starts counting from 1st of January 1970. The resolution is 1s.
|
|
|
|
|
// // Both times ignore leap seconds.
|
|
|
|
|
// // Thus the UNIX time is running 11644473600 seconds behind the filetime and is a factor 10000000 larger.
|
|
|
|
|
// ULARGE_INTEGER sCreateTime{}, sChangeTime{};
|
|
|
|
|
// sCreateTime.QuadPart = (rsFileDesc.uiCreationDate + 11644473600ull) * 10000000ull;
|
|
|
|
|
// FILETIME ftCreateTime{ sCreateTime.LowPart, sChangeTime.HighPart };
|
|
|
|
|
// sChangeTime.QuadPart = (rsFileDesc.uiChangeDate + 11644473600ull) * 10000000ull;
|
|
|
|
|
// FILETIME ftChangeTime{ sChangeTime.LowPart, sChangeTime.HighPart };
|
|
|
|
|
// BOOL bRes = SetFileTime(hFile, &ftCreateTime, nullptr, &ftChangeTime);
|
|
|
|
|
//
|
|
|
|
|
// // Close our handle.
|
|
|
|
|
// CloseHandle(hFile);
|
|
|
|
|
//
|
|
|
|
|
// if (!bRes)
|
|
|
|
|
// {
|
|
|
|
|
// SDV_LOG_ERROR("Cannot create files or directories in \"", pathInstallation, "\"");
|
|
|
|
|
// throw sdv::installation::XIncorrectPath();
|
|
|
|
|
// }
|
|
|
|
|
//#elif defined __unix__
|
|
|
|
|
// // Note: POSIX doesn't define the creation time.
|
|
|
|
|
// utimbuf sTimes{};
|
|
|
|
|
// sTimes.actime = std::time(0);
|
|
|
|
|
// sTimes.modtime = static_cast<time_t>(rsFileDesc.uiChangeDate);
|
|
|
|
|
// if (utime(pathFile.native().c_str(), &sTimes))
|
|
|
|
|
// {
|
|
|
|
|
// SDV_LOG_ERROR("Cannot create files or directories in \"", pathInstallation, "\"");
|
|
|
|
|
// throw sdv::installation::XIncorrectPath();
|
|
|
|
|
// }
|
|
|
|
|
//#endif
|
|
|
|
|
//
|
|
|
|
|
// // Set attributes
|
|
|
|
|
// if (rsFileDesc.bAttrExecutable)
|
|
|
|
|
// std::filesystem::permissions(pathFile, std::filesystem::perms::owner_exec | std::filesystem::perms::group_exec |
|
|
|
|
|
// std::filesystem::perms::others_exec, std::filesystem::perm_options::add);
|
|
|
|
|
// if (rsFileDesc.bAttrReadonly)
|
|
|
|
|
// std::filesystem::permissions(pathFile, std::filesystem::perms::owner_write | std::filesystem::perms::group_write |
|
|
|
|
|
// std::filesystem::perms::others_write, std::filesystem::perm_options::remove);
|
|
|
|
|
//
|
|
|
|
|
// // Component paths are stored
|
|
|
|
|
// //if (rsFileDesc.bAttrComponent) vecComponents.push_back(pathFile);
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
// catch (const std::filesystem::filesystem_error& rexcept)
|
|
|
|
|
// {
|
|
|
|
|
// SDV_LOG_ERROR("Cannot create files or directories in \"", pathInstallation, "\": ", rexcept.what());
|
|
|
|
|
// throw sdv::installation::XIncorrectPath();
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// // TODO: Restriction. Only complex services will be started dynamically and will be added to the configuration. Utilities will
|
|
|
|
|
// // also be installed but will not be started. Devices and basic services will not be available this way.
|
|
|
|
|
//
|
|
|
|
|
// // TODO:
|
|
|
|
|
// // For each component, get the manifest using the manifest helper utility.
|
|
|
|
|
// // Note: to get the manifest, the component module needs to be loaded and the GetManifest exported function is called. Loading
|
|
|
|
|
// // a component module causes part of the component to start already and dependencies to be resolved. If this malfunctions, the
|
|
|
|
|
// // component could crash or do other malicious things. OR if on purpose, an application or complex service component, that
|
|
|
|
|
// // normally would run in an isolated environment, gets access to the memory and everything else within the process. If this
|
|
|
|
|
// // module is not isolated from the core, it could access the core and do nasty things. THEREFORE, the manifest helper utility is
|
|
|
|
|
// // started as a component in its own isolated environment and then loading the component. No component code is executed within
|
|
|
|
|
// // the core process.
|
|
|
|
|
// sdv::TObjectPtr ptrManifestUtil = sdv::core::CreateUtility("ManifestHelperUtility");
|
|
|
|
|
// sdv::helper::IModuleManifestHelper* pManifestHelper = ptrManifestUtil.GetInterface<sdv::helper::IModuleManifestHelper>();
|
|
|
|
|
// if (!pManifestHelper)
|
|
|
|
|
// {
|
|
|
|
|
// SDV_LOG_ERROR("Manifest helper utility cannot be loaded.");
|
|
|
|
|
// throw sdv::installation::XComponentNotLoadable();
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// // Get the manifest of all components
|
|
|
|
|
// std::map<std::filesystem::path, sdv::u8string> mapComponentManifests;
|
|
|
|
|
// for (const std::filesystem::path& rpathComponent : vecComponents)
|
|
|
|
|
// {
|
|
|
|
|
// sdv::u8string ssManifest = pManifestHelper->ReadModuleManifest(rpathComponent.generic_u8string());
|
|
|
|
|
// if (!ssManifest.empty())
|
|
|
|
|
// mapComponentManifests[rpathComponent] = ssManifest;
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// // Check whether there ary any manifests
|
|
|
|
|
// if (mapComponentManifests.empty())
|
|
|
|
|
// {
|
|
|
|
|
// SDV_LOG_ERROR("No component in installation", ssInstallName, ".");
|
|
|
|
|
// throw sdv::installation::XNoManifest();
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// // Load the modules
|
|
|
|
|
// bool bSuccess = true;
|
|
|
|
|
// for (const auto& rvtModule : mapComponentManifests)
|
|
|
|
|
// {
|
|
|
|
|
// sdv::core::TModuleID tModuleID = GetModuleControl().ContextLoad(rvtModule.first, rvtModule.second);
|
|
|
|
|
// if (!tModuleID)
|
|
|
|
|
// {
|
|
|
|
|
// bSuccess = false;
|
|
|
|
|
// break;
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// // TODO: What about the dependent modules that were started as well. They are not rolled back...
|
|
|
|
|
// rollback.AddLoadedModule(tModuleID);
|
|
|
|
|
// }
|
|
|
|
|
// if (!bSuccess)
|
|
|
|
|
// {
|
|
|
|
|
// SDV_LOG_ERROR("Manifest helper utility cannot be loaded.");
|
|
|
|
|
// throw sdv::installation::XComponentNotLoadable();
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// // Add the installation to the installation map
|
|
|
|
|
// //SInstallation sInstallation{ seqFiles };
|
|
|
|
|
// //for (sdv::core::)
|
|
|
|
|
// //m_mapInstallations[ssInstallName] = SInstallation{ seqFiles };
|
|
|
|
|
//
|
|
|
|
|
// // Commit the changes - prevent rollback
|
|
|
|
|
// rollback.Commit();
|
2025-11-04 13:28:06 +01:00
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef _MSC_VER
|
|
|
|
|
#pragma warning(pop)
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
bool CAppConfig::Update(/*in*/ const sdv::u8string& ssInstallName, /*in*/ const sdv::sequence<sdv::installation::SFileDesc>& seqFiles)
|
|
|
|
|
{
|
|
|
|
|
// TODO: as soon as the configuration is updatable as well, the update uses the same configuration and possibly updates the
|
|
|
|
|
// configuration.
|
|
|
|
|
// Until the config update feature is available, the update can stop and update and start the installation by uninstall and
|
|
|
|
|
// install.
|
|
|
|
|
|
|
|
|
|
bool bRet = Uninstall(ssInstallName);
|
|
|
|
|
if (bRet) bRet = Install(ssInstallName, seqFiles);
|
|
|
|
|
return bRet;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool CAppConfig::Uninstall(/*in*/ const sdv::u8string& /*ssInstallName*/)
|
|
|
|
|
{
|
|
|
|
|
// Installations can only be done in the main application.
|
2026-03-27 14:12:49 +01:00
|
|
|
if (!GetAppSettings().IsMainApplication()) return false;
|
2025-11-04 13:28:06 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sdv::sequence<sdv::u8string> CAppConfig::GetInstallations() const
|
|
|
|
|
{
|
|
|
|
|
return sdv::sequence<sdv::u8string>();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sdv::sequence<sdv::installation::SFileDesc> CAppConfig::GetInstallationFiles(/*in*/ const sdv::u8string& /*ssInstallName*/) const
|
|
|
|
|
{
|
|
|
|
|
return sdv::sequence<sdv::installation::SFileDesc>();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::filesystem::path CAppConfig::FindInstalledModule(const std::filesystem::path& rpathRelModule)
|
|
|
|
|
{
|
2026-03-27 14:12:49 +01:00
|
|
|
// Check for allowance
|
|
|
|
|
bool bServerApp = false;
|
|
|
|
|
switch (GetAppSettings().GetContextType())
|
|
|
|
|
{
|
|
|
|
|
case sdv::app::EAppContext::main:
|
|
|
|
|
case sdv::app::EAppContext::isolated:
|
|
|
|
|
case sdv::app::EAppContext::maintenance:
|
|
|
|
|
bServerApp = true;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-04 13:28:06 +01:00
|
|
|
// Search for the module in the following order:
|
|
|
|
|
// - Core manifest
|
|
|
|
|
// - Exe manifest
|
|
|
|
|
// - User manifests
|
|
|
|
|
|
|
|
|
|
std::filesystem::path pathModule = m_manifestCore.FindModule(rpathRelModule);
|
|
|
|
|
if (pathModule.empty())
|
|
|
|
|
pathModule = m_manifestExe.FindModule(rpathRelModule);
|
2026-03-27 14:12:49 +01:00
|
|
|
if (bServerApp)
|
2025-11-04 13:28:06 +01:00
|
|
|
{
|
2026-03-27 14:12:49 +01:00
|
|
|
for (const CInstallManifest& rmanifest : m_vecUserManifests)
|
|
|
|
|
{
|
|
|
|
|
if (!pathModule.empty())
|
|
|
|
|
break;
|
|
|
|
|
pathModule = rmanifest.FindModule(rpathRelModule);
|
|
|
|
|
}
|
2025-11-04 13:28:06 +01:00
|
|
|
}
|
|
|
|
|
return pathModule;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string CAppConfig::FindInstalledModuleManifest(const std::filesystem::path& rpathRelModule)
|
|
|
|
|
{
|
2026-03-27 14:12:49 +01:00
|
|
|
// Check for allowance
|
|
|
|
|
bool bServerApp = false;
|
|
|
|
|
switch (GetAppSettings().GetContextType())
|
|
|
|
|
{
|
|
|
|
|
case sdv::app::EAppContext::main:
|
|
|
|
|
case sdv::app::EAppContext::isolated:
|
|
|
|
|
case sdv::app::EAppContext::maintenance:
|
|
|
|
|
bServerApp = true;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-04 13:28:06 +01:00
|
|
|
// Search for the module in the following order:
|
|
|
|
|
// - Core manifest
|
|
|
|
|
// - Exe manifest
|
|
|
|
|
// - User manifests
|
|
|
|
|
|
|
|
|
|
std::string ssManifest = m_manifestCore.FindModuleManifest(rpathRelModule);
|
|
|
|
|
if (ssManifest.empty())
|
|
|
|
|
ssManifest = m_manifestExe.FindModuleManifest(rpathRelModule);
|
2026-03-27 14:12:49 +01:00
|
|
|
if (bServerApp)
|
2025-11-04 13:28:06 +01:00
|
|
|
{
|
2026-03-27 14:12:49 +01:00
|
|
|
for (const CInstallManifest& rmanifest : m_vecUserManifests)
|
|
|
|
|
{
|
|
|
|
|
if (!ssManifest.empty())
|
|
|
|
|
break;
|
|
|
|
|
ssManifest = rmanifest.FindModuleManifest(rpathRelModule);
|
|
|
|
|
}
|
2025-11-04 13:28:06 +01:00
|
|
|
}
|
|
|
|
|
return ssManifest;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-27 14:12:49 +01:00
|
|
|
std::optional<sdv::SClassInfo> CAppConfig::FindInstalledComponent(const std::string& rssClass) const
|
2025-11-04 13:28:06 +01:00
|
|
|
{
|
2026-03-27 14:12:49 +01:00
|
|
|
// Check for allowance
|
|
|
|
|
bool bServerApp = false;
|
|
|
|
|
switch (GetAppSettings().GetContextType())
|
|
|
|
|
{
|
|
|
|
|
case sdv::app::EAppContext::main:
|
|
|
|
|
case sdv::app::EAppContext::isolated:
|
|
|
|
|
case sdv::app::EAppContext::maintenance:
|
|
|
|
|
bServerApp = true;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
2025-11-04 13:28:06 +01:00
|
|
|
|
|
|
|
|
// Search for the component in the following order:
|
|
|
|
|
// - Core manifest
|
|
|
|
|
// - Exe manifest
|
|
|
|
|
// - User manifests
|
|
|
|
|
auto optManifest = m_manifestCore.FindComponentByClass(rssClass);
|
|
|
|
|
if (!optManifest) optManifest = m_manifestExe.FindComponentByClass(rssClass);
|
2026-03-27 14:12:49 +01:00
|
|
|
if (bServerApp)
|
2025-11-04 13:28:06 +01:00
|
|
|
{
|
2026-03-27 14:12:49 +01:00
|
|
|
for (const CInstallManifest& rmanifest : m_vecUserManifests)
|
|
|
|
|
{
|
|
|
|
|
if (optManifest)
|
|
|
|
|
break;
|
|
|
|
|
optManifest = rmanifest.FindComponentByClass(rssClass);
|
|
|
|
|
}
|
2025-11-04 13:28:06 +01:00
|
|
|
}
|
|
|
|
|
return optManifest;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-27 14:12:49 +01:00
|
|
|
bool CAppConfig::RemoveFromConfig(const CInstallManifest& /*rManifest*/)
|
|
|
|
|
{
|
|
|
|
|
//// Check for allowance
|
|
|
|
|
//bool bServerApp = false;
|
|
|
|
|
//switch (GetAppSettings().GetContextType())
|
|
|
|
|
//{
|
|
|
|
|
//case sdv::app::EAppContext::main:
|
|
|
|
|
//case sdv::app::EAppContext::isolated:
|
|
|
|
|
//case sdv::app::EAppContext::maintenance:
|
|
|
|
|
// bServerApp = true;
|
|
|
|
|
//default:
|
|
|
|
|
// break;
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
|
|
// auto fnRemoveFromConfig = [&](const std::filesystem::path& /*rpathConfig*/, bool /*bIsSysConfig*/)
|
|
|
|
|
//{
|
|
|
|
|
// // Iterate through the manifest and search for the component in the configuration.
|
|
|
|
|
//};
|
|
|
|
|
|
|
|
|
|
// Get the list of system configurations.
|
|
|
|
|
//auto vecSysConfigs = GetAppSettings().GetSystemConfigPaths();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::filesystem::path CAppConfig::ComposeConfigPathStandaloneApp(const std::filesystem::path& rpathConfigFile) const
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
// Get the search paths if the module path is relative
|
|
|
|
|
std::list<std::filesystem::path> lstSearchPaths;
|
|
|
|
|
if (rpathConfigFile.is_relative())
|
|
|
|
|
lstSearchPaths = m_lstSearchPaths;
|
|
|
|
|
|
|
|
|
|
// Add an empty path to allow the OS to search when our own search paths could not find the module.
|
|
|
|
|
lstSearchPaths.push_back(std::filesystem::path());
|
|
|
|
|
|
|
|
|
|
// Run through the search paths and try to find the config file.
|
|
|
|
|
for (const std::filesystem::path& rpathDirectory : lstSearchPaths)
|
|
|
|
|
{
|
|
|
|
|
// Compose the path
|
|
|
|
|
std::filesystem::path pathConfigTemp;
|
|
|
|
|
if (rpathDirectory.is_absolute())
|
|
|
|
|
pathConfigTemp = (rpathDirectory / rpathConfigFile).lexically_normal();
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (rpathDirectory.empty())
|
|
|
|
|
pathConfigTemp = rpathConfigFile.lexically_normal();
|
|
|
|
|
else
|
|
|
|
|
pathConfigTemp = (GetExecDirectory() / rpathDirectory / rpathConfigFile).lexically_normal();
|
|
|
|
|
}
|
|
|
|
|
if (std::filesystem::exists(pathConfigTemp))
|
|
|
|
|
{
|
|
|
|
|
return pathConfigTemp;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (const std::exception& re)
|
|
|
|
|
{
|
|
|
|
|
SDV_LOG_ERROR("Supplied path to config load is not valid \"", rpathConfigFile, "\": ", re.what());
|
|
|
|
|
}
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-04 13:28:06 +01:00
|
|
|
void CAppConfig::AddCurrentPath()
|
|
|
|
|
{
|
|
|
|
|
std::unique_lock<std::mutex> lock(m_mtxSearchPaths);
|
|
|
|
|
|
|
|
|
|
// Do this only once.
|
|
|
|
|
if (!m_lstSearchPaths.empty()) return;
|
|
|
|
|
|
|
|
|
|
// Add the core directory
|
|
|
|
|
std::filesystem::path pathCoreDir = GetCoreDirectory().lexically_normal();
|
|
|
|
|
m_lstSearchPaths.push_back(pathCoreDir / "config/");
|
|
|
|
|
|
|
|
|
|
// Add the exe dir
|
|
|
|
|
std::filesystem::path pathExeDir = GetExecDirectory().lexically_normal();
|
|
|
|
|
if (pathExeDir != pathCoreDir) m_lstSearchPaths.push_back(pathExeDir / "config/");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CAppConfig& CAppConfigService::GetAppConfig()
|
|
|
|
|
{
|
|
|
|
|
return ::GetAppConfig();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool CAppConfigService::EnableAppConfigAccess()
|
|
|
|
|
{
|
2026-03-27 14:12:49 +01:00
|
|
|
return GetAppSettings().IsStandaloneApplication() ||
|
|
|
|
|
GetAppSettings().IsEssentialApplication();
|
2025-11-04 13:28:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool CAppConfigService::EnableAppInstallAccess()
|
|
|
|
|
{
|
2026-03-27 14:12:49 +01:00
|
|
|
return GetAppSettings().IsMainApplication();
|
2025-11-04 13:28:06 +01:00
|
|
|
}
|