[DBUS] gdbus-codegen을 활용한 기본 예제
2025. 11. 25. 23:17
client가 덧셈(Add), 뺄셈(Sub) DBus method 요청을 하면 server가 이를 수행하여 이 결과를 DBus method 응답으로 반환하고, server가 덧셈, 뺄셈 연산의 결과를 signal로 전송하는 예제이다.
이 때, gdbus-codegen을 활용하여 DBus method와 signal을 핸들링하는 코드를 자동 생성하게하고 이를 이용하여 빌드하고 실행해본다.
프로젝트 구조
project/
├── CMakeLists.txt
├── example.xml
├── server.c
└── client.c
1. example.xml
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<interface name="org.example.Calculator">
<method name="Add">
<arg type="i" name="a" direction="in"/>
<arg type="i" name="b" direction="in"/>
<arg type="i" name="result" direction="out"/>
</method>
<method name="Sub">
<arg type="i" name="a" direction="in"/>
<arg type="i" name="b" direction="in"/>
<arg type="i" name="result" direction="out"/>
</method>
<signal name="CalculationDone">
<arg type="s" name="operation"/>
<arg type="i" name="result"/>
</signal>
</interface>
</node>
2. server.c
#include <glib.h>
#include <gio/gio.h>
#include "calculator-generated.h"
static Calculator *calculator_skeleton = NULL;
static gboolean
on_handle_add(Calculator *object,
GDBusMethodInvocation *invocation,
gint a, gint b,
gpointer user_data)
{
gint result = a + b;
calculator_complete_add(object, invocation, result);
// Signal 발송
gchar *msg = g_strdup_printf("Add(%d, %d)", a, b);
calculator_emit_calculation_done(object, msg, result);
g_free(msg);
g_print("Add: %d + %d = %d\n", a, b, result);
return TRUE;
}
static gboolean
on_handle_sub(Calculator *object,
GDBusMethodInvocation *invocation,
gint a, gint b,
gpointer user_data)
{
gint result = a - b;
calculator_complete_sub(object, invocation, result);
// Signal 발송
gchar *msg = g_strdup_printf("Sub(%d, %d)", a, b);
calculator_emit_calculation_done(object, msg, result);
g_free(msg);
g_print("Sub: %d - %d = %d\n", a, b, result);
return TRUE;
}
static void
on_bus_acquired(GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
GError *error = NULL;
calculator_skeleton = calculator_skeleton_new();
g_signal_connect(calculator_skeleton, "handle-add",
G_CALLBACK(on_handle_add), NULL);
g_signal_connect(calculator_skeleton, "handle-sub",
G_CALLBACK(on_handle_sub), NULL);
if (!g_dbus_interface_skeleton_export(
G_DBUS_INTERFACE_SKELETON(calculator_skeleton),
connection,
"/org/example/Calculator",
&error))
{
g_printerr("Failed to export object: %s\n", error->message);
g_error_free(error);
}
else
{
g_print("Calculator service ready\n");
}
}
static void
on_name_acquired(GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
g_print("Name acquired: %s\n", name);
}
static void
on_name_lost(GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
g_printerr("Name lost: %s\n", name);
}
int main(void)
{
GMainLoop *loop;
guint owner_id;
loop = g_main_loop_new(NULL, FALSE);
owner_id = g_bus_own_name(G_BUS_TYPE_SESSION,
"org.example.Calculator",
G_BUS_NAME_OWNER_FLAGS_NONE,
on_bus_acquired,
on_name_acquired,
on_name_lost,
NULL, NULL);
g_main_loop_run(loop);
g_bus_unown_name(owner_id);
g_main_loop_unref(loop);
if (calculator_skeleton)
g_object_unref(calculator_skeleton);
return 0;
}
3. client.c
#include <glib.h>
#include <gio/gio.h>
#include "calculator-generated.h"
static void
on_calculation_done(Calculator *proxy,
const gchar *operation,
gint result,
gpointer user_data)
{
g_print("Signal received: %s = %d\n", operation, result);
}
int main(void)
{
GError *error = NULL;
Calculator *proxy;
gint result;
proxy = calculator_proxy_new_for_bus_sync(
G_BUS_TYPE_SESSION,
G_DBUS_PROXY_FLAGS_NONE,
"org.example.Calculator",
"/org/example/Calculator",
NULL, &error);
if (error)
{
g_printerr("Failed to create proxy: %s\n", error->message);
g_error_free(error);
return 1;
}
// Signal 연결
g_signal_connect(proxy, "calculation-done",
G_CALLBACK(on_calculation_done), NULL);
// Add 호출
if (calculator_call_add_sync(proxy, 10, 5, &result, NULL, &error))
{
g_print("Add result: %d\n", result);
}
else
{
g_printerr("Add failed: %s\n", error->message);
g_error_free(error);
}
// Sub 호출
if (calculator_call_sub_sync(proxy, 10, 5, &result, NULL, &error))
{
g_print("Sub result: %d\n", result);
}
else
{
g_printerr("Sub failed: %s\n", error->message);
g_error_free(error);
}
// Signal 수신 대기
g_print("Waiting for signals (Press Ctrl+C to exit)...\n");
GMainLoop *loop = g_main_loop_new(NULL, FALSE);
g_main_loop_run(loop);
g_object_unref(proxy);
g_main_loop_unref(loop);
return 0;
}
4. CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(dbus_example C)
find_package(PkgConfig REQUIRED)
pkg_check_modules(GLIB REQUIRED glib-2.0 gio-2.0)
## gdbus-codegen을 위해 GIO-UNIX 라이브러리가 필요하다
pkg_check_modules(GIO-UNIX gio-unix-2.0 REQUIRED)
find_program(GDBUS_CODEGEN gdbus-codegen REQUIRED)
set(DBUS_XML ${CMAKE_CURRENT_SOURCE_DIR}/example.xml)
set(GENERATED_SOURCES
${CMAKE_CURRENT_BINARY_DIR}/calculator-generated.c
${CMAKE_CURRENT_BINARY_DIR}/calculator-generated.h
)
add_custom_command(
OUTPUT ${GENERATED_SOURCES}
COMMAND ${GDBUS_CODEGEN}
--interface-prefix org.example.
--generate-c-code ${CMAKE_CURRENT_BINARY_DIR}/calculator-generated
${DBUS_XML}
DEPENDS ${DBUS_XML}
COMMENT "Generating D-Bus code"
)
add_executable(calculator_server
server.c
${GENERATED_SOURCES}
)
target_include_directories(calculator_server PRIVATE
${GLIB_INCLUDE_DIRS}
${GIO-UNIX_INCLUDE_DIRS}
${CMAKE_CURRENT_BINARY_DIR}
)
target_link_libraries(calculator_server
${GLIB_LIBRARIES}
${GIO-UNIX_LDFLAGS}
)
add_executable(calculator_client
client.c
${GENERATED_SOURCES}
)
target_include_directories(calculator_client PRIVATE
${GLIB_INCLUDE_DIRS}
${GIO-UNIX_INCLUDE_DIRS}
${CMAKE_CURRENT_BINARY_DIR}
)
target_link_libraries(calculator_client
${GLIB_LIBRARIES}
${GIO-UNIX_LDFLAGS}
)
빌드 방법
mkdir build && cd build
cmake ..
make
실행 결과
서버:
Name acquired: org.example.Calculator
Calculator service ready
Add: 10 + 5 = 15
Sub: 10 - 5 = 5
클라이언트:
Add result: 15
Sub result: 5
Waiting for signals (Press Ctrl+C to exit)...
Signal received: Add(10, 5) = 15
Signal received: Sub(10, 5) = 5
■ (수동으로 calculator-generated 코드를 생성하고 싶은 경우) gdbus-codegen 명령어
◈ cmake를 통해 gdbus-codegen이 호출이 될 것이므로 굳이 수동으로 gdbus-codegen을 호출할 필요가 없다
gdbus-codegen \
--interface-prefix org.example. \
--generate-c-code calculator-generated \
example.xml
빌드 시 필요한 패키지들
- cmake (sudot apt get install cmake)
- pkg-config (PkgConfig) (sudo apt-get install pkg-config)
- glib-2.0, gio-2.0 (sudo apt install libglib2.0-dev이 패키지 하나에 glib, gio, gio-unix-2.0가 포함됨)
'DBUS' 카테고리의 다른 글
| [DBUS] 에러해결: fatal error: gio/gunixfdlist.h: No such file or directory 17 | # include <gio/gunixfdlist.h> (0) | 2025.11.26 |
|---|---|
| [DBUS] Ubuntu에서 glib, gio 라이브러리 설치하기 (glib-2.0, gio-2.0) (0) | 2025.11.25 |
| [D-BUS] method 기본 예제 (request/response) (0) | 2025.10.16 |
| [D-BUS] signal 기본 예제 (0) | 2025.10.15 |
| (임) DBUS 그림 (0) | 2025.10.15 |
