Linux デスクトップの環境変数、どこに設定してますか?

f:id:masm11:20210530233947j:plain

こんにちは、時々 Linux デスクトップの話を混ぜていきたい masm11 です。 今回は Linux デスクトップの環境変数をどこに設定するか、 というテーマで書きたいと思います。

環境変数の設定方法はいくつか用意されています。その紹介と、結局どこがいいの? という話です。

シェルの rc

環境変数と言えば真っ先に思いつくのがこれだと思います。 bash なら ~/.bashrc や ~/.bash_profile、私は zsh を使っているので ~/.zshenv になります。

export FOO=bar

という形式で設定しますね。

しかし、ここに書いても、効果があるのはシェルおよびそこから起動したプロセスのみですね。 デスクトップには効果がありません。 例えば gdm 等からログインすると、デスクトップから Emacs を起動しても、 ここに設定した環境変数は反映されていません。

~/.xprofile

「それならここじゃん!」と思う方、結構いらっしゃるのでは? ~/.xprofile です。

gdm からログインすると、デスクトップ起動前にこのファイルを読み込んで 実行してくれますね。

中身はシェルスクリプトですので、

export FOO=bar

と書いておけばいいです。また、シェルが実行するわけですから、

. $HOME/.zshenv

と書けば、設定箇所は一箇所で済みます。

しかしこれにも効果がない場合があります。

~/.xprofile はファイル名のとおり、X の場合にしか通用しません。 最近流行りの Wayland では読み込んでくれません。 私は Wayland なのです。

~/.pam_environment

さて、この辺からなんだか怪しくなってきます。 ~/.pam_environment というファイルに設定することもできます。

PAM をご存知でしょうか? Pluggable Authentication Module の略で、 認証まわりを引き受けているプログラムや設定ファイル群です。

設定ファイルは /usr/lib/pam.d/ や /etc/pam.d/ にあります。 例えば、私の /etc/pam.d/system-login には以下の行があります。

session    required   pam_env.so           user_readenv=1

この設定により、ログインセッションの初期化時に pam_env.so が実行されます。 pam_env.so に与えた user_readenv=1 によって、~/.pam_environment が 読み込まれます。

この ~/.pam_environment は以下のような形式です。

PATH    DEFAULT=/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin

DEFAULT= だけでなく OVERRIDE= も書けるようなのですが、 詳細はまだ私も理解していないので省略します。

さすが PAM から読み込まれるだけあって、結構優秀です。いろんなパターンで使えます。

で、これの残念な点は、マニュアルに以下のように書いてあることです。

Due to problematic security this functionality is deprecated since
the 1.5.0 version and will be removed completely at some point in
the future.

なんと非推奨! 将来的には削除されるそうです。

homectl

次行きましょう。systemd-homed は以前の記事で一言だけ紹介した記憶があります。 そのコマンドで homectl というものがあります。

homectl update --setenv FOO=bar

と実行すると、~/.identity ファイルの中に次のように書き込まれます。

"environment" : [
        "FOO=bar"
],

JSON です。この状態でログインすると、環境変数が設定されています。

なお、この環境変数も、PAM 中で設定されます。/etc/pam.d/system-login の中に

-session   optional   pam_systemd.so

という行があり、この pam_systemd.so が設定してくれます。

これも良さそうなのですが、こちらも残念な点が。pam_systemd.so のコード中に以下の部分があります。

https://github.com/systemd/systemd/blob/4ab334472cd1e4a0dd17a4e35e888d380f56ff04/src/login/pam_systemd.c#L545-L549

既に環境変数がセットされていたら、セットしない、という処理です。

PAM のモジュールと言えど、PATH や LANG が設定されていないわけがありません。 つまり、PATH や LANG は homectl で設定しても反映されないのです。

使えね-ー

あ、ちなみに homectl にはいくつか癖がありまして…

  • homectl --setenv は最後に実行したものしか残らない

    つまり、FOO=barBAR=baz を設定しようとして

    homectl --setenv FOO=bar; homectl --setenv BAR=baz

    としても、後者しか残らない、ということです。--setenv は複数書くことができますので、

    homectl --setenv FOO=bar --setenv BAR=baz

    と実行してください。

  • ~/.identity を直接編集しても効果はない

    homectl コマンドなんて使わなくても ~/.identity を直接書けばいいじゃん、と思ったあなた。 それは意味がありません。直接書き換えても反映されません。

    諦めて homectl コマンドを使いましょう。

結局、

どれがいいんでしょう……?

私がぐぐりまくって辿り着いた結論を以下に書きます。

基本的には ~/.zshenv に書きます。しかしそれだけではデスクトップに効果がありませんので、

/usr/share/wayland-sessions/wayfire.desktop:

[Desktop Entry]
Name=Wayfire
Exec=/usr/local/bin/start-wayland-session wayfire
TryExec=wayfire
Icon=
Type=Application
DesktopNames=Wayfire

Exec= の行を書き換えて、 wayfire 起動時に wayfire そのものでなく /usr/local/bin/start-wayland-session を起動するようにします。

/usr/local/bin/start-wayland-session:

#!/bin/bash

if [ -x "$HOME"/.wayland-session ]; then
  exec "$HOME"/.wayland-session "$@"
else
  exec "$@"
fi

こうして、~/.wayland-session があったらそれを実行します。なければ引数の wayfire を そのまま実行します。

~/.wayland-session:

#!/bin/zsh
exec "$@"

このスクリプトは zsh で実行されます。zsh は ~/.zshenv を読み込みますので、そこで 環境変数が設定されます。その後、引数の wayfire を実行します。

まとめ

システム側のファイルを書き換えて解決してしまいました。 以前はユーザ側ファイルだけでなんとかなりました。

最近の Linux デスクトップのヘタレっぷりはとても残念です。 環境変数を一箇所で設定して、いろんなパターンで反映されて欲しい、 たったそれだけのことなのに、何故こんなに悩まないといけないのでしょう? 既に設定されてたら上書きしないとか、意味わかんないです。 設定しろって設定したんだから、ちゃんと設定してくれよ!

ちなみに gdm は PATH を無駄に上書きするので、使うのをやめました。 gdm は複雑すぎるためか、よく不具合起こしますね。

こんなことを愚痴りあえる人が欲しいです。以下のページからお願いします!

https://ingage.co.jp/recruit/