connection between 2 systems and bug fixes (#14)

This commit is contained in:
tompzf
2026-07-03 14:53:48 +02:00
committed by GitHub
parent 0fb5660d27
commit 46842200f1
284 changed files with 35200 additions and 8265 deletions

View File

@@ -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)

View File

@@ -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__)

View File

@@ -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

View File

@@ -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;

View 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__)

View 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__)