以前、Raspberry Pi 2にsystemdを入れたので、次はその動作を試してみることにしました。
何にしようか考えながらsystemdに関する記事を調べていたところ、
デバイスを表すdeviceタイプのUnitも動的に生成されます。これは、udevが新しいデバイスを認識したタイミングで、「systemd」というudevタグを付けると、それを受け取ったsystemdが対応するUnitを生成して有効化します
というのを見かけ、気になりました。
そこで、以下を参考に、「USBデバイス挿入時にサービスを起動する」ことを試してみました。
Linux: Start daemon on connected USB-serial dongle - Stack Overflow
目次
環境
Windows 7
Raspberry Pi
- Raspberry Pi 2 Model B
- Raspbian 2015-02-16
- systemd 204
- backportのパッケージよりインストール済
- udev 175
- パソリ RC-S320
- USBデバイスとして使用
事前確認
systemdのUnitの一つ、.device
について、
システムが認識しているデバイス情報を保持する udevデーモンによって自動作成される
という説明がありました。
そこで、デフォルトの場合、パソリを挿入した時に systemctl
コマンドで自動的に表示されるか試してみました。
pi@raspberrypi ~ $ systemctl --t device -a --full UNIT LOAD ACTIVE SUB DESCRIPTION ... sys-devices-platform-...x2d1-1\x2d1.1-1\x2d1.1:1.0-net-eth0.device loaded active plugged ec00 sys-devices-platform-...-target0:0:0-0:0:0:0-block-sda-sda1.device loaded active plugged Silicon-Power8G sys-devices-platform-...-target0:0:0-0:0:0:0-block-sda-sda2.device loaded active plugged Silicon-Power8G sys-devices-platform-...host0-target0:0:0-0:0:0:0-block-sda.device loaded active plugged Silicon-Power8G sys-devices-platform-...0-mmc0:59b4-block-mmcblk0-mmcblk0p1.device loaded active plugged /sys/devices/platform... ...
デフォルトのままでは表示されないようです。
udevによるUSBデバイス挿入の検知
USBデバイス挿入を検知してsystemctlに表示するには、udevのruleファイルの作成が必要そうだったため、その作業から行います。
idVendorとidProductの値の確認
ruleファイルを作成するためには、対象のUSBデバイスのidVendor
とidProduct
の値が必要となります。
そこで、dmesg
コマンドでusb
で絞り込んで確認すると、それっぽい行がありました。
Linuxコマンド集 - 【 dmesg 】カーネルのリングバッファの内容を出力する:ITpro
pi@raspberrypi ~ $ dmesg | grep usb ... [ 3.867886] usb 1-1.4: new low-speed USB device number 5 using dwc_otg [ 3.994069] usb 1-1.4: New USB device found, idVendor=054c, idProduct=01bb [ 4.002768] usb 1-1.4: New USB device strings: Mfr=0, Product=0, SerialNumber=0
LinuxのUSBデバイスIDのリストを見ても そのベンダーIDとデバイスIDの組み合わせがFeliCa S320 [PaSoRi]
だったため、やはりこれがパソリのもののようでした。
List of USB ID's - linux-usb.org
USBデバイス挿入時に動作するかのテスト
udevではUSBデバイス挿入時の動作について設定するところがあったため、まずはsystemdを使わずにudevだけで動作しているかのテストを行います。
以下の記事を参考に、USBデバイス挿入時に日付をログファイルへ出力するruleを作成します。
pi@raspberrypi ~ $ sudo nano /etc/udev/rules.d/90-rc-s320.rules
- RaspberryPiのUSB接続を認識する : 時々、失業SEの開発日誌
- udev の設定をカスタマイズする - いますぐ実践! Linuxシステム管理 / Vol.115
- udev - How to run custom scripts upon USB device plug-in? - Unix & Linux Stack Exchange
/etc/udev/rules.d/90-rc-s320.rules
表示の都合上複数行に見えますが、実際は改行なしの一行です。もし改行を入れたい場合は、\
を使います。
SUBSYSTEM=="usb", ACTION=="add", ATTRS{idProduct}=="01bb", ATTRS{idVendor}=="054c", RUN+="/bin/sh -c 'echo Hello at `date` > /home/pi/hello.log'"
なお、udevのキーRUN
において、/bin/sh
に引数を渡すためにシングルクォート(')で囲んでいます。
variables - passing arguments to shell script from udev rules file - Stack Overflow
結果を確認するために、USBポートにパソリを挿入したままRaspberry Piを再起動し、起動時に実行されるかを確認します。
# 再起動前にファイルが無いことを確認 pi@raspberrypi ~ $ ls python_games pi@raspberrypi ~ $ sudo reboot # 再起動後 pi@raspberrypi ~ $ ls hello.log python_games pi@raspberrypi ~ $ cat hello.log Hello at Tue Jun 23 09:30:03 UTC 2015
実行されているようでした。
systemdが認識するためのruleファイルの作成
どのようなruleファイルを作ればよいのか調べてみたところ、
デバイスを表すdeviceタイプのUnitも動的に生成されます。これは、udevが新しいデバイスを認識したタイミングで、「systemd」というudevタグを付けると、それを受け取ったsystemdが対応するUnitを生成して有効化します
とありました。
そこで、上記で作成した /etc/udev/rules.d/90-rc-s320.rules
を
- デバイスの所属するグループであるキー
GROUP
にusb
をセット - udevタグであるキー
TAG
に、systemd
をセット - systemdのサービスと依存関係を持たせるため、
ENV{SYSTEMD_WANTS}="rc-s320.service"
をセット- systemdのdeviceへ表示させるだけなら不要
- キー
NAME
に、任意の名前(例えば、pasori320
)をセット
のように修正します。
なお、キーの意味などは以下が参考になりました。
/etc/udev/rules.d/90-rc-s320.rules
SUBSYSTEM=="usb", ACTION=="add", ATTRS{idProduct}=="01bb", ATTRS{idVendor}=="054c", GROUP="usb", TAG+="systemd", ENV{SYSTEMD_WANTS}+="rc-s320.service", NAME="pasori320"
再度RaspberryPiを再起動し、systemctlで表示されるかを確認します。
pi@raspberrypi ~ $ systemctl --type=device --all UNIT LOAD ACTIVE SUB DESCRIPTION ... dev-pasori320.device loaded active plugged 01bb dev-root.device loaded active plugged Silicon-Power8G ... sys-devices-platfor...target0:0:0-0:0:0:0-block-sda.device loaded active plugged Silicon-Power8G sys-devices-platfor...2708_usb-usb1-1\x2d1-1\x2d1.4.device loaded active plugged 01bb ...
元々のsys-devices-platform-bcm2708_usb-usb1-1\x2d1-1\x2d1.4.device
の他、dev-pasori320.device
が追加されました。
これでsystemdでUSBデバイスの挿入を検知できました。
systemdによるudevのdeivceの検知
.service
ファイルの作成
次は、systemdにて「deviceファイルを認識したらサービスを起動する」ことを試してみます。なお、ファイルの作成の流れでは以下が参考になりました。
【Serf】systemdでserfを自動起動する方法 | Pocketstudio.jp log3
今回は、/etc/systemd/system/rc-s320.service
というファイルを作成し、その中にUnitに関する設定を行います。
pi@raspberrypi ~ $ sudo nano /etc/systemd/system/rc-s320.service
作成するUnitの仕様は、
- udevがパソリを認識後に、サービスを起動
- udevでNAMEキーを指定した時に作成される、
/dev/pasori320
に依存・前後関係あり
- udevでNAMEキーを指定した時に作成される、
- USBデバイスシステム起動時の自動起動は無効
- USBデバイス接続時に初めて起動するため
systemctl enable
コマンドは不要
- 他のUnitからの固定的な依存関係になるので、
static
なサービスにする[Install]
セクションは記述しない
- 動作確認のため、サービスが起動時にログファイルへ書き込み
としました。
なお、staticなサービスについては、以下の記事が参考になりました。
一方、[Install]セクションを持たないserviceは、有効化/無効化の対象にはなりません。先の出力で「static」と表示されたものがこれにあたります。一般には、他のUnitからの固定的な依存関係で起動することになります。 Systemd入門(1) - Unitの概念を理解する - めもめも
また、Restart=always
を追加すると、systemctl status rc-s320.service
でサービスの状態を見た時に、以下の様なエラーが出ます。今回のサービスでは再実行不要なことから、追加していません。
pi@raspberrypi ~ $ systemctl status rc-s320.service rc-s320.service - Pasori RC-S320 service Loaded: loaded (/etc/systemd/system/rc-s320.service; static) Active: failed (Result: start-limit) since Wed 2015-06-24 **:**:** UTC; 43min ago Process: 1282 ExecStart=/bin/sh -c echo Hello by `date` >> /home/pi/hello2.log (code=exited, status=0/SUCCESS) Jun 24 **:**:** raspberrypi systemd[1]: rc-s320.service holdoff time over, scheduling restart. Jun 24 **:**:** raspberrypi systemd[1]: Stopping Pasori RC-S320 service... Jun 24 **:**:** raspberrypi systemd[1]: Starting Pasori RC-S320 service... Jun 24 **:**:** raspberrypi systemd[1]: rc-s320.service start request repeated too quickly, refusing to start. Jun 24 **:**:** raspberrypi systemd[1]: Failed to start Pasori RC-S320 service. Jun 24 **:**:** raspberrypi systemd[1]: Unit rc-s320.service entered failed state.
以下を参考にしつつ、最終的なのは次のものとなります。
systemd.service - freedesktop.org
/etc/systemd/system/rc-s320.service
[Unit] Description=Pasori RC-S320 service Requires=dev-pasori320.device After=dev-pasori320.device [Service] Type=simple ExecStart=/bin/sh -c "echo Hello by `date` >> /home/pi/hello2.log"
systemdの設定の反映と確認
必要に応じて、設定の反映と確認を行います。
# 設定の再読み込み pi@raspberrypi ~ $ sudo systemctl --system daemon-reload # 状態確認 pi@raspberrypi ~ $ systemctl status rc-s320.service
動作テスト
Raspberry Piをシャットダウンして、USBポートからパソリを抜きます。
その後、Raspberry Pi を起動して、サービスが起動していないことを確認します。
pi@raspberrypi ~ $ systemctl status rc-s320.service rc-s320.service - Pasori RC-S320 service Loaded: loaded (/etc/systemd/system/rc-s320.service; static) Active: inactive (dead)
続いて、USBポートへパソリを挿入し、サービスが起動したことを確認します。
pi@raspberrypi ~ $ systemctl status rc-s320.service rc-s320.service - Pasori RC-S320 service Loaded: loaded (/etc/systemd/system/rc-s320.service; static) Active: inactive (dead) since Wed 2015-06-24 10:29:07 UTC; 58s ago Process: 1308 ExecStart=/bin/sh -c echo Hello by `date` >> /home/pi/hello2.log (code=exited, status=0/SUCCESS) # ログファイルの中身 pi@raspberrypi ~ $ cat hello2.log Hello by Wed Jun 24 10:29:07 UTC 2015
以上で、USBデバイス挿入時にサービスを起動することができました。
ソースコード
使用した3つのファイルはgistsに上げました。
https://gist.github.com/thinkAmi/3726e0cc9bd009419b65
悩んだこと
Linuxまわりが詳しくないので...。
Raspbianで使われているshellの確認
以下を参考にして、確認してみました。
デフォルトのShell を変更する
使用できる種類の確認
pi@raspberrypi ~ $ cat /etc/shells # /etc/shells: valid login shells /bin/sh /bin/dash /bin/bash /bin/rbash
ユーザごとのデフォルトのshell
pi@raspberrypi ~ $ cat /etc/passwd root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/bin/sh bin:x:2:2:bin:/bin:/bin/sh sys:x:3:3:sys:/dev:/bin/sh sync:x:4:65534:sync:/bin:/bin/sync games:x:5:60:games:/usr/games:/bin/sh man:x:6:12:man:/var/cache/man:/bin/sh lp:x:7:7:lp:/var/spool/lpd:/bin/sh mail:x:8:8:mail:/var/mail:/bin/sh news:x:9:9:news:/var/spool/news:/bin/sh uucp:x:10:10:uucp:/var/spool/uucp:/bin/sh proxy:x:13:13:proxy:/bin:/bin/sh www-data:x:33:33:www-data:/var/www:/bin/sh backup:x:34:34:backup:/var/backups:/bin/sh list:x:38:38:Mailing List Manager:/var/list:/bin/sh irc:x:39:39:ircd:/var/run/ircd:/bin/sh gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/bin/sh nobody:x:65534:65534:nobody:/nonexistent:/bin/sh libuuid:x:100:101::/var/lib/libuuid:/bin/sh pi:x:1000:1000:,,,:/home/pi:/bin/bash sshd:x:101:65534::/var/run/sshd:/usr/sbin/nologin ntp:x:102:104::/home/ntp:/bin/false statd:x:103:65534::/var/lib/nfs:/bin/false messagebus:x:104:106::/var/run/dbus:/bin/false usbmux:x:105:46:usbmux daemon,,,:/home/usbmux:/bin/false lightdm:x:106:109:Light Display Manager:/var/lib/lightdm:/bin/false
その他の部分
- Bourne Shell - Wikipedia
-c
はcオプションと呼び、引数を実行コマンドとして解釈