systemdを用いたプログラムの自動起動

# はじめに

 現在のLinuxでは, 従来のinitに代わりsystemdというシステム管理機能が導入されています．
 しかしながら, 従来のSystemV initに慣れ親しんだ方からすると少しわかりにくい部分があります．
そこで，組込みLinuxなどで装置固有のプログラムを自動起動させることに絞ってsystemdの簡単な使い方をまとめてみます．
本説明では, sudoコマンドを用いて管理者権限(root権限)でコマンドを実行できることを前提に作業手順を記載しています．

# 用意するファイル
用意するファイルは以下の2つです．

|連番|ファイル名|概要|ファイル名の付け方|
|:--:|:--:|:--:|:--:|
|1|/etc/systemd/system/XXXX.service|systemdからプログラムを起動するための設定ファイル|XXXXのところを好きな名前（英数字）に変えて下さい．|
|2|/opt/パッケージ名/bin/YYYY.sh|systemdからプログラムを起動するプログラムを呼び出すためのシェルスクリプト|パッケージ名やYYYYのところは好きな名前（英数字）に変えて下さい．|


本稿では, 各種実行ファイルを[Filesystem Hierarchy Standard]( http://www.pathname.com/fhs/ )に従ってサードパーティーのプログラムとして``/opt``配下に配置することにします．Filesystem Hierarchy Standardでは, サードパーティーのプログラムは, ``/opt/パッケージ名``配下に配置することになっているためです．

パッケージ名のところは, 他のサードパーティーのプログラムと名前が衝突しないようにします．ここでは, パッケージ名として, ``RasPiSample``という名前を使用し, ``/opt/RasPiSample/bin``配下にプログラムを配置します．

## /etc/systemd/system/XXXX.serviceの中身
プログラムを自動起動するには，/etc/systemd/systemディレクトリに.serviceで終わる設定ファイルを用意する必要があります．
これをユニットファイルと呼びます．
ユニットファイルにはおおよそ以下のような内容を記述します．”数値：”は説明のために入れた行番号です．入力する必要はありません．

```Bash:/etc/systemd/system/XXXX.service
1: [Unit]
2: Description = 説明文（好きにに入れて良いです）
3: After=local-fs.target
4: ConditionPathExists=自動起動するプログラムが配置されているディレクトリ(/opt/パッケージ名/bin)
5:
6: [Service]
7: ExecStart=自動起動するプログラムへのフルパス(/opt/パッケージ名/bin/YYYY.sh)
8: Restart=no
9: Type=simple
10:
11:
12: [Install]
13: WantedBy=multi-user.target
```

書き換える必要があるのは, **2行目**, **4行目** と **7行目** のみです．
2行目のDescription=の後に起動するプログラムについての説明文を記載します．英数字で好きな内容を記載してかまいません．短く，起動するプログラムを良く表す内容を記載することが望ましいですね．

4行目のConditionPathExists=の後にsystemdから起動するプログラムを配置したディレクトリを記述します．

7行目には，systemdから起動するプログラムを指定します．
典型的には, ExecStart=のあとに起動するプログラムへのフルパスを指定します．ここでは簡略化のためと問題発生時の切り分けを容易化するために前節で示したシェルスクリプトへのパスを記載します．

具体的な記述例を以下に示します．ここではこのファイルをRasPiAuto.serviceという名前で保存します．

```Bash:/etc/systemd/system/RasPiAuto.service
1: [Unit]
2: Description = A sample unit file.
3: After=local-fs.target
4: ConditionPathExists=/opt/RasPiSample/bin
5:
6: [Service]
7: ExecStart=/opt/RasPiSample/bin/autoexec.sh
8: Restart=no
9: Type=simple
10:
11:
12: [Install]
13: WantedBy=multi-user.target
```

カレントディレクトリでRasPiAuto.serviceというファイルを作ったら以下のように/etc/systemd/systemにコピーします．

```shell-session
$ sudo cp RasPiAuto.service /etc/systemd/system
$ sudo chown root:root /etc/systemd/system/RasPiAuto.service
$ sudo chmod 644 /etc/systemd/system/RasPiAuto.service
```

### プログラム配置先ディレクトリについて

本節は, ちょっと応用的な話題なので, 最初に読まれる方は飛ばしていただいてかまいません．

Filesystem Hierarchy Standardでは, /optは, ''/''（ルートディレクトリ)とは, 別のパーティションに存在しても良いことになっています．Filesystem Hierarchy Standardの記述を引用します．

> 3.1. Purpose
>
>*   To boot a system, enough must be present on the root partition to mount other filesystems. This includes utilities, configuration, boot loader information, and other essential start-up data. /usr, /opt, and /var are designed such that they may be located on other partitions or filesystems.

このため起動直後は/optがマウントされていないシステムも存在します．

通常, 組込Linuxの場合は, サードパーティーのプログラムを導入して自動起動させるはずですので, ``/opt/パッケージ名``のディレクトリは, 存在することを期待して良いと考えられますが, 念のため, ローカルファイルシステムへのマウントが完了した後に実行されることを保証するために, 3行目に

```
3: After=local-fs.target
```

を記載しています．``After=``には, 以下のような実行条件を記載することが出来ます．

*   local-fs.target 装置に直接繋がったストレージのマウントが完了した後で実行する
*   network-online.target ネットワークを利用可能になった後で実行する
*   remote-fs.target ネットワークファイルシステム（NFSなど）を利用可能になった後で実行する
*   nss-lookup.target DNSなどのホスト/ネットワークサービスを利用可能になった後で実行する

上記の場合, local-fs.targetの完了後, つまり, 装置に直接繋がったストレージのマウントが完了した後でプログラムを実行する指示になります．

また, これらの条件は, 半角スペースで区切って複数指定することもできます．例えば, /optをNFSなどでマウントする場合, ネットワークファイルシステム利用可能後にプログラムを起動するために以下のように記載します:

```
After=networking.service.target remote-fs.target nss-lookup.target
```

もし, ファイルシステムのマウントに何らかの理由で失敗した場合に正しくエラーを返せるように
4行目に
```
4: ConditionPathExists=/opt/RasPiSample/bin
```
を記載しています．この記述は, ``/opt/RasPiSample/bin``というパスが存在していることがプログラム起動条件であることを指定するものです．もし, マウントの失敗などによりパスが存在しない場合は, ''所定の条件を満たさなかったことでプログラムの起動に失敗した''ことが記録されます．

## /opt/パッケージ名/bin/YYYY.shの中身
/opt/パッケージ名/bin/YYYY.shは, 実際に実行させたいプログラムを起動するためのラッパスクリプトです．
systemdのユニットファイル中の`ExecStart=`行に直接プログラムを起動させる指示を記述することももちろん可能ですが, 最初の段階では, systemdの設定による誤りとプログラムの記述誤りを切り分けやすいように, プログラムを起動するためのシェルスクリプトをプログラム本体とは別に作成することにします．

例えば, /opt/RasPiSample/bin/RasPi-Sample.py というパイソンスクリプトを実行させる場合は,
以下のような内容を記載します．

```Bash:/opt/RasPiSample/bin/autoexec.sh
#!/bin/sh
exec /usr/bin/env python /opt/RasPiSample/bin/RasPi-Sample.py
```

前節の例に合わせて, 上記のシェルスクリプトを/opt/RasPiSample/bin/autoexec.shという名前で保存することにします．
カレントディレクトリでautoexec.shというファイルを作ったら以下のように/opt/RasPiSample/binを作成し，/opt/RasPiSample/binにスクリプトをコピーします．
また, 実行権を忘れずに付けておきます．

```shell-session
$ sudo mkdir -p /opt/RasPiSample/bin
$ sudo chmod 755 /opt/RasPiSample/bin
$ sudo cp autoexec.sh /opt/RasPiSample/bin
$ sudo chown root:root /opt/RasPiSample/bin/autoexec.sh
$ sudo chmod 755 /opt/RasPiSample/bin/autoexec.sh
```

また, 本稿では, /opt/RasPiSample/bin/RasPi-Sample.pyの中身を以下のようにしています:

```py:/opt/RasPiSample/bin/RasPi-Sample.py
#!/usr/bin/env python
import sys
from time import sleep

if __name__ == '__main__':
    while True:
        print "Hello world %s" % (sys.argv)
        sys.stdout.flush()
        sleep(1)
```

ここで, /opt/RasPiSample/binの中身は以下のようになっています．実行権が
ついていること(一番左のカラムが-rwxr-xr-xとなっていること)を
確認して下さい．

```shell-session
$ ls -l /opt/RasPiSample/bin
total 8
-rwxr-xr-x. 1 root root 189 Oct 30 00:08 RasPi-Sample.py
-rwxr-xr-x. 1 root root  61 Oct 31 00:38 autoexec.sh
```
# 自動起動させるために必要な手順
上記のファイルを用意した後で，自動起動を実現するために実行する手順は以下の通りです．

1.  systemdにユニットファイルを追加・更新したことを通知する
2.  ユニットファイルからのプログラムの自動起動を有効にする
3.  動作確認をする
4.  再起動

## systemdにユニットファイルを追加・更新したことを通知する
systemdにユニットファイルを追加・更新したことを通知するコマンドは, ```systemctl daemon-reload```です．
ユニットファイルを追加，削除，修正したら必ず以下のコマンドを実行します．

```shell-session
$ sudo systemctl daemon-reload
```

## ユニットファイルからのプログラムの自動起動を有効にする
systemctl daemon-reloadを実行した段階では, 追加，修正したユニットファイルの存在をsystemdが認識しているだけで，自動起動が行えるようにはなっていません．```systemctl status ユニットファイル名```を実行すると, 対象のユニットファイルをsystemdがどう認識しているかが分かります．

```shell-session
$ sudo systemctl status RasPiAuto.service
● RasPiAuto.service - A sample automatic execution
   Loaded: loaded (/etc/systemd/system/RasPiAuto.service; disabled; vendor preset: disabled)
   Active: inactive (dead)
```

`Loaded:`の行の括弧内に

```
/etc/systemd/system/RasPiAuto.service; disabled;vendor preset: disabled
```

と出ています．これは,

*   `/etc/systemd/system/RasPiAuto.service`の自動起動が無効(`disabled`)になっていて, かつ,
*   デフォルト値(`vendor preset`)は, 無効（`disabled`）に設定される

ことを意味します．

プログラムの自動起動を行うように指示するコマンドは, ```systemctl enable ユニットファイル名```です．
本稿の例の場合, プログラムの自動起動を行うためには, 以下のコマンドを実行します．

```shell-session
$sudo systemctl enable RasPiAuto.service
Created symlink from /etc/systemd/system/multi-user.target.wants/RasPiAuto.service to /etc/systemd/system/RasPiAuto.service.
```

もう一度, statusを見てみましょう．`systemctl status RasPiAuto.service`を実行します．

```shell-session
$ sudo  systemctl status RasPiAuto.service
● RasPiAuto.service - A sample automatic execution
   Loaded: loaded (/etc/systemd/system/RasPiAuto.service; enabled; vendor preset: disabled)
   Active: inactive (dead)
```

先ほど`disabled`となっていた部分, /etc/systemd/system/RasPiAuto.service; に続くdisabledの表示がenabledに変わったことが確認できます．

## 動作確認をする
さて，再起動してみましょうと言いたいところですが, その前に動作確認をしておきます．
ユニットファイルからのプログラムの起動・停止には以下のコマンドを実行します．

*   プログラムの起動・・・systemctl start ユニットファイル名
*   プログラムの停止・・・systemctl stop ユニットファイル名

では,以下のコマンドを実行し プログラムを起動させてみましょう．

```shell-session
$ sudo systemctl start RasPiAuto.service
$
```

パイソンスクリプト中では, print文による出力指示があるのに, コンソールに何も出ないことに驚かれるかもしれません．

実は, systemdから起動されたプログラムの標準出力・エラー出力は, 最終的にsystemdによってユニットファイルから起動されたプログラム群を単位としてそれぞれのログに分配, 記録され, デフォルトではコンソールに出力されません。

`systemctl status`を実行することで, 起動確認とプログラムの出力を確認することができます．

```shell-session
$ sudo systemctl status RasPiAuto.service
* RasPiAuto.service - A sample automatic execution
   Loaded: loaded (/etc/systemd/system/RasPiAuto.service; enabled; vendor preset: disabled)
   Active: active (running) since Tue 2017-10-31 00:47:24 JST; 3min 21s ago
 Main PID: 5428 (python)
   CGroup: /system.slice/RasPiAuto.service
           `-5428 python /opt/RasPiSample/bin/RasPi-Sample.py

Oct 31 00:50:36 vmlinux1 autoexec.sh[5428]: Hello world ['/opt/RasPiSample/bin/RasPi-Sample.py']
Oct 31 00:50:37 vmlinux1 autoexec.sh[5428]: Hello world ['/opt/RasPiSample/bin/RasPi-Sample.py']
Oct 31 00:50:38 vmlinux1 autoexec.sh[5428]: Hello world ['/opt/RasPiSample/bin/RasPi-Sample.py']
Oct 31 00:50:39 vmlinux1 autoexec.sh[5428]: Hello world ['/opt/RasPiSample/bin/RasPi-Sample.py']
Oct 31 00:50:40 vmlinux1 autoexec.sh[5428]: Hello world ['/opt/RasPiSample/bin/RasPi-Sample.py']
Oct 31 00:50:41 vmlinux1 autoexec.sh[5428]: Hello world ['/opt/RasPiSample/bin/RasPi-Sample.py']
Oct 31 00:50:42 vmlinux1 autoexec.sh[5428]: Hello world ['/opt/RasPiSample/bin/RasPi-Sample.py']
Oct 31 00:50:43 vmlinux1 autoexec.sh[5428]: Hello world ['/opt/RasPiSample/bin/RasPi-Sample.py']
Oct 31 00:50:44 vmlinux1 autoexec.sh[5428]: Hello world ['/opt/RasPiSample/bin/RasPi-Sample.py']
Oct 31 00:50:45 vmlinux1 autoexec.sh[5428]: Hello world ['/opt/RasPiSample/bin/RasPi-Sample.py']
```

``Active:``の行が``active (running)``になりました．また, ``Main PID: 5428 (python)``という表示があることやprint文の出力が最後の方に出ていることからプログラムは起動できています．

パイソンスクリプトからの出力に関する謎は後で説明するとして，次にプログラムを止めてみましょう．

```shell-session
$ sudo systemctl stop RasPiAuto.service
$
```

では, statusを見てみます．

```shell-session
 systemctl status RasPiAuto.service
* RasPiAuto.service - A sample automatic execution
   Loaded: loaded (/etc/systemd/system/RasPiAuto.service; enabled; vendor preset: disabled)
   Active: inactive (dead) since Tue 2017-10-31 01:01:34 JST; 20s ago
  Process: 5428 ExecStart=/opt/RasPiSample/bin/autoexec.sh (code=killed, signal=TERM)
 Main PID: 5428 (code=killed, signal=TERM)

Oct 31 01:01:27 vmlinux1 autoexec.sh[5428]: Hello world ['/opt/RasPiSample/bin/RasPi-Sample.py']
Oct 31 01:01:28 vmlinux1 autoexec.sh[5428]: Hello world ['/opt/RasPiSample/bin/RasPi-Sample.py']
Oct 31 01:01:29 vmlinux1 autoexec.sh[5428]: Hello world ['/opt/RasPiSample/bin/RasPi-Sample.py']
Oct 31 01:01:30 vmlinux1 autoexec.sh[5428]: Hello world ['/opt/RasPiSample/bin/RasPi-Sample.py']
Oct 31 01:01:31 vmlinux1 autoexec.sh[5428]: Hello world ['/opt/RasPiSample/bin/RasPi-Sample.py']
Oct 31 01:01:32 vmlinux1 autoexec.sh[5428]: Hello world ['/opt/RasPiSample/bin/RasPi-Sample.py']
Oct 31 01:01:33 vmlinux1 autoexec.sh[5428]: Hello world ['/opt/RasPiSample/bin/RasPi-Sample.py']
Oct 31 01:01:34 vmlinux1 autoexec.sh[5428]: Hello world ['/opt/RasPiSample/bin/RasPi-Sample.py']
Oct 31 01:01:34 vmlinux1 systemd[1]: Stopping A sample automatic execution...
Oct 31 01:01:34 vmlinux1 systemd[1]: Stopped A sample automatic execution.
```
``Active:``の行が``Active: inactive (dead) ``になり, プログラムは停止していることが確認できます．

## プログラムの出力ログを見るコマンド

systemdでは, 起動したプログラムの出力ログを見るコマンドとして, journalctlというコマンドが搭載されています．``journalctl -u ユニットファイル名``を実行することで出力内容を参照することができます．

```shell-session
$  journalctl -u RasPiAuto.service
-- Logs begin at Mon 2017-10-30 22:41:51 JST, end at Tue 2017-10-31 00:59:06 JST. --
Oct 31 00:47:24 vmlinux1 systemd[1]: Started A sample automatic execution.
Oct 31 00:47:24 vmlinux1 systemd[1]: Starting A sample automatic execution...
Oct 31 00:47:24 vmlinux1 autoexec.sh[5428]: Hello world ['/opt/RasPiSample/bin/RasPi-Sample.py']
Oct 31 00:47:25 vmlinux1 autoexec.sh[5428]: Hello world ['/opt/RasPiSample/bin/RasPi-Sample.py']
```

## 再起動前に最終チェック
さて, ここまで無事に実行できているなら再起動後にプログラムが自動起動します．
ここで，最終チェックをしておきます．
以下の項目を確認しましょう．

1.  /opt/パッケージ名/binディレクトリに実行権はついていますか？
2.  /opt/パッケージ名/binディレクトリに配置したシェルスクリプトやプログラムファイルには実行権が適切についていますか？
3.  ユニットファイルに読み取り権限がついていますか？
4.  ユニットファイルのExecStart=行に指定したシェルスクリプトへのパスは正しいですか？
5.  シェルスクリプトからプログラムが正しく起動できていますか？
6.  プログラムの自動起動が有効になっていますか？

順番に確認します．

### /opt/パッケージ名/binディレクトリのアクセス権確認
まず, /opt/パッケージ名/binディレクトリの実行権は, ``ls -l /opt/パッケージ名``で確認できます．
本稿の例の場合, ``ls -l /opt/RasPiSample``を実行します．
```shell-session
$sudo ls -l /opt/RasPiSample
total 0
drwxr-xr-x. 2 root root 48 Oct 31 00:38 bin
```

一番左のカラムが```drwxr-xr-x```になっていることを確認します．

### シェルスクリプトやプログラムファイルの実行権確認

/opt/パッケージ名/binディレクトリに配置したシェルスクリプトやプログラムファイルの実行権は, ``ls -l /opt/パッケージ名/bin``で確認できます．本稿の例の場合, ``ls -l /opt/RasPiSample/bin``を実行します．

```shell-session
$sudo  ls -l /opt/RasPiSample/bin
total 8
-rwxr-xr-x. 1 root root 189 Oct 30 00:08 RasPi-Sample.py
-rwxr-xr-x. 1 root root  61 Oct 31 00:38 autoexec.sh
```
一番左のカラムが```-rwxr-xr-x```になっていることを確認します．

### ユニットファイルの読み取り権確認

ユニットファイルの読み取り権限は, ``ls -l /etc/systemd/system/ユニットファイル名``で確認できます．本稿の例の場合だと以下のようになります:

```shell-session
$sudo ls -l /etc/systemd/system/RasPiAuto.service
-rw-r--r--. 1 root root 199 Oct 30 00:34 /etc/systemd/system/RasPiAuto.service
```

一番左のカラムが``-rw-r--r--``になっていることを確認します．

### ユニットファイル中のプログラムパスの確認

次にユニットファイルの中身を確認します．
lessやmoreなどでユニットファイルの中身を確認して下さい．
特にExecStart=の行のパス名が正しいことを確認します．

```
[Unit]
Description = A sample automatic execution
After=local-fs.target
ConditionPathExists=/opt/RasPiSample/bin

[Service]
ExecStart=/opt/RasPiSample/bin/autoexec.sh
Restart=no
Type=simple

[Install]
WantedBy=multi-user.target
```

### シェルスクリプトの記述内容・動作確認

次にシェルスクリプトからプログラムが正しく起動できることを確認します．
シェルスクリプトを実際にフルパス指定で実行してみます．本稿の例の場合無限ループするプログラムなので, Ctrl-Cで終了させます．

```shell-session
$ sudo /opt/RasPiSample/bin/autoexec.sh
Hello world ['/opt/RasPiSample/bin/RasPi-Sample.py']
Hello world ['/opt/RasPiSample/bin/RasPi-Sample.py']
Hello world ['/opt/RasPiSample/bin/RasPi-Sample.py']
^CTraceback (most recent call last):
  File "/opt/RasPiSample/bin/RasPi-Sample.py", line 9, in <module>
    sleep(1)
KeyboardInterrupt
```

### systemdのプログラム自動起動設定確認

最後にsystemdに自動起動の指示が設定されていることを確認します．
このために, ``systemctl status ユニットファイル名``を実行します．
本稿の例の場合, ```systemctl status RasPiAuto.service```を実行します．

```shell-session
$ sudo systemctl status RasPiAuto.service
* RasPiAuto.service - A sample automatic execution
   Loaded: loaded (/etc/systemd/system/RasPiAuto.service; enabled; vendor preset: disabled)
   Active: inactive (dead) since Tue 2017-10-31 01:01:34 JST; 19min ago
  Process: 5428 ExecStart=/opt/RasPiSample/bin/autoexec.sh (code=killed, signal=TERM)
                      略
```

確認するところは, Loaded:の行中にある括弧内を;で区切った第2項目がenabledになっていることです．
もし, そうなっていなければ, 以下のコマンドを入力し,念のため起動しているプログラムを停止した後で,  設定ファイルの反映と自動起動の有効化を行います．

```shell-session
$ sudo systemctl stop RasPiAuto.service
$ sudo systemctl daemon-reload
$ sudo systemctl enable RasPiAuto.service
```
良くある誤りは, systemctlの前にsudoを付け忘れたことで設定が反映されないことです．**sudoを付けて実行していること**を確認しましょう．
上記のコマンドを実行したら再度``systemctl status ``で状態を確認します．

上記の確認が済んだら再起動します．

## 起動後の確認
ここまで進んできた方は, 再起動後に自動的にプログラムが起動されるようになっているはずです．
再起動後にログインしたら, ``journalctl -u ユニットファイル名``を実行することで出力内容を参照し, 自動起動が出来ていることを確認して下さい．


# プログラムから出力したメッセージの格納先について
本節は, systemdの内部動作に関する話題なので, 最初に読まれる方は飛ばしていただいてかまいません．

前述したとおり, systemdから起動されるコマンドからの表示(標準出力）は, systemdが自動的に内部のログに記録するようになっています．

本節では, 実際に, 標準入出力のファイル記述子が何を指しているか確認してみましょう．
上記の出力から, パイソンスクリプトのプロセスIDは, 5428(PID: 5428)ですから,
/proc/5428/fdを見れば，どこにつながっているか分かるはずです．

```shell-session
$  sudo ls -l /proc/5428/fd
total 0
lr-x------. 1 root root 64 Oct 31 00:47 0 -> /dev/null
lrwx------. 1 root root 64 Oct 31 00:47 1 -> socket:[33996]
lrwx------. 1 root root 64 Oct 31 00:47 2 -> socket:[33996]
```

標準入力が/dev/nullに繋がっており事実上無効になっていることと標準出力・エラー出力がUNIXドメインソケットに繋がっていることが確認できます．
では, このUNIXドメインソケットを開いているプロセスを調べてみましょう．``iproute``パッケージに附属している``ss``コマンドを使用することでソケットの両端に繋がっているプロセスを調べることができます．

```shell-session
$ sudo ss -x -p|grep 33996
u_str  ESTAB      0      0       * 33996                 * 13310                 users:(("python",pid=5428,fd=2),("python",pid=5428,fd=1))
u_str  ESTAB      0      0      /run/systemd/journal/stdout 13310                 * 33996                 users:(("systemd-journal",pid=381,fd=26))
```
上記の出力を見ると,対象のUNIXドメインソケットは

*   パイソンスクリプトの標準出力(``("python",pid=5428,fd=1)``)と標準エラー出力(``("python",pid=5428,fd=2)``)から書き込まれること
*   /run/systemd/journal/stdoutを通して, ''systemd-journal''というプロセス(``("systemd-journal",pid=381,fd=26)``)から読み取られること

が分かります．``systemd-journal``というのは, 従来のsyslogdを置き換えた新しいシステムログ機構を構成するプロセス群の一つです．

## コンソールに出力するには

上記の通り, systemdから起動したプロセスの標準出力はsystemd内部のログに記録されます．どうしても, コンソールにprint文で文字列を表示させたい場合は,

1.  プログラム(パイソンスクリプト)中からコンソールを開く
2.  systemdにコンソールにも出力を表示させるように指示する

の2つの方法が考えられます．

### プログラム中からコンソールを開く方法
まず, スクリプト起動前にsystemdが設定した標準出力の出力先を, パイソンスクリプト中で再度コンソールを指すように指定し直すことが考えられます．
つまり, ``sys.stdout = open('/dev/console', 'w')``の一文を出力開始前に入れれば良いわけです．例えば, メイン処理の開始直後を以下のように書き換えます．

```py:/opt/RasPiSample/bin/RasPi-Sample.py
              略
if __name__ == '__main__':
    sys.stdout = open('/dev/console', 'w')
    while True:
              略
```

上記の修正後にサービスを起動させた場合, コンソールに出力がでるようになります．その一方で, ログへの書き込みが行われなくなるため, ``systemctl status``を実行すると以下のようになります.

```shell-session
$ sudo systemctl status RasPiAuto.service
● RasPiAuto.service - A sample automatic execution
   Loaded: loaded (/etc/systemd/system/RasPiAuto.service; enabled; vendor preset: enabled)
   Active: active (running) since Thu 2017-11-02 19:48:32 UTC; 1min 5s ago
 Main PID: 346 (python)
   CGroup: /system.slice/RasPiAuto.service
           mq346 python /opt/RasPiSample/bin/RasPi-Sample.py

Nov 02 19:48:32 raspberrypi systemd[1]: Started A sample automatic execution.
```

### systemdにコンソールにも出力を表示させるように指示する方法
組込機器の場合, トラブル発生時の出力を利用者が取っておいてくれることは希かと思われます．このため, トラブル発生後に過去の出力を参照して, 原因究明をせざる得ないことも多い考えられますので, ログにも出力を記録しておけると便利なことがあります．

この場合には, systemdにコンソールへの出力を指示するように設定することも可能です．

``StandardOutput=syslog+console``をsystemdの自動起動設定ファイル(本稿の例の場合, `/etc/systemd/system/RasPiAuto.service`)の``[Service]``部分に記載することでコンソール出力とSystemD内のログの双方にprint文の内容が出力されます．

```Bash:/etc/systemd/system/RasPiAuto.service
1: [Unit]
2: Description = A sample unit file.
3: After=local-fs.target
4: ConditionPathExists=/opt/RasPiSample/bin
5:
6: [Service]
7: ExecStart=/opt/RasPiSample/bin/autoexec.sh
8: Restart=no
9: Type=simple
10: StandardOutput=journal+console
11:
12: [Install]
13: WantedBy=multi-user.target
```

上記の書換を行ってから, ``systemctl daemon-reload``実行後, ``systemctl stop``, ``systemctl start``を実行します．

```shell-session
$ sudo systemctl daemon-reload
$ sudo systemctl stop RasPiAuto.service
$ sudo systemctl start RasPiAuto.service
```

この場合, ``systemctl status``を実行すると以下のようになり, コンソールに出力されると共にログにも記録されるようになります．

```shell-session
$ sudo systemctl status RasPiAuto.service
● RasPiAuto.service - A sample automatic execution
   Loaded: loaded (/etc/systemd/system/RasPiAuto.service; enabled; vendor preset: enabled)
   Active: active (running) since Thu 2017-11-02 20:18:51 UTC; 1s ago
 Main PID: 774 (python)
   CGroup: /system.slice/RasPiAuto.service
           mq774 python /opt/RasPiSample/bin/RasPi-Sample.py

Nov 02 20:18:51 raspberrypi systemd[1]: Started A sample automatic execution.
Nov 02 20:18:51 raspberrypi autoexec.sh[774]: Hello world ['/opt/RasPiSample/bin/RasPi-Sample.p
Nov 02 20:18:52 raspberrypi autoexec.sh[774]: Hello world ['/opt/RasPiSample/bin/RasPi-Sample.p
```

# コマンドのまとめ

上記で説明したコマンドのまとめを記載しておきます．
基本的に, systemctlコマンドを実行するときは, **sudoコマンドによって管理者権限で実行**することに注意して下さい．

|機能|コマンド|例|
|:--:|:--:|:--:|
|ユニットファイルの更新を通知|systemctl daemon-reload|sudo systemctl daemon-reload|
|状態の確認|systemctl status ユニットファイル名|sudo systemctl status RasPiAuto.service|
|自動起動を有効化|systemctl enable ユニットファイル名|sudo systemctl enable RasPiAuto.service|
|自動起動を無効化|systemctl disable ユニットファイル名|sudo systemctl disable RasPiAuto.service|
|プログラムの起動|systemctl start ユニットファイル名|sudo systemctl start RasPiAuto.service|
|プログラムの停止|systemctl stop ユニットファイル名|sudo systemctl stop RasPiAuto.service|
|プログラム中からの出力の確認|journalctl -u ユニットファイル名| journalctl -u RasPiAuto.service|