Raspberry Pi 3 のshutdown時に自動で外付けHDDを停止させる

## 目的
Raspberry Pi 3 ModelB で外付けHDDを停止してから終了(shutdownなど)させるようにしたい[^1]
[^1]: ぶっちゃけると`umount`してshutdownすればいいだろうと思っていたのだが、そんな簡単じゃなかったので、まじめに方法を考える羽目になりました
(そうしないとキュイーン？みたいな異音が鳴りつつ終了してしまうので・・)

## 環境
Raspberry Pi 3 ModelB

## 作業メモ

### 外付けHDDの動作を止める

ちなみに外付けHDDのマウント方法については、[こちらの投稿](https://qiita.com/marumen/items/de150089d95f52849e5b)を参照

<b>参考サイト</b>
https://linux.die.net/man/8/sdparm

+ コマンドのインストール

```
$ sudo apt-get install sdparm
```

+ 外付けHDDを動作停止

`fdisk`コマンドなどでHDDのブロックデバイスを調べて停止させてみる

```
$ sudo fdisk -l
(略)
Device     Boot Start        End    Sectors   Size Id Type
/dev/sda1        2048 1953520064 1953518017 931.5G  7 HPFS/NTFS/exFAT
```

```
$ sudo sdparm --command=stop /dev/sda1
```

+ rebootしてみる

```
$ sudo reboot
```

効果なし・・。終了時に異音がする


### USBポートの電源OFF

もっと根本的なところに立ち返って、USBポートの電源OFFを試してみる

<b>参考サイト</b>
http://s.webry.info/sp/vogel.at.webry.info/201711/article_2.html

+ `hub-ctrl`のインストール

```
$ sudo apt-get install libusb-dev
$ wget http://www.gniibe.org/oitoite/ac-power-control-by-USB-hub/hub-ctrl.c
$ gcc -O2 hub-ctrl.c -o hub-ctrl-armhf-static -lusb -static
$ sudo mv hub-ctrl-armhf-static /usr/local/bin/hub-ctrl
```

+ BUSやPORTの番号を調べる[^2]

[^2]: `udevadm`で調べたときに、`1-1.2`に接続されているとうまく電源OFFできなかった。理由は不明

```
$ lsusb -t
/:  Bus 01.Port 1: Dev 1, Class=root_hub, Driver=dwc_otg/1p, 480M
    |__ Port 1: Dev 2, If 0, Class=Hub, Driver=hub/5p, 480M
        |__ Port 1: Dev 3, If 0, Class=Vendor Specific Class, Driver=smsc95xx, 480M
        |__ Port 5: Dev 4, If 0, Class=Mass Storage, Driver=usb-storage, 480M
```

分かりづらい場合はこちらで調べる

```
$ udevadm info --query=path --name=/dev/sda1
/devices/platform/soc/3f980000.usb/usb1/1-1/1-1.5/1-1.5:1.0/host0/target0:0:0/0:0:0:0/block/sda/sda1
```

`--name=path`の`path`は調べたいブロックデバイス名
`1-1.5`が該当箇所になる

+ デバイスの取り外しをする

```
$ echo -n "1-1.5" | sudo tee /sys/bus/usb/drivers/usb/unbind
```

確認すると、以下が無くなっており取り外されたことが分かる
>`Port 5: Dev 4, If 0, Class=Mass Storage, Driver=usb-storage, 480M`

```
$ lsusb -t
/:  Bus 01.Port 1: Dev 1, Class=root_hub, Driver=dwc_otg/1p, 480M
    |__ Port 1: Dev 2, If 0, Class=Hub, Driver=hub/5p, 480M
        |__ Port 1: Dev 3, If 0, Class=Vendor Specific Class, Driver=smsc95xx, 480M
```

+ 該当ポートの電源OFF

```
$ sudo hub-ctrl -b 1 -d 2 -P 5 -p 0
```

`-b`がBUS、`-d`がDevice、`-P`がPORTの数字を入れる
`-p`は0がOFF、1がON

+ rebootしてみる

```
$ sudo reboot
```

効果あり！終了時の異音がなくなった


### Serviceとして登録する

これで目的達成でもいいが、このままだと毎回手動で処理しないといけないので、システムのサービスとして登録してみる

<b>参考サイト</b>
https://qiita.com/DQNEO/items/0b5d0bc5d3cf407cb7ff

+ `init`がどの仕組みで動いているか確認

`init`は起動/終了処理を司るプロセスで、ディストリビューションやバージョンによって使われている仕組みが違うらしい(あまり詳しくない)
そのため、どの仕組みで動いているか確認する

```
$ ps -aux
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.6  27096  6128 ?        Ss    4月20   0:03 /sbin/init splash
(略)
```

```
$ ls -l /sbin/init
lrwxrwxrwx 1 root root 20  7月  6  2017 /sbin/init -> /lib/systemd/systemd
```

どうやら`systemd`という仕組みで動いているらしい

+ サービスの作成

[参考サイト](https://qiita.com/DQNEO/items/0b5d0bc5d3cf407cb7ff)の内容を見ると、今回やりたいことからすると、**.serviceというサービスを作って、それが終了時に呼ばれるようにすればよさそうだと分かった

まず、`hdd_off.service`については以下のようにした

```hdd_off.service
[Unit]
Description=hdd off for shutdown and reboot
Before=shutdown.target
DefaultDependencies=no

[Service]
Type=oneshot
ExecStart=/usr/bin/hdd_off.sh
RemainAfterExit=true

[Install]
WantedBy=shutdown.target
```

ざっくり説明すると、<b>shutdown/reboot/halt時に1度だけ外付けHDDの電源をOFFする</b>、ようにした

以下、ちゃんとした説明
`shutdown.target`がshutdown/reboot/halt全てのタイミングで関連づけられているため、このtargetが呼ばれる時に実行する設定にした

>`Before=shutdown.target`
>`WantedBy=shutdown.target`

外付けHDDの電源をOFFするスクリプトを作り、サービス起動時に実行するようにする
>`ExecStart=/usr/bin/hdd_off.sh`

ちなみにスクリプト`hdd_off.sh`については長くなるので、内容の記載は割愛する

+ 作成したファイルの配置

ファイルの権限などを変更して

```
$ sudo chown root:root hdd_off.sh
$ sudo chown root:root hdd_off.service
$ sudo chmod +x /usr/bin/hdd_off.sh
```

必要な場所に配置する
どうやらユーザー設定のサービスは`/etc/systemd/system`に置くのがお作法らしい

```
$ sudo mv hdd_off.sh /usr/bin/
$ sudo mv hdd_off.service /etc/systemd/system/
```

+ サービスとして認識されているか確認

```
$ sudo systemctl list-unit-files --type=service | grep hdd_off
hdd_off.service                        disabled
```

サービスとして認識されていればいいので、`disabled`でOK

+ 動作確認

```
$ sudo systemctl start hdd_off
$ sudo systemctl stop hdd_off
```

ちなみにサービスの動作確認だけが目的だったので、外付けHDD未接続の状態で試した

```
$ sudo systemctl status hdd_off
● hdd_off.service - hdd off for shutdown and reboot
   Loaded: loaded (/etc/systemd/system/hdd_off.service; disabled; vendor preset: enabled)
   Active: inactive (dead)

 4月 21 10:44:26 raspberrypi systemd[1]: Starting hdd off for shutdown and reboot...
 4月 21 10:44:26 raspberrypi sudo[1201]:     root : TTY=unknown ; PWD=/ ; USER=root ; COMMAND=/sbin/blkid
 4月 21 10:44:26 raspberrypi sudo[1201]: pam_unix(sudo:session): session opened for user root by (uid=0)
 4月 21 10:44:26 raspberrypi sudo[1201]: pam_unix(sudo:session): session closed for user root
 4月 21 10:44:26 raspberrypi hdd_off.sh[1199]: HDD is nothing
 4月 21 10:44:26 raspberrypi systemd[1]: Started hdd off for shutdown and reboot.
 4月 21 10:44:29 raspberrypi systemd[1]: Stopped hdd off for shutdown and reboot.
```

ログを確認すると、`hdd_off.sh`が実行され、HDD未接続時のログが出ているのでOK
>`4月 21 10:44:26 raspberrypi hdd_off.sh[1199]: HDD is nothing`

+ サービスの有効化

```
$ sudo systemctl enable hdd_off
Created symlink /etc/systemd/system/shutdown.target.wants/hdd_off.service → /etc/systemd/system/hdd_off.service.
```

+ 自動で外付けHDDの電源がOFFになってくれるか確認

```
$ sudo reboot
```

いい感じで電源OFFでrebootしたように見える！
一応ログで確認したいので、`syslog`の出力を確認してみる

```
$ less /var/log/messages
```

が、それとわかるようなログは見当たらなかった
どうやら`systemd`のログは`syslog`には出力されないらしい？
この辺はよく分からないので、調査が必要

## まとめ
目的は達成！のはず
>Raspberry Pi 3 ModelB で外付けHDDを停止してから終了(shutdownなど)させるようにしたい

終了時にHDDからの異音はなくなったが、ログでそれが確認できなかった。
その点は課題として残っている
- - -