/* * SPDX-License-Identifier: LicenseRef-Unisoc-General-1.0 * Copyright 2016-2023 Unisoc (Shanghai) Technologies Co., Ltd * Licensed under the Unisoc General Software License, version 1.0 (the License); * you may not use this file except in compliance with the License. You may obtain a copy of the License at * https://www.unisoc.com/en_us/license/UNISOC_GENERAL_LICENSE_V1.0-EN_US * Software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OF ANY KIND, either express or implied. * See the Unisoc General Software License, version 1.0 for more details. */ #include #include #include "InternalError.h" #include #include #include #include "MessageInternal.h" #include "ConnectionInternal.h" using namespace UDBus; Object::Object(Connection &conn, const UDBus::Path &path, const char *service) : mConn(conn), mPath(path), mService(service ? service : "") { } Object::~Object() { } struct UDbusObjectAdaptor::UDBusPrivate { static void FuncStub_UnRegister(DBusConnection *, void *); static DBusHandlerResult FuncStub_Message(DBusConnection *, DBusMessage *, void *); }; static DBusObjectPathVTable _vtable = { UDbusObjectAdaptor::UDBusPrivate::FuncStub_UnRegister, UDbusObjectAdaptor::UDBusPrivate::FuncStub_Message, nullptr, nullptr, nullptr, nullptr }; typedef std::map UDbusObjectAdaptorTable; static UDbusObjectAdaptorTable sAdaptorTable; void UDbusObjectAdaptor::UDBusPrivate::FuncStub_UnRegister(DBusConnection *conn, void *data) { } DBusHandlerResult UDbusObjectAdaptor::UDBusPrivate::FuncStub_Message(DBusConnection *, DBusMessage *dbusmsg, void *data) { UDbusObjectAdaptor *obj = static_cast(data); if (obj) { Message message(new Message::UDBusPrivate(dbusmsg)); ERROR("%s serial:%d %s -> %s", obj->GetPath().c_str(), message.Serial(), message.Sender(), message.Destination()); return obj->HandleMessage(message) ? DBUS_HANDLER_RESULT_HANDLED : DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } else { ERROR("no obj registered") return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } } UDbusObjectAdaptor *UDbusObjectAdaptor::FromPath(const UDBus::Path &path) { UDbusObjectAdaptorTable::iterator adapti = sAdaptorTable.find(path); if (adapti != sAdaptorTable.end()) return adapti->second; return nullptr; } UDbusObjectAdaptorPList UDbusObjectAdaptor::FromPathPrefix(const std::string &prefix) { UDbusObjectAdaptorPList ali; UDbusObjectAdaptorTable::iterator adapti = sAdaptorTable.begin(); size_t plen = prefix.length(); while (adapti != sAdaptorTable.end()) { if (!strncmp(adapti->second->GetPath().c_str(), prefix.c_str(), plen)) ali.push_back(adapti->second); ++adapti; } return ali; } ObjectPathList UDbusObjectAdaptor::ChildNodesFromPrefix(const std::string &prefix) { ObjectPathList objPathlist; UDbusObjectAdaptorTable::iterator iter = sAdaptorTable.begin(); size_t plen = prefix.length(); while (iter != sAdaptorTable.end()) { if (iter->second->GetPath() != "/" && !strncmp(iter->second->GetPath().c_str(), prefix.c_str(), plen)) { std::string path = iter->second->GetPath().substr(plen); path = path.substr(0, path.find('/')); objPathlist.push_back(path); } ++iter; } objPathlist.sort(); objPathlist.unique(); return objPathlist; } UDbusObjectAdaptor::UDbusObjectAdaptor(Connection &Connection, const UDBus::Path &path) : Object(Connection, path, Connection.UniqueName()), mEflag(USE_EXCEPTIONS) { RegisterObj(); } UDbusObjectAdaptor::UDbusObjectAdaptor(Connection &Connection, const UDBus::Path &path, RegistrationTime rtime) : Object(Connection, path, Connection.UniqueName()), mEflag(USE_EXCEPTIONS) { if (rtime == REGISTER_NOW) RegisterObj(); } UDbusObjectAdaptor::UDbusObjectAdaptor(Connection &Connection, const UDBus::Path &path, RegistrationTime rtime, ExceptionsFlag eflag) : Object(Connection, path, Connection.UniqueName()), mEflag(eflag) { if (rtime == REGISTER_NOW) RegisterObj(); } UDbusObjectAdaptor::~UDbusObjectAdaptor() { try { UnRegisterObj(); } catch(UDBusError &e) { DEBUG("Error{%s}", e.what()); } } void UDbusObjectAdaptor::RegisterObj() { DEBUG("registering local object %s", GetPath().c_str()); if (GetConn().mPrivate->Connection == nullptr) { throw ErrorInvalidArgs("nullptr connection"); } else if (GetPath().c_str() == nullptr) { throw ErrorInvalidArgs("nullptr path"); } else if (GetPath().c_str()[0] != '/') { std::string message = "Path must start with '/': " + GetPath(); throw ErrorInvalidArgs(message.c_str()); } DBusError error = DBUS_ERROR_INIT; if (!dbus_connection_try_register_object_path( GetConn().mPrivate->Connection, GetPath().c_str(), &_vtable, this, &error)) { if (dbus_error_has_name(&error, DBUS_ERROR_OBJECT_PATH_IN_USE)) { dbus_error_free(&error); throw ErrorObjectPathInUse("unable to register object path"); } else { dbus_error_free(&error); throw ErrorNoMemory("unable to register object path"); } } sAdaptorTable[GetPath()] = this; } void UDbusObjectAdaptor::UnRegisterObj() { if (!IsRegistered()) return; sAdaptorTable.erase(GetPath()); DEBUG("unregistering local object %s", GetPath().c_str()); if (GetConn().mPrivate->Connection == nullptr) { throw ErrorInvalidArgs("nullptr connection"); } else if (GetPath().c_str() == nullptr) { throw ErrorInvalidArgs("nullptr path"); } else if (GetPath().c_str()[0] != '/') { std::string message = "Path must start with '/': " + GetPath(); throw ErrorInvalidArgs(message.c_str()); } dbus_connection_unregister_object_path(GetConn().mPrivate->Connection, GetPath().c_str()); } bool UDbusObjectAdaptor::IsRegistered() { return sAdaptorTable.find(GetPath()) != sAdaptorTable.end(); } void UDbusObjectAdaptor::EmitSignalInternal(SignalMessage &sig) { sig.SetPath(GetPath().c_str()); GetConn().Send(sig); } struct UDbusReturnLaterError { const UDBus::Tag *tag; }; // for dbus method_call message bool UDbusObjectAdaptor::HandleMessage(const Message &msg) { switch (msg.Type()) { case DBUS_MESSAGE_TYPE_METHOD_CALL: { const CallMessage &cmsg = reinterpret_cast(msg); const char* member = cmsg.GetMember(); const char* interface = cmsg.GetInterface(); DEBUG(" invoking method %s.%s", interface, member); UDbusInterfaceAdaptor *interfaceProxy = FindInterface(interface); if (interfaceProxy) { if (mEflag == AVOID_EXCEPTIONS) { Message ret = interfaceProxy->DispatchMethod(cmsg); if (cmsg.isSync == false) return true; Tag *tag = ret.GetTag(); if (tag) { mContinuations[tag] = new UDbusContinuation(GetConn(), cmsg, tag); } else { GetConn().Send(ret); } return true; } try { Message ret = interfaceProxy->DispatchMethod(cmsg); if (cmsg.isSync == false) { return true; } GetConn().Send(ret); } catch(UDBusError &e) { UDbusErrorMessage em(cmsg, e.GetName(), e.message()); GetConn().Send(em); } catch(UDbusReturnLaterError &UDbusRle) { mContinuations[UDbusRle.tag] = new UDbusContinuation(GetConn(), cmsg, UDbusRle.tag); } return true; } else { return false; } } default: return false; } } void UDbusObjectAdaptor::ReturnLater(const UDBus::Tag *tag) { UDbusReturnLaterError rle = { tag }; throw rle; } void UDbusObjectAdaptor::ReturnNow(UDbusContinuation *ret) { ret->mConn.Send(ret->mReturn); ContinuationMap::iterator di = mContinuations.find(ret->mTag); delete di->second; mContinuations.erase(di); } void UDbusObjectAdaptor::ReturnError(UDbusContinuation *ret, const UDBusError error) { ret->mConn.Send(UDbusErrorMessage(ret->mCall, error.GetName(), error.message())); ContinuationMap::iterator di = mContinuations.find(ret->mTag); delete di->second; mContinuations.erase(di); } bool UDbusObjectAdaptor::ReplyAsyncMessage(const CallMessage & cmsg, const Message &msg) { try { GetConn().Send(msg); } catch(UDBusError &e) { UDbusErrorMessage em(msg, e.GetName(), e.message()); GetConn().Send(em); } catch(UDbusReturnLaterError &UDbusRle) { mContinuations[UDbusRle.tag] = new UDbusContinuation(GetConn(), cmsg, UDbusRle.tag); } return true; } UDbusObjectAdaptor::UDbusContinuation *UDbusObjectAdaptor::FindContinuation(const UDBus::Tag *tag) { ContinuationMap::iterator di = mContinuations.find(tag); return di != mContinuations.end() ? di->second : nullptr; } UDbusObjectAdaptor::UDbusContinuation::UDbusContinuation(Connection &Connection, const CallMessage &call, const UDBus::Tag *tag) : mConn(Connection), mCall(call), mReturn(mCall), mTag(tag) { mWriter = mReturn.Writer(); } UDbusObjectProxy::UDbusObjectProxy(Connection &Connection, const UDBus::Path &path, const char *service) : Object(Connection, path, service) { RegisterObj(); } UDbusObjectProxy::~UDbusObjectProxy() { CancelPendingCalls(); try { UnRegisterObj(); } catch(UDBusError &e) { DEBUG("Error{%s}", e.what()); } } void UDbusObjectProxy::CancelPendingCalls() { PendingCallList::const_iterator pi = mPendingCalls.begin(); while (pi != mPendingCalls.end()) { (*pi)->Cancel(); delete *pi; ++pi; } mPendingCalls.clear(); } void UDbusObjectProxy::_RemovePendingCall(PendingCall *pending) { PendingCallList::iterator iter = mPendingCalls.begin(); while (iter != mPendingCalls.end()) { if (pending == *iter) { mPendingCalls.erase(iter); break; } ++iter; } delete pending; } bool UDbusObjectProxy::IsRegistered() { return true; } void UDbusObjectProxy::RegisterObj() { DEBUG("registering remote object %s", GetPath().c_str()); mFiltered = new Callback(this, &UDbusObjectProxy::HandleMessage); GetConn().AddFilter(mFiltered); InterfaceProxyTable::const_iterator interfaceProxy = mInterfaces.begin(); while (interfaceProxy != mInterfaces.end()) { std::string im = "type='signal',interface='" + interfaceProxy->first + "',path='" + GetPath() + "'"; GetConn().AddMatch(im.c_str()); ++interfaceProxy; } DEBUG(""); } void UDbusObjectProxy::UnRegisterObj() { DEBUG("unregistering remote object %s", GetPath().c_str()); InterfaceProxyTable::const_iterator interfaceProxy = mInterfaces.begin(); while (interfaceProxy != mInterfaces.end()) { std::string im = "type='signal',interface='" + interfaceProxy->first + "',path='" + GetPath() + "'"; GetConn().RmoveMatch(im.c_str()); ++interfaceProxy; } GetConn().RemoveFilter(mFiltered); } Message UDbusObjectProxy::_InvokeMethod(CallMessage &call) { if (call.GetPath() == nullptr) call.SetPath(GetPath().c_str()); if (call.Destination() == nullptr) call.SetDestination(GetService().c_str()); return GetConn().SendBlocking(call); } Message UDbusObjectProxy::_InvokeMethodWaitTime(CallMessage &call, int timeout) { if (call.GetPath() == nullptr) call.SetPath(GetPath().c_str()); if (call.Destination() == nullptr) call.SetDestination(GetService().c_str()); return GetConn().SendBlocking(call, timeout); } bool UDbusObjectProxy::_InvokeMethodNoReply(CallMessage &call) { if (call.GetPath() == nullptr) call.SetPath(GetPath().c_str()); if (call.Destination() == nullptr) call.SetDestination(GetService().c_str()); return GetConn().Send(call); } // for dbus signal message bool UDbusObjectProxy::HandleMessage(const Message &msg) { switch (msg.Type()) { case DBUS_MESSAGE_TYPE_SIGNAL: { const SignalMessage &smsg = reinterpret_cast(msg); const char* interface = smsg.GetInterface(); const char* member = smsg.GetMember(); const char* objpath = smsg.GetPath(); if (objpath != GetPath()) return false; ERROR("filtered signal %s(in %s) from %s to object %s", member, interface, msg.Sender(), objpath); InterfaceProxy* interfaceProxy = FindInterface(interface); if (interfaceProxy) { bool done = true; try { done = interfaceProxy->DispatchSignal(smsg); } catch(UDBusError &e) { ERROR("Error{%s}", e.what()); done = true; } return done; } else { ERROR("interfaceProxy is null return false"); return false; } } default: return false; } } PendingCall *UDbusObjectProxy::_InvokeMethodAsync(CallMessage &call, int timeout) { if (call.GetPath() == nullptr) call.SetPath(GetPath().c_str()); if (call.Destination() == nullptr) call.SetDestination(GetService().c_str()); PendingCall *pending = GetConn().SendAsync(call, timeout); mPendingCalls.push_back(pending); return pending; }