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 環境変数を設定されたプロセスはどれなのかを確認する。実際に起動できているので factotum や plumber に設定できていることは間違いないが、環境変数は親のプロセスから伝播されていくものなので、親の systemd —user も更新されているのか。このとき、ユーザーセッションのプロセスオーナーは該当ユーザーだけど run0 で特権ユーザーに昇格しておかなければ環境変数を読めない。
$ run0 xargs -0 -a /proc/1417/environ -n1 echoLANG=ja_JP.UTF-8NOTIFY_SOCKET=/run/systemd/notifyUSER=lufiaLOGNAME=lufiaJOURNAL_STREAM=9:8081SYSTEMD_EXEC_PID=1417XDG_SESSION_ID=2XDG_RUNTIME_DIR=/run/user/60331XDG_SESSION_TYPE=unspecifiedXDG_SESSION_CLASS=manager結果をみると設定されていないことが分かる。次に、ユニットで実行したプロセスに DISPLAY 環境変数が設定されていることも確認する。これは昇格しなくても読める。
$ xargs -0 -a /proc/2903/environ -n1 echoLANG=ja_JP.UTF-8LOGNAME=lufiaUSER=lufiaXDG_RUNTIME_DIR=/run/user/60331DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/60331/busDISPLAY=:0SWAYSOCK=/run/user/60331/sway-ipc.60331.1452.sockWAYLAND_DISPLAY=wayland-1XDG_CURRENT_DESKTOP=swayMANAGERPID=1417JOURNAL_STREAM=9:46015SYSTEMD_EXEC_PID=2902PLAN9=/usr/lib/plan9secstore=localhost最後、念の為に 9pserve プロセスも確認するが、変わりはない。
$ xargs -0 -a /proc/2907/environ -n1 echoLANG=ja_JP.UTF-8LOGNAME=lufiaUSER=lufiaXDG_RUNTIME_DIR=/run/user/60331DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/60331/busDISPLAY=:0SWAYSOCK=/run/user/60331/sway-ipc.60331.1452.sockWAYLAND_DISPLAY=wayland-1XDG_CURRENT_DESKTOP=swayMANAGERPID=1417JOURNAL_STREAM=9:46015SYSTEMD_EXEC_PID=2902PLAN9=/usr/lib/plan9secstore=localhostところで、DISPLAY や SWAYSOCK のような環境変数は、少なくともプロセスツリーとしては systemd —user と各ユニットの間に存在しないし、systemctl で実行したのはSway上だけど実際はVarlinkで通信しているはずなので、どこから環境変数が渡されているのか不思議に思う。どういった仕組みで設定されるのだろうか。それはsystemdには環境ブロックという概念があり、systemctl import-environment, show-environment, set-environment などのコマンドで取得または更新できる。systemctl(1)によると、ユニットを起動する時にこれらの環境変数が渡されるらしい。