spiegel-keyman/linux/ibus-keyman/tests/dbus-test-server/KmDbusTestServer.cpp
2025-07-25 15:53:15 +02:00

157 lines
3.7 KiB
C++

// DBus test server. The server will start and listen on a non-standard DBus.
// It runs until the DBus Exit method gets called.
#include "config.h"
#include <fstream>
#include <gio/gio.h>
#include <iostream>
#if DBUS_IMPLEMENTATION == SYSTEMD
#include <systemd/sd-bus.h>
#else
#include <basu/sd-bus.h>
#endif
#include "KmDbusTestServer.h"
#ifndef KEYMAN_TEST_SERVICE_PATH
#warning KEYMAN_TEST_SERVICE_PATH is undefined
#define KEYMAN_TEST_SERVICE_PATH "."
#endif
using namespace std;
class KmDbusTestServer
{
public:
GTestDBus *dbus;
sd_bus *bus = NULL;
gboolean exitFlag = FALSE;
public:
KmDbusTestServer();
~KmDbusTestServer();
void Loop();
};
static int32_t
on_exit_method(sd_bus_message *msg, void *user_data, sd_bus_error *ret_error) {
g_debug("%s called", __FUNCTION__);
KmDbusTestServer* server = (KmDbusTestServer*)user_data;
*ret_error = SD_BUS_ERROR_NULL;
server->exitFlag = TRUE;
return sd_bus_reply_method_return(msg, "");
}
static const sd_bus_vtable test_service_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_METHOD("Exit", "", "", on_exit_method, 0),
SD_BUS_VTABLE_END
};
KmDbusTestServer::KmDbusTestServer()
{
dbus = g_test_dbus_new(G_TEST_DBUS_NONE);
// Add the private directory with our in-tree service files.
g_test_dbus_add_service_dir(dbus, KEYMAN_TEST_SERVICE_PATH);
std::cout << "Add service dir to " << KEYMAN_TEST_SERVICE_PATH << std::endl;
// Start the private D-Bus daemon
g_test_dbus_up(dbus);
const gchar *address = g_test_dbus_get_bus_address(dbus);
std::cout << "Test server running on: " << address << std::endl;
ofstream file("/tmp/km-test-server.env", ios_base::out);
if (file) {
// write the address to a file which can be sourced
file << "export DBUS_SESSION_BUS_ADDRESS=" << address << std::endl;
file.close();
}
}
KmDbusTestServer::~KmDbusTestServer()
{
if (bus) {
sd_bus_release_name(bus, KEYMAN_TESTSVC_BUS_NAME);
sd_bus_close(bus);
sd_bus_unref(bus);
}
g_test_dbus_down(dbus);
g_object_unref(dbus);
}
void KmDbusTestServer::Loop()
{
int ret;
ret = sd_bus_open_user(&bus);
if (ret < 0) {
g_error("Failed to connect to system bus: %s", strerror(-ret));
return;
}
// Install the object
ret = sd_bus_add_object_vtable(bus, NULL, KEYMAN_TESTSVC_OBJECT_PATH,
KEYMAN_TESTSVC_INTERFACE_NAME, test_service_vtable, this);
if (ret < 0) {
g_error("Failed to issue method call: %s", strerror(-ret));
return;
}
// Take a well-known service name so that clients can find us
ret = sd_bus_request_name(bus, KEYMAN_TESTSVC_BUS_NAME, 0);
if (ret < 0) {
g_error("Failed to acquire service name: %s", strerror(-ret));
return;
}
for (;;) {
// Process requests
ret = sd_bus_process(bus, NULL);
g_debug("sd_bus_process returned %d, exitFlag=%d", ret, this->exitFlag);
if (ret < 0) {
g_error("Failed to process bus: %s", strerror(-ret));
return;
}
if (this->exitFlag) {
// `exitFlag` can be modified by the callback function
// `on_exit_method` which can be called by `sd_bus_process`
g_debug("Exiting loop");
return;
}
if (ret > 0) {
// we processed a request, try to process another one, right-away
continue;
}
// Wait for the next request to process
ret = sd_bus_wait(bus, (uint64_t)-1);
g_debug("sd_bus_wait returned %d", ret);
if (ret < 0) {
g_error("Failed to wait on bus: %s", strerror(-ret));
return;
}
}
}
int
main(int argc, char *argv[]) {
if (!g_file_test(KEYMAN_TEST_SERVICE_PATH, G_FILE_TEST_IS_DIR)) {
std::cerr << "ERROR: Directory " << KEYMAN_TEST_SERVICE_PATH << " doesn't exist! Exiting." << std::endl;
return 1;
}
KmDbusTestServer testServer;
testServer.Loop();
return 0;
}