MochiuWiki : SUSE, EC, PCB
案内
メインページ
最近の更新
おまかせ表示
MediaWiki についてのヘルプ
ツール
リンク元
関連ページの更新状況
特別ページ
ページ情報
We ask for
Donations
検索
個人用ツール
ログイン
Toggle dark mode
名前空間
ページ
議論
表示
閲覧
ソースを閲覧
履歴を表示
C++の応用 - Systemdのソースを表示
提供: MochiuWiki : SUSE, EC, PCB
←
C++の応用 - Systemd
あなたには「このページの編集」を行う権限がありません。理由は以下の通りです:
この操作は、次のグループのいずれかに属する利用者のみが実行できます:
管理者
、new-group。
このページのソースの閲覧やコピーができます。
== 概要 == Systemdは、Linuxにおける代表的なシステムおよびサービスマネージャであり、起動、停止、依存関係管理、ログ収集、リソース制御を一元的に扱う。<br> <br> C++からSystemdを利用する場合、最も重要な入口は<u>libsystemd</u>である。<br> libsystemdはC APIを提供するライブラリであり、純粋なC++アプリケーションから直接利用できる。<br> <br> 代表的なヘッダには、<u>sd-bus.h</u>、<u>sd-event.h</u>、<u>sd-journal.h</u>、<u>sd-daemon.h</u>がある。<br> これらを組み合わせることで、ユニット操作、イベントループ統合、journaldへの構造化ログ出力、<code>Type=notify</code>サービスとの連携を実装できる。<br> <br> Systemd Managerと通信する場合、通常はD-Bus経由で <u>org.freedesktop.systemd1</u> にアクセスする。<br> このとき、サービスの開始や停止は単純なシェルコマンド実行ではなく、<code>StartUnit</code> や <code>StopUnit</code>等のメソッド呼び出しとして扱う。<br> <br> 特に重要なのは、<code>StartUnit</code> の戻り値が「サービスが完全に起動した」という意味ではなく、ジョブが作成されたことを示す点である。<br> 実際の状態確認には、ジョブの監視や <code>ActiveState</code> プロパティの確認が必要になる。<br> <br> また、Systemdはシステムユニットだけでなく、ユーザごとのユーザユニットも管理できる。<br> 一般ユーザのサービスを制御する場合は <code>sd_bus_open_user()</code>、システム全体のユニットを制御する場合は <code>sd_bus_open_system()</code> を使い分ける。<br> <br> 近年のlibsystemdでは、<code>sd-json</code> や <code>sd-varlink</code> 等の公開APIが追加され、イベントループや通知機能も強化されている。<br> 一方で、実務で最も利用頻度が高いのは、依然として <code>sd-bus</code>、<code>sd-event</code>、<code>sd-journal</code>、<code>sd_notify</code>である。<br> <br> C++でlibsystemdを使う場合は、GUIフレームワークに依存せず、<code>std::unique_ptr</code> や 専用デリータを使用してRAIIでリソース管理する構成が扱いやすい。<br> また、ビルド時は <code>pkg-config --cflags --libs libsystemd</code> を使用するのが最も安全である。<br> <br><br> == libsystemdの導入 == ==== ライセンス ==== 通常のアプリケーションがリンクする<u>libsystemd</u>は、主としてLGPL 2.1以降の条件で利用できる。<br> ただし、systemdのソースツリー全体にはGPL系コンポーネントも含まれるため、再配布方針が厳密な環境では公式ライセンス表記も確認すること。<br> <br> ==== パッケージ管理システムからインストール ==== # RHEL sudo dnf install systemd-devel # SUSE sudo zypper install systemd-devel <br> ==== ソースコードからインストール ==== 最新のupstream機能を利用する場合は、ソースコードからビルドする。<br> ただし、依存ライブラリが多く、ディストリビューションのBuildRequires相当のパッケージが必要になるため、通常は配布パッケージの利用を優先する。<br> <br> Systemdのビルドに必要なライブラリをインストールする。<br> <br> # RHEL sudo dnf install -y epel-release sudo dnf config-manager --set-enabled crb sudo dnf install meson ninja-build python3-jinja2 glib2-devel dbus-devel p11-kit-devel libarchive-devel pcre2-devel \ libcurl-devel libcap-devel libmount-devel libfdisk-devel libblkid-devel elfutils-devel libpwquality-devel \ kmod-devel libbpf-devel zlib-ng-compat-devel lz4-devel libzstd-devel xz-devel bzip2-devel iptables-devel \ pam-devel gnutls-devel openssl-devel cryptsetup-devel libgcrypt-devel libgpg-error-devel \ libmicrohttpd-devel libxkbcommon-devel libfido2-devel tpm2-tss-devel libseccomp-devel \ libacl-devel audit-libs-devel libselinux-devel # SUSE sudo zypper install meson ninja python3-Jinja2 glib2-devel dbus-1-devel p11-kit-devel libarchive-devel pcre2-devel \ libcurl-devel libcap-devel libmount-devel libfdisk-devel libblkid-devel libdw-devel libpwquality-devel \ passwdqc-devel libkmod-devel libbpf-devel zlib-devel liblz4-devel libzstd-devel xz-devel libbz2-devel \ pam-devel libgnutls-devel libopenssl-devel libopenssl-1_1-devel libcryptsetup-devel libgcrypt-devel \ libgpg-error-devel qrencode-devel libiptc-devel libidn2-devel libmicrohttpd-devel \ libxkbcommon-devel libfido2-devel tpm2-0-tss-devel libseccomp-devel libacl-devel audit-devel \ libapparmor-devel # AppArmorを使用する場合 libselinux-devel # SELinuxを使用する場合 xen-devel # Xenを使用する場合 <br> 次に、[https://github.com/systemd/systemd SystemdのGithub]にアクセスして、ソースコードをダウンロードする。<br> ダウンロードしたファイルを解凍する。<br> <br> tar xf systemd-<バージョン>.tar.gz cd systemd-<バージョン> <br> または、<code>git clone</code> コマンドを実行して、ソースコードをダウンロードする。<br> <br> git clone https://github.com/systemd/systemd.git cd systemd <br> Systemdをビルドおよびインストールする。<br> <br> meson setup build -Dmode=release --prefix=<Systemdのインストールディレクトリ> meson compile -C build sudo meson install -C build <br> libsystemdの利用だけが目的であれば、Systemd本体をソースから導入するよりも、パッケージ管理システムの開発パッケージ用いる方が保守しやすい。<br> <br> ==== CMakeファイルの設定 ==== C++からlibsystemdを利用する場合、<code>pkg-config</code> 経由で検出する構成が最も安全である。<br> <br> <syntaxhighlight lang="cmake"> cmake_minimum_required(VERSION 3.21) project(systemd_cpp_example LANGUAGES CXX) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) find_package(PkgConfig REQUIRED) pkg_check_modules(SYSTEMD REQUIRED libsystemd) add_executable(systemd_cpp_example main.cpp ) target_include_directories(systemd_cpp_example PRIVATE ${SYSTEMD_INCLUDE_DIRS} ) target_link_libraries(systemd_cpp_example PRIVATE ${SYSTEMD_LIBRARIES} ) target_compile_options(systemd_cpp_example PRIVATE ${SYSTEMD_CFLAGS_OTHER} ) </syntaxhighlight> <br> ==== 主要ヘッダと用途 ==== <center> {| class="wikitable" |+ libsystemdの主要ヘッダ ! ヘッダ !! 主な用途 |- | <code><systemd/sd-bus.h></code> || Systemd Managerや他のD-Busサービスとの通信 |- | <code><systemd/sd-event.h></code> || epollベースのイベントループ、タイマ、シグナル、I/O監視 |- | <code><systemd/sd-journal.h></code> || journaldへのログ出力、ジャーナルの読み出し |- | <code><systemd/sd-daemon.h></code> || <code>sd_notify()</code>、ウォッチドッグ、ソケットアクティベーション支援 |- | <code><systemd/sd-id128.h></code> || 128ビットIDの生成と管理 |- | <code><systemd/sd-login.h></code> || ログインセッションやseat情報の取得 |} </center> <br><br> == libsystemdの主要なAPI == C++からSystemdを扱う場合、最初に理解しておくべきAPI群を整理する。<br> <br> <center> {| class="wikitable" |+ 純粋なC++からよく使うlibsystemd API ! 分類 !! 主なAPI !! 用途 |- | Systemd Manager操作 || <code>sd_bus_open_system()</code><br><code>sd_bus_call_method()</code><br><code>sd_bus_add_match()</code> || ユニットの起動・停止・プロパティ監視 |- | イベントループ || <code>sd_event_default()</code><br><code>sd_event_run()</code><br><code>sd_bus_attach_event()</code> || 非同期イベント処理とD-Bus統合 |- | journald出力 || <code>sd_journal_print()</code><br><code>sd_journal_send()</code> || 通常ログと構造化ログの送信 |- | サービス通知 || <code>sd_notify()</code><br><code>sd_watchdog_enabled()</code> || <code>Type=notify</code>サービスの状態通知 |- | ジャーナル読み出し || <code>sd_journal_open()</code><br><code>sd_journal_next()</code><br><code>sd_journal_get_data()</code> || ログの追跡と解析 |} </center> <br><br> == Systemd Managerとの通信 == ==== 基本情報 ==== Systemd Managerは、D-Bus上で以下のサービスを提供する。<br> * サービス名 *: <code>org.freedesktop.systemd1</code> * オブジェクトパス *: <code>/org/freedesktop/systemd1</code> * 主要インターフェース *: <code>org.freedesktop.systemd1.Manager</code> <br> ユニットを起動する代表的なメソッドは、<code>StartUnit</code> である。<br> 停止は <code>StopUnit</code>、再起動は <code>RestartUnit</code>、リロードは <code>ReloadUnit</code> を使用する。<br> <br> ==== ジョブモード ==== Systemd Managerのユニット操作では、しばしば「ジョブモード」を同時に指定する。<br> <br> * <code>replace</code> *: 既存の競合ジョブを置き換えて実行する。通常はこの値を使う。 * <code>fail</code> *: 競合ジョブが存在する場合は失敗する。 * <code>isolate</code> *: 対象ユニットとその依存を残し、他を停止する。 * <code>ignore-dependencies</code> *: 依存関係を無視する。 * <code>ignore-requirements</code> *: requirement関係を無視する。<br> <br> <u><code>ignore-dependencies</code> および <code>ignore-requirements</code> は、通常のアプリケーションから安易に使うべきではない。</u><br> <br> ==== C++によるサービス起動例 ==== 以下の例では、システムバスへ接続し、sshd.serviceに対して <code>StartUnit</code> を呼び出す。<br> <br> この例が返す <u>ジョブオブジェクトパス</u> は、開始要求が受理されたことを示すものであり、サービスが完全に起動済みであることを意味しない。<br> <br> <syntaxhighlight lang="c++"> #include <systemd/sd-bus.h> #include <cstring> #include <iostream> #include <memory> #include <stdexcept> #include <string> namespace { struct SdBusDeleter { void operator()(sd_bus* bus) const { if (bus) { sd_bus_flush_close_unref(bus); } } }; struct SdBusMessageDeleter { void operator()(sd_bus_message* message) const { if (message) { sd_bus_message_unref(message); } } }; class SdBusErrorGuard { public: SdBusErrorGuard() : error_(SD_BUS_ERROR_NULL) {} ~SdBusErrorGuard() { sd_bus_error_free(&error_); } sd_bus_error* get() { return &error_; } const char* message() const { return error_.message ? error_.message : ""; } private: sd_bus_error error_; }; using BusPtr = std::unique_ptr<sd_bus, SdBusDeleter>; using MessagePtr = std::unique_ptr<sd_bus_message, SdBusMessageDeleter>; BusPtr open_system_bus() { sd_bus* raw_bus = nullptr; const int ret = sd_bus_open_system(&raw_bus); if (ret < 0) { throw std::runtime_error("システムバスのオープンに失敗しました: " + std::string(std::strerror(-ret))); } return BusPtr(raw_bus); } std::string start_unit(sd_bus* bus, const std::string& unit_name, const std::string& mode) { SdBusErrorGuard error; sd_bus_message* raw_reply = nullptr; const int ret = sd_bus_call_method(bus, "org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "StartUnit", error.get(), &raw_reply, "ss", unit_name.c_str(), mode.c_str()); if (ret < 0) { throw std::runtime_error("StartUnitの呼び出しに失敗しました: " + std::string(error.message())); } MessagePtr reply(raw_reply); const char* job_path = nullptr; const int read_ret = sd_bus_message_read(reply.get(), "o", &job_path); if (read_ret < 0) { throw std::runtime_error("ジョブパスの読み出しに失敗しました: " + std::string(std::strerror(-read_ret))); } return std::string(job_path); } } int main() { try { BusPtr bus = open_system_bus(); const std::string job_path = start_unit(bus.get(), "sshd.service", "replace"); std::cout << "StartUnit request accepted" << std::endl; std::cout << "job path: " << job_path << std::endl; std::cout << "この時点ではジョブが作成された段階であり、サービスの完全起動確認は別途必要です" << std::endl; return 0; } catch (const std::exception& ex) { std::cerr << ex.what() << std::endl; return 1; } } </syntaxhighlight> <br> この後、実際の起動完了まで追跡したい場合は、<code>JobRemoved</code> シグナルを監視するか、対象ユニットの <code>ActiveState</code> プロパティを問い合わせる。<br> <br> ==== ActiveStateの確認 ==== ジョブ作成後に状態を確認する場合は、対象ユニットのD-Busオブジェクトパスを取得し、<code>org.freedesktop.DBus.Properties.Get</code> で <code>ActiveState</code> を参照する。<br> <br> イベント駆動で監視する場合は、<code>sd_bus_add_match()</code> で <code>PropertiesChanged</code> または <code>JobRemoved</code> を購読するとよい。<br> <br><br> == sd-eventとの統合 == <code>sd-event</code> は、epollベースのイベントループであり、タイマ、シグナル、I/O、子プロセス終了、メモリ圧力等を統合的に扱える。<br> 単純な同期呼び出しだけであれば必須ではないが、非同期D-Bus呼び出しや長寿命デーモンでは非常に有用である。<br> <br> ==== 基本的な使用方法 ==== * <code>sd_event_default()</code> *: 既定のイベントループを作成する。 * <code>sd_bus_attach_event()</code> *: D-Bus接続をイベントループへ接続する。 * <code>sd_event_run()</code> / <code>sd_event_loop()</code> *: イベント処理を実行する。 <br> ==== 利用例 ==== 以下のような場面で <code>sd-event</code> が有効である。<br> <br> * 非同期の <code>sd_bus_call_method_async()</code> を使う場合 * タイマ駆動で定期的にサービス状態を確認する場合 * シグナル受信とD-Busイベントを1つのループでまとめたい場合 * <code>sd_event_set_watchdog()</code> でウォッチドッグ通知を自動化したい場合 <br> ==== 注意点 ==== 非同期処理を書き始めたら、イベントループを実際に駆動しなければコールバックは返ってこない。<br> <br> また、長時間ブロックする処理をイベントハンドラ内で実行すると、ウォッチドッグ通知やD-Bus応答が遅延する。<br> <br><br> == journaldとの連携 == ==== ログの書き込み ==== Systemd環境では、単なる標準出力だけでなく、journaldに対して構造化ログを送ると、検索性と保守性が大きく向上する。<br> 主なAPIは、<code>sd_journal_print()</code> と <code>sd_journal_send()</code> である。<br> <br> * <code>sd_journal_print()</code> *: printf風の簡易ログ出力 * <code>sd_journal_send()</code> *: <code>MESSAGE=...</code>、<code>PRIORITY=...</code> 等のフィールドを付与した構造化ログ * <code>sd_journal_sendv()</code> *: <code>iovec</code>配列による柔軟な送信 <br> ==== 構造化ログの例 ==== <syntaxhighlight lang="c++"> #include <systemd/sd-journal.h> #include <syslog.h> void write_systemd_log(const char* unit_name, const char* detail) { sd_journal_send("MESSAGE=Systemd unit operation finished", "PRIORITY=%i", LOG_INFO, "UNIT=%s", unit_name, "DETAIL=%s", detail, nullptr); } </syntaxhighlight> <br> 構造化ログを使うと、<code>journalctl</code> 側でフィールド検索しやすくなる。<br> <br> ==== ジャーナルの読み出し ==== ログを追跡する場合は、<code>sd_journal_open()</code>、<code>sd_journal_next()</code>、<code>sd_journal_get_data()</code>を使用する。<br> <br> ユニット名やプライオリティでフィルタを掛ける場合は、<code>sd_journal_add_match()</code> が便利である。<br> <br><br> == Type=notifyサービスとの連携 == Systemd管理下のデーモンをC++で実装する場合、<code>Type=notify</code> または <code>Type=notify-reload</code> を使用すると、起動完了やリロード状態をSystemdへ正確に伝えられる。<br> <br> ==== 主な通知内容 ==== * <code>READY=1</code> *: サービスの初期化完了 * <code>STATUS=...</code> *: 現在の状態メッセージ * <code>RELOADING=1</code> *: リロード開始 * <code>STOPPING=1</code> *: 終了処理の開始 * <code>WATCHDOG=1</code> *: ウォッチドッグのキープアライブ <br> ==== 最小構成の例 ==== <syntaxhighlight lang="c++"> #include <systemd/sd-daemon.h> int notify_ready() { return sd_notify(0, "READY=1\nSTATUS=Initialization completed"); } </syntaxhighlight> <br> ==== ウォッチドッグ ==== ウォッチドッグが有効な場合は、<code>sd_watchdog_enabled()</code> で間隔を取得し、その半分程度の周期で <code>WATCHDOG=1</code> を送信する構成が一般的である。<br> <br> <code>sd-event</code> を使用している場合は、<code>sd_event_set_watchdog()</code> を使って自動化できる。<br> <br> ==== ユニットファイル側の注意 ==== アプリケーションが <code>sd_notify()</code> を送っても、ユニットファイルで <code>Type=notify</code> と <code>NotifyAccess=</code> が適切に設定されていなければ期待通りに動作しない。<br> <br> 通常は <code>NotifyAccess=main</code> から検討し、必要がある場合のみ <code>all</code> を使用する。<br> <br><br> == Transient Unit == Transient Unitは、ディスク上の恒久的なunitファイルを作成せずに、実行時に動的なユニットを生成する仕組みである。<br> <br> これは、一時的なscopeやserviceを作成したい場合に有効である。<br> <br> ==== 主な用途 ==== * 一時的なワーカープロセスを専用scopeで実行する。 * リソース制限付きのserviceを動的に生成する。 * コンテナやジョブ実行器から専用cgroupを切る。 <br> ==== 代表的なプロパティ ==== <center> {| class="wikitable" |+ StartTransientUnitでよく使うプロパティ ! プロパティ !! 用途 |- | <code>Description</code> || 一時ユニットの説明 |- | <code>Slice</code> || 所属するsliceの指定 |- | <code>PIDs</code> || scopeへ初期PIDを渡す |- | <code>Delegate</code> || cgroupの委譲を有効化する |- | <code>RemainAfterExit</code> || service終了後も状態を保持する |} </center> <br> ==== 注意点 ==== <code>StartTransientUnit</code> のD-Busシグネチャは複雑であり、<code>a(sv)</code> 形式のプロパティ配列を正確に構築する必要がある。<br> <br> また、恒久unitファイルで使用可能な全ディレクティブがTransient Unitで使えるわけではない。<br> <br> 最新の対応状況は、[https://github.com/systemd/systemd/blob/main/docs/TRANSIENT-SETTINGS.md TRANSIENT-SETTINGS.md]を参照すること。<br> <br><br> == 権限とPolKit == Systemd Managerに対する読み取り操作は比較的容易だが、ユニットの開始、停止、再起動、ユニットファイルの有効化等は権限が必要になる。<br> <br> システムバスでの管理操作では、PolKit認証や適切なケーパビリティが関わる。<br> <br> ==== 代表的な権限 ==== * <code>org.freedesktop.systemd1.manage-units</code> *: ユニットの開始、停止、再起動、プロパティ変更 * <code>org.freedesktop.systemd1.manage-unit-files</code> *: ユニットファイルの有効化、無効化 * <code>org.freedesktop.systemd1.set-environment</code> *: Systemd Managerの環境変数変更 <br> ==== 実務上の注意 ==== * 一般ユーザでシステムユニットを操作すると、PolKit認証を求められることがある。 * 同じユーザのユーザユニットを扱うだけであれば、<code>sd_bus_open_user()</code> でユーザマネージャへ接続する方が簡潔である。 * デスクトップ環境が存在しないサーバでは、PolKit対話認証ダイアログを前提にしない設計が必要である。 <br> 詳しい認可制御は、[[C++の応用_-_PolKit]]のページを参照すること。<br> <br><br> == トラブルシューティング == ==== StartUnitが成功したのにサービスが動いていない ==== <code>StartUnit</code> の成功は、開始要求が受理されたことを示すだけである。<br> ジョブ終了後に失敗している可能性があるため、<code>JobRemoved</code> や <code>ActiveState</code> を確認する。<br> <br> ==== 権限エラーになる ==== 一般ユーザでシステムユニットを操作している可能性がある。<br> 対象がユーザユニットであれば <code>sd_bus_open_user()</code> へ切り替え、システムユニットであればPolKit設定や実行権限を見直す。<br> <br> ==== sd_notify()が効かない ==== ユニットファイル側で <code>Type=notify</code> または <code>Type=notify-reload</code> が設定されているか確認する。<br> <br> さらに、<code>NotifyAccess=</code> が通知元プロセスを許可している必要がある。<br> <br> ==== 非同期コールバックが返ってこない ==== <code>sd_bus_call_method_async()</code> を使う場合、イベントループを実際に回していなければコールバックは実行されない。<br> <br> <code>sd_bus_attach_event()</code> または <code>sd_bus_process()</code> / <code>sd_bus_wait()</code> の駆動を確認する。<br> <br> ==== ジャーナルへログが出ない ==== journaldが利用可能な環境であること、また、ログ送信後すぐにプロセスを終了していないことを確認する。<br> <br> 詳細な追跡には、<code>journalctl -xeu <unit名></code> が有効である。<br> <br><br> == 関連情報 == * [https://www.freedesktop.org/software/systemd/man/latest/libsystemd.html libsystemd] * [https://www.freedesktop.org/software/systemd/man/latest/sd-bus.html sd-bus] * [https://www.freedesktop.org/software/systemd/man/latest/sd-event.html sd-event] * [https://www.freedesktop.org/software/systemd/man/latest/sd-journal.html sd-journal] * [https://www.freedesktop.org/software/systemd/man/latest/sd_notify.html sd_notify] * [https://www.freedesktop.org/software/systemd/man/latest/org.freedesktop.systemd1.html org.freedesktop.systemd1] * [https://systemd.io/PORTABILITY_AND_STABILITY/ PORTABILITY_AND_STABILITY] * [https://github.com/systemd/systemd systemd GitHub] * [[C++の応用 - PolKit]] <br><br> {{#seo: |title={{PAGENAME}} : Exploring Electronics and SUSE Linux | MochiuWiki |keywords=MochiuWiki,Mochiu,Wiki,Mochiu Wiki,Electric Circuit,Electric,pcb,Mathematics,AVR,TI,STMicro,AVR,ATmega,MSP430,STM,Arduino,Xilinx,FPGA,Verilog,HDL,PinePhone,Pine Phone,Raspberry,Raspberry Pi,C,C++,C#,Shell,Bash,Zsh,Fish,SUSE,SLE,Suse Enterprise,Suse Linux,openSUSE,open SUSE,Leap,Linux,uCLnux,systemd,libsystemd,D-Bus,journald,PolKit,電気回路,電子回路,基板,プリント基板 |description={{PAGENAME}} - 電子回路とSUSE Linuxに関する情報 | This page is {{PAGENAME}} in our wiki about electronic circuits and SUSE Linux |image=/resources/assets/MochiuLogo_Single_Blue.png }} __FORCETOC__ [[カテゴリ:C++]]
C++の応用 - Systemd
に戻る。
案内
メインページ
最近の更新
おまかせ表示
MediaWiki についてのヘルプ
ツール
リンク元
関連ページの更新状況
特別ページ
ページ情報
We ask for
Donations
Collapse