systemdで呼び出されるかどうかをシェルスクリプトでどのように確認できますか?

systemdで呼び出されるかどうかをシェルスクリプトでどのように確認できますか?

さまざまな理由から、ベンダーのアプリケーションをラップするシェルスクリプトがあります。私たちのシステム管理者とアプリケーションの所有者は、systemdに対してさまざまなレベルの使いやすさを持っています。したがって、アプリケーションが失敗した場合(systemctlもこれを示す)、一部のエンドユーザー(「root」システム管理者を含む)は、を使用する代わりにラッパースクリプトを使用して「直接」アプリケーションを起動できますsystemctl restart。正しい終了スクリプトを呼び出しません。アプリケーションがすでに停止しているからです。

私はsystemdへの移行を案内するためにラッパースクリプトを更新してsystemdによって呼び出されるのか、エンドユーザーによって呼び出されるのかを確認したいと思います。外部systemdでは、呼び出し元にsystemctlを使用するように求めるメッセージを印刷したいと思います。

systemdで呼び出されるかどうかをシェルスクリプトでどのように確認できますか?

次のように仮定できます。

  • スクリプトをラップするbashシェル
  • ラッパースクリプトがアプリケーションを正常に起動および停止します。
  • systemd サービスが期待どおりに動作します。

systemd サービスの例は次のとおりです。

[Unit]
Description=Vendor's Application 
After=network-online.target

[Service]
ExecStart=/path/to/wrapper start
ExecStop=/path/to/wrapper stop
Type=forking

[Install]
WantedBy=multi-user.target

私は興味がない初期化システムの検出、私は既にそれがシステム化されていることを知っているので。

答え1

~からルーカスウェイクマイスター豊富なコンテンツ応答サーバーエラー:

  • systemd バージョン 231 以降では、ログに接続する stdout または stderr サービスに設定された JOURNAL_STREAM 変数があります。
  • systemd バージョン 232 以降では、INVOCATION_ID 変数が設定されます。

これらの変数に依存したくない場合、または231以前のシステムバージョンの場合は、親PIDが1であることを確認できます。

if [[ $PPID -ne 1 ]]
then
  echo "Don't call me directly; instead, call 'systemctl start/stop service-name'"
  exit 1
fi >&2

答え2

短い答え

if ! grep -qEe '[.]service$' /proc/self/cgroup; then
    echo "This script should be started with systemctl" >&2
    exit 1
fi

...または実行する予定の特定のサービスの名前を知っていて、ユーザーセッションの作成を妨げる誤った設定から防御できるようにしたい場合:

if ! grep -qEe '/myservice[.]service$' /proc/self/cgroup; then
    echo "This service should be started with systemctl start myservice" >&2
    exit 1
fi

なぜ働くか。

現在プロセスを開始しているサービス(存在する場合)を確認する1つの方法は、を確認することです/proc/self/cgroup。トリガーされたサービスの場合、systemdこれにはサービス名が含まれます。

12:pids:/system.slice/dhcpcd.service
11:rdma:/
10:memory:/system.slice/dhcpcd.service
9:blkio:/system.slice/dhcpcd.service
8:devices:/system.slice/dhcpcd.service
7:hugetlb:/
6:cpuset:/
5:freezer:/
4:cpu,cpuacct:/system.slice/dhcpcd.service
3:net_cls,net_prio:/
2:perf_event:/
1:name=systemd:/system.slice/dhcpcd.service
0::/system.slice/dhcpcd.service

...そしてユーザーセッションに関連するプロセスの場合、cgroupは次のようになります/user.slice/user-1000.slice/session-337.scope(最後の再起動後にシステム上のユーザーUID 1000の337番目のセッションであると仮定)。


よりエキゾチックな実装

実行中の特定のサービスを検索する場合は、そのサービスを抽出することもできます/proc/self/cgroup。たとえば、次のことを考えてみましょう。

cgroup_full=$(awk -F: '$1 == 0 { print $3 }' /proc/self/cgroup)
cgroup_short=${cgroup_full##*/}
case $cgroup_full in
  /system.slice/*.service) echo "Run from system service ${cgroup_short%.*}";;
  /user.slice/*.service)   echo "Run from user service ${cgroup_short%.*}";;
  *.service)               echo "Service ${cgroup_short%.*} type unknown";;
  *)                       echo "Not run from a systemd service; in $cgroup_full";;
esac

答え3

心に浮かぶもう一つの確実な解決策は、次のようなものを追加することです。

Environment=FROM_SYSTEMD=1

サービスファイルに追加し、対応する環境変数をテストします。

答え4

systemd別のアプローチは、より緊密に結合された検査を得るために明示的に照会することです。

たとえば、同様のユースケースでは、次のように実行しました。

if [ "$(systemctl show -p ControlPID vendorservice)" != "ControlPID=$$" ]; then
    echo 'no go'
    exit 1
fi

type=forking上記のコードスニペットは特定の状態を利用するため、サービスでのみ機能します。それ職業は一生続くことができます。

ControlPID具体的にはthisに設定された値を照会し、操作のsystemd場合type=forking表現するプロセスの作成まっすぐsystemdPIDFile同時に、それ自体は何かが表示される(またはGuessMainPID何かを検索するのを待つ)後にControlPID値がリセットされます0。この特別な動作は、systemd可能なPIDラップラウンドから検査を保護する必要があります。

いくつかの属性を検索することができ、そのsystemctl show一部は特定のユースケースによってtype=異なります。したがって、type=forking実行する特定のユースケース以外のサービスの種類について、および/またはそれに基づいて最も適切な属性を照会する必要があります。同等の「ネットワーク共有」を調べます。

したがって、ここの解決策は普遍的な解決策ではありませんが、おそらくより強力で古いバージョンと互換性があり、1将来に備えています2。

また、照会する正しいサービス名を既に知っている必要があります。具体的には、インスタンスサービスの場合は、クエリする正確なインスタンス名を知る必要がありますvendorservice@1(例:)。


1 ControlPID財産そしてsystemctl showコマンドそれで-pオプション最初のリリースからずっと続いてきました。systemd

2 systemctl show出力は次の一部です。安定性の約束

関連情報