SystemdユニットでExecStopを書いたのが初めてだったのだけれど、どうしてもExecStopを書くとTypeによらずstopされてしまう。
Typeがoneshotであるならばこれは正しい。 Type=oneshotである場合、.serviceユニット起動時にExecStartを実行し、この終了を待つ。 ExecStartプロセスの実行中はactiveとなり、実行が終了するとサービスそのものが終了したとみなし、inactiveになる。
Systemdユニットに詳しい人は割と少ないのでサービスタイプについて改めて解説しておこう。
oneshotは単純にその時に実行するだけのサービスである。 起動は実行終了を待ち、終了したらサービス自体を終了する。
simpleはデフォルトのサービスタイプである。 このサービスタイプはプロセスを実行していることで機能するサービスである。 フォアグラウンドで実行を継続するタイプのサービスに対して適用し、起動時は実行すると起動完了とみなす。oneshotのように起動したプロセスの実行中がactiveとなる。
forkingはoneshotのようにサービスの実行終了を待つが、ステータスは直接実行したプロセス($MAINPID)だけでなく、その子プロセスで判断する。 これは実行するのがサービスそのものではなく、起動スクリプトである場合に有用である。
さぁ、ここまではいい。 問題は、ExecStopがいつ実行されるのか、である。
普通に考えればsystemctl stop foo.serviceしたときに実行されるものだろう。 ところが実際は「サービスが終了した場合にも実行される」。 これはExecStopPostと同じだ。
だが、そうでないケース(今回の場合は、iscsiターゲットにログインし、ファイルシステムをマウントする)は困ってしまう。 このようなケースにおいてはiscsiターゲットログインとファイルシステムマウントをそれぞれ別のユニットとして管理するという方法もあるが、いずれにせよ「実行時に終わっては困る」のである。 このようなケースにおいてはTypeとしてoneshotを指定した上でRemainAfterExit=trueとすることで目的を達することができる。 これにより、実行が終了した(起動したプロセスが終了した)場合でも実行ステータスは実行中になる。
つまり、ExecStopというのは、「サービスを停止する」のではなく「サービスプロセスのガベージコレクション」なのだ。
RemainAfterExit=trueは基本的にoneshotと組み合わせる前提になっている。 simpleやforkingでも作用するが、マニュアルにはoneshot前提で書かれている。 つまりSystemdが起動したプロセスが終了してもサービスは有効な状態になっているようなスクリプトはoneshotであるべきだと考えられているのだろう。
だが、これは適切なのだろうか? 確かに、実行終了してもactiveな状態になっているようなものはsimpleの「サービスを提供するために実行しつづける」には該当しないし、forkingの「子プロセスがサービスとして機能する」である必然性もない。 だからoneshotだけの例外的な振舞いだと考えていいように思われるが、それでも「サービス終了時に明示的にstopしてしまう」のであればRemainAfterExit=falseがデフォルトであることには違和感がある。
それ以上に「ExecStopは明示的に停止された場合にのみ実行する」オプションがないのは問題ではないのか。 OnSuccessやOnFailreはあるが、「明示的に指定された場合のみ実行したい」という希望には沿わない。 (あるいは、存在するが私が見つけられないのだろうか)
むしろ「OnSuccessやOnFailreがあるのだから、ExecStopは明示的停止した場合にのみ実行したい」というのが自然なように思うのだが…