mirror of
https://github.com/eclipse-openvehicle-api/openvehicle-api.git
synced 2026-08-30 04:15:10 +00:00
connection between 2 systems and bug fixes (#14)
This commit is contained in:
@@ -12,20 +12,55 @@ if(UNIX)
|
||||
# Define project
|
||||
project(uds_unix_sockets VERSION 1.0 LANGUAGES CXX)
|
||||
|
||||
# Define target
|
||||
add_library(uds_unix_sockets STATIC
|
||||
# Build sources once and expose them in two forms:
|
||||
# - uds_unix_sockets: runtime-loadable .sdv module for ModuleControl
|
||||
# - uds_unix_sockets_static: static link target for unit tests / static consumers
|
||||
set(UDS_UNIX_SOCKETS_SOURCES
|
||||
channel_mgnt.cpp
|
||||
connection.cpp
|
||||
)
|
||||
watchdog.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(uds_unix_sockets rt ${CMAKE_DL_LIBS} ${CMAKE_THREAD_LIBS_INIT})
|
||||
add_library(uds_unix_sockets SHARED
|
||||
${UDS_UNIX_SOCKETS_SOURCES}
|
||||
)
|
||||
|
||||
add_library(uds_unix_sockets_static STATIC
|
||||
${UDS_UNIX_SOCKETS_SOURCES}
|
||||
)
|
||||
|
||||
target_include_directories(uds_unix_sockets
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
./include/
|
||||
)
|
||||
|
||||
target_include_directories(uds_unix_sockets_static
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
./include/
|
||||
)
|
||||
|
||||
target_link_libraries(uds_unix_sockets
|
||||
PUBLIC
|
||||
rt
|
||||
${CMAKE_DL_LIBS}
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
)
|
||||
|
||||
target_link_libraries(uds_unix_sockets_static
|
||||
PUBLIC
|
||||
rt
|
||||
${CMAKE_DL_LIBS}
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
)
|
||||
|
||||
target_include_directories(uds_unix_sockets PRIVATE ./include/)
|
||||
set_target_properties(uds_unix_sockets PROPERTIES PREFIX "")
|
||||
set_target_properties(uds_unix_sockets PROPERTIES SUFFIX ".sdv")
|
||||
|
||||
# Build dependencies
|
||||
add_dependencies(uds_unix_sockets CompileCoreIDL)
|
||||
add_dependencies(uds_unix_sockets_static CompileCoreIDL)
|
||||
|
||||
# Appending the service in the service list
|
||||
set(SDV_Service_List ${SDV_Service_List} uds_unix_sockets PARENT_SCOPE)
|
||||
|
||||
@@ -81,19 +81,19 @@ namespace
|
||||
// Directory selection (/run/user/<uid>/sdv or /tmp/sdv)
|
||||
std::string CUnixDomainSocketsChannelMgnt::MakeUserRuntimeDir()
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << "/run/user/" << ::getuid();
|
||||
const std::string path = "/run/ipc/sdv";
|
||||
|
||||
struct stat st{};
|
||||
if (::stat(oss.str().c_str(), &st) == 0)
|
||||
if (::stat(path.c_str(), &st) == 0)
|
||||
{
|
||||
std::string path = oss.str() + "/sdv";
|
||||
::mkdir(path.c_str(), 0770);
|
||||
return path;
|
||||
}
|
||||
|
||||
::mkdir("/tmp/sdv", 0770);
|
||||
return "/tmp/sdv";
|
||||
// fallback if /run/ipc/sdv is not available
|
||||
const std::string fallback = "/tmp/sdv";
|
||||
::mkdir(fallback.c_str(), 0770);
|
||||
|
||||
return fallback;
|
||||
}
|
||||
|
||||
bool CUnixDomainSocketsChannelMgnt::OnInitialize()
|
||||
@@ -102,7 +102,11 @@ bool CUnixDomainSocketsChannelMgnt::OnInitialize()
|
||||
}
|
||||
|
||||
void CUnixDomainSocketsChannelMgnt::OnShutdown()
|
||||
{}
|
||||
|
||||
void CUnixDomainSocketsChannelMgnt::OnDestroy()
|
||||
{
|
||||
m_watchdog.Clear();
|
||||
}
|
||||
|
||||
// Endpoint creation (server)
|
||||
@@ -129,33 +133,133 @@ sdv::ipc::SChannelEndpoint CUnixDomainSocketsChannelMgnt::CreateEndpoint(const s
|
||||
|
||||
path = ClampSunPath(path);
|
||||
|
||||
// Use a shared_ptr and store it to keep the server connection alive
|
||||
auto server = std::make_shared<CUnixSocketConnection>(-1, true, path);
|
||||
m_ServerConnections.push_back(server);
|
||||
// Keep lifetime consistent with shared_mem: watchdog owns active connections.
|
||||
std::shared_ptr<CUnixSocketConnection> server = std::make_shared<CUnixSocketConnection>(-1, true, path);
|
||||
// Ignore cppcheck warning; if construction failed, an exception is expected first.
|
||||
// cppcheck-suppress knownConditionTrueFalse
|
||||
if (!server)
|
||||
return {};
|
||||
server->SetWatchDogRemoveCallback([this](const void* connection)
|
||||
{
|
||||
m_watchdog.RemoveConnection(connection);
|
||||
});
|
||||
m_watchdog.AddConnection(server);
|
||||
|
||||
sdv::ipc::SChannelEndpoint ep{};
|
||||
ep.pConnection = static_cast<IInterfaceAccess*>(server.get());
|
||||
ep.ssConnectString = server->GetConnectionString();
|
||||
|
||||
// Publish a Provider-wrapped connect string for compatibility with
|
||||
// CCommunicationControl::CreateClientConnection() flows.
|
||||
const std::string udsConnectString = server->GetConnectionString();
|
||||
ep.ssConnectString = std::string("[Provider]\n") +
|
||||
"Name = \"unix_domain_sockets\"\n" +
|
||||
"ConnectString = \"" + udsConnectString + "\"\n";
|
||||
return ep;
|
||||
}
|
||||
|
||||
// Access existing endpoint (server or client)
|
||||
sdv::IInterfaceAccess* CUnixDomainSocketsChannelMgnt::Access(const sdv::u8string& ssConnectString)
|
||||
{
|
||||
const auto kv = ParseKV(static_cast<std::string>(ssConnectString));
|
||||
const bool isServer = (kv.count("role") && kv.at("role") == "server");
|
||||
const std::string path = kv.count("path") ? kv.at("path") : (MakeUserRuntimeDir() + "/UDS_auto.sock");
|
||||
const std::string input = static_cast<std::string>(ssConnectString);
|
||||
|
||||
if (isServer)
|
||||
bool isServer = false;
|
||||
bool parsed = false;
|
||||
std::string path;
|
||||
|
||||
// Parse structured TOML forms first, but only for non-raw inputs.
|
||||
// Raw connect strings (proto=uds;...) are intentionally not TOML and
|
||||
// would produce noisy parser diagnostics like "Missing value".
|
||||
if (input.rfind("proto=uds", 0) != 0)
|
||||
{
|
||||
auto server = std::make_shared<CUnixSocketConnection>(-1, true, path);
|
||||
m_ServerConnections.push_back(server);
|
||||
return static_cast<IInterfaceAccess*>(server.get());
|
||||
sdv::toml::CTOMLParser parser(input);
|
||||
if (!parser.IsValid())
|
||||
return nullptr;
|
||||
|
||||
const std::string providerName = parser.GetDirect("Provider.Name").GetValue();
|
||||
if (!providerName.empty())
|
||||
{
|
||||
if (providerName != "unix_domain_sockets" &&
|
||||
providerName != "UnixSocketsChannelControl" &&
|
||||
providerName != "UnixDomainSocketsChannelControl")
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const std::string nested = parser.GetDirect("Provider.ConnectString").GetValue();
|
||||
if (!nested.empty())
|
||||
{
|
||||
const auto kv = ParseKV(nested);
|
||||
if (kv.count("path"))
|
||||
path = kv.at("path");
|
||||
}
|
||||
|
||||
// Provider descriptions are consumed as client endpoints.
|
||||
parsed = true;
|
||||
isServer = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Shared-memory style callers pass [IpcChannel] config directly to Access().
|
||||
parsed = true;
|
||||
isServer = false;
|
||||
}
|
||||
|
||||
if (path.empty())
|
||||
{
|
||||
auto pathNode = parser.GetDirect("IpcChannel.Path");
|
||||
if (pathNode.GetType() == sdv::toml::ENodeType::node_string)
|
||||
{
|
||||
path = static_cast<std::string>(pathNode.GetValue());
|
||||
}
|
||||
else
|
||||
{
|
||||
auto nameNode = parser.GetDirect("IpcChannel.Name");
|
||||
if (nameNode.GetType() == sdv::toml::ENodeType::node_string)
|
||||
{
|
||||
const std::string name = static_cast<std::string>(nameNode.GetValue());
|
||||
if (!name.empty())
|
||||
path = MakeUserRuntimeDir() + "/" + name + ".sock";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Client: allocated raw pointer (expected to be managed by SDV framework)
|
||||
auto* client = new CUnixSocketConnection(-1, false, path);
|
||||
return static_cast<IInterfaceAccess*>(client);
|
||||
// Raw KV fallback (legacy/tests): proto=uds;role=...;path=...;
|
||||
if (!parsed)
|
||||
{
|
||||
// Only treat as raw format when it actually starts with proto=uds.
|
||||
if (input.rfind("proto=uds", 0) != 0)
|
||||
return nullptr;
|
||||
|
||||
const auto kv = ParseKV(input);
|
||||
parsed = true;
|
||||
isServer = (kv.count("role") && kv.at("role") == "server");
|
||||
if (kv.count("path"))
|
||||
path = kv.at("path");
|
||||
}
|
||||
|
||||
if (!parsed)
|
||||
return nullptr;
|
||||
|
||||
if (path.empty())
|
||||
path = MakeUserRuntimeDir() + "/UDS_auto.sock";
|
||||
|
||||
path = ClampSunPath(path);
|
||||
|
||||
std::shared_ptr<CUnixSocketConnection> connection =
|
||||
std::make_shared<CUnixSocketConnection>(-1, isServer, path);
|
||||
// Ignore cppcheck warning; if construction failed, an exception is expected first.
|
||||
// cppcheck-suppress knownConditionTrueFalse
|
||||
if (!connection)
|
||||
return nullptr;
|
||||
|
||||
connection->SetWatchDogRemoveCallback([this](const void* instance)
|
||||
{
|
||||
m_watchdog.RemoveConnection(instance);
|
||||
});
|
||||
|
||||
m_watchdog.AddConnection(connection);
|
||||
return static_cast<IInterfaceAccess*>(connection.get());
|
||||
}
|
||||
|
||||
#endif // defined(__unix__)
|
||||
@@ -17,6 +17,8 @@
|
||||
|
||||
#include <support/component_impl.h>
|
||||
#include <interfaces/ipc.h>
|
||||
#include "watchdog.h"
|
||||
#include <algorithm>
|
||||
|
||||
class CUnixSocketConnection;
|
||||
|
||||
@@ -37,9 +39,9 @@ public:
|
||||
|
||||
// Object declarations
|
||||
DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::system_object)
|
||||
DECLARE_OBJECT_CLASS_NAME("UnixDomainSocketsChannelControl")
|
||||
DECLARE_OBJECT_CLASS_ALIAS("LocalChannelControl")
|
||||
DECLARE_DEFAULT_OBJECT_NAME("LocalChannelControl")
|
||||
DECLARE_OBJECT_CLASS_NAME("UnixSocketsChannelControl")
|
||||
DECLARE_OBJECT_CLASS_ALIAS("unix_domain_sockets")
|
||||
DECLARE_DEFAULT_OBJECT_NAME("unix_domain_sockets")
|
||||
DECLARE_OBJECT_SINGLETON()
|
||||
|
||||
/**
|
||||
@@ -53,6 +55,11 @@ public:
|
||||
*/
|
||||
virtual void OnShutdown() override;
|
||||
|
||||
/**
|
||||
* @brief Last function called before destruction. Overload of sdv::CSdvObject::OnDestroy.
|
||||
*/
|
||||
virtual void OnDestroy() override;
|
||||
|
||||
/**
|
||||
* @brief Create IPC connection object and return the endpoint information. Overload of
|
||||
* sdv::ipc::ICreateEndpoint::CreateEndpoint.
|
||||
@@ -80,8 +87,7 @@ private:
|
||||
|
||||
// Helper: choose runtime dir (/run/user/<uid>/sdv) or fallback (/tmp/sdv)
|
||||
static std::string MakeUserRuntimeDir();
|
||||
|
||||
std::vector<std::shared_ptr<CUnixSocketConnection>> m_ServerConnections;
|
||||
CUnixSocketsConnectionWatchDog m_watchdog;
|
||||
|
||||
};
|
||||
DEFINE_SDV_OBJECT(CUnixDomainSocketsChannelMgnt)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -22,6 +22,8 @@
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <condition_variable>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
@@ -98,6 +100,9 @@ public:
|
||||
/** @brief Destroy object (IObjectDestroy). */
|
||||
void DestroyObject() override;
|
||||
|
||||
/** @brief Register callback used to remove this connection from manager watchdog storage. */
|
||||
void SetWatchDogRemoveCallback(std::function<void(const void*)> callback);
|
||||
|
||||
/** @brief Set state and notify listeners (callback-safe). */
|
||||
void SetConnectState(sdv::ipc::EConnectState eConnectState);
|
||||
|
||||
@@ -153,21 +158,27 @@ public:
|
||||
SMsgHdr GetMsgHdr() const
|
||||
{
|
||||
if (GetSize() < sizeof(SMsgHdr)) return SMsgHdr{0, EMsgType::connect_term};
|
||||
return *reinterpret_cast<const SMsgHdr*>(GetData());
|
||||
SMsgHdr hdr{};
|
||||
std::memcpy(&hdr, GetData(), sizeof(SMsgHdr));
|
||||
return hdr;
|
||||
}
|
||||
|
||||
/** @return SDV connect header (or default if undersized). */
|
||||
SConnectMsg GetConnectHdr() const
|
||||
{
|
||||
if (GetSize() < sizeof(SConnectMsg)) return SConnectMsg{};
|
||||
return *reinterpret_cast<const SConnectMsg*>(GetData());
|
||||
SConnectMsg hdr{};
|
||||
std::memcpy(&hdr, GetData(), sizeof(SConnectMsg));
|
||||
return hdr;
|
||||
}
|
||||
|
||||
/** @return SDV fragmented header (or default if undersized). */
|
||||
SFragmentedMsgHdr GetFragmentedHdr() const
|
||||
{
|
||||
if (GetSize() < sizeof(SFragmentedMsgHdr)) return SFragmentedMsgHdr{};
|
||||
return *reinterpret_cast<const SFragmentedMsgHdr*>(GetData());
|
||||
SFragmentedMsgHdr hdr{};
|
||||
std::memcpy(&hdr, GetData(), sizeof(SFragmentedMsgHdr));
|
||||
return hdr;
|
||||
}
|
||||
|
||||
/** @return true if the SDV envelope is well-formed. */
|
||||
@@ -253,6 +264,10 @@ public:
|
||||
*/
|
||||
void ReceiveMessages();
|
||||
|
||||
bool SendConnectMessage(EMsgType type);
|
||||
|
||||
bool SendControlMessage(EMsgType type);
|
||||
|
||||
/**
|
||||
* @brief Handle an incoming sync_request message.
|
||||
*
|
||||
@@ -360,6 +375,10 @@ public:
|
||||
*/
|
||||
void StopThreadsAndCloseSockets(bool unlinkPath);
|
||||
|
||||
void WaitForClientEnd();
|
||||
|
||||
void CloseClientSocketOnly();
|
||||
|
||||
private:
|
||||
//Transport state
|
||||
int m_Fd { -1 }; ///< Active connection FD.
|
||||
@@ -370,19 +389,24 @@ private:
|
||||
//Threads & control
|
||||
std::atomic<bool> m_StopReceiveThread { false };
|
||||
std::atomic<bool> m_StopConnectThread { false };
|
||||
std::thread m_ReceiveThread;
|
||||
std::thread m_ConnectThread;
|
||||
sdv::core::secure_thread m_ReceiveThread;
|
||||
sdv::core::secure_thread m_ConnectThread;
|
||||
|
||||
//State & synchronization
|
||||
std::condition_variable m_StateCv;
|
||||
std::atomic<sdv::ipc::EConnectState> m_eConnectState { sdv::ipc::EConnectState::uninitialized };
|
||||
std::atomic<bool> m_bConnectedOnce { false }; ///< Latched true once a connection was established.
|
||||
sdv::ipc::IDataReceiveCallback* m_pReceiver { nullptr };
|
||||
sdv::ipc::IConnectEventCallback* m_pEvent { nullptr };
|
||||
std::mutex m_MtxConnect;
|
||||
std::condition_variable m_CvConnect;
|
||||
std::condition_variable m_cvConnect;
|
||||
std::mutex m_StateMtx; ///< Protects receiver/event assignment.
|
||||
std::list<SEventCallback> m_lstEventCallbacks; ///< List containing event callbacks
|
||||
std::shared_mutex m_mtxEventCallbacks; ///< Protect access to callback list
|
||||
std::mutex m_WatchdogMtx;
|
||||
std::function<void(const void*)> m_WatchdogRemoveCallback;
|
||||
std::atomic<bool> m_DestroyObjectCalled { false };
|
||||
std::atomic<bool> m_CancelWait { false };
|
||||
|
||||
//TX synchronization
|
||||
std::mutex m_SendMtx;
|
||||
|
||||
58
sdv_services/uds_unix_sockets/watchdog.cpp
Normal file
58
sdv_services/uds_unix_sockets/watchdog.cpp
Normal file
@@ -0,0 +1,58 @@
|
||||
/********************************************************************************
|
||||
* Copyright (c) 2025-2026 ZF Friedrichshafen AG
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Apache License Version 2.0 which is available at
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Contributors:
|
||||
* Denisa Ros - initial API and implementation
|
||||
********************************************************************************/
|
||||
|
||||
#if defined(__unix__)
|
||||
|
||||
#include "watchdog.h"
|
||||
|
||||
void CUnixSocketsConnectionWatchDog::AddConnectionImpl(const std::shared_ptr<void>& connection)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_Mutex);
|
||||
m_Connections[connection.get()] = connection;
|
||||
}
|
||||
|
||||
void CUnixSocketsConnectionWatchDog::RemoveConnection(const void* connection)
|
||||
{
|
||||
if (!connection)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::shared_ptr<void> removed;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_Mutex);
|
||||
auto it = m_Connections.find(connection);
|
||||
if (it == m_Connections.end())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
removed = std::move(it->second);
|
||||
m_Connections.erase(it);
|
||||
}
|
||||
|
||||
removed.reset();
|
||||
}
|
||||
|
||||
void CUnixSocketsConnectionWatchDog::Clear()
|
||||
{
|
||||
std::map<const void*, std::shared_ptr<void>> localConnections;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_Mutex);
|
||||
localConnections.swap(m_Connections);
|
||||
}
|
||||
|
||||
localConnections.clear();
|
||||
}
|
||||
|
||||
#endif // defined(__unix__)
|
||||
47
sdv_services/uds_unix_sockets/watchdog.h
Normal file
47
sdv_services/uds_unix_sockets/watchdog.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/********************************************************************************
|
||||
* Copyright (c) 2025-2026 ZF Friedrichshafen AG
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Apache License Version 2.0 which is available at
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Contributors:
|
||||
* Denisa Ros - initial API and implementation
|
||||
********************************************************************************/
|
||||
|
||||
#if defined(__unix__)
|
||||
#ifndef UDS_UNIX_SOCKETS_WATCHDOG_H
|
||||
#define UDS_UNIX_SOCKETS_WATCHDOG_H
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
class CUnixSocketsConnectionWatchDog
|
||||
{
|
||||
public:
|
||||
template<typename T>
|
||||
void AddConnection(const std::shared_ptr<T>& connection)
|
||||
{
|
||||
if (!connection)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AddConnectionImpl(std::static_pointer_cast<void>(connection));
|
||||
}
|
||||
|
||||
void RemoveConnection(const void* connection);
|
||||
void Clear();
|
||||
|
||||
private:
|
||||
void AddConnectionImpl(const std::shared_ptr<void>& connection);
|
||||
|
||||
std::mutex m_Mutex;
|
||||
std::map<const void*, std::shared_ptr<void>> m_Connections;
|
||||
};
|
||||
|
||||
#endif // UDS_UNIX_SOCKETS_WATCHDOG_H
|
||||
#endif // defined(__unix__)
|
||||
Reference in New Issue
Block a user