Raspberry Piでプログラムの自動起動をsystemdでやってみた

#本題に入る前に
この記事は[「インターホンを押すとLINEに通知が行く仕組みを1からつくってみた」](https://qiita.com/G-san/private/a5977968ac1c708d0fe0)
からの続きになります。

ご興味があればこちらも見ていってください。
###記事の見方
- :pencil2:このマークがある箇所は入力を表しています。
- :mag:このマークがある箇所は確認を表しています。

##本題
「RaspberryPiでつくったプログラムを、起動時に自動実行させたいな…」
と思ったので、systemdを使った方法でプログラムの自動起動に挑戦してみました。

自動起動の方法にはいくつかあるようですが、systemdを使った記事が多くあったので
いろんな記事を摘みながら実装していきました。

###プログラム実装で参考にさせて頂いた記事一覧
- [Raspberry Piでプログラムを自動起動する5種類の方法を比較・解説](https://qiita.com/karaage0703/items/ed18f318a1775b28eab4)
- [Raspberry Pi で systemd を使ってプログラムを自動実行する](https://qiita.com/molchiro/items/ee32a11b81fa1dc2fd8d)
- [RasPi のプログラムを自動起動に！その2 systemd 編](http://miho-diary.hatenablog.com/entry/2017/10/30/004153)

##環境

- 本体：[Raspberry Pi 4 4B-32GB](https://www.amazon.co.jp/gp/product/B082VVJCPT/ref=ppx_yo_dt_b_asin_title_o07_s00?ie=UTF8&psc=1)
- PC：[MacBook Pro(OS:catalina 10.15.5)]
(https://www.amazon.co.jp/gp/product/B06Y4LRHJK/ref=ppx_yo_dt_b_asin_title_o07_s01?ie=UTF8&psc=1)
- 私：RaspberryPi、Pythonを初めて触る。Ruby on RailsでWebアプリが作れる程度。UINX、Linuxに関してぱっぱらぱー(知識が乏しい)

##systemdとは
プロセスの起動を管理するソフトウェアであり、
Raspbian Jessieから推奨されている自動起動の方法だそうです。

##実装の手順
以下の手順で実装をしました。

- 自動起動するプログラムを置く、ディレクトの作成
- プログラム作成
- service ファイル作成
- サービスが自動で起動する設定
- テスト
- 課題

##自動起動するプログラムを置く、ディレクトの作成
まずは、プログラムを置く場所を作ります。
/opt/ 以下に置くのが望ましいとのことだったので、
/opt/ 以下 「mybin」 ディレクトリを作りました。

望ましい理由は、起動時にマウント（OSなどのソフトウェアに認識させ、操作・利用可能な状態に）
されていることが保証されているからだそうです。

:pencil2:ではターミナルを開いて下記のコードを入力していきます。

```
master@raspberrypi:~ $ sudo mkdir /opt/mybin
```
これで作れると思っていたのですが、なぜか作れなかったので、
su コマンド「$ su」 と実行してなんとか作れました。

:point_up:後で、いろいろ調べてみたら、sudo(root権限)の設定をめちゃくちゃにしてたので、上手くいかなかっただけでした…
###:pencil2:私の場合（sudoで上手くいかなかった場合）

```
master@raspberrypi:~ $ su
```
```
パスワード入力
```
```
/opt ディレクトに移動
```
```
root@raspberrypi:/opt# mkdir mybin
```

##プログラム作成
/opt/mybin　に自動実行させたい Pythonプログラムと、それを実行するためのプログラム
（シェル）を作っていきます。

その前に、作業ディレクトリを/opt/mybin　に移動してから作業します。　

:pencil2:**作業ディレクトリ移動**

```
master@raspberrypi:~ $ cd /opt/mybin
```

:pencil2:**Pythonプログラムのファイル作成**

```
master@raspberrypi:/opt/mybin $ touch techcall.py
```
プログラムは前回作ったものをコピぺしました。
ちなみにコピペは以下のように実施しました。
[前回作ったプログラムはこちら:file_folder:](https://qiita.com/G-san/private/a5977968ac1c708d0fe0#%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%A0%E3%82%92%E8%BF%BD%E5%8A%A0)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628696/e7c64f7e-8bda-aad9-68c0-cccc72b15d91.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628696/b869dad2-078a-4de6-1791-f6d0bbdf88b5.png)
:pencil2:**techcall.pyをviエディタで開きます**

```
master@raspberrypi:/opt/mybin# vi techcall.py
```
今回viコマンド（vimコマンド）を使用したのですが、もしviで起動できない、
もしくはインストールされていない場合は、nanoコマンドでトライしてみましょう。

:pencil2:**techcall.pyをnanoエディタで開きます**

```
master@raspberrypi:/opt/mybin# nano techcall.py
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628696/956564a6-bfef-6db7-220a-2345957ad827.png)
:pencil2:貼り付けが完了したら、Escボタンを押してコマンドモード（編集を終了するモード）
　に切り替えて、「Shift」＋「Z」を2回押して保存、エディタを閉じます。

上記と同じ要領で、techcall.pyを実行させるための
シェルスクリプトを作っていきます。

シェルのファイル作成と中身は下記の通りです。

:pencil2:**シェルの作成**

```
master@raspberrypi:/opt/mybin# touch TechCall.sh
```

:pencil2:**TechCall.shをviエディタで開きます**

```
master@raspberrypi:/opt/mybin# vi TechCall.sh
```
:pencil2:**シェルの中身**

```
#!/bin/sh
/usr/bin/env python /opt/mybin/techcall.py
```

シェル・スクリプトのファイル自体に実行権限を設定して実行できるようします。
シェル・スクリプトとはシェルによって解釈・実行される一連の処理を記述したスクリプトです。

では、作成したファイルの実行属性を確認してみましょう。

:pencil2:**/opt/mybin ディレクトリに移動して下記のコマンドを入力します。**

```
master@raspberrypi:/opt/mybin $ ls -al
```
:mag:**結果は以下の通りになると思います。**

```
合計 16
drwxr-xr-x 2 root root 4096  8月 23 10:59 .
drwxr-xr-x 5 root root 4096  8月 23 10:32 ..
-rw-r--r-- 1 master  master   53  8月 23 10:56 TechCall.sh
-rw-r--r-- 1 master  master  915  8月 23 10:32 techcall.py
```
###解説
ファイル名が表示してある列の左側に規則性のあるような…ないような…文字列を確認できます。
この文字列が実行属性（ファイルのアクセス権）の状態を表しています。
属性には「読み出しの許可」「書き込みの許可」「実行の許可」の3種類があります。
[もっと詳しく知りたい方はコチラへ:bulb:](https://www.atmarkit.co.jp/ait/articles/1605/18/news015_2.html)

現在の状態だと、TechCall.sh（techcall.pyを実行させるためのシェルスクリプト）
の実行許可がない状態です。

なので、下記の操作を実行してTechCall.shに実行許可を付与します。

:pencil2:**下記のコマンドを入力します。**

```
master@raspberrypi:/opt/mybin $ sudo chmod 755 TechCall.sh
```
:pencil2:**再び$ ls -alを入力して実行属性を確認します。**

```
master@raspberrypi:/opt/mybin $ ls -al
```
:mag:**以下の通り、文字列が変わっていれば成功です。**

```
合計 16
drwxr-xr-x 2 root root 4096  8月 23 10:59 .
drwxr-xr-x 5 root root 4096  8月 23 10:32 ..
-rwxr-xr-x 1 master  master   53  8月 23 10:56 TechCall.sh
-rw-r--r-- 1 master  master  915  8月 23 10:32 techcall.py
```

##サービスが自動で起動する設定
Systemdの仕組みを使って、自動で起動する設定をやっていきます。

###service ファイルを作成
/etc/systemd/systemにtechcall.serviceと言う名前で.service ファイルを作ります。

:pencil2:**おっと、その前に下記のコマンドで作業ディレクトリを移動します。**

```
master@raspberrypi:/opt/mybin $ cd /etc/systemd/system/
```
:mag:**移動が完了しました。**

```
master@raspberrypi:/etc/systemd/system $ 
```
:pencil2:**下記のコマンドで.service ファイルを作ります。**

```
master@raspberrypi:/etc/systemd/system $ sudo vi techcall.service
```
:pencil2:**TechCall.shを起動時に自動実行するために、中身を以下の通りにします。**

```
[Unit]                                                                      
Description=TechCall_pikopiko
 
[Service]
ExecStart=/opt/mybin/TechCall.sh
Restart=no
Type=simple

[Install]
WantedBy=multi-user.target

```
###解説
[コチラが的確に説明されているリンクになります:bulb:](https://qiita.com/molchiro/items/ee32a11b81fa1dc2fd8d#%E8%A7%A3%E8%AA%AC)

###起動時にサービスが自動で起動するように設定
:pencil2:**下記のコマンドを入力します。**

```
master@raspberrypi:/etc/systemd/system $ sudo systemctl enable techcall
```
:mag:**このようにCreatedされていればOKです。**

```
Created symlink /etc/systemd/system/multi-user.target.wants/techcall.service → /etc/systemd/system/techcall.service.

```

##テスト
ここで、RaspberryPiを起動させた時に自動起動されるか確認するのですが、
念のために再起動させる前に動作するか確認してみましょう。

:pencil2:**下記のコマンドを入力します。**

```
master@raspberrypi:/etc/systemd/system $ sudo systemctl start techcall.service
```
:mag:**LINEから通知が来たらプログラムは正常に機能しています。**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628696/ceccb7e7-2ef0-9da6-d7e3-19f0deaeb65b.png)

では、本題のRaspberryPiを起動後の確認をします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628696/14be9bd1-b073-2bac-7114-83ba5581f126.png)

再起動後も問題なく動作することを確認できました。

最後にサービスのステータスを確認してみましょう。

:pencil2:**下記のコマンドを入力します。**

```
master@raspberrypi:~ $ sudo systemctl status techcall
```
:mag:**Active:の行がActive: active (running)になり、プログラムが起動していることが確認できます**

```
● techcall.service - TechCall_pikopiko
   Loaded: loaded (/etc/systemd/system/techcall.service; enabled; vendor preset:
   Active: active (running) since Sun 2020-08-23 15:05:01 JST; 3min 15s ago
 Main PID: 343 (TechCall.sh)
    Tasks: 2 (limit: 2065)
   CGroup: /system.slice/techcall.service
           ├─343 /bin/sh /opt/mybin/TechCall.sh
           └─347 python /opt/mybin/techcall.py

 8月 23 15:06:01 raspberrypi TechCall.sh[343]: runnning
 8月 23 15:06:01 raspberrypi TechCall.sh[343]: runnning
 8月 23 15:06:47 raspberrypi TechCall.sh[343]: runnning
 8月 23 15:06:47 raspberrypi TechCall.sh[343]: runnning
 8月 23 15:06:47 raspberrypi TechCall.sh[343]: Customer is here
 8月 23 15:06:47 raspberrypi TechCall.sh[343]: runnning
 8月 23 15:06:47 raspberrypi TechCall.sh[343]: runnning
 8月 23 15:07:33 raspberrypi TechCall.sh[343]: runnning
 8月 23 15:07:33 raspberrypi TechCall.sh[343]: runnning
 8月 23 15:07:33 raspberrypi TechCall.sh[343]: runnning
lines 1-19/19 (END)
```
##追記
ちょっと自動起動を止めたいなという時は、下記のコマンドでサービスを停止させます。

:pencil2:**下記のコマンドを入力します。**

```
master@raspberrypi:~ $ sudo systemctl stop techcall
```
:pencil2:**ステータスを確認するため、下記のコマンドを入力します。**

```
master@raspberrypi:~ $ sudo systemctl status techcall
```

:mag:**Active:の行がActive: inactive (dead)になり、プログラムが停止していることが確認できます**

```
● techcall.service - do something
   Loaded: loaded (/etc/systemd/system/techcall.service; enabled; vendor preset: enabled)
   Active: inactive (dead) since Tue 2020-08-25 06:58:26 JST; 3s ago
  Process: 381 ExecStart=/opt/mybin/TechCall.sh (code=killed, signal=TERM)
 Main PID: 381 (code=killed, signal=TERM)

 8月 25 06:57:17 raspberrypi TechCall.sh[381]: runnning
 8月 25 06:57:17 raspberrypi TechCall.sh[381]: runnning
 8月 25 06:58:02 raspberrypi TechCall.sh[381]: runnning
 8月 25 06:58:02 raspberrypi TechCall.sh[381]: runnning
 8月 25 06:58:02 raspberrypi TechCall.sh[381]: runnning
 8月 25 06:58:26 raspberrypi TechCall.sh[381]: runnn
 8月 25 06:58:26 raspberrypi systemd[1]: Stopping do something...
 8月 25 06:58:26 raspberrypi systemd[1]: techcall.service: Main process exited, code=killed, statu
 8月 25 06:58:26 raspberrypi systemd[1]: techcall.service: Succeeded.
 8月 25 06:58:26 raspberrypi systemd[1]: Stopped do something.
lines 1-16/16 (END)
```

##課題
###たまに、起動後プログラムが起動しない
プログラムが起動しなかった時に復帰する方法を考える

(2020/8/24)
.serviceファイルの下記の通り変更しました。これが正しいかわからないので、間違っていたら
ご指摘願います。

```
[Unit]                                                                      
Description=TechCall_pikopiko
 
[Service]
ExecStart=/opt/mybin/TechCall.sh
Restart=on-failure
Type=simple

[Install]
WantedBy=multi-user.target
```
▽**変更点**
Restart=no から　Restart=on-failure に変更しました。
on-failure（サービスプロセスが正常終了以外の時に再起動される）

[参考：systemdによる自動起動](https://www.souichi.club/technology/systemd/)


###時間を置いてボタンを押すと、ラインの通知がタイムリーに来ない
PCのみ。長時間ボタンの反応がない状態からボタンを押してもダイアログが表示されないが、
デスクトップを切り替えるとダイアログが表示される謎の現象がある。

:tools:課題が解決次第、更新して参ります:tools:

##参考
###シェル
- [【入門者向け】シェルスクリプトの作成・実行方法をわかりやすく解説！](https://udemy.benesse.co.jp/development/web/shellscript.html)
- [【 シェル・スクリプトの実行 】](https://xtech.nikkei.com/it/article/COLUMN/20060228/231019/)

###管理者権限
- [「sudo su」を理解する](https://neos21.hatenablog.com/entry/2019/12/27/080000)
- [管理者権限でのコマンド実行](http://epsilon27.hatenadiary.jp/entry/2017/02/04/%E7%AE%A1%E7%90%86%E8%80%85%E6%A8%A9%E9%99%90%E3%81%A7%E3%81%AE%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E5%AE%9F%E8%A1%8C)

###エディタ
- [viエディタの使い方](https://prev.net-newbie.com/linux/commands/vi.html)
- [nano エディタの使い方](http://dotnsf.blog.jp/archives/1066662653.html)

###UINX・LINUX
- [３分間で人に説明できるようになるUnixとLinuxの違い](https://eng-entrance.com/unix_linux)

###属性
- [「ls -l」コマンドの表示からファイルの属性を理解しよう](https://www.atmarkit.co.jp/ait/articles/1605/18/news015.html)