Skip to content

systemdで環境変数が伝播するしくみ

Plan 9関連ユニットの設計で調査をしていたとき、plumber.service などはSway実行直後の自動起動では DISPLAY 環境変数が設定されていなくて失敗するが、その後で同じユニットファイルを手で実行すると問題なく動作することに気づいた。このとき、プロセスツリーとしては以下のようになる。

flowchart LR
user("1417: systemd --user")
subgraph factotum.service
factotum(2903: factotum)
factotum9p(2907: 9pserve)
end
subgraph plumber.service
plumber(8054: plumber)
plumber9p(8056: 9pserve)
end
user --> factotum & factotum9p
user --> plumber & plumber9p

9pserve の親プロセスが systemd —user となっているが、間違ってはいない。

では DISPLAY 環境変数を設定されたプロセスはどれなのかを確認する。実際に起動できているので factotumplumber に設定できていることは間違いないが、環境変数は親のプロセスから伝播されていくものなので、親の systemd —user も更新されているのか。このとき、ユーザーセッションのプロセスオーナーは該当ユーザーだけど run0 で特権ユーザーに昇格しておかなければ環境変数を読めない。

Terminal window
$ run0 xargs -0 -a /proc/1417/environ -n1 echo
LANG=ja_JP.UTF-8
NOTIFY_SOCKET=/run/systemd/notify
USER=lufia
LOGNAME=lufia
JOURNAL_STREAM=9:8081
SYSTEMD_EXEC_PID=1417
XDG_SESSION_ID=2
XDG_RUNTIME_DIR=/run/user/60331
XDG_SESSION_TYPE=unspecified
XDG_SESSION_CLASS=manager

結果をみると設定されていないことが分かる。次に、ユニットで実行したプロセスに DISPLAY 環境変数が設定されていることも確認する。これは昇格しなくても読める。

Terminal window
$ xargs -0 -a /proc/2903/environ -n1 echo
LANG=ja_JP.UTF-8
LOGNAME=lufia
USER=lufia
XDG_RUNTIME_DIR=/run/user/60331
DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/60331/bus
DISPLAY=:0
SWAYSOCK=/run/user/60331/sway-ipc.60331.1452.sock
WAYLAND_DISPLAY=wayland-1
XDG_CURRENT_DESKTOP=sway
MANAGERPID=1417
JOURNAL_STREAM=9:46015
SYSTEMD_EXEC_PID=2902
PLAN9=/usr/lib/plan9
secstore=localhost

最後、念の為に 9pserve プロセスも確認するが、変わりはない。

Terminal window
$ xargs -0 -a /proc/2907/environ -n1 echo
LANG=ja_JP.UTF-8
LOGNAME=lufia
USER=lufia
XDG_RUNTIME_DIR=/run/user/60331
DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/60331/bus
DISPLAY=:0
SWAYSOCK=/run/user/60331/sway-ipc.60331.1452.sock
WAYLAND_DISPLAY=wayland-1
XDG_CURRENT_DESKTOP=sway
MANAGERPID=1417
JOURNAL_STREAM=9:46015
SYSTEMD_EXEC_PID=2902
PLAN9=/usr/lib/plan9
secstore=localhost

ところで、DISPLAYSWAYSOCK のような環境変数は、少なくともプロセスツリーとしては systemd —user と各ユニットの間に存在しないし、systemctl で実行したのはSway上だけど実際はVarlinkで通信しているはずなので、どこから環境変数が渡されているのか不思議に思う。どういった仕組みで設定されるのだろうか。それはsystemdには環境ブロックという概念があり、systemctl import-environment, show-environment, set-environment などのコマンドで取得または更新できる。systemctl(1)によると、ユニットを起動する時にこれらの環境変数が渡されるらしい。