399 lines
10 KiB
C++
399 lines
10 KiB
C++
/*
|
|||
|
|
* 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 <Debug.h>
|
||
|
|
#include <Connection.h>
|
||
|
|
|
||
|
|
#include <dbus/dbus.h>
|
||
|
|
#include <string>
|
||
|
|
|
||
|
|
#include "InternalError.h"
|
||
|
|
|
||
|
|
#include "ConnectionInternal.h"
|
||
|
|
#include "DispatcherInternal.h"
|
||
|
|
#include "MessageInternal.h"
|
||
|
|
#include "PendingcallInternal.h"
|
||
|
|
|
||
|
|
using namespace UDBus;
|
||
|
|
|
||
|
|
Connection::PrivateSuffix::PrivateSuffix(DBusBusType BusType)
|
||
|
|
:dispatcher(nullptr)
|
||
|
|
{
|
||
|
|
UDbusInternalError err;
|
||
|
|
Connection = dbus_bus_get_private(BusType, err);
|
||
|
|
|
||
|
|
if (err)
|
||
|
|
throw UDBusError(err);
|
||
|
|
|
||
|
|
InitSuffix();
|
||
|
|
}
|
||
|
|
|
||
|
|
Connection::PrivateSuffix::~PrivateSuffix()
|
||
|
|
{
|
||
|
|
if (dbus_connection_get_is_connected(Connection)) {
|
||
|
|
std::vector<std::string>::iterator s = VtNames.begin();
|
||
|
|
|
||
|
|
while (s != VtNames.end()) {
|
||
|
|
dbus_bus_release_name(Connection, s->c_str(), nullptr);
|
||
|
|
++s;
|
||
|
|
}
|
||
|
|
dbus_connection_close(Connection);
|
||
|
|
}
|
||
|
|
dbus_connection_unref(Connection);
|
||
|
|
}
|
||
|
|
|
||
|
|
void Connection::PrivateSuffix::InitSuffix()
|
||
|
|
{
|
||
|
|
DEBUG("");
|
||
|
|
dbus_connection_ref(Connection);
|
||
|
|
dbus_connection_ref(Connection); //todo: the library has to own another reference
|
||
|
|
|
||
|
|
disconnFilter = new Callback<Connection::PrivateSuffix, bool, const Message &>(this, &Connection::PrivateSuffix::DisconnFilterFunction);
|
||
|
|
if (!dbus_connection_add_filter(Connection, MessageFilterStub, &disconnFilter, nullptr))
|
||
|
|
{
|
||
|
|
dbus_connection_unref(Connection);
|
||
|
|
dbus_connection_unref(Connection);
|
||
|
|
throw ErrorFailed("dbus_connection_add_filter failed");
|
||
|
|
}
|
||
|
|
dbus_connection_set_dispatch_status_function(Connection, DispatchStatusStub, this, 0);
|
||
|
|
dbus_connection_set_exit_on_disconnect(Connection, false); //why was this set to true??
|
||
|
|
}
|
||
|
|
|
||
|
|
bool Connection::PrivateSuffix::DoDispatch()
|
||
|
|
{
|
||
|
|
if (!dbus_connection_get_is_connected(Connection)) {
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
return dbus_connection_dispatch(Connection) != DBUS_DISPATCH_DATA_REMAINS;
|
||
|
|
}
|
||
|
|
|
||
|
|
void Connection::PrivateSuffix::DispatchStatusStub(DBusConnection *dbuscon, DBusDispatchStatus status, void *data)
|
||
|
|
{
|
||
|
|
PrivateSuffix *ps = static_cast<PrivateSuffix *>(data);
|
||
|
|
switch (status) {
|
||
|
|
case DBUS_DISPATCH_DATA_REMAINS:
|
||
|
|
ps->dispatcher->QueueConnection(ps);
|
||
|
|
break;
|
||
|
|
|
||
|
|
case DBUS_DISPATCH_COMPLETE:
|
||
|
|
break;
|
||
|
|
|
||
|
|
case DBUS_DISPATCH_NEED_MEMORY: //uh oh...
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
DBusHandlerResult Connection::PrivateSuffix::MessageFilterStub(DBusConnection *Connection, DBusMessage *dmsg, void *data_void)
|
||
|
|
{
|
||
|
|
MessageSlot *Msglot = static_cast<MessageSlot *>(data_void);
|
||
|
|
|
||
|
|
Message NewMsg = Message(new Message::UDBusPrivate(dmsg));
|
||
|
|
return Msglot && !Msglot->IsEmpty() && Msglot->Call(NewMsg)
|
||
|
|
? DBUS_HANDLER_RESULT_HANDLED
|
||
|
|
: DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool Connection::PrivateSuffix::DisconnFilterFunction(const Message &CMsg)
|
||
|
|
{
|
||
|
|
if (CMsg.IsSignal(DBUS_INTERFACE_LOCAL, "Disconnected")) {
|
||
|
|
dbus_connection_close(Connection);
|
||
|
|
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
DBusDispatchStatus Connection::PrivateSuffix::DispatchStatus()
|
||
|
|
{
|
||
|
|
return dbus_connection_get_dispatch_status(Connection);
|
||
|
|
}
|
||
|
|
|
||
|
|
bool Connection::PrivateSuffix::HasSomethingToDispatch()
|
||
|
|
{
|
||
|
|
return DispatchStatus() == DBUS_DISPATCH_DATA_REMAINS;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
Connection Connection::SystemBus()
|
||
|
|
{
|
||
|
|
return Connection(new PrivateSuffix(DBUS_BUS_SYSTEM));
|
||
|
|
}
|
||
|
|
|
||
|
|
Connection Connection::SessionBus()
|
||
|
|
{
|
||
|
|
return Connection(new PrivateSuffix(DBUS_BUS_SESSION));
|
||
|
|
}
|
||
|
|
|
||
|
|
Connection Connection::ActivationBus()
|
||
|
|
{
|
||
|
|
return Connection(new PrivateSuffix(DBUS_BUS_STARTER));
|
||
|
|
}
|
||
|
|
|
||
|
|
Connection::Connection(Connection::PrivateSuffix *p)
|
||
|
|
: mPrivate(p), mTimeout(-1)
|
||
|
|
{
|
||
|
|
Setup(gDefaultDispatcher);
|
||
|
|
}
|
||
|
|
|
||
|
|
Connection::Connection(const Connection &c)
|
||
|
|
: mPrivate(c.mPrivate), mTimeout(c.mTimeout)
|
||
|
|
{
|
||
|
|
dbus_connection_ref(mPrivate->Connection);
|
||
|
|
}
|
||
|
|
|
||
|
|
Connection::~Connection()
|
||
|
|
{
|
||
|
|
dbus_connection_unref(mPrivate->Connection);
|
||
|
|
}
|
||
|
|
|
||
|
|
Dispatcher *Connection::Setup(Dispatcher *dispatcher)
|
||
|
|
{
|
||
|
|
|
||
|
|
if (!dispatcher) dispatcher = gDefaultDispatcher;
|
||
|
|
|
||
|
|
if (!dispatcher) throw ErrorFailed("no default dispatcher set for new connection");
|
||
|
|
|
||
|
|
mPrivate->dispatcher = dispatcher;
|
||
|
|
dispatcher->QueueConnection(mPrivate.Get());
|
||
|
|
dbus_connection_set_watch_functions(
|
||
|
|
mPrivate->Connection,
|
||
|
|
Dispatcher::UDBusPrivate::OnAddWatch,
|
||
|
|
Dispatcher::UDBusPrivate::OnRemoveWatch,
|
||
|
|
Dispatcher::UDBusPrivate::OnToggleWatch,
|
||
|
|
dispatcher,
|
||
|
|
nullptr
|
||
|
|
);
|
||
|
|
dbus_connection_set_timeout_functions(
|
||
|
|
mPrivate->Connection,
|
||
|
|
Dispatcher::UDBusPrivate::OnAddTimeout,
|
||
|
|
Dispatcher::UDBusPrivate::OnRemTimeout,
|
||
|
|
Dispatcher::UDBusPrivate::OnToggleTimeout,
|
||
|
|
dispatcher,
|
||
|
|
nullptr
|
||
|
|
);
|
||
|
|
return dispatcher;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool Connection::operator == (const Connection &c) const
|
||
|
|
{
|
||
|
|
return mPrivate->Connection == c.mPrivate->Connection;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool Connection::RegisterBus()
|
||
|
|
{
|
||
|
|
UDbusInternalError e;
|
||
|
|
bool r = dbus_bus_register(mPrivate->Connection, e);
|
||
|
|
|
||
|
|
if (e) throw (e);
|
||
|
|
|
||
|
|
return r;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool Connection::Connected() const
|
||
|
|
{
|
||
|
|
return dbus_connection_get_is_connected(mPrivate->Connection);
|
||
|
|
}
|
||
|
|
|
||
|
|
void Connection::Disconnect()
|
||
|
|
{
|
||
|
|
dbus_connection_close(mPrivate->Connection);
|
||
|
|
}
|
||
|
|
|
||
|
|
void Connection::ExitOnDisconnect(bool exit)
|
||
|
|
{
|
||
|
|
dbus_connection_set_exit_on_disconnect(mPrivate->Connection, exit);
|
||
|
|
}
|
||
|
|
|
||
|
|
bool Connection::UniqueName(const char *n)
|
||
|
|
{
|
||
|
|
return dbus_bus_set_unique_name(mPrivate->Connection, n);
|
||
|
|
}
|
||
|
|
|
||
|
|
const char *Connection::UniqueName() const
|
||
|
|
{
|
||
|
|
return dbus_bus_get_unique_name(mPrivate->Connection);
|
||
|
|
}
|
||
|
|
|
||
|
|
void Connection::Flush()
|
||
|
|
{
|
||
|
|
dbus_connection_flush(mPrivate->Connection);
|
||
|
|
}
|
||
|
|
|
||
|
|
void Connection::AddMatch(const char *rule)
|
||
|
|
{
|
||
|
|
UDbusInternalError e;
|
||
|
|
dbus_bus_add_match(mPrivate->Connection, rule, e);
|
||
|
|
if (e) throw UDBusError(e);
|
||
|
|
DEBUG("%s: added match rule %s", UniqueName(), rule);
|
||
|
|
}
|
||
|
|
|
||
|
|
void Connection::RmoveMatch(const char *rule)
|
||
|
|
{
|
||
|
|
UDbusInternalError e;
|
||
|
|
dbus_bus_remove_match(mPrivate->Connection, rule, e);
|
||
|
|
|
||
|
|
DEBUG("%s: removed match rule %s", UniqueName(), rule);
|
||
|
|
|
||
|
|
if (e) throw UDBusError(e);
|
||
|
|
}
|
||
|
|
|
||
|
|
bool Connection::AddFilter(MessageSlot &s)
|
||
|
|
{
|
||
|
|
DEBUG("%s: adding filter", UniqueName());
|
||
|
|
return dbus_connection_add_filter(mPrivate->Connection, PrivateSuffix::MessageFilterStub, &s, nullptr);
|
||
|
|
}
|
||
|
|
|
||
|
|
void Connection::RemoveFilter(MessageSlot &s)
|
||
|
|
{
|
||
|
|
DEBUG("%s: removing filter", UniqueName());
|
||
|
|
dbus_connection_remove_filter(mPrivate->Connection, PrivateSuffix::MessageFilterStub, &s);
|
||
|
|
}
|
||
|
|
|
||
|
|
bool Connection::Send(const Message &cmsg, unsigned int *serial)
|
||
|
|
{
|
||
|
|
return dbus_connection_send(mPrivate->Connection, cmsg.mPrivate->msg, serial);
|
||
|
|
}
|
||
|
|
|
||
|
|
Message Connection::SendBlocking(Message &cmsg, int Timeout_ms)
|
||
|
|
{
|
||
|
|
DBusMessage *DBusreply;
|
||
|
|
UDbusInternalError error;
|
||
|
|
|
||
|
|
if (this->mTimeout != -1) {
|
||
|
|
DBusreply = dbus_connection_send_with_reply_and_block(mPrivate->Connection, cmsg.mPrivate->msg, this->mTimeout, error);
|
||
|
|
} else {
|
||
|
|
DBusreply = dbus_connection_send_with_reply_and_block(mPrivate->Connection, cmsg.mPrivate->msg, Timeout_ms, error);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (error) throw UDBusError(error);
|
||
|
|
|
||
|
|
return Message(new Message::UDBusPrivate(DBusreply), false);
|
||
|
|
}
|
||
|
|
|
||
|
|
PendingCall *Connection::SendAsync(Message &Sendmsg, int Timeout_ms)
|
||
|
|
{
|
||
|
|
DBusPendingCall *DBusPending;
|
||
|
|
if (!dbus_connection_send_with_reply(mPrivate->Connection, Sendmsg.mPrivate->msg, &DBusPending, Timeout_ms)) {
|
||
|
|
throw ErrorNoMemory("Unable to start asynchronous call");
|
||
|
|
}
|
||
|
|
if (!DBusPending)
|
||
|
|
throw ErrorNoMemory("Unable to start asynchronous call");
|
||
|
|
|
||
|
|
return new PendingCall(new PendingCall::UDBusPrivate(DBusPending));
|
||
|
|
}
|
||
|
|
|
||
|
|
int Connection::RequestNameInternal(const char *charName, int intFlags)
|
||
|
|
{
|
||
|
|
UDbusInternalError error;
|
||
|
|
|
||
|
|
int returnNum = dbus_bus_request_name(mPrivate->Connection, charName, intFlags, error);
|
||
|
|
|
||
|
|
if (returnNum == -1) {
|
||
|
|
if (error) throw UDBusError(error);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (charName) {
|
||
|
|
mPrivate->VtNames.push_back(charName);
|
||
|
|
std::string match = "destination='" + mPrivate->VtNames.back() + "'";
|
||
|
|
AddMatch(match.c_str());
|
||
|
|
}
|
||
|
|
|
||
|
|
return returnNum;
|
||
|
|
}
|
||
|
|
|
||
|
|
void Connection::RequestName(const char *name, int flags)
|
||
|
|
{
|
||
|
|
RequestNameInternal(name, flags);
|
||
|
|
}
|
||
|
|
|
||
|
|
bool Connection::AcquireName(const char *name)
|
||
|
|
{
|
||
|
|
int ret = RequestNameInternal(name, 0);
|
||
|
|
switch (ret) {
|
||
|
|
case DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER:
|
||
|
|
case DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER:
|
||
|
|
return true;
|
||
|
|
case DBUS_REQUEST_NAME_REPLY_IN_QUEUE:
|
||
|
|
break;
|
||
|
|
case DBUS_REQUEST_NAME_REPLY_EXISTS:
|
||
|
|
default:
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
CallMessage ping_request(name, "/", "smartlink.netcon.UDBus.Peer", "Ping");
|
||
|
|
try {
|
||
|
|
SendBlocking(ping_request, DBUS_TIMEOUT_USE_DEFAULT);
|
||
|
|
} catch (const UDBus::UDBusError &e) {
|
||
|
|
ERROR("%s", e.what());
|
||
|
|
}
|
||
|
|
|
||
|
|
ret = RequestNameInternal(name, DBUS_NAME_FLAG_DO_NOT_QUEUE);
|
||
|
|
switch (ret) {
|
||
|
|
case DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER:
|
||
|
|
case DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER:
|
||
|
|
return true;
|
||
|
|
case DBUS_REQUEST_NAME_REPLY_EXISTS:
|
||
|
|
return false;
|
||
|
|
case DBUS_REQUEST_NAME_REPLY_IN_QUEUE:
|
||
|
|
default:
|
||
|
|
ERROR("%s %d", dbus_bus_get_unique_name(mPrivate->Connection), ret);
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
unsigned long Connection::SenderUnixUid(const char *sender)
|
||
|
|
{
|
||
|
|
UDbusInternalError e;
|
||
|
|
unsigned long ul = dbus_bus_get_unix_user(mPrivate->Connection, sender, e);
|
||
|
|
|
||
|
|
if (e) throw UDBusError(e);
|
||
|
|
|
||
|
|
return ul;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool Connection::HasName(const char *name)
|
||
|
|
{
|
||
|
|
UDbusInternalError e;
|
||
|
|
bool b = dbus_bus_name_has_owner(mPrivate->Connection, name, e);
|
||
|
|
|
||
|
|
if (e) throw UDBusError(e);
|
||
|
|
|
||
|
|
return b;
|
||
|
|
}
|
||
|
|
|
||
|
|
const std::vector<std::string> &Connection::names()
|
||
|
|
{
|
||
|
|
return mPrivate->VtNames;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool Connection::StartService(const char *name, unsigned long flags)
|
||
|
|
{
|
||
|
|
UDbusInternalError e;
|
||
|
|
bool b = dbus_bus_start_service_by_name(mPrivate->Connection, name, flags, nullptr, e);
|
||
|
|
|
||
|
|
if (e) throw UDBusError(e);
|
||
|
|
|
||
|
|
return b;
|
||
|
|
}
|
||
|
|
|
||
|
|
void Connection::SetTimeout(int Timeout_ms)
|
||
|
|
{
|
||
|
|
mTimeout = Timeout_ms;
|
||
|
|
}
|
||
|
|
|
||
|
|
int Connection::GetTimeout()
|
||
|
|
{
|
||
|
|
return mTimeout;
|
||
|
|
}
|