systemdのカスタムサービスファイルを書くときにどのTypeで起動を判定するかのTypeでType=notifyについて調べたメモです。
Type=notifyとは
以下のような記載があります。フォアグラウウンドで実行を継続するデーモンで使えるType=simpleとほぼ同じようですが起動/停止などをsd_notify(3)というsystemd用の関数用いて通知を行います。ちなみに使用するにはsystemd-devel
をインストールする必要があります。
Type=notify: Type=simple と同じですが、利用可能になったときにデーモンが systemd に信号を送るように条件がつけられます。この通知のリファレンス実装は libsystemd-daemon.so によって提供されています。
simpleとの違い
simpleはコマンドを実行したタイミングで起動完了と判断します。極端な例ですが以下のようなソースでinit_service()
でサービスの初期化処理をしてそこで失敗してデーモンが終了してもsystemctlのstartコマンドは正常終了となります。
#include <unistd.h> #include <stdio.h> int init_service() { // config読んだり、他サービスから情報を取ってきたりしてデーモン起動準備をする。失敗する可能性もある } int main(int arg, char **argv) { if(init_service()) return 0; start_daemon(); }
これ自体は何が問題になるかというとinit_service()の結果を待たずにコマンドが戻ってきてしまうのでsystemctl start test && 前段のコマンドが成功している前提のコマンド
みたいに&&を繋いだだけの処理は失敗する可能性が出てきてしまいます。(例えば何らかのデータストアを起動させつつクライアントアプリを起動するみたいなケース)
この話自体はそこまで問題にはならなそうでExecStartPre/Postを使うことでサービス起動前後にsleepなりコネクションの状態をloopで監視するスクリプトを入れることで解決はできる。解決策はいろいろありそうだがその中の一つである"Type=notify"を例に挙げて調べてみる。
Type=notify
を使うにはsd_notify(3)
という関数を用いて起動が完了したことをsystemdに通知することができる。以下のようにsleep(30)の部分でsystemctlは戻らずに通知が来るまでコマンドが停止してくれる。
#include <unistd.h> #include <stdio.h> #include <systemd/sd-daemon.h> int main(int arg, char **argv) { sleep(30) if (sd_notify(0, "READY=1")) fprintf(stderr, "failed sd_notify"); return 0; }
これを入れることでsystemctl start test && systemctl start test2
とか内部のユニットファイルでtest.serviceとtest2.serviceで依存関係を持たせてもtestのサービス起動通知はまではtest2は起動を待ってくれるようになる。パッと用途は思いつきませんでしたが起動以外にもステータスをsystemdに送信することができてSTOPPINGなどでプロセス停止中などを通知することも可能となります。
apache/httpdなんかでも利用されていて有効にしておくことでsystemctl statusの結果に統計情報を出力することが可能となるようです。こんな機能があるんですね〜という。
static int systemd_monitor(apr_pool_t *p, server_rec *s) { sd_notifyf(0, "READY=1\n" "STATUS=Total requests: %lu; Idle/Busy workers %d/%d;" // STATUSの部分がsystemctl statusに出力されるようになる "Requests/sec: %.3g; Bytes served/sec: %sB/sec\n", sload.access_count, sload.idle, sload.busy, ((float) sload.access_count) / (float) up_time, bps); return DECLINED; }
どうやって通知してるのか
上記の記事にある通りUnixドメインソケットを用いた通信を行なっているようです。netstatをgrepすると以下のようになっている。
$ netstat --protocol=unix |grep notify unix 3 [ ] DGRAM 10138 /run/systemd/notify
httpdをtraceすると以下のような通信を行なっていることがわかった。sd_notify(3)なしで通信も容易にできそうですね。
sendmsg(8, {msg_name={sa_family=AF_UNIX, sun_path="/run/systemd/notify"}, msg_namelen=21, msg_iov=[{iov_base="READY=1\nSTATUS=Total requests: 2; Current requests/sec: 0; Current traffic: 0 B/sec\n", iov_len=86}], msg_iovlen=1, msg_controllen=0, msg_flags=0}, MSG_NOSIGNAL) = 86