Skip to content

Swayをsystemd経由で起動する

以下の条件を満たした環境にしたかった。

  • コンソールで使う場合は LANG=C で動作させたい
  • Swayを実行した後は LANG=ja_JP.UTF-8 で動作させたい

Setting Environmental Variablesによると実現する方法は複数あるが、environment.d やPAMを使う方法は影響範囲がシステム全体や全てのユニットに波及するのでやりたくない。なので、どうせならSwayをsystemd経由で実行させるのが自然じゃないかと思ったのでやってみた。

起動だけなら以下のユニットが使える。概ねはsystemd integrationStart sway without login manager like gdm/sddm を踏襲しているが不足していた部分は追加した。

[Unit]
Description=Sway window manager
Documentation=man:sway(1)
BindsTo=default.target
Wants=default.target
After=default.target
[Service]
Type=simple
EnvironmentFile=-%h/.config/sway/env
ExecStartPre=systemctl --user unset-environment WAYLAND_DISPLAY DISPLAY
ExecStart=%s -lc 'exec sway'
[Install]
WantedBy=default.target

[! Note] この方法ではsystemdのユーザーマネージャが sway を実行するので、agetty でログインしたとき起動したシェルのプロセスとは別のツリーとして実行されることになる。実用上は問題ないが、プロセスツリー的に微妙な気持ち悪さはあるので greetd 等を使うことも検討していいかもしれない。

上記のユニットで

ExecStart=%s -lc 'exec sway'

のようにsystemdのユニットで利用できる変数%s を使ってシェルを挟んでいるのは、ユーザー環境で .bash_profile 等に記述した環境変数をSwayに渡したいためにこうなっている。systemdのユーザーユニットに環境変数を設定したい場合はEnvironパラメータまたはhomectlで設定することを調査したように、各ユニットはユーザーユニットマネージャから起動されるので、ログイン時の ~/.bash_profile 等で設定した環境変数はsystemd経由で実行したコマンドに引き継がれない。systemctl —import-environment で引き継がれるようにしてもいいのだが、そうすると他のユニットにまで PATH 等の不要な環境変数を渡してしまうので、全てに対応するためシェルを経由している。

シェルを挟むのはGDMもcommon/gdm-common.cgdm_get_script_environment 辺りで近いことをやっていそうだったので筋が悪いわけでもなさそう。

以前はログインしたとき環境変数のエクスポートと一緒に $XDG_RUNTIME_DIR/env へ書き込んでおいて sway.service ユニットで EnvironmentFile を使って読み込ませていたが、

  • 素朴に実装するとシェルを起動するたびに env へ追記されてしまう
  • ファイルがなければ書き出すようにすると、今度はログアウトしても env が残っていて期待通りに動かない

また、上記のユニットで

ExecStartPre=systemctl --user unset-environment WAYLAND_DISPLAY DISPLAY

この行を消してしまうと以下のようなエラーになる。

Nov 19 00:00:05 firefly systemd[726]: Starting Sway window manager...
Nov 19 00:00:05 firefly systemd[726]: Started Sway window manager.
Nov 19 00:00:05 firefly sway[19962]: 00:00:00.003 [ERROR] [wlr] [backend/wayland/backend.c:608] Could not connect to remote display: No such file or directory
Nov 19 00:00:05 firefly sway[19962]: 00:00:00.003 [ERROR] [sway/server.c:247] Unable to create backend
Nov 19 00:00:05 firefly systemd[726]: sway.service: Main process exited, code=exited, status=1/FAILURE
Nov 19 00:00:05 firefly systemd[726]: sway.service: Failed with result 'exit-code'.

一度 sway を実行したあとは SWAYSOCK 等の環境変数がsystemdユーザーユニットに残ってしまうらしく、次に sway をsystemd経由で実行したとき意図せず引き継がれてしまう。これらの環境変数があると sway はリモートディスプレイに接続しようとして落ちる。なので最初に掲示したユニットのように systemctl —unset-environment で消す必要があった。

前提としてSwayではgraphical-session.targetを明示的に開始する必要があるのだが、これをsystemdユニットの順序を制御するで書いたような依存でうまく記述できないかと考えた。なんだけど、After= 等を使って分かりづらく書くよりは、このくらいなら sway のコンフィグで直接実行すればいいと思う。