D-Busとは
D-Bus(Desktop Bus)は、LinuxなどのUnix系オペレーティングシステムでプロセス間通信を行うためのメッセージバスシステムです。
メッセージバスは、異なるプロセスやアプリケーション間でメッセージを交換するための実装のことです。
D-Busは、専用のデーモン(D-Bus daemon)によってメッセージを管理しており、以下の2つのデーモンが存在します。
- システム管理デーモン
- ログインセッション管理デーモン
システム管理デーモンでは、プリンタキューの追加やデバイスの追加・削除などを通知しており、デスクトップアプリケーション間の通信に使われているのはログインセッション管理デーモンです。
参考:公式ドキュメント
C++からの使用方法
C++からD-Busを利用する方法はいくつかありますが、今回はlibdbusというライブラリを使った例を紹介します。
libdbusのインストール
まず、以下のコマンドでlibdbusをインストールします。
sudo apt-get install libdbus-1-dev
libdbusのリンク
CMakeを使用している場合、CMakeLists.txtにヘッダーファイルのインクルードパスを追加する必要があります。
cmake_minimum_required(VERSION 3.10)
project(Example)
find_package(PkgConfig REQUIRED)
pkg_check_modules(DBUS REQUIRED dbus-1)
include_directories(${DBUS_INCLUDE_DIRS})
link_directories(${DBUS_LIBRARY_DIRS})
add_executable(Example example.cpp)
target_link_libraries(Example ${DBUS_LIBRARIES})
ソースコード
このコードは、D-Busシステムバスに接続し、ListNamesメソッドを呼び出して、現在D-Busに登録されているサービスの名前を取得し、出力するプログラムです。
#include <dbus/dbus.h>
#include <iostream>
int main() {
DBusError err;
DBusConnection* conn;
DBusMessage* msg;
DBusMessage* reply;
DBusMessageIter args, sub_iter;
char* str;
// Initialize error handler
dbus_error_init(&err);
// Connect to the system bus
conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
if (dbus_error_is_set(&err)) {
std::cerr << "Connection Error: " << err.message << std::endl;
dbus_error_free(&err);
}
if (!conn) {
return 1;
}
// Create a new method call and check for errors
msg = dbus_message_new_method_call("org.freedesktop.DBus", // target for the method call
"/org/freedesktop/DBus", // object to call on
"org.freedesktop.DBus", // interface to call on
"ListNames"); // method name
if (!msg) {
std::cerr << "Message Null" << std::endl;
return 1;
}
// Send message and get a reply
reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err);
dbus_message_unref(msg);
if (dbus_error_is_set(&err)) {
std::cerr << "Error sending message: " << err.message << std::endl;
dbus_error_free(&err);
}
if (!reply) {
return 1;
}
// Read the parameters
if (!dbus_message_iter_init(reply, &args)) {
std::cerr << "Message has no arguments!" << std::endl;
} else if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_ARRAY) {
std::cerr << "Argument is not an array!" << std::endl;
} else {
dbus_message_iter_recurse(&args, &sub_iter);
while (dbus_message_iter_get_arg_type(&sub_iter) == DBUS_TYPE_STRING) {
dbus_message_iter_get_basic(&sub_iter, &str);
std::cout << str << std::endl;
dbus_message_iter_next(&sub_iter);
}
}
// Free reply
dbus_message_unref(reply);
return 0;
}
コードの流れ
- 1. エラーハンドリングの初期化 (
dbus_error_init
) -
D-Bus操作中に発生するエラーを処理するための構造体を初期化します。
- 2. D-Busシステムバスに接続 (
dbus_bus_get
) -
システムバスに接続します。接続に失敗した場合、エラーメッセージを出力します。
- 3. D-Busメッセージの作成 (
dbus_message_new_method_call
) -
org.freedesktop.DBus
サービスのListNames
メソッドを呼び出すためのD-Busメッセージを作成します。このメソッドは、D-Busに現在登録されているサービスの名前をリストとして返します。 - 4. メッセージの送信と返信の取得 (
dbus_connection_send_with_reply_and_block
) -
作成したメッセージを送信し、返信を待ちます。返信がない場合やエラーが発生した場合、エラーメッセージを出力します。
- 5. 返信メッセージの解析
-
返信メッセージからメッセージの引数(サービス名のリスト)を取得し、各サービス名を取得します。
- 6. サービス名の出力
-
各サービス名をコンソールに出力します。
- 7. 返信メッセージの解放 (
dbus_message_unref
) -
使用したD-Busメッセージを解放してメモリリークを防ぎます。
実行結果
上記のプログラムを実行すると、D-Busシステムバスに現在接続されているサービスやクライアントのリストが表示されます。各行は、現在のD-Busに登録されている名前を表しています。
サービス名が表示されているものと数字が表示されているものがありますが、定義済みのサービス名と動的に割り当てられた一時的な接続名の違いです。
org.freedesktop.DBus
:1.7
org.freedesktop.timesync1
:1.8
:1.9
org.freedesktop.systemd1
org.freedesktop.ModemManager1
org.freedesktop.NetworkManager
org.freedesktop.oom1
net.hadess.PowerProfiles
org.freedesktop.resolve1
org.freedesktop.RealtimeKit1
org.freedesktop.Accounts
:1.60
:1.83
:1.61
:1.62
:1.40
:1.63
:1.41
:1.64
:1.42
org.freedesktop.PolicyKit1
:1.21
:1.44
:1.67
:1.23
net.hadess.SwitcherooControl
:1.24
:1.69
:1.25
:1.26
:1.27
:1.28
:1.29
org.gnome.RemoteDesktop
org.gnome.DisplayManager
org.freedesktop.Avahi
org.freedesktop.UDisks2
fi.w1.wpa_supplicant1
org.freedesktop.fwupd
org.freedesktop.login1
:1.71
org.freedesktop.ColorManager
:1.73
:1.51
:1.74
:1.75
:1.53
:1.76
:1.54
:1.32
:1.10
:1.77
:1.55
:1.33
:1.11
:1.120
:1.78
:1.56
org.freedesktop.UPower
:1.34
:1.12
:1.35
:1.0
:1.13
org.freedesktop.UPower.PowerProfiles
:1.58
:1.36
:1.1
:1.14
:1.59
:1.37
:1.2
:1.38
:1.3
:1.39
:1.4
:1.17
:1.5
:1.18
:1.6
特定のサービスのPIDを取得する例
次は、D-Busを使って特定のサービスのPIDを取得する例を紹介します。
ソースコード
特定のサービスのPIDの特定には、org.freedesktop.DBus.GetConnectionUnixProcessID
メソッドを使用して行います。
#include <dbus/dbus.h>
#include <iostream>
int main() {
DBusError err;
DBusConnection* conn;
DBusMessage* msg;
DBusMessage* reply;
DBusMessageIter args;
int32_t pid;
// Initialize the errors
dbus_error_init(&err);
// Connect to the system bus
conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
if (dbus_error_is_set(&err)) {
std::cerr << "Connection Error: " << err.message << std::endl;
dbus_error_free(&err);
}
if (!conn) {
return 1;
}
// Create a new method call and check for errors
msg = dbus_message_new_method_call("org.freedesktop.DBus", // target for the method call
"/org/freedesktop/DBus", // object to call on
"org.freedesktop.DBus", // interface to call on
"GetConnectionUnixProcessID"); // method name
if (!msg) {
std::cerr << "Message Null" << std::endl;
return 1;
}
// Append arguments
const char* service_name = "org.freedesktop.NetworkManager"; // ここに調べたいサービス名を指定
dbus_message_append_args(msg, DBUS_TYPE_STRING, &service_name, DBUS_TYPE_INVALID);
// Send message and get a reply
reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err);
dbus_message_unref(msg); // message objectを解放
if (dbus_error_is_set(&err)) {
std::cerr << "Error sending message: " << err.message << std::endl;
dbus_error_free(&err);
}
if (!reply) {
return 1;
}
// Read the reply arguments
if (dbus_message_iter_init(reply, &args) &&
dbus_message_iter_get_arg_type(&args) == DBUS_TYPE_UINT32) {
dbus_message_iter_get_basic(&args, &pid);
std::cout << "PID of " << service_name << " is: " << pid << std::endl;
} else {
std::cerr << "Did not get correct reply" << std::endl;
}
// Free the reply
dbus_message_unref(reply);
return 0;
}
実行結果
NetworkManagerのPIDが954であることが確認できました。
PID of org.freedesktop.NetworkManager is: 954
確認のために、LinuxのコマンドでNetworkManagerのPIDを確認してみます。
pidof NetworkManager
954
D-Busを使用して得られた結果と一致することが確認できました。