2026-08-19 15:50:49 -03:00
|
|
|
#include "logos_provider_object.h"
|
|
|
|
|
#include "logos_api.h"
|
|
|
|
|
#include "token_manager.h"
|
2026-08-22 18:37:01 -03:00
|
|
|
// LOGOS_PROTOCOL_VERSION_{MAJOR,MINOR} for the guard below — by name, so an
|
|
|
|
|
// undefined macro cannot quietly turn the guard FALSE and leave every module
|
|
|
|
|
// with a permanently unknown caller.
|
|
|
|
|
#include "logos_protocol.h"
|
2026-08-19 15:50:49 -03:00
|
|
|
#include <QDebug>
|
2026-08-22 18:37:01 -03:00
|
|
|
#include <QMetaObject>
|
|
|
|
|
#include <QString>
|
|
|
|
|
|
|
|
|
|
#if defined(LOGOS_PROTOCOL_VERSION_MINOR) && (LOGOS_PROTOCOL_VERSION_MAJOR > 0 || \
|
|
|
|
|
(LOGOS_PROTOCOL_VERSION_MAJOR == 0 && LOGOS_PROTOCOL_VERSION_MINOR >= 6))
|
|
|
|
|
// Only for logos::callerUnknownJson() — the fail-closed document. NOT for
|
|
|
|
|
// logos::currentInboundCallerJson(): calling that here would read THIS image's
|
|
|
|
|
// thread-local, which is the bug this whole file exists to avoid. See below.
|
|
|
|
|
#include "logos_caller_scope.h"
|
|
|
|
|
#endif
|
2026-08-19 15:50:49 -03:00
|
|
|
|
|
|
|
|
// The LogosProviderObject universal-interface defaults and Std bridges moved
|
|
|
|
|
// to logos-protocol (logos_provider_interface.cpp) together with the abstract
|
|
|
|
|
// interface. What remains here is LogosProviderBase — the base the generated
|
|
|
|
|
// Qt plugin glue (<name>_cdylib_glue.cpp) derives from — because it talks to
|
|
|
|
|
// LogosAPI, which layers above the protocol. It used to be hand-derived by
|
|
|
|
|
// `interface: "provider"` module code; that interface is gone, and module code
|
|
|
|
|
// now derives from logos-cpp-sdk's Qt-free LogosModuleContext instead.
|
|
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
// LogosProviderBase
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
void LogosProviderBase::init(void* apiInstance)
|
|
|
|
|
{
|
|
|
|
|
m_logosAPI = static_cast<LogosAPI*>(apiInstance);
|
|
|
|
|
qDebug() << "[LogosProviderObject] LogosProviderBase::init called";
|
|
|
|
|
onInit(m_logosAPI);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool LogosProviderBase::informModuleToken(const QString& moduleName, const QString& token)
|
|
|
|
|
{
|
|
|
|
|
if (!m_logosAPI) {
|
|
|
|
|
qWarning() << "[LogosProviderObject] informModuleToken: LogosAPI not available";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TokenManager* tokenManager = m_logosAPI->getTokenManager();
|
|
|
|
|
if (!tokenManager) {
|
|
|
|
|
qWarning() << "[LogosProviderObject] informModuleToken: TokenManager not available";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-24 14:24:03 -03:00
|
|
|
// THE INBOUND DOOR, and the direction is the whole of the choice.
|
|
|
|
|
//
|
|
|
|
|
// `moduleName` here is the module that will CALL US; `token` is what it
|
|
|
|
|
// will present. saveToken() is the OUTBOUND half — "what I present when I
|
|
|
|
|
// call moduleName" — and writing here landed both directions in one map
|
|
|
|
|
// under one key, which is what let a grant <m -> b> silently also authorize
|
|
|
|
|
// <b -> m> and let this push clobber our own per-target cache for the same
|
|
|
|
|
// peer. See the DIRECTION note in logos-protocol cpp/token_manager.h and
|
|
|
|
|
// the detector at tests/protocol/test_token_direction.cpp.
|
|
|
|
|
qDebug() << "[LogosProviderObject] Saving inbound token for caller:" << moduleName;
|
|
|
|
|
tokenManager->saveInboundToken(moduleName, token);
|
2026-08-19 15:50:49 -03:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-22 18:37:01 -03:00
|
|
|
// THE PULL. Everything about the four lines below is deliberate; see the
|
|
|
|
|
// declaration in logos_provider_object.h and logos_caller_scope.h in
|
|
|
|
|
// logos-protocol for the measurement behind it.
|
|
|
|
|
//
|
|
|
|
|
// invokeMethod, NOT m_logosAPI->currentCallerJson(). This translation unit is
|
|
|
|
|
// compiled into the MODULE image, which links its own copy of LogosAPI —
|
|
|
|
|
// meta-object included — with its own function-local statics at its own
|
|
|
|
|
// addresses and no undefined reference to the host's. A direct call binds to
|
|
|
|
|
// THIS image's copy and reads THIS image's caller thread-local, which no
|
|
|
|
|
// CallerScope ever wrote. It is silently empty forever on macOS and Windows,
|
|
|
|
|
// and correct on Linux, so a green Linux run proves nothing about it.
|
|
|
|
|
// invokeMethod resolves through metaObject()/qt_metacall — virtual, vptr
|
|
|
|
|
// written by the HOST's constructor — and therefore lands in host code.
|
|
|
|
|
//
|
|
|
|
|
// BY NAME, so this string and the Q_INVOKABLE in logos_api.h are one
|
|
|
|
|
// contract with no compiler between them. tests/test-caller-contract.nix is
|
|
|
|
|
// what holds them together; without it a rename is a silent, permanent
|
|
|
|
|
// Unknown across the whole fleet.
|
|
|
|
|
//
|
|
|
|
|
// DirectConnection, because the answer is per-THREAD. The scope is open on
|
|
|
|
|
// the thread the dispatch was delivered on, and this call must read that
|
|
|
|
|
// thread's slot rather than hop to LogosAPI's owner thread. (Qt also refuses
|
|
|
|
|
// Q_RETURN_ARG on a queued connection, so the alternative is a runtime
|
|
|
|
|
// warning and a permanent Unknown, not a build failure.)
|
|
|
|
|
std::string LogosProviderBase::currentCallerJson() const
|
|
|
|
|
{
|
|
|
|
|
#if defined(LOGOS_PROTOCOL_VERSION_MINOR) && (LOGOS_PROTOCOL_VERSION_MAJOR > 0 || \
|
|
|
|
|
(LOGOS_PROTOCOL_VERSION_MAJOR == 0 && LOGOS_PROTOCOL_VERSION_MINOR >= 6))
|
|
|
|
|
QString caller;
|
|
|
|
|
if (m_logosAPI
|
|
|
|
|
&& QMetaObject::invokeMethod(m_logosAPI, "currentCallerJson",
|
|
|
|
|
Qt::DirectConnection,
|
|
|
|
|
Q_RETURN_ARG(QString, caller))
|
|
|
|
|
&& !caller.isEmpty()) {
|
|
|
|
|
return caller.toStdString();
|
|
|
|
|
}
|
|
|
|
|
// No LogosAPI yet, a host too old to carry the invokable, or an empty
|
|
|
|
|
// answer. Fail closed to the ONE document logos-protocol produces for
|
|
|
|
|
// "could not name the caller" — never to an empty string, which on the
|
|
|
|
|
// module-impl C ABI is not a weaker identity but a different operation.
|
|
|
|
|
return logos::callerUnknownJson();
|
|
|
|
|
#else
|
|
|
|
|
// Below protocol 0.6 there is no caller surface at either end: no
|
|
|
|
|
// logos_module_set_call_caller for the glue to push into, and no
|
|
|
|
|
// callerUnknownJson() to name. Empty is the absence of the whole mechanism,
|
|
|
|
|
// and the glue's push is guarded on this same expression, so nothing reads
|
|
|
|
|
// it.
|
|
|
|
|
return std::string();
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-19 15:50:49 -03:00
|
|
|
void LogosProviderBase::emitEvent(const QString& eventName, const QVariantList& data)
|
|
|
|
|
{
|
|
|
|
|
if (m_eventCallback) {
|
|
|
|
|
qDebug() << "[LogosProviderObject] emitEvent:" << eventName;
|
|
|
|
|
m_eventCallback(eventName, data);
|
|
|
|
|
} else {
|
|
|
|
|
qWarning() << "[LogosProviderObject] emitEvent: no listener set for" << eventName;
|
|
|
|
|
}
|
|
|
|
|
}
|