Precommit (#1)

* first commit

* cleanup
This commit is contained in:
tompzf
2025-11-04 13:28:06 +01:00
committed by GitHub
parent dba45dc636
commit 6ed4b1534e
898 changed files with 256340 additions and 0 deletions

View File

@@ -0,0 +1,23 @@
# Define project
project (sdv_control VERSION 1.0 LANGUAGES CXX)
# Add include directories
include_directories(../export)
# Define the executable
add_executable(sdv_control
main.cpp
"list_elements.cpp"
"list_elements.h"
"startup_shutdown.h"
"startup_shutdown.cpp"
"context.h"
"print_table.h" "start_stop_service.cpp" "start_stop_service.h" "installation.h" "installation.cpp")
target_link_libraries(sdv_control ${CMAKE_DL_LIBS})
# Build dependencies
add_dependencies(sdv_control core_services)
# Appending the executable to the service list
set(SDV_Executable_List ${SDV_Executable_List} sdv_control PARENT_SCOPE)

View File

@@ -0,0 +1,52 @@
#ifndef CONTEXT_H
#define CONTEXT_H
#include <cstdint>
#include <filesystem>
#include <support/string.h>
#include <support/sequence.h>
/**
* @brief SDV context
*/
struct SContext
{
bool bSilent = false; ///< Silence flag
bool bVerbose = false; ///< Verbose flag
bool bServerSilent = false; ///< Silence flag of server
bool bServerVerbose = false; ///< Verbose flag of server
uint32_t uiInstanceID = 1000; ///< Instance ID
bool bListNoHdr = false; ///< Do not print a header with the listing table.
bool bListShort = false; ///< Print only a shortened list with one column.
sdv::sequence<sdv::u8string> seqCmdLine; ///< The commands provided on the command line.
std::filesystem::path pathInstallDir; ///< Optional installation directory.
};
#include <cctype> // std::tolower
#include <algorithm> // std::equal
/**
* @brief Compare whether two characters are identical when both are converted to lower case.
* @remarks Cannot deal with Unicode letters comprising of more than one characters.
* @param[in] cLeft First character
* @param[in] cRight Second character
* @return The case independent equality of the characters.
*/
inline bool ichar_equals(char cLeft, char cRight)
{
return std::tolower(static_cast<unsigned char>(cLeft)) ==
std::tolower(static_cast<unsigned char>(cRight));
}
/**
* @brief Compare for case-independent equality.
* @param[in] rssLeft Reference to the left string.
* @param[in] rssRight Reference to the right string.
* @return The case-independent equality of the strings.
*/
inline bool iequals(const std::string& rssLeft, const std::string& rssRight)
{
return std::equal(rssLeft.begin(), rssLeft.end(), rssRight.begin(), rssRight.end(), ichar_equals);
}
#endif // !defined CONTEXT_H

View File

@@ -0,0 +1,124 @@
#include "installation.h"
#include "../../global/cmdlnparser/cmdlnparser.h"
#include <interfaces/process.h>
#include <support/interface_ptr.h>
#include <support/local_service_access.h>
#include <interfaces/com.h>
#include <interfaces/app.h>
#include "../error_msg.h"
void InstallationHelp(const SContext& rsContext)
{
// First argument should be "INSTALL", "UPDATE" or "UNINSTALL".
if (rsContext.seqCmdLine.size() < 1)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: missing install/update/uninstall command..." << std::endl;
return;
}
if (iequals(rsContext.seqCmdLine[0], "INSTALL"))
{
CCommandLine::PrintHelpText(std::cout, "Usage: sdv_control INSTALL <name> [files...] [options...]\n\n"
"Make a new installation with a set of files. This command succeeds when the files contain at least one component "
"module and another installation with the same name is not existing. Complex services are automatically added to the "
"configuration.\n\n");
return;
}
if (iequals(rsContext.seqCmdLine[0], "UPDATE"))
{
CCommandLine::PrintHelpText(std::cout, "Usage: sdv_control UPDATE <name> [files...] [options...]\n\n"
"Update an existing installation. This will not remove any configuration (unless a component is not available any "
"more after, or a new component was added by the update.\n\n");
return;
}
if (iequals(rsContext.seqCmdLine[0], "UNINSTALL"))
{
CCommandLine::PrintHelpText(std::cout, "Usage: sdv_control UNINSTALL <name> [files...] [options...]\n\n"
"Remove an existing installation. This will remove the configuration for the components as well. Dependent components "
"become inactive and will be activated again when the component is installed again.\n\n");
return;
}
if (!rsContext.bSilent)
std::cerr << "ERROR: invalid install/update/uninstall command..." << std::endl;
}
int Install(const SContext& rsContext)
{
// First argument should be "INSTALL"
if (rsContext.seqCmdLine.size() < 3)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << CMDLN_ARG_ERR_MSG << " Not enough parameters for installation command." << std::endl;
return CMDLN_ARG_ERR;
}
if (!iequals(rsContext.seqCmdLine[0], "INSTALL"))
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << CMDLN_ARG_ERR_MSG << " Invalid command : " << rsContext.seqCmdLine[0] << std::endl;
return CMDLN_ARG_ERR;
}
// Followed by the installation name
//std::string ssName = rsContext.seqCmdLine[1];
#if 0
// Determine the service to start
sdv::u8string ssClass, ssName;
if (rsContext.seqCmdLine.size() == 3)
{
ssClass = rsContext.seqCmdLine[1];
ssName = rsContext.seqCmdLine[2];
} else
ssName = rsContext.seqCmdLine[1];
// Try to connect
sdv::TObjectPtr ptrRepository = sdv::com::ConnectToLocalServerRepository(rsContext.uiInstanceID);
if (!ptrRepository)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << CONNECT_SDV_SERVER_ERROR_MSG << " Instance #" << rsContext.uiInstanceID << std::endl;
return CONNECT_SDV_SERVER_ERROR;
}
// Get access to the module control service
sdv::core::IObjectAccess* pObjectAccess = ptrRepository.GetInterface<sdv::core::IObjectAccess>();
sdv::core::IRepositoryControl* pRepoControl = nullptr;
if (pObjectAccess)
pRepoControl = sdv::TInterfaceAccessPtr(pObjectAccess->GetObject("RepositoryService")).
GetInterface<sdv::core::IRepositoryControl>();
if (!pRepoControl)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << REPO_SERVICE_ACCESS_ERROR_MSG << std::endl;
return REPOSITORY_SERVICE_ACCESS_ERROR;
}
// Create the object
sdv::core::TObjectID tObjectID = pRepoControl->CreateObject(ssClass, ssName, {});
if (!tObjectID)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << START_OBJECT_ERROR_ERROR_MSG << " Object name: " << ssName << std::endl;
return START_OBJECT_ERROR;
}
#endif
// All good...
return NO_ERROR;
}
int Update(const SContext& /*rsContext*/)
{
std::cout << "ERROR: " << NOT_IMPLEMENTED_MSG << " :-(" << std::endl;
return NOT_IMPLEMENTED;
}
int Uninstall(const SContext& /*rsContext*/)
{
std::cout << "ERROR: " << NOT_IMPLEMENTED_MSG << " :-(" << std::endl;
return NOT_IMPLEMENTED;
}

View File

@@ -0,0 +1,39 @@
#ifndef INSTALLATION_H
#define INSTALLATION_H
#include "context.h"
#include <support/sequence.h>
#include <support/string.h>
#include <support/interface_ptr.h>
/**
* @brief Help for installing, updating and uninstalling components.
* @param[in] rsContext Reference to the context.
*/
void InstallationHelp(const SContext& rsContext);
/**
* @brief Install components.
* @details The command line context.
* @param[in] rsContext Reference to the context.
* @return The application exit code. 0 is no error.
*/
int Install(const SContext& rsContext);
/**
* @brief Update installation.
* @details The command line context.
* @param[in] rsContext Reference to the context.
* @return The application exit code. 0 is no error.
*/
int Update(const SContext& rsContext);
/**
* @brief Uninstall components.
* @details The command line context.
* @param[in] rsContext Reference to the context.
* @return The application exit code. 0 is no error.
*/
int Uninstall(const SContext& rsContext);
#endif // !defined INSTALLATION_H

View File

@@ -0,0 +1,416 @@
#include "list_elements.h"
#include "print_table.h"
#include <interfaces/config.h>
#include <support/sdv_core.h>
#include <support/local_service_access.h>
#include "../../global/cmdlnparser/cmdlnparser.h"
#include "../../global/exec_dir_helper.h"
#include "../../global/flags.h"
#include "../error_msg.h"
void ListHelp(const SContext& rsContext)
{
// First argument should be "LIST"
if (rsContext.seqCmdLine.size() < 1)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: missing listing command..." << std::endl;
return;
}
if (!iequals(rsContext.seqCmdLine[0], "LIST"))
{
if (!rsContext.bSilent)
std::cerr << "ERROR: invalid command: " << rsContext.seqCmdLine[0] << std::endl;
return;
}
// Which list command help is requested
enum class EListCommand { unknown, modules, classes, components, installations, connections } eListCommand = EListCommand::unknown;
if (rsContext.seqCmdLine.size() >= 2)
{
if (iequals(rsContext.seqCmdLine[1], "MODULES")) eListCommand = EListCommand::modules;
else if (iequals(rsContext.seqCmdLine[1], "CLASSES")) eListCommand = EListCommand::classes;
else if (iequals(rsContext.seqCmdLine[1], "COMPONENTS")) eListCommand = EListCommand::components;
else if (iequals(rsContext.seqCmdLine[1], "INSTALLATIONS")) eListCommand = EListCommand::installations;
else if (iequals(rsContext.seqCmdLine[1], "CONNECTIONS")) eListCommand = EListCommand::connections;
}
// Print help if requested
if (!rsContext.bSilent)
{
switch (eListCommand)
{
case EListCommand::modules:
CCommandLine::PrintHelpText(std::cout, "Usage: sdv_control LIST MODULES [options...]\n\nShow a list of all loaded modules.\n");
break;
case EListCommand::classes:
CCommandLine::PrintHelpText(std::cout, "Usage: sdv_control LIST CLASSES [options...]\n\nShow a list of available component classes.\n");
break;
case EListCommand::components:
CCommandLine::PrintHelpText(std::cout, "Usage: sdv_control LIST COMPONENTS [options...]\n\nShow a list of all loaded components.\n");
break;
case EListCommand::installations:
CCommandLine::PrintHelpText(std::cout, "Usage: sdv_control LIST INSTALLATIONS [options...]\n\nShow a list of all installations.\n");
break;
case EListCommand::connections:
CCommandLine::PrintHelpText(std::cout, "Usage: sdv_control LIST CONNECTIONS [options...]\n\nShow a list of all connected applications.\n");
break;
default:
CCommandLine::PrintHelpText(std::cout, R"code(Usage: sdv_control LIST <list_command> [options...]
Supported listing commands:
LIST MODULES Show a list of loaded server modules.
LIST CLASSES Show a list of available component classes.
LIST COMPONENTS Show a list of instantiated server components
LIST INSTALLATIONS Show a list of installations.
LIST CONNECTIONS Show a list of current connections.
)code");
break;
}
std::cout << "Options:\n";
std::cout << " --no_header Do not print a header for the listing table.\n";
std::cout << " --short Print only the most essential information as one column.\n\n";
}
}
int ListElements(const SContext& rsContext, std::ostream& rstream /*= std::cout*/)
{
// First argument should be "LIST"
if (rsContext.seqCmdLine.size() < 1)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << CMDLN_ARG_ERR_MSG << " Missing listing command." << std::endl;
return CMDLN_ARG_ERR;
}
if (!iequals(rsContext.seqCmdLine[0], "LIST"))
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << CMDLN_ARG_ERR_MSG << " Invalid command: " << rsContext.seqCmdLine[0] << std::endl;
return CMDLN_ARG_ERR;
}
// Which list command help is requested
enum class EListCommand { unknown, modules, classes, components, installations, connections } eListCommand = EListCommand::unknown;
if (rsContext.seqCmdLine.size() >= 2)
{
if (iequals(rsContext.seqCmdLine[1], "MODULES")) eListCommand = EListCommand::modules;
else if (iequals(rsContext.seqCmdLine[1], "CLASSES")) eListCommand = EListCommand::classes;
else if (iequals(rsContext.seqCmdLine[1], "COMPONENTS")) eListCommand = EListCommand::components;
else if (iequals(rsContext.seqCmdLine[1], "INSTALLATIONS")) eListCommand = EListCommand::installations;
else if (iequals(rsContext.seqCmdLine[1], "CONNECTIONS")) eListCommand = EListCommand::connections;
}
else
{
if (!rsContext.bSilent)
{
std::cerr << "ERROR: " << CMDLN_ARG_ERR_MSG << " Missing listing command.." << std::endl << std::endl;
ListHelp(rsContext);
}
return CMDLN_ARG_ERR;
}
// Try to connect
sdv::TObjectPtr ptrRepository = sdv::com::ConnectToLocalServerRepository(rsContext.uiInstanceID);
if (!ptrRepository)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << CONNECT_SDV_SERVER_ERROR_MSG << " Instance #" << rsContext.uiInstanceID << "." << std::endl;
return CONNECT_SDV_SERVER_ERROR;
}
int iRet = -100;
switch (eListCommand)
{
case EListCommand::modules:
iRet = ListModules(rsContext, ptrRepository, rstream);
break;
case EListCommand::classes:
iRet = ListClasses(rsContext, ptrRepository, rstream);
break;
case EListCommand::components:
iRet = ListComponents(rsContext, ptrRepository, rstream);
break;
case EListCommand::installations:
iRet = ListInstallations(rsContext, ptrRepository, rstream);
break;
case EListCommand::connections:
iRet = ListConnections(rsContext, ptrRepository, rstream);
break;
default:
break;
}
return iRet;
}
int ListModules(const SContext& rsContext, const sdv::TObjectPtr& rptrRepository, std::ostream& rstream)
{
// Get access to the module control service
sdv::core::IObjectAccess* pObjectAccess = rptrRepository.GetInterface<sdv::core::IObjectAccess>();
const sdv::core::IModuleInfo* pModuleInfo = nullptr;
if (pObjectAccess)
pModuleInfo = sdv::TInterfaceAccessPtr(pObjectAccess->GetObject("ModuleControlService")).
GetInterface<sdv::core::IModuleInfo>();
if (!pModuleInfo)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << MODULE_CONTROL_SERVICE_ACCESS_ERROR_MSG << std::endl;
return MODULE_CONTROL_SERVICE_ACCESS_ERROR;
}
// Relative path from exe
auto fnPrintRelPath = [](const sdv::u8string& rssPath)
{
std::filesystem::path path = rssPath.c_str();
if (path.is_relative()) return path.generic_u8string();
return path.lexically_relative(GetExecDirectory()).generic_u8string();
};
// Create the module list
sdv::sequence<sdv::core::SModuleInfo> seqModules = pModuleInfo->GetModuleList();
if (rsContext.bListShort)
{
std::vector<std::array<std::string, 1>> vecShortModuleList;
vecShortModuleList.push_back({ "Path" });
for (const sdv::core::SModuleInfo& rsModuleInfo : seqModules)
vecShortModuleList.push_back({ fnPrintRelPath(rsModuleInfo.ssPath) });
// Print the module list
PrintTable(vecShortModuleList, rstream, rsContext.bListNoHdr);
}
else
{
std::vector<std::array<std::string, 5>> vecModuleList;
vecModuleList.push_back({ "Module ID", "Filename", "Version", "Active", "Path" });
for (const sdv::core::SModuleInfo& rsModuleInfo : seqModules)
vecModuleList.push_back({
std::to_string(static_cast<int64_t>(rsModuleInfo.tModuleID)),
rsModuleInfo.ssFilename,
std::to_string(rsModuleInfo.uiVersion / 100) + "." + std::to_string(rsModuleInfo.uiVersion % 100),
(rsModuleInfo.bActive ? " +" : " -"),
fnPrintRelPath(rsModuleInfo.ssPath) });
// Print the module list
PrintTable(vecModuleList, rstream, rsContext.bListNoHdr);
}
return NO_ERROR;
}
int ListClasses(const SContext& rsContext, const sdv::TObjectPtr& rptrRepository, std::ostream& rstream)
{
// Get access to the module control service
sdv::core::IObjectAccess* pObjectAccess = rptrRepository.GetInterface<sdv::core::IObjectAccess>();
const sdv::core::IModuleInfo* pModuleInfo = nullptr;
if (pObjectAccess)
pModuleInfo = sdv::TInterfaceAccessPtr(pObjectAccess->GetObject("ModuleControlService")).
GetInterface<sdv::core::IModuleInfo>();
if (!pModuleInfo)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << MODULE_CONTROL_SERVICE_ACCESS_ERROR_MSG << std::endl;
return MODULE_CONTROL_SERVICE_ACCESS_ERROR;
}
// Class type
auto fnPrintClassType = [](sdv::EObjectType eType)
{
switch (eType)
{
case sdv::EObjectType::SystemObject: return "SystemObject";
case sdv::EObjectType::Device: return "Device";
case sdv::EObjectType::BasicService: return "BasicService";
case sdv::EObjectType::ComplexService: return "ComplexService";
case sdv::EObjectType::Application: return "Application";
case sdv::EObjectType::Proxy: return "Proxy";
case sdv::EObjectType::Stub: return "Stub";
case sdv::EObjectType::Utility: return "Utility";
default: return "Unknown";
}
};
// Class flags
auto fnPrintClassFlags = [](uint32_t uiFlags)
{
hlpr::flags<sdv::EObjectFlags> flags(uiFlags);
std::stringstream sstream;
if (flags.check(sdv::EObjectFlags::singleton))
{
if (!sstream.str().empty()) sstream << ", ";
sstream << "Singleton";
}
return std::string("{") + sstream.str() + std::string("}");
};
// Dependencies
auto fnPrintList = [](const sdv::sequence<sdv::u8string>& rseqList)
{
std::stringstream sstream;
for (const sdv::u8string& rss : rseqList)
{
if (!sstream.str().empty()) sstream << ", ";
sstream << rss;
}
return std::string("{") + sstream.str() + std::string("}");
};
// Create the module list
if (rsContext.bListShort)
{
std::vector<std::array<std::string, 2>> vecShortClassList;
vecShortClassList.push_back({ "Class name", "Aliases"});
sdv::sequence<sdv::core::SModuleInfo> seqModules = pModuleInfo->GetModuleList();
for (const sdv::core::SModuleInfo& rsModuleInfo : seqModules)
{
sdv::sequence<sdv::SClassInfo> seqClasses = pModuleInfo->GetClassList(rsModuleInfo.tModuleID);
for (const sdv::SClassInfo& rsClassInfo : seqClasses)
vecShortClassList.push_back({ rsClassInfo.ssClassName, fnPrintList(rsClassInfo.seqClassAliases) });
}
// Print the module list
PrintTable(vecShortClassList, rstream, rsContext.bListNoHdr);
}
else
{
std::vector<std::array<std::string, 7>> vecClassList;
vecClassList.push_back({ "Class name", "Aliases", "Default name", "Type", "Flags", "Module ID", "Dependencies" });
sdv::sequence<sdv::core::SModuleInfo> seqModules = pModuleInfo->GetModuleList();
for (const sdv::core::SModuleInfo& rsModuleInfo : seqModules)
{
sdv::sequence<sdv::SClassInfo> seqClasses = pModuleInfo->GetClassList(rsModuleInfo.tModuleID);
for (const sdv::SClassInfo& rsClassInfo : seqClasses)
vecClassList.push_back({
rsClassInfo.ssClassName,
fnPrintList(rsClassInfo.seqClassAliases),
rsClassInfo.ssDefaultObjectName,
fnPrintClassType(rsClassInfo.eType),
fnPrintClassFlags(rsClassInfo.uiFlags),
std::to_string(static_cast<int64_t>(rsModuleInfo.tModuleID)),
fnPrintList(rsClassInfo.seqDependencies) });
}
// Print the module list
PrintTable(vecClassList, rstream, rsContext.bListNoHdr);
}
return NO_ERROR;
}
int ListComponents(const SContext& rsContext, const sdv::TObjectPtr& rptrRepository, std::ostream& rstream)
{
// Get access to the module control service
sdv::core::IObjectAccess* pObjectAccess = rptrRepository.GetInterface<sdv::core::IObjectAccess>();
const sdv::core::IRepositoryInfo* pRepoInfo = nullptr;
if (pObjectAccess)
pRepoInfo = sdv::TInterfaceAccessPtr(pObjectAccess->GetObject("RepositoryService")).
GetInterface<sdv::core::IRepositoryInfo>();
if (!pRepoInfo)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << REPOSITORY_SERVICE_ACCESS_ERROR_MSG << std::endl;
return REPOSITORY_SERVICE_ACCESS_ERROR;
}
// Class type
auto fnPrintClassType = [](sdv::EObjectType eType)
{
switch (eType)
{
case sdv::EObjectType::SystemObject: return "SystemObject";
case sdv::EObjectType::Device: return "Device";
case sdv::EObjectType::BasicService: return "BasicService";
case sdv::EObjectType::ComplexService: return "ComplexService";
case sdv::EObjectType::Application: return "Application";
case sdv::EObjectType::Proxy: return "Proxy";
case sdv::EObjectType::Stub: return "Stub";
case sdv::EObjectType::Utility: return "Utility";
default: return "Unknown";
}
};
// Object flags
auto fnPrintObjectFlags = [](uint32_t uiFlags)
{
hlpr::flags<sdv::core::EObjectInfoFlags> flags(uiFlags);
std::stringstream sstream;
if (flags.check(sdv::core::EObjectInfoFlags::object_controlled))
{
if (!sstream.str().empty()) sstream << ", ";
sstream << "Controlled";
}
if (flags.check(sdv::core::EObjectInfoFlags::object_foreign))
{
if (!sstream.str().empty()) sstream << ", ";
sstream << "Foreign";
}
if (flags.check(sdv::core::EObjectInfoFlags::object_isolated))
{
if (!sstream.str().empty()) sstream << ", ";
sstream << "Isolated";
}
return std::string("{") + sstream.str() + std::string("}");
};
// Create the object list
if (rsContext.bListShort)
{
std::vector<std::array<std::string, 6>> vecShortObjectList;
vecShortObjectList.push_back({ "Object name" });
sdv::sequence<sdv::core::SObjectInfo> seqObjects = pRepoInfo->GetObjectList();
for (const sdv::core::SObjectInfo& rsObjectInfo : seqObjects)
vecShortObjectList.push_back({ rsObjectInfo.ssObjectName });
// Print the module list
PrintTable(vecShortObjectList, rstream, rsContext.bListNoHdr);
}
else
{
std::vector<std::array<std::string, 6>> vecObjectList;
vecObjectList.push_back({ "Object ID", "Class name", "Object name", "Type", "Flags", "Module ID" });
sdv::sequence<sdv::core::SObjectInfo> seqObjects = pRepoInfo->GetObjectList();
for (const sdv::core::SObjectInfo& rsObjectInfo : seqObjects)
vecObjectList.push_back({
std::to_string(static_cast<int64_t>(rsObjectInfo.tModuleID)),
rsObjectInfo.sClassInfo.ssClassName,
rsObjectInfo.ssObjectName,
fnPrintClassType(rsObjectInfo.sClassInfo.eType),
fnPrintObjectFlags(rsObjectInfo.uiFlags),
std::to_string(static_cast<int64_t>(rsObjectInfo.tModuleID)) });
// Print the module list
PrintTable(vecObjectList, rstream, rsContext.bListNoHdr);
}
return NO_ERROR;
}
int ListInstallations(const SContext& rsContext, const sdv::TObjectPtr& rptrRepository, std::ostream& rstream)
{
// Get access to the module control service
sdv::core::IObjectAccess* pObjectAccess = rptrRepository.GetInterface<sdv::core::IObjectAccess>();
const sdv::installation::IAppInstall* pAppInstall = nullptr;
if (pObjectAccess)
pAppInstall = sdv::TInterfaceAccessPtr(pObjectAccess->GetObject("ConfigService")).
GetInterface<sdv::installation::IAppInstall>();
if (!pAppInstall)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << CONFIG_SERVICE_ACCESS_ERROR_MSG << std::endl;
return CONFIG_SERVICE_ACCESS_ERROR;
}
// Create the object list
std::vector<std::array<std::string, 1>> vecInstallList;
vecInstallList.push_back({"Installation"});
sdv::sequence<sdv::u8string> seqInstallations = pAppInstall->GetInstallations();
for (const sdv::u8string& rssInstallation : seqInstallations)
vecInstallList.push_back({
rssInstallation});
// Print the module list
PrintTable(vecInstallList, rstream, rsContext.bListNoHdr);
return NO_ERROR;
}
int ListConnections(const SContext& /*rsContext*/, const sdv::TObjectPtr& /*rptrRepository*/, std::ostream& /*rstream = std::cout */ )
{
std::cout << "ERROR: " << NOT_IMPLEMENTED_MSG << " :-(" << std::endl;
return NOT_IMPLEMENTED;
}

View File

@@ -0,0 +1,69 @@
#ifndef LIST_ELEMENTS_H
#define LIST_ELEMENTS_H
#include "context.h"
#include <support/sequence.h>
#include <support/string.h>
#include <support/interface_ptr.h>
/**
* @brief Help for listing elements.
* @param[in] rsContext Reference to the context.
*/
void ListHelp(const SContext& rsContext);
/**
* @brief List elements in a table. Base function. The elements to list are to be parsed from the arguments. The context could
* indicate additional flags.
* @param[in] rsContext Reference to the context.
* @param[in] rstream The output stream to use for printing (table only).
* @return The application exit code. 0 is no error.
*/
int ListElements(const SContext& rsContext, std::ostream& rstream = std::cout);
/**
* @brief List the loaded server modules.
* @param[in] rsContext Reference to the context.
* @param[in] rptrRepository Reference to the saerver repository.
* @param[in] rstream The output stream to use for printing (table only).
* @return The application exit code. 0 is no error.
*/
int ListModules(const SContext& rsContext, const sdv::TObjectPtr& rptrRepository, std::ostream& rstream);
/**
* @brief List the available component classes.
* @param[in] rsContext Reference to the context.
* @param[in] rptrRepository Reference to the saerver repository.
* @param[in] rstream The output stream to use for printing (table only).
* @return The application exit code. 0 is no error.
*/
int ListClasses(const SContext& rsContext, const sdv::TObjectPtr& rptrRepository, std::ostream& rstream);
/**
* @brief List the instantiated server components.
* @param[in] rsContext Reference to the context.
* @param[in] rptrRepository Reference to the saerver repository.
* @param[in] rstream The output stream to use for printing (table only).
* @return The application exit code. 0 is no error.
*/
int ListComponents(const SContext& rsContext, const sdv::TObjectPtr& rptrRepository, std::ostream& rstream);
/**
* @brief List the installations.
* @param[in] rsContext Reference to the context.
* @param[in] rptrRepository Reference to the saerver repository.
* @param[in] rstream The output stream to use for printing (table only).
* @return The application exit code. 0 is no error.
*/
int ListInstallations(const SContext& rsContext, const sdv::TObjectPtr& rptrRepository, std::ostream& rstream);
/**
* @brief List the current connections.
* @param[in] rsContext Reference to the context.
* @param[in] rptrRepository Reference to the saerver repository.
* @param[in] rstream The output stream to use for printing (table only).
* @return The application exit code. 0 is no error.
*/
int ListConnections(const SContext& rsContext, const sdv::TObjectPtr& rptrRepository, std::ostream& rstream);
#endif // !defined LIST_ELEMENTS_H

View File

@@ -0,0 +1,223 @@
#include "../../global/process_watchdog.h"
#include <support/sdv_core.h>
#include "../../global/cmdlnparser/cmdlnparser.cpp"
#include "../../global/exec_dir_helper.h"
#include <support/mem_access.h>
#include <support/sdv_core.h>
#include <support/app_control.h>
#include <iostream>
#include <array>
#include <algorithm>
#include <thread>
#include "context.h"
#include "startup_shutdown.h"
#include "list_elements.h"
#include "start_stop_service.h"
#include "installation.h"
#include "../error_msg.h"
/**
* @brief Add a slag to the string.
* @param[in] rssString The string to add a flag to.
* @param[in] rssFlag The flag text to add.
*/
void AddFlagString(std::string& rssString, const std::string& rssFlag)
{
if (!rssString.empty()) rssString += " | ";
rssString += rssFlag;
}
#if defined(_WIN32) && defined(_UNICODE)
extern "C" int wmain(int iArgc, const wchar_t* rgszArgv[])
#else
extern "C" int main(int iArgc, const char* rgszArgv[])
#endif
{
// Workaround for GCC to make certain that POSIX thread library is loaded before the components are loaded.
// REASON: If the first call to a thread is done in a dynamic library, the application is already classified as single
// threaded and a termination is initiated.
// See: https://stackoverflow.com/questions/51209268/using-stdthread-in-a-library-loaded-with-dlopen-leads-to-a-sigsev
// NOTE EVE 27.05.2025: This task has been taken over by the process watchdog.
CProcessWatchdog watchdog;
// If not set, set the runtime location to the EXE directory.
if (sdv::app::CAppControl::GetFrameworkRuntimeDirectory().empty())
sdv::app::CAppControl::SetFrameworkRuntimeDirectory(GetExecDirectory());
if (sdv::app::CAppControl::GetComponentInstallDirectory().empty())
sdv::app::CAppControl::SetComponentInstallDirectory(GetExecDirectory());
CCommandLine cmdln(static_cast<uint32_t>(CCommandLine::EParseFlags::no_assignment_character));
SContext sContext;
bool bError = false;
bool bHelp = false;
bool bVersion = false;
std::string ssArgError;
try
{
auto& rArgHelpDef = cmdln.DefineOption("?", bHelp, "Show help. Use <COMMAND> --help for specific help on the command.");
rArgHelpDef.AddSubOptionName("help");
auto& rArgSilentDef = cmdln.DefineOption("s", sContext.bSilent, "Do not show any information on std::cout. Not compatible with 'verbose'.");
rArgSilentDef.AddSubOptionName("silent");
auto& rArgVerboseDef = cmdln.DefineOption("v", sContext.bVerbose, "Provide verbose information. Not compatible with 'silent'.");
rArgVerboseDef.AddSubOptionName("verbose");
cmdln.DefineSubOption("version", bVersion, "Show version information.");
cmdln.DefineSubOption("instance", sContext.uiInstanceID, "The instance ID of the SDV instance (default ID is 1000).");
cmdln.DefineSubOption("server_silent", sContext.bServerSilent, "Only used with STARTUP command: Server is started using "
"silent option. Not compatible with 'server_verbose'.");
cmdln.DefineSubOption("server_verbose", sContext.bServerVerbose, "Only used with STARTUP command: Server is started using "
"verbose option. Not compatible with 'server_silent'.");
cmdln.DefineSubOption("install_dir", sContext.pathInstallDir, "Only used with STARTUP command: Installation directory "
"(absolute or relative to the sdv_core executable).");
cmdln.DefineSubOption("no_header", sContext.bListNoHdr, "Only used with LIST command: Do not print a header for the "
"listing table.");
cmdln.DefineSubOption("short", sContext.bListShort, "Only used with LIST command: Print only the most essential "
"information as one column.");
cmdln.DefineDefaultArgument(sContext.seqCmdLine, "COMMAND");
cmdln.Parse(static_cast<size_t>(iArgc), rgszArgv);
} catch (const SArgumentParseException& rsExcept)
{
ssArgError = rsExcept.what();
bHelp = true;
bError = true;
}
if (!sContext.bSilent)
{
std::cout << "SDV Server Control Utility" << std::endl;
std::cout << "Copyright (C): 2022-2025 ZF Friedrichshafen AG" << std::endl;
std::cout << "Author: Erik Verhoeven" << std::endl << std::endl;
}
if (!ssArgError.empty() && !sContext.bSilent)
std::cerr << "ERROR: " << ssArgError << std::endl;
if (!bHelp && !bVersion && sContext.seqCmdLine.empty())
{
if (!sContext.bSilent)
std::cerr << "ERROR: Missing command!" << std::endl;
bHelp = true;
bError = true;
}
enum class ECommand { unknown, startup, shutdown, list, install, update, uninstall, start, stop } eCommand = ECommand::unknown;
if (!sContext.seqCmdLine.empty())
{
if (iequals(sContext.seqCmdLine[0], "STARTUP")) eCommand = ECommand::startup;
else if (iequals(sContext.seqCmdLine[0], "SHUTDOWN")) eCommand = ECommand::shutdown;
else if (iequals(sContext.seqCmdLine[0], "LIST")) eCommand = ECommand::list;
else if (iequals(sContext.seqCmdLine[0], "INSTALL")) eCommand = ECommand::install;
else if (iequals(sContext.seqCmdLine[0], "UPDATE")) eCommand = ECommand::update;
else if (iequals(sContext.seqCmdLine[0], "UNINSTALL")) eCommand = ECommand::uninstall;
else if (iequals(sContext.seqCmdLine[0], "START")) eCommand = ECommand::start;
else if (iequals(sContext.seqCmdLine[0], "STOP")) eCommand = ECommand::stop;
else
{
if (!sContext.bSilent)
std::cerr << "ERROR: Invalid command: " << sContext.seqCmdLine[0] << std::endl;
bHelp = true;
bError = true;
}
}
if (bHelp)
{
if (!sContext.bSilent)
{
if (bError)
std::cout << std::endl;
switch (eCommand)
{
case ECommand::startup:
case ECommand::shutdown:
StartupShutdownHelp(sContext);
break;
case ECommand::start:
case ECommand::stop:
StartStopServiceHelp(sContext);
break;
case ECommand::list:
ListHelp(sContext);
break;
case ECommand::install:
case ECommand::update:
case ECommand::uninstall:
InstallationHelp(sContext);
break;
default:
cmdln.PrintHelp(std::cout, R"code(Supported commands:
STARTUP Start the core application server
SHUTDOWN Stop the core application server
LIST List module/classes/component/installation/connection information.
INSTALL Install a new application or service.
UPDATE Update an existing installation.
UNINSTALL Uninstall an installed application or service.
START Start a service (complex services only).
STOP Stop a service (complex services only).
)code");
break;
}
}
return bError ? CMDLN_ARG_ERR : NO_ERROR;
}
if (bError) return CMDLN_ARG_ERR;
if (bVersion)
std::cout << "Version: " << (SDVFrameworkBuildVersion / 100) << "." << (SDVFrameworkBuildVersion % 100) << " build " <<
SDVFrameworkSubbuildVersion << " interface " << SDVFrameworkInterfaceVersion << std::endl;
if (sContext.seqCmdLine.empty()) return NO_ERROR; // Done.
// Start the application control
if (sContext.bVerbose)
std::cout << "Starting local application control.." << std::endl;
sdv::app::CAppControl appcontrol;
std::string ssAppConfig = std::string(R"code(
[Application]
Mode = "Maintenance"
Instance = )code") + std::to_string(sContext.uiInstanceID) + R"code(
)code";
if (!appcontrol.Startup(ssAppConfig))
{
if (!sContext.bSilent)
std::cerr << "ERROR: " << APP_CONTROL_STARTUP_ERROR_MSG << std::endl;
return APP_CONTROL_STARTUP_ERROR;
}
int iRet = 0;
switch (eCommand)
{
case ECommand::startup:
iRet = StartupSDVServer(sContext);
break;
case ECommand::shutdown:
iRet = ShutdownSDVServer(sContext);
break;
case ECommand::list:
iRet = ListElements(sContext);
break;
case ECommand::start:
iRet = StartService(sContext);
break;
case ECommand::stop:
iRet = StopService(sContext);
break;
case ECommand::install:
iRet = Install(sContext);
break;
case ECommand::update:
iRet = Update(sContext);
break;
case ECommand::uninstall:
iRet = Uninstall(sContext);
break;
default:
std::cout << "Command missing :-(" << std::endl;
break;
}
if (sContext.bVerbose) std::cout << "Shutting down local application control.." << std::endl;
appcontrol.Shutdown();
return iRet;
}

View File

@@ -0,0 +1,83 @@
#ifndef TABLE_H
#define TABLE_H
#include <string>
#include <vector>
#include <array>
#include <iostream>
/**
* @brief Print a table of a vector of rows.
* @tparam nSize The array size.
* @details the first line is the header.
* @param[in] rvecRows Reference to the vector of rows.
* @param[in] rstream The output stream to use for printing.
* @param[in] bNoHeader When set, skip the first row (the header row).
*/
template <size_t nSize>
void PrintTable(const std::vector<std::array<std::string, nSize>>& rvecRows, std::ostream& rstream, bool bNoHeader = false)
{
// Calculate the maximum length of the column by going through each cell.
bool bSkipFirst = bNoHeader;
size_t rgnLength[nSize] = {};
for (const auto& rrgssRow : rvecRows)
{
if (bSkipFirst)
{
bSkipFirst = false;
continue;
}
for (size_t nIndex = 0; nIndex < nSize; nIndex++)
{
if (rrgssRow[nIndex].size() > rgnLength[nIndex])
rgnLength[nIndex] = rrgssRow[nIndex].size();
}
}
// Print a cell
auto fnPrintCell = [&](size_t nIndex, const std::string& rssText)
{
// Print the text
rstream << rssText;
// Check index
if (nIndex == nSize - 1) // Last column; nl needed
rstream << std::endl;
else // Separator needed
rstream << std::string(rgnLength[nIndex] - rssText.size() + 2, ' ');
};
// Print each line
bSkipFirst = bNoHeader;
bool bInitialLine = !bNoHeader;
for (const auto& rrgssRow : rvecRows)
{
if (bSkipFirst)
{
bSkipFirst = false;
continue;
}
// Start with a space if a header is present
if (!bNoHeader)
rstream << " ";
// Print the text
for (size_t nIndex = 0; nIndex < nSize; nIndex++)
fnPrintCell(nIndex, rrgssRow[nIndex]);
// Need a vertical separator?
if (bInitialLine)
{
size_t nSeparatorLength = 1;
for (size_t nIndex = 0; nIndex < nSize; nIndex++)
nSeparatorLength += rgnLength[nIndex] + 2;
nSeparatorLength--;
rstream << std::string(nSeparatorLength, '-') << std::endl;
bInitialLine = false;
}
}
}
#endif // !defined TABLE_H

View File

@@ -0,0 +1,176 @@
#include "start_stop_service.h"
#include "../../global/cmdlnparser/cmdlnparser.h"
#include <interfaces/process.h>
#include <support/interface_ptr.h>
#include <support/local_service_access.h>
#include <interfaces/com.h>
#include <interfaces/app.h>
#include "../error_msg.h"
void StartStopServiceHelp(const SContext& rsContext)
{
// First argument should be "START" or "STOP".
if (rsContext.seqCmdLine.size() < 1)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: missing start/stop command..." << std::endl;
return;
}
if (iequals(rsContext.seqCmdLine[0], "START"))
{
CCommandLine::PrintHelpText(std::cout, "Usage: sdv_control START <name> [options...]\n"
" sdv_control START <class> <name> [options...]\n\n"
"Start a complex service with the supplied object name as is defined in the configuration (first usage) or the "
"supplied class name and with object name assignment and add to the configuration (second usage).\n"
"Only complex services can be started. If the service depends on not running other services, these are started as "
"well.\n\n");
return;
}
if (iequals(rsContext.seqCmdLine[0], "STOP"))
{
CCommandLine::PrintHelpText(std::cout, "Usage: sdv_control STOP <name> [options...]\n"
" sdv_control STOP <ID> [options...]\n"
" sdv_control STOP <class> [options...]\n"
" sdv_control STOP <module> [options...]\n\n"
"Stop the complex service(s) with the supplied object name (first usage), with supplied object ID (second usage), with "
"supplied class name (third usage) or contained in the module with the supplied module name (fourth usage).\n"
"Only complex services can be stopped. Dependent services are stopped as well. If one of object to be stopped is not a "
"complex service, the command fails.\n\n");
return;
}
if (!rsContext.bSilent)
std::cerr << "ERROR: invalid start/stop command..." << std::endl;
}
int StartService(const SContext& rsContext)
{
// First argument should be "START"
if (rsContext.seqCmdLine.size() < 2)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << CMDLN_ARG_ERR_MSG << " Not enough parameters for START command." << std::endl;
return CMDLN_ARG_ERR;
}
if (rsContext.seqCmdLine.size() > 3)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << CMDLN_ARG_ERR_MSG << " Too many parameters for START command." << std::endl;
return CMDLN_ARG_ERR;
}
if (!iequals(rsContext.seqCmdLine[0], "START"))
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << CMDLN_ARG_ERR_MSG << " Invalid command: " << rsContext.seqCmdLine[0] << std::endl;
return CMDLN_ARG_ERR;
}
// Determine the service to start
sdv::u8string ssClass, ssName;
if (rsContext.seqCmdLine.size() == 3)
{
ssClass = rsContext.seqCmdLine[1];
ssName = rsContext.seqCmdLine[2];
} else
ssName = rsContext.seqCmdLine[1];
// Try to connect
sdv::TObjectPtr ptrRepository = sdv::com::ConnectToLocalServerRepository(rsContext.uiInstanceID);
if (!ptrRepository)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << CONNECT_SDV_SERVER_ERROR_MSG << " Instance #" << rsContext.uiInstanceID << std::endl;
return CONNECT_SDV_SERVER_ERROR;
}
// Get access to the module control service
sdv::core::IObjectAccess* pObjectAccess = ptrRepository.GetInterface<sdv::core::IObjectAccess>();
sdv::core::IRepositoryControl* pRepoControl = nullptr;
if (pObjectAccess)
pRepoControl = sdv::TInterfaceAccessPtr(pObjectAccess->GetObject("RepositoryService")).
GetInterface<sdv::core::IRepositoryControl>();
if (!pRepoControl)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << REPOSITORY_SERVICE_ACCESS_ERROR_MSG << std::endl;
return REPOSITORY_SERVICE_ACCESS_ERROR;
}
// Create the object
sdv::core::TObjectID tObjectID = pRepoControl->CreateObject(ssClass, ssName, {});
if (!tObjectID)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << START_OBJECT_ERROR_MSG << " Object name: " << ssName << std::endl;
return START_OBJECT_ERROR;
}
// All good...
return NO_ERROR;
}
int StopService(const SContext& rsContext)
{
// First argument should be "STOP"
if (rsContext.seqCmdLine.size() < 2)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << CMDLN_ARG_ERR_MSG << " Not enough parameters for STOP command." << std::endl;
return CMDLN_ARG_ERR;
}
if (rsContext.seqCmdLine.size() > 3)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << CMDLN_ARG_ERR_MSG << " Too many parameters for STOP command." << std::endl;
return CMDLN_ARG_ERR;
}
if (!iequals(rsContext.seqCmdLine[0], "STOP"))
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << CMDLN_ARG_ERR_MSG << " Invalid command : " << rsContext.seqCmdLine[0] << std::endl;
return CMDLN_ARG_ERR;
}
// Determine the service to start
sdv::u8string ssClass, ssName;
if (rsContext.seqCmdLine.size() == 3)
{
ssClass = rsContext.seqCmdLine[1];
ssName = rsContext.seqCmdLine[2];
} else
ssName = rsContext.seqCmdLine[1];
// Try to connect
sdv::TObjectPtr ptrRepository = sdv::com::ConnectToLocalServerRepository(rsContext.uiInstanceID);
if (!ptrRepository)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << CONNECT_SDV_SERVER_ERROR_MSG << " Instance #" << rsContext.uiInstanceID << std::endl;
return CONNECT_SDV_SERVER_ERROR;
}
// Get access to the module control service
sdv::core::IObjectAccess* pObjectAccess = ptrRepository.GetInterface<sdv::core::IObjectAccess>();
sdv::core::IRepositoryControl* pRepoControl = nullptr;
if (pObjectAccess)
pRepoControl = sdv::TInterfaceAccessPtr(pObjectAccess->GetObject("RepositoryService")).
GetInterface<sdv::core::IRepositoryControl>();
if (!pRepoControl)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << REPOSITORY_SERVICE_ACCESS_ERROR_MSG << std::endl;
return REPOSITORY_SERVICE_ACCESS_ERROR;
}
// Create the object
sdv::core::TObjectID tObjectID = pRepoControl->DestroyObject(ssName);
if (!tObjectID)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << STOP_OBJECT_ERROR_MSG << " Object name: " << ssName << std::endl;
return STOP_OBJECT_ERROR;
}
// All good...
return NO_ERROR;
}

View File

@@ -0,0 +1,30 @@
#ifndef START_STOP_SERVICE_H
#define START_STOP_SERVICE_H
#include "context.h"
#include <support/sequence.h>
#include <support/string.h>
#include <support/interface_ptr.h>
/**
* @brief Help for start or stop a complex service.
* @param[in] rsContext Reference to the context.
*/
void StartStopServiceHelp(const SContext& rsContext);
/**
* @brief Start the service.
* @details The command line context includes the service to start or stop.
* @param[in] rsContext Reference to the context.
* @return The application exit code. 0 is no error.
*/
int StartService(const SContext& rsContext);
/**
* @brief Stop the complex service.
* @param[in] rsContext Reference to the context.
* @return The application exit code. 0 is no error.
*/
int StopService(const SContext& rsContext);
#endif // !defined START_STOP_SERVICE_H

View File

@@ -0,0 +1,206 @@
#include "startup_shutdown.h"
#include "../../global/cmdlnparser/cmdlnparser.h"
#include <interfaces/process.h>
#include <support/interface_ptr.h>
#include <support/local_service_access.h>
#include <interfaces/com.h>
#include <interfaces/app.h>
#include "../error_msg.h"
sdv::process::TProcessID g_tServerProcessID = 0;
/**
* @brief Process lifetime monitor struct
*/
struct SProcessLifetimeCallback : sdv::IInterfaceAccess, sdv::process::IProcessLifetimeCallback
{
// Interface map
BEGIN_SDV_INTERFACE_MAP()
SDV_INTERFACE_ENTRY(sdv::process::IProcessLifetimeCallback)
END_SDV_INTERFACE_MAP()
/**
* @brief Called when the process was terminated.
* @remarks The process return value is not always valid. The validity depends on the support of the underlying system.
* @param[in] tProcessID The process ID of the process being terminated.
* @param[in] iRetValueParam Process return value or 0 when not supported.
*/
virtual void ProcessTerminated([[maybe_unused]] /*in*/ sdv::process::TProcessID tProcessID, /*in*/ int64_t iRetValueParam) override
{
iRetValue = iRetValueParam;
}
/**
* @brief Get the process return value or 0 when no value was set.
* @return The process return value.
*/
int64_t GetRetValue() const
{
return iRetValue;
}
int64_t iRetValue = 0; ///< Process return value to be updated by the process monitor.
};
void StartupShutdownHelp(const SContext& rsContext)
{
// First and only argument should be "STARTUP" or "SHUTDOWN".
if (rsContext.seqCmdLine.size() < 1)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: missing startup/shutdown command..." << std::endl;
return;
}
if (rsContext.seqCmdLine.size() > 1)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: too many commands following startup/shutdown command..." << std::endl;
return;
}
if (iequals(rsContext.seqCmdLine[0], "STARTUP"))
{
CCommandLine::PrintHelpText(std::cout, "Usage: sdv_control STARTUP [options...]\n\n"
"Start the SDV server with the supplied instance ID. If no instance ID is supplied, use the default instance "
"ID #1000.\n\n"
"Options:\n"
" --server_silent Server is started using silent option. Not compatible with 'server_verbose'.\n"
" --server_verbose Server is started using verbose option. Not compatible with 'server_silent'.\n"
" --install_dir Installation directory (absolute or relative to the sdv_core executable).\n\n");
return;
}
if (iequals(rsContext.seqCmdLine[0], "SHUTDOWN"))
{
CCommandLine::PrintHelpText(std::cout, "Usage: sdv_control SHUTDOWN [options...]\n\nStop the SDV server with the supplied "
"instance ID. If no instance ID is supplied, use the default instance ID #1000.\n\n");
return;
}
if (!rsContext.bSilent)
std::cerr << "ERROR: invalid startup/shutdown command..." << std::endl;
}
int StartupSDVServer(const SContext& rsContext)
{
if (rsContext.seqCmdLine.size() != 1)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << CMDLN_ARG_ERR_MSG << " Invalid arguments following STARTUP command." << std::endl;
return CMDLN_ARG_ERR;
}
if (!rsContext.bSilent)
std::cout << "Starting the SDV Server..." << std::endl;
// Get the process control service
sdv::process::IProcessControl* pProcessControl = sdv::core::GetObject<sdv::process::IProcessControl>("ProcessControlService");
sdv::process::IProcessLifetime* pProcessLifetime = sdv::core::GetObject<sdv::process::IProcessLifetime>("ProcessControlService");
if (!pProcessControl || !pProcessLifetime)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << PROCESS_CONTROL_SERVICE_ACCESS_ERROR_MSG << std::endl;
return PROCESS_CONTROL_SERVICE_ACCESS_ERROR;
}
sdv::sequence<sdv::u8string> seqArgTemp;
if (rsContext.bVerbose)
std::cout << "Instance ID: " << rsContext.uiInstanceID << std::endl;
seqArgTemp.push_back("-server");
seqArgTemp.push_back("--instance" + std::to_string(rsContext.uiInstanceID));
seqArgTemp.push_back("--no_banner");
if (rsContext.bServerSilent) seqArgTemp.push_back("--silent");
if (rsContext.bServerVerbose) seqArgTemp.push_back("--verbose");
if (!rsContext.pathInstallDir.empty())
seqArgTemp.push_back("--install_dir\"" + rsContext.pathInstallDir.generic_u8string() + "\"");
g_tServerProcessID = pProcessControl->Execute("sdv_core", seqArgTemp, sdv::process::EProcessRights::parent_rights);
if (!g_tServerProcessID)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << STARTUP_CORE_ERROR_MSG << std::endl;
return STARTUP_CORE_ERROR;
}
// Register the process monitor...
SProcessLifetimeCallback sProcessLifetimeCallback;
uint32_t uiCookie = pProcessLifetime->RegisterMonitor(g_tServerProcessID, &sProcessLifetimeCallback);
if (!uiCookie)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << REGISTER_PROCESS_MONITOR_ERROR_MSG << std::endl;
return REGISTER_PROCESS_MONITOR_ERROR;
}
if (rsContext.bVerbose)
std::cout << "Process ID: " << g_tServerProcessID << std::endl;
// Wait for 3 seconds to see if the process terminates... (which should not occur).
bool bTerminated = pProcessLifetime->WaitForTerminate(g_tServerProcessID, 3000);
pProcessLifetime->UnregisterMonitor(uiCookie);
if (bTerminated)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << CORE_NOT_STARTED_MSG << " Instance: #" << rsContext.uiInstanceID << " Exit code: " <<
sProcessLifetimeCallback.iRetValue << std::endl;
return CORE_NOT_STARTED;
}
// Try to connect
auto ptrRepository = sdv::com::ConnectToLocalServerRepository(rsContext.uiInstanceID);
if (!ptrRepository)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << CONNECT_SDV_SERVER_ERROR_MSG << " SDV Server instance #:" << rsContext.uiInstanceID <<
std::endl;
return CONNECT_SDV_SERVER_ERROR;
}
if (!rsContext.bSilent)
std::cout << "SDV Server #" << rsContext.uiInstanceID << " has successfully started..." << std::endl;
return NO_ERROR;
}
int ShutdownSDVServer(const SContext& rsContext)
{
if (rsContext.seqCmdLine.size() != 1)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << CMDLN_ARG_ERR_MSG << " Invalid arguments following SHUTDOWN command." << std::endl;
return CMDLN_ARG_ERR;
}
if (!rsContext.bSilent)
std::cout << "Connecting to the SDV #" << rsContext.uiInstanceID << " server..." << std::endl;
// Try to connect
auto ptrRepository = sdv::com::ConnectToLocalServerRepository(rsContext.uiInstanceID);
if (!ptrRepository)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << CONNECT_SDV_SERVER_ERROR_MSG << std::endl;
return CONNECT_SDV_SERVER_ERROR;
}
if (!rsContext.bSilent)
std::cout << "Requesting shutdown..." << std::endl;
sdv::core::IObjectAccess* pObjAccess = ptrRepository.GetInterface<sdv::core::IObjectAccess>();
sdv::app::IAppShutdownRequest* pShutdownRequest = nullptr;
if (pObjAccess)
pShutdownRequest = sdv::TInterfaceAccessPtr(pObjAccess->GetObject("AppControlService")).
GetInterface<sdv::app::IAppShutdownRequest>();
if (pShutdownRequest)
pShutdownRequest->RequestShutdown();
ptrRepository.Clear();
if (!pShutdownRequest)
{
if (!rsContext.bSilent)
std::cerr << "ERROR: " << APP_CONTROL_SERVICE_ACCESS_ERROR_MSG << std::endl;
return APP_CONTROL_SERVICE_ACCESS_ERROR;
}
if (!rsContext.bSilent)
std::cout << "Shutdown initiated..." << std::endl;
return NO_ERROR;
}
sdv::process::TProcessID GetServerProcessID()
{
return g_tServerProcessID;
}

View File

@@ -0,0 +1,39 @@
#ifndef STARTUP_SHUTDOWN_H
#define STARTUP_SHUTDOWN_H
#include "context.h"
#include <vector>
#include <string>
#include <support/sequence.h>
#include <support/string.h>
#include <interfaces/process.h>
/**
* @brief Help for startup or shutdown the server.
* @param[in] rsContext Reference to the context.
*/
void StartupShutdownHelp(const SContext& rsContext);
/**
* @brief Startup the SDV server.
* @param[in] rsContext Reference to the context.
* @return The application exit code. 0 is no error.
*/
int StartupSDVServer(const SContext& rsContext);
/**
* @brief Shutdown the SDV Server.
* @param[in] rsContext Reference to the context.
* @return The application exit code. 0 is no error.
*/
int ShutdownSDVServer(const SContext& rsContext);
/**
* @brief Get the server process ID after a startup of the server.
* @remarks The process ID is set when server execution is initiated. The presence of the process ID doesn't guarantee a successful
* running of the server.
* @return The process ID if the process execution was initiated. Or zero when the execution failed.
*/
sdv::process::TProcessID GetServerProcessID();
#endif // !defined STARTUP_SHUTDOWN_H