LoginSignup
21
12

More than 5 years have passed since last update.

Linux でデバイスを接続している時だけ動くサービスを作る

Last updated at Posted at 2017-10-26

Linux でデバイスを接続している時だけ動くサービスを作る

systemd と udev を使うと、とあるデバイスを接続すると動き始め、切断すると停止するようなサービスを作る事が出来る。

サンプルのサービスは何でも良いが 前に作った /usr/bin/timeserver.py を使う。

#!/usr/bin/env python
# Time server program

import socket
import datetime
import sys

HOST = ''                 # Symbolic name meaning all available interfaces
PORT = 50007              # Arbitrary non-privileged port
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((HOST, PORT))
s.listen(1)

while True:
    conn, addr = s.accept()
    now = datetime.datetime.now().isoformat()
    print('Connected by %s at %s\n' % (addr, now))
    conn.sendall(now.encode())
    conn.close()

まず、サービスの動作に必要な動作を整理する。USB 機器としては、たまたま手元にあった USB BT ドングル (BT アダプタ) を使う。

  1. 標準で起動する (WantedBy で multi-user.true -> timeserver への依存を作る)
  2. BT アダプタが有効になるまで待つ。(systemd の After を使う)
  3. BT アダプタが無効になれば停止する。(systemd の BindsTo を使う)
  4. BT アダプタが再び有効になれば再起動する。(WantedBy で デバイス -> timeserver への依存を作る)

Systemd の世界では、「BT アダプタが存在する」事を示す unit がある。このようにデバイスに依存する unit を device と呼ぶ。systemctl list-units -t device で定義済のリストを見る事が出来る。

# systemctl list-units -t device
UNIT                                                                                                       LOAD   ACTIVE SUB     DESCRIPTION
...
sys-devices-pci0000:00-0000:00:11.0-0000:02:00.0-usb2-2\x2d2-2\x2d2.1-2\x2d2.1:1.0-bluetooth-hci0.device   loaded active plugged /sys/devices/pci0000:00/0000:00:11.0/0000:02:00.0/usb2/2-2/2-2.1/2-2.1:1.0/bluetooth/hci0
...
sys-subsystem-bluetooth-devices-hci0.device                                                                loaded active plugged /sys/subsystem/bluetooth/devices/hci0
...

BT アダプタを表す hci0 device は /lib/udev/rules/99-systemd.rules で定義されている。

SUBSYSTEM=="bluetooth", TAG+="systemd", ENV{SYSTEMD_ALIAS}+="/sys/subsystem/bluetooth/devices/%k"

これは、もしも SUBSYSTEM=="bluetooth" にマッチするイベントが来たら TAG+="systemd" によって systemd device unit を作成し、ついでに ENV{SYSTEMD_ALIAS}+="/sys/subsystem/bluetooth/devices/%k" によってエイリアスを作成するという意味だ。%k は hci0 に置換されるので、unit 名としては sys-subsystem-bluetooth-devices-hci0.device が出来上がる。
自分の監視したいデバイス用の unit が既存の udev ルールにお定義されていなければ自分でも定義出来る。

この unit を BindsTo, After, WantedBy に指定して /etc/systemd/system/timeserver.service を書けば良い。

[Unit]
Description=Some Time server
After=network.target

# 2. Stop the service when the BT is disconnected
BindsTo=sys-subsystem-bluetooth-devices-hci0.device

# 3. Wait until BT is connected
After=sys-subsystem-bluetooth-devices-hci0.device

[Service]
ExecStart=/usr/bin/python -u /usr/bin/timeserver.py
Restart=always
StandardOutput=journal
StandardError=journal

[Install]
# 1. Start by default
WantedBy=multi-user.target

# 4. Start when BT is connected
WantedBy=sys-subsystem-bluetooth-devices-hci0.device

[Install] セクションに書いた定義は systemctl enable を行うとはじめて有効になる。

# systemctl daemon-reload 
# systemctl enable timeserver
Created symlink /etc/systemd/system/multi-user.target.wants/timeserver.service → /etc/systemd/system/timeserver.service.
Created symlink /etc/systemd/system/sys-subsystem-bluetooth-devices-hci0.device.wants/timeserver.service → /etc/systemd/system/timeserver.service.

設定ファイルが書けたら start してみる。BT アダプタが無い場合は BT アダプタを接続するまで処理が止まる。その後 BT アダプタと連動して timeserver が再生停止を行う。

# systemctl start timeserver
... BT アダプタを挿すまで停止
# journalctl -f -u timeserver # timeserver の状態を観察
Oct 26 03:28:36 qemux86-64 systemd[1]: Started Some Time server.
Oct 26 03:28:49 qemux86-64 systemd[1]: Stopping Some Time server...
Oct 26 03:28:49 qemux86-64 systemd[1]: Stopped Some Time server.

参考

21
12
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
21
12