MochiuWiki : SUSE, EC, PCB
検索
個人用ツール
ログイン
Toggle dark mode
名前空間
ページ
議論
表示
閲覧
ソースを閲覧
履歴を表示
C++の応用 - D-Busのソースを表示
提供: MochiuWiki : SUSE, EC, PCB
←
C++の応用 - D-Bus
あなたには「このページの編集」を行う権限がありません。理由は以下の通りです:
この操作は、次のグループのいずれかに属する利用者のみが実行できます:
管理者
、new-group。
このページのソースの閲覧やコピーができます。
== 概要 == D-Busは、オープンソースのプロセス間通信(IPC:Inter Process Communication)機構であり、freedesktop.orgプロジェクトの一部である。<br> IPCとは、1台のコンピュータ上で動作する複数のプログラムの間で情報を交換するシステムのことである。<br> <br> IPCには、パイプ、名前付きパイプ、シグナル、共有メモリ、Unixソケット、ループバックソケット等がある。<br> D-Busもリンク層はUnixソケットで動作しているが、手順とフォーマット(プレゼンテーション層)が既定されていることが、「生の」Unixソケットとは異なる。<br> <br> 開発当初はGNOME等のGUIの制御を目的としていが、今日では、GUIに限らず幅広いソフトウェアで使用されており、<br> デスクトップの通知、メディアプレーヤー制御、XDGポータル等、多くのfreedesktop.org標準がD-Busをベースに構築されている。<br> <br> IPCとは、あるプロセスから別のプロセスへ情報を取得する方法を説明するために使用することができる。<br> これは、データの交換、メソッドの呼び出し、イベントのリスニング等がある。<br> <br> * デスクトップにおけるIPCの使用例 ** スクリプト(ユーザが共通の環境でスクリプトを実行して、実行中の様々なソフトウェアと対話または制御する)<br> ** 集中型サービスへのアクセスの提供 ** 協調型ソフトウェアの複数のインスタンス間の調整 <br> * D-Busの使用例 ** freedesktop.orgのnotification仕様 **: これは、ソフトウェアは通知を中央サーバ(Plasma等)に送信して、中央サーバは通知を表示して、通知が閉じられたりアクションが実行されたりといったイベントを送り返すものである。 <br> * IPCの他の使用例 ** ユニークなソフトウェアのサポート **: これは、ソフトウェアの起動時に、まず、同じソフトウェアの他の実行中のインスタンスを確認して、 **: もし存在すれば、IPCを介して実行中のインスタンスにメッセージを送信して、自分自身を表示して終了させる。 <br> D-Busは、言語やツールキットに囚われないため、あらゆるプロバイダのソフトウェアやサービスが相互作用することができる。<br> デーモン(軽量サービスプロバイダ)とそれを利用したいソフトウェアが、必要なサービス以上のことを知らなくても通信できるようにするためによく利用される。<br> <br> D-Busに仕様を詳しく知りたい場合は、[https://dbus.freedesktop.org/doc/dbus-specification.html freedesktopの公式Webサイト]を参照すること。<br> <br><br> == sdbus-c++ライブラリ == ==== sdbus-c++ライブラリとは ==== sdbus-c++ライブラリは、C++で理解できるAPIを提供するように設計されたLinux向けの高水準C++ D-Busライブラリである。<br> SystemdによるC D-Bus実装であるsd-busの上に、抽象化の別のレイヤーを追加する。<br> <br> sdbus-c++ライブラリは、主にdbus-c++を置き換えるものとして開発されている。<br> dbus-c++は、現在多くの未解決のバグ、並行性の問題、固有の設計の複雑さと制限に悩まされている。<br> <br> sdbus-c++ライブラリはsd-busライブラリを使用しているが、必ずしもSystemdに制約されているわけではなく、Systemd以外の環境でも完璧に使用することができる。<br> <br> sdbus-c++ライブラリの詳細な使用方法を知りたい場合は、[https://kistler-group.github.io/sdbus-cpp/docs/using-sdbus-c++.html sdbus-c++ライブラリの公式Webサイト]を参照すること。<br> <br> ==== sdbus-c++ライブラリのライセンス ==== sdbus-c++ライブラリは、LGPL 2.1でライセンスされている。<br> ただし、ライブラリのヘッダファイルではLGPL Exception version 1.0ライセンスもあり、これは、以下に示すようなことを許可している。<br> <br> LGPL 2.1に対する追加的な許可として、「ライブラリを使用する作品」のオブジェクトコード形式は、ライブラリの一部であるヘッダファイルの素材を組み込んでもよい。 あなたは、そのようなオブジェクトコードを、あなたが選択した条件の下で頒布することができる。 (i) ライブラリのヘッダファイルが変更されていないこと。 (ii) 組み込まれる内容が、数値パラメータ、データ構造のレイアウト、アクセサ、マクロ、インライン関数、およびテンプレートに限定されていること。 (iii) LGPL 2.1の第6項の条項を遵守していること。 ただし、そのような改変が、 (i) 数値パラメータ (ii) データ構造のレイアウト (iii) アクセサ (iv) 長さが5行以下の小さなマクロ、テンプレート、インライン関数に限定される場合 はこの限りではない。 さらに、ライブラリの改変バージョンにこの追加許可を適用する必要ない。 <br> ==== sdbus-c++ライブラリのインストール ==== ===== パッケージ管理システムからインストール ===== # SUSE sudo zypper install sdbus-cpp-devel <br> ===== ソースコードからインストール ===== sdbus-c++ライブラリのビルドに必要なライブラリをインストールする。<br> # SUSE sudo zypper install pkg-config make cmake gcc gcc-c++ libcap-devel libexpat-devel libmount-devel systemd-devel doxygen # ドキュメントもビルドする場合 <br> [https://github.com/Kistler-Group/sdbus-cpp sdbus-c++ライブラリのGithub]にアクセスして、ソースコードをダウンロードする。<br> ダウンロードしたファイルを解凍する。<br> tar xf sdbus-cpp-<バージョン>.tar.gz cd sdbus-cpp-<バージョン> <br> または、<code>git clone</code>コマンドを実行して、ソースコードをダウンロードすることもできる。<br> git clone https://github.com/Kistler-Group/sdbus-cpp.git cd sdbus-cpp <br> sdbus-c++ライブラリをビルドおよびインストールする。<br> cmake -DCMAKE_C_COMPILER=<GCC 8以降のGCCコンパイラのパス> \ -DCMAKE_CXX_COMPILER=<G++ 8以降のG++コンパイラのパス> \ -DCMAKE_INSTALL_PREFIX=<sdbus-c++ライブラリのインストールディレクトリ> \ -DCMAKE_BUILD_TYPE=Release \ -DSDBUSCPP_BUILD_CODEGEN=ON \ -DSDBUSCPP_BUILD_EXAMPLES=ON \ -DSDBUSCPP_BUILD_DOCS=ON \ -DSDBUSCPP_BUILD_DOXYGEN_DOCS=ON \ # Doxygenでドキュメントを生成する場合 .. make -j $(nproc) make install <br> ==== 使用例 : Systemdサービスユニットの開始 ==== 以下の例では、sdbus-c++ライブラリを使用して、<u>sudo systemctl start smb</u>コマンドと同等の操作を行っている。<br> D-Bus経由でSystemdと直接通信するため、より柔軟で、プログラム内から制御できるようになっている。<br> <br> ただし、サンプルコードを実行するには、適切な権限が必要となる。<br> <syntaxhighlight lang="c++"> #include <iostream> #include <string> #include <sdbus-c++/sdbus-c++.h> // Systemdサービスはシグナルを送信しないため、今回は使用しない // #include <atomic> // #include <chrono> // #include <thread> // std::atomic<bool> g_keep_running(false); // UnitStateChangedシグナルハンドラ // ただし、Systemdサービスはシグナルを送信しないため、今回は定義しない // void onUnitStateChanged(sdbus::Signal signal) // { // std::string unitName, newState, oldState; // signal >> unitName >> newState >> oldState; // std::cout << "Unit " << unitName << " changed state from " << oldState << " to " << newState << std::endl; // if (unitName == "smb.service" && (newState == "active" || newState == "failed")) { // g_keep_running.store(true); // } // } // smb.serviceが正常に開始されているかどうかを確認する bool checkServiceStatus(sdbus::IProxy &systemdProxy, sdbus::InterfaceName &interfaceName, const std::string &serviceName) { try { // smb.serviceのユニットパスを取得する sdbus::MethodName methodName{"GetUnit"}; auto method = systemdProxy.createMethodCall(interfaceName, methodName); method << serviceName; auto reply = systemdProxy.callMethod(method); sdbus::ObjectPath unitPath; reply >> unitPath; // 取得したsmb.serviceのユニットパスを使用して、smb.serviceの開始状態を取得 /// D-Busサービス名は、"org.freedesktop.systemd1" /// D-Busオブジェクト名は、smb.serviceのユニットパス auto unitProxy = sdbus::createProxy(systemdProxy.getConnection(), std::move(sdbus::ServiceName{"org.freedesktop.systemd1"}), unitPath); /// D-Busインターフェース名は、"org.freedesktop.DBus.Properties" /// D-Busインターフェースメソッド名は、"Get" auto getProperty = unitProxy->createMethodCall(sdbus::InterfaceName{"org.freedesktop.DBus.Properties"}, sdbus::MethodName{"Get"}); /// D-Busインターフェースメソッド"Get"の引数を指定 getProperty << "org.freedesktop.systemd1.Unit" << "ActiveState"; /// D-Busインターフェースメソッドを実行して、戻り値を取得 auto propertyReply = unitProxy->callMethod(getProperty); /// 戻り値は文字列型であるため、文字列型に変換 /// smb.serviceが正常に開始されている場合は、文字列は"active"となる sdbus::Variant variant; propertyReply >> variant; std::string state = variant.get<std::string>(); return (state == "active"); } catch (const sdbus::Error &e) { // sdbus::Errorをキャッチしてエラーメッセージを表示 std::cerr << "Error checking service status: " << e.getName() << " - " << e.getMessage() << std::endl; return false; } catch (const std::exception &e) { // std::exceptionをキャッチして一般エラーを処理 std::cerr << "Error: " << e.what() << std::endl; return false; } } int main(int argc, char *argv[]) { // システムバスへの接続を作成 auto connection = sdbus::createSystemBusConnection(); // Systemdサービスのマネージャーインターフェースへのプロキシオブジェクトを作成 sdbus::ServiceName destination("org.freedesktop.systemd1"); sdbus::ObjectPath objectPath("/org/freedesktop/systemd1"); auto systemdProxy = sdbus::createProxy(*connection, std::move(destination), std::move(objectPath)); // D-Busインターフェース名を指定 sdbus::InterfaceName interfaceName("org.freedesktop.systemd1.Manager"); // UnitStateChangedシグナルのハンドラを登録 // ただし、Systemdサービスはシグナルを送信しないため、今回は設定しない //sdbus::SignalName signalName("UnitStateChanged"); //systemdProxy->registerSignalHandler(interfaceName, signalName, &onUnitStateChanged); try { // "StartUnit"はSystemdサービスのメソッド名 sdbus::MethodName methodName{"StartUnit"}; auto method = systemdProxy->createMethodCall(interfaceName, methodName); // Systemdサービスはシグナルを送信しないため、今回は定義しない //systemdProxy->uponSignal("JobRemoved") // .onInterface("org.freedesktop.systemd1.Manager") // .call(&onUnitStateChanged); // D-Busサービスを実行する // "smb.service"は開始するSystemdサービス名 // "replace"はSystemdサービスの起動モード method << "smb.service" << "replace"; auto reply = systemdProxy->callMethod(method); // または、以下に示す方法でもD-Busサービスを実行することができる //systemdProxy->callMethod("StartUnit") // .onInterface("org.freedesktop.systemd1.Manager") // .withArguments("smb.service", "replace"); // Systmedサービスでsmb.service (D-Busサービス) を開始する場合は、ジョブ番号の値が返る sdbus::ObjectPath jobPath; reply >> jobPath; std::cout << "StartUnit job path: " << jobPath << std::endl; // イベントループを開始 // Systemdサービスはシグナルを送信しないため、今回は使用しない //while (g_keep_running) { // connection->processPendingEvent(); // std::this_thread::sleep_for(std::chrono::milliseconds(100)); // 100[mS]のスリープ // // これにより、CPUの過剰な使用を避けつつ適度な応答性を維持 //} // smb.service (D-Busサービス) が正常に開始されたかどうかを確認する std::string serviceName = "smb.service"; if (checkServiceStatus(*systemdProxy, interfaceName, serviceName)) { std::cout << "Service " << serviceName << " state: " << "avtive" << std::endl; } } catch (const sdbus::Error &e) { // sdbus::Errorをキャッチしてエラーメッセージを表示 std::cerr << "Error starting smb.service: " << e.getName() << " - " << e.getMessage() << std::endl; return -1; } catch (const std::exception &e) { // std::exceptionをキャッチして一般エラーを処理 std::cerr << "Error: " << e.what() << std::endl; return -1; } return 0; } </syntaxhighlight> <br> ==== 使用例 : ユーザ定義のD-Busサービスの実行 ==== ===== D-Busサービス ===== 以下に示すようなユーザ定義のD-Busサービスがあるとする。<br> * D-Busサービス名 *: org.example.mochiu * D-Busオブジェクト名 *: /org/example/mochiu * D-Busインターフェース名 : org.example.mochiu.method * D-Busインターフェースメソッド名 *: func1 ** func1の引数 **: 第1引数 int型 **: 第2引数 std::stringクラスの参照 ** func1の戻り値 **: int型 <br> 上記のD-Busサービスにおいて、指定したD-Busサービス、オブジェクト、インターフェース、メソッドを使用して実行している。<br> <br> ===== D-Busインターフェースの定義ファイル (XMLファイル) ===== また、D-Busインターフェースの定義ファイル (XMLファイル) は、以下に示すようなものとする。<br> <syntaxhighlight lang="xml"> <!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> <node> <!-- D-Busインターフェースの定義 --> <interface name="org.example.mochiu.method"> <!-- func1メソッドの定義 --> <!-- func1メソッドは、2つの入力引数 (int型とstd::string型) を受け取り、int型の値を返すことを定義している --> <method name="func1"> <!-- 第1引数 : int型 --> <arg name="arg1" type="i" direction="in"/> <!-- 第2引数 : std::string型 (ここでは参照型として使用する) --> <arg name="arg2" type="s" direction="in"/> <!-- 戻り値 : int型 --> <arg name="result" type="i" direction="out"/> </method> </interface> </node> </syntaxhighlight> <br> std::stringクラスの参照渡しは明示的に行っていないが、これは、sdbus-c++ライブラリが内部で適切に処理するためである。<br> <u>sdbus-c++ライブラリは効率的な引数の受け渡しを行うよう設計されているため、大きなオブジェクトは自動的に参照として扱われる。</u><br> <br> ===== sdbus-c++-xml2cppツール (スタブジェネレータ) の実行 ===== <u>次に、上記のDBusインターフェース定義ファイルからC++ヘッダファイルを自動生成する。</u><br> <br> sdbus-c++ライブラリには、XMLファイルからC++のソースコードを生成するツールであるsdbus-c++-xml2cppツール (スタブジェネレータ) が存在する。<br> このツールは、D-Busインタフェースを記述したXMLファイルを受け取り、そのインタフェースを経由して呼び出すことができるC++ヘッダファイルを自動生成するものである。<br> この自動生成されたC++ヘッダファイルは、D-Bus経由の呼び出しをサービスのプロバイダに転送して、結果を呼び出し元に返す役割がある。<br> <br> サービスを実装するサーバは、<code>--adaptor</code>オプションで生成されたヘッダファイルのインターフェイスクラスから派生したクラスを定義して、そのメソッドを実装する必要がある。<br> <br> 以下のコマンド例は、sdbus-c++-xml2cppツールを実行して、D-Busのサーバ側 (ヘルパー実行ファイル) のC++ヘッダファイルを自動生成している。<br> sdbus-c++-xml2cpp <D-Busインターフェース定義ファイル名 (XMLファイル)> \ --adaptor=<生成するアダプタのヘッダファイル名> # 例 : sdbus-c++-xml2cpp org.example.mochiu.xml \ --adaptor=MochiuAdaptor.h <br> ===== D-Busのサーバ側 (ヘルパー実行ファイル) ===== <u>次に、D-Busのサーバ側 (ヘルパー実行ファイル) を作成する。</u><br> <br> <u>上記で自動生成されたC++ヘッダファイルを、ヘルパー実行ファイルのソースコードにインクルードする。</u> <syntaxhighlight lang="c++"> // 例: MochiuHelper.hファイル #include "MochiuAdapter.h" // 生成されたアダプタヘッダをインクルード </syntaxhighlight> <br> <syntaxhighlight lang="c++"> // 例: MochiuHelper.hファイル #ifndef MOCHIUHELPER_H #define MOCHIUHELPER_H #include <sdbus-c++/sdbus-c++.h> #include <iostream> #include "MochiuAdapter.h" // 生成されたアダプタヘッダをインクルード // アダプタ名を明示的に指定しない場合は、以下に示すようなクラス継承の記述でも可能 // class MochiuHelper : public org::example::mochiu::method_adaptor class MochiuHelper : public MochiuAdaptor { public: MochiuHelper(sdbus::IConnection& connection, std::string objectPath) : MochiuAdaptor(connection, std::move(objectPath)) { } // func1メソッドの実装 int func1(const int32_t& arg1, const std::string& arg2) override { std::cout << "func1 called with args: " << arg1 << ", " << arg2 << std::endl; return arg1 * 2; // 例: 入力値の2倍を返す } }; #endif // MOCHIUHELPER_H </syntaxhighlight> <br> <syntaxhighlight lang="c++"> // 例: MochiuHelper.cppファイル #include <sdbus-c++/sdbus-c++.h> #include <iostream> #include <future> #include <chrono> #include "MochiuHelper.h" // イベントループを実行し、60秒後に終了するための関数 void runEventLoopWithTimeout(std::shared_ptr<sdbus::IConnection> connection) { // 60秒後に終了するための非同期タスクを作成 auto future = std::async(std::launch::async, []{ std::this_thread::sleep_for(std::chrono::seconds(60)); }); // イベントループを開始 while (future.wait_for(std::chrono::seconds(0)) == std::future_status::timeout) { connection->processPendingRequest(); } std::cout << "60 seconds timeout reached. Exiting..." << std::endl; } int main() { try { // システムバスを接続する場合 auto connection = sdbus::createSystemBusConnection(); // セッションバスを接続する場合 //auto connection = sdbus::createSessionBusConnection(); // D-Busサービス名を指定 connection->requestName("org.example.mochiu"); // D-Busオブジェクトの作成 MochiuHelper mochiuHelper(*connection, "/org/example/mochiu"); // タイムアウト付きでイベントループを実行 runEventLoopWithTimeout(connection); } catch (const sdbus::Error& e) { std::cerr << "D-Bus error: " << e.what() << std::endl; return -1; } catch (const std::exception& e) { std::cerr << "Error: " << e.what() << std::endl; return -1; } return 0; } </syntaxhighlight> <br> ===== D-Busのクライアント側 (呼び出し側) ===== <syntaxhighlight lang="c++"> #include <sdbus-c++/sdbus-c++.h> #include <iostream> #include <cstring> int main() { try { // システムバスへの接続を作成する場合 auto connection = sdbus::createSystemBusConnection(); // セッションバスへの接続を作成する場合 //auto connection = sdbus::createSessionBusConnection(); // 指定されたD-Busサービスとオブジェクトのプロキシオブジェクトを作成 auto proxy = sdbus::createProxy(*connection, sdbus::ServiceName{"org.example.mochiu"}, // D-Busサービス名 sdbus::ObjectPath{"/org/example/mochiu"} // D-Busオブジェクト名 ); // func1メソッドの引数 int arg1 = 42; std::string arg2 = "Hello, D-Bus!"; // D-Busインターフェースメソッド (func1) を呼び出して、戻り値を受け取る auto result = proxy->callMethod(sdbus::MethodName{"func1"}) // D-Busインターフェースメソッド名 .onInterface(sdbus::InterfaceName{"org.example.mochiu.method"}) // D-Busインターフェース名 .withArguments(arg1, arg2) // 引数を指定 .returnValue<int>(); // 戻り値の型を指定 std::cout << "Method 'func1' called successfully." << std::endl; std::cout << "Result: " << result << std::endl; } catch (const sdbus::Error &e) { // sdbus::Errorをキャッチしてエラーメッセージを表示 std::cerr << "D-Bus error: " << e.what() << std::endl; return -1; } catch (const std::exception &e) { // std::exceptionをキャッチして一般エラーを処理 std::cerr << "Error: " << e.what() << std::endl; return -1; } return 0; } </syntaxhighlight> <br> ===== CMakeを使用する場合 ===== <syntaxhighlight lang="cmake"> cmake_minimum_required(VERSION 3.16) project(<プロジェクト名>) set(CMAKE_CXX_STANDARD 17) find_package(sdbus-c++ REQUIRED) add_executable(<ターゲット名> main.cpp ) target_link_libraries(<ターゲット名> PRIVATE SDBusCpp::sdbus-c++ ) </syntaxhighlight> <br><br> __FORCETOC__ [[カテゴリ:C++]]
C++の応用 - D-Bus
に戻る。
案内
メインページ
最近の更新
おまかせ表示
MediaWiki についてのヘルプ
ツール
リンク元
関連ページの更新状況
特別ページ
ページ情報
We ask for
Donations
Collapse