さまざまな理由から、ベンダーのアプリケーションをラップするシェルスクリプトがあります。私たちのシステム管理者とアプリケーションの所有者は、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
表現するプロセスの作成まっすぐsystemd
PIDFile
同時に、それ自体は何かが表示される(またはGuessMainPID
何かを検索するのを待つ)後にControlPID
値がリセットされます0
。この特別な動作は、systemd
可能なPIDラップラウンドから検査を保護する必要があります。
いくつかの属性を検索することができ、そのsystemctl show
一部は特定のユースケースによってtype=
異なります。したがって、type=forking
実行する特定のユースケース以外のサービスの種類について、および/またはそれに基づいて最も適切な属性を照会する必要があります。同等の「ネットワーク共有」を調べます。
したがって、ここの解決策は普遍的な解決策ではありませんが、おそらくより強力で古いバージョンと互換性があり、1将来に備えています2。
また、照会する正しいサービス名を既に知っている必要があります。具体的には、インスタンスサービスの場合は、クエリする正確なインスタンス名を知る必要がありますvendorservice@1
(例:)。
1 ControlPID
財産そしてsystemctl show
コマンドそれで-p
オプション最初のリリースからずっと続いてきました。systemd
2 systemctl show
出力は次の一部です。安定性の約束