SystemdでOS起動/停止時にスクリプト実行させるサービスを作ってみた

Systemdを改めて勉強する必要があり、関係がありそうなドキュメントを色々と読んでみました。
が、結局は「実際に作って見ないと分からないな」ということで、
素人まるだしでServiceを作ってみました。
自分向けの備忘録を兼ねて、下記にまとめてみようと思います。

##今回のゴール
OS起動時、および停止時に、ある特定のスクリプトを実行するサービスを作成する
具体的には、下記のような処理

- 起動時に、現在時刻を"/work/hogehoge.start"に追記する
- 停止時に、現在時刻を"/work/hogehoge.stop"に追記する

##今回の環境
Red Hat Enterprise Linux 7.2
※IBM Power Development Cloudで用意しました。

##作業の記録

###1. 既存のserviceファイルからコピペで流用してみる
**/usr/lib/systemd/system/sshd.service**
上記ファイルをベースに、下記のようなUnitファイルを作成してみた

```:/etc/systemd/system/hogehoge.service
[Unit]
Description=hogehoge Service Test
Documentation=man:sshd(8) man:sshd_config(5)
After=network.target

[Service]
ExecStart=date +%T.%3N >> /work/hogehoge.start
ExecStop=date +%T.%3N >> /work/hogehoge.stop

[Install]
WantedBy=multi-user.target
[root@sys-90562 system]# 
```

###2. コマンドが実行されず、試行錯誤を繰り返す、、、

では、早速hogehogeサービスを起動させてみます。
サービス起動前に、ファイルの修正内容の読み込ませるために**systemctl daemon-reload**を実行しておきます。

```
[root@sys-90562 system]# systemctl daemon-reload
[root@sys-90562 system]# systemctl start hogehoge.service
[root@sys-90562 system]# systemctl status hogehoge.service -l
● hogehoge.service - hogehoge Service Test
   Loaded: error (Reason: Invalid argument)
   Active: inactive (dead)
     Docs: man:sshd(8)
           man:sshd_config(5)

Dec 17 18:00:26 sys-90562.dal-ebis.ihost.com systemd[1]: [/etc/systemd/system/hogehoge.service:7] Executable path is not absolute, ignoring: date +%T.%3N >> /work/hogehoge.start
Dec 17 18:00:26 sys-90562.dal-ebis.ihost.com systemd[1]: [/etc/systemd/system/hogehoge.service:8] Executable path is not absolute, ignoring: date +%T.%3N >> /work/hogehoge.stop
```
エラー部分をgoogle翻訳にかけると「実行可能パスは絶対パスではありません」、
じゃあdateコマンドをフルパスで指定してやればいいのかな

```:hogehoge.service(v1)修正箇所のみ
[Service]
ExecStart=/bin/date +%T.%3N >> /work/hogehoge.start
ExecStop=/bin/date +%T.%3N >> /work/hogehoge.stop
```

修正後、リロード＆サービス起動！
これでどうかな〜

```
[root@sys-90562 system]# systemctl status hogehoge.service -l
● hogehoge.service - hogehoge Service Test
   Loaded: loaded (/etc/systemd/system/hogehoge.service; disabled; vendor preset: disabled)
   Active: failed (Result: exit-code) since Sun 2017-12-17 18:04:06 JST; 3s ago
     Docs: man:sshd(8)
           man:sshd_config(5)
  Process: 2265 ExecStop=/bin/date +%T.%3N >> /work/hogehoge.stop (code=exited, status=1/FAILURE)
  Process: 2263 ExecStart=/bin/date +%T.%3N >> /work/hogehoge.start (code=exited, status=1/FAILURE)
 Main PID: 2263 (code=exited, status=1/FAILURE)

Dec 17 18:04:06 sys-90562.dal-ebis.ihost.com systemd[1]: Started hogehoge Service Test.
Dec 17 18:04:06 sys-90562.dal-ebis.ihost.com systemd[1]: Starting hogehoge Service Test...
Dec 17 18:04:06 sys-90562.dal-ebis.ihost.com date[2263]: /bin/date: extra operand ‘>>’
Dec 17 18:04:06 sys-90562.dal-ebis.ihost.com date[2263]: Try '/bin/date --help' for more information.
Dec 17 18:04:06 sys-90562.dal-ebis.ihost.com systemd[1]: hogehoge.service: main process exited, code=exited, status=1/FAILURE
Dec 17 18:04:06 sys-90562.dal-ebis.ihost.com date[2265]: /bin/date: extra operand ‘>>’
Dec 17 18:04:06 sys-90562.dal-ebis.ihost.com date[2265]: Try '/bin/date --help' for more information.
Dec 17 18:04:06 sys-90562.dal-ebis.ihost.com systemd[1]: hogehoge.service: control process exited, code=exited status=1
Dec 17 18:04:06 sys-90562.dal-ebis.ihost.com systemd[1]: Unit hogehoge.service entered failed state.
Dec 17 18:04:06 sys-90562.dal-ebis.ihost.com systemd[1]: hogehoge.service failed.
```

またもやエラーが。なんとなくだけど、dateコマンドとリダイレクトのコマンドが別に解釈されているように見える
じゃあ、ダブルクォーテーションで囲ってやればいいのかな

```:hogehoge.service(v2)修正箇所のみ
[Service]
ExecStart="/bin/date +%T.%3N >> /work/hogehoge.start"
ExecStop="/bin/date +%T.%3N >> /work/hogehoge.stop"
```

次に出たエラーは。。。

```
[root@sys-90562 system]# systemctl status hogehoge.service -l
● hogehoge.service - hogehoge Service Test
   Loaded: loaded (/etc/systemd/system/hogehoge.service; disabled; vendor preset: disabled)
   Active: failed (Result: exit-code) since Sun 2017-12-17 18:06:05 JST; 3s ago
     Docs: man:sshd(8)
           man:sshd_config(5)
  Process: 2293 ExecStop=/bin/date +%T.%3N >> /work/hogehoge.stop (code=exited, status=203/EXEC)
  Process: 2291 ExecStart=/bin/date +%T.%3N >> /work/hogehoge.start (code=exited, status=203/EXEC)
 Main PID: 2291 (code=exited, status=203/EXEC)

Dec 17 18:06:05 sys-90562.dal-ebis.ihost.com systemd[1]: Started hogehoge Service Test.
Dec 17 18:06:05 sys-90562.dal-ebis.ihost.com systemd[1]: Starting hogehoge Service Test...
Dec 17 18:06:05 sys-90562.dal-ebis.ihost.com systemd[1]: hogehoge.service: main process exited, code=exited, status=203/EXEC
Dec 17 18:06:05 sys-90562.dal-ebis.ihost.com systemd[1]: hogehoge.service: control process exited, code=exited status=203
Dec 17 18:06:05 sys-90562.dal-ebis.ihost.com systemd[1]: Unit hogehoge.service entered failed state.
Dec 17 18:06:05 sys-90562.dal-ebis.ihost.com systemd[1]: hogehoge.service failed.
```

これだけでは、何がいけないのかが分からないので、
**/ver/log/messages**も見てみると、エラー内容がわかった。

```
Dec 17 18:06:05 sys-90562 systemd: Started hogehoge Service Test.
Dec 17 18:06:05 sys-90562 systemd: Starting hogehoge Service Test...
Dec 17 18:06:05 sys-90562 systemd: Failed at step EXEC spawning /bin/date +%T.%3N >> /work/hogehoge.start: No such file or directory
Dec 17 18:06:05 sys-90562 systemd: hogehoge.service: main process exited, code=exited, status=203/EXEC
Dec 17 18:06:05 sys-90562 systemd: Failed at step EXEC spawning /bin/date +%T.%3N >> /work/hogehoge.stop: No such file or directory
Dec 17 18:06:05 sys-90562 systemd: hogehoge.service: control process exited, code=exited status=203
Dec 17 18:06:05 sys-90562 systemd: Unit hogehoge.service entered failed state.
Dec 17 18:06:05 sys-90562 systemd: hogehoge.service failed.
```

ダブルクォーテーションで囲ったことで、コマンド行がファイル名として認識されて、
それが無いからエラーになってしまっているのか。
じゃあ、コマンド実行用のラッパーシェルでも用意しますかね。

```shell:hoge_start.bash
#!/bin/bash

/bin/date +%T.%3N >> /work/hogehoge.start
exit 0;
```

```shell:hoge_stop.bash
#!/bin/bash

/bin/date +%T.%3N >> /work/hogehoge.stop
exit 0;
```

hogehoge.serviceの中身も、ラッパーシェルを呼び出すように修正

```:hogehoge.service(v3)修正箇所のみ
[Service]
ExecStart=/work/hoge_start.bash
ExecStop=/work/hoge_stop.bash
```

###3. サービス起動と停止が同時に動いてしまう現象を直す

ラッパーシェルを呼び出すようにhogehoge.serviceを修正してみた。
これで動くか。。。？

```
[root@sys-90562 system]# systemctl status hogehoge.service -l
● hogehoge.service - hogehoge Service Test
   Loaded: loaded (/etc/systemd/system/hogehoge.service; disabled; vendor preset: disabled)
   Active: inactive (dead)
     Docs: man:sshd(8)
           man:sshd_config(5)

Dec 17 18:10:18 sys-90562.dal-ebis.ihost.com systemd[1]: Started hogehoge Service Test.
Dec 17 18:10:18 sys-90562.dal-ebis.ihost.com systemd[1]: Starting hogehoge Service Test...
```
エラーは出てないっぽいけど、"active"のステータスになっていないのが気になる。
肝心のコマンドの実行結果は、、、

```:hogehoge.service(v4)修正箇所のみ
[root@sys-90562 system]# tail -n1 /work/hogehoge.start 
18:10:18.889
[root@sys-90562 system]# tail -n1 /work/hogehoge.stop
18:10:18.893
[root@sys-90562 system]# 
```

確かに実行されているけど、ExecStartとExecStopがほぼ同時に動いてしまっている。
サービス起動だけ実行したので、ExecStopが動くのは想定外。

少し調べると、今回みたいな一回実行して完了になるサービスでは、**Type=oneshot**、および**RemainAfterExit=yes**を設定すると良いらしい。

それを設定して、再チャレンジ。。。

```
[Service]
Type=oneshot
ExecStart=/work/hoge_start.bash
ExecStop=/work/hoge_stop.bash
RemainAfterExit=yes
```

```
[root@sys-90562 system]# systemctl status hogehoge.service
● hogehoge.service - hogehoge Service Test
   Loaded: loaded (/etc/systemd/system/hogehoge.service; disabled; vendor preset: disabled)
   Active: active (exited) since Sun 2017-12-17 18:13:43 JST; 9s ago
     Docs: man:sshd(8)
           man:sshd_config(5)
  Process: 2363 ExecStart=/work/hoge_start.bash (code=exited, status=0/SUCCESS)
 Main PID: 2363 (code=exited, status=0/SUCCESS)

Dec 17 18:13:43 sys-90562.dal-ebis.ihost.com systemd[1]: Starting hogehoge Service Test...
Dec 17 18:13:43 sys-90562.dal-ebis.ihost.com systemd[1]: Started hogehoge Service Test.
```

やったー！、activeになっている！
では、ExecStartとExecStopの方はどうか。。。？

```
[root@sys-90562 system]# tail -n1 /work/hogehoge.start 
18:13:43.117
[root@sys-90562 system]# tail -n1 /work/hogehoge.stop
18:10:18.893
[root@sys-90562 system]# 
```
hogehoge.startにしか追記がされてない、つまりExecStartのみ実行されているので、サービス起動が成功！
**systemctl stop hogehoge.service**で、hogehoge.stopに時刻が追記されることも確認済み

###4. OS再起動時にサービス起動されるようにする

ではOSを再起動してみる。

```
[root@sys-90562 ~]# shutdown -Fr
Shutdown scheduled for Sun 2017-12-17 18:57:36 JST, use 'shutdown -c' to cancel.
[root@sys-90562 ~]# 
```
おそらくOS停止時にhogehoge.stopへ追記があり、
約1分後、OSが起動したタイミングでhogehoge.startにも追記があるはずだが。。。

```
[root@sys-90562 ~]# tail -n1 /work/hogehoge.stop
18:57:36.649
[root@sys-90562 ~]# tail -n1 /work/hogehoge.start
18:50:28.560
[root@sys-90562 ~]# 
```
OS停止時はExecStopの処理が動いたようだが、OS起動時にExecStartの処理が動いていない。
**systemctl status hogehoge.service**でも、起動中になっていない

どうやら、hogehogeサービスが有効の状態になっていなかったのが原因らしい。

```
[root@sys-90562 ~]# systemctl list-unit-files --no-pager | grep hogehoge
hogehoge.service                            disabled
[root@sys-90562 ~]# 
```
上記、disable(無効)になっているので、enable(有効)に変更する。

```
[root@sys-90562 ~]# systemctl enable hogehoge.service
Created symlink from /etc/systemd/system/multi-user.target.wants/hogehoge.service to /etc/systemd/system/hogehoge.service.
[root@sys-90562 ~]# systemctl list-unit-files --no-pager | grep hogehoge
hogehoge.service                            enabled 
[root@sys-90562 ~]# 
```
修正後、もう一度OS再起動。。。

```
[root@sys-90562 ~]# tail -n1 /work/hogehoge.stop
19:13:41.847
[root@sys-90562 ~]# tail -n1 /work/hogehoge.start 
19:14:44.713
[root@sys-90562 ~]# systemctl status hogehoge.service
● hogehoge.service - hogehoge Service Test
   Loaded: loaded (/etc/systemd/system/hogehoge.service; enabled; vendor preset: disabled)
   Active: active (exited) since Sun 2017-12-17 19:14:44 JST; 3min 33s ago
     Docs: man:sshd(8)
           man:sshd_config(5)
  Process: 1384 ExecStart=/work/hoge_start.bash (code=exited, status=0/SUCCESS)
 Main PID: 1384 (code=exited, status=0/SUCCESS)
   CGroup: /system.slice/hogehoge.service

Dec 17 19:14:44 sys-90562.dal-ebis.ihost.com systemd[1]: Starting hogehoge Service Test...
Dec 17 19:14:44 sys-90562.dal-ebis.ihost.com systemd[1]: Started hogehoge Service Test.
[root@sys-90562 ~]# 
```

hogehoge.stop/startへの出力も想定通り、
サービスの状態もactiveなので、一通りの処理が正常にできました。

正式な設計のお作法とはズレているかもしれないですが、
とりあえず動くようにはなりました。

##まとめ
- サービス起動/停止のコマンドはラッパーにまとめた方が無難そう
- スクリプト実行系はType,RemainAfterExitを忘れずに設定する
- サービスの有効化も忘れない

##参考にしたページ

RedHatのマニュアルページ
~~[9.2. システムサービスの管理](https://access.redhat.com/documentation/ja-jp/red_hat_enterprise_linux/7/html/system_administrators_guide/sect-managing_services_with_systemd-services)
[9.6. システムのユニットファイルの作成および変更](https://access.redhat.com/documentation/ja-jp/red_hat_enterprise_linux/7/html/system_administrators_guide/sect-managing_services_with_systemd-unit_files)~~
<font color="red">2021/02/13 上記リンク切れのため、リンク先を変更</font>
[第10章 systemd によるサービス管理](https://access.redhat.com/documentation/ja-jp/red_hat_enterprise_linux/7/html/system_administrators_guide/chap-managing_services_with_systemd)


Unit設定ファイルの書き方が分かり易くまとめられています
[Systemd入門(4) - serviceタイプUnitの設定ファイル](http://enakai00.hatenablog.com/entry/20130917/1379374797)