コンテンツにスキップ

systemd/ユーザー

提供: ArchWiki
Systemctl --userから転送)

systemd は、ユーザーの制御下でサービスを管理するためのユーザーごとの systemd インスタンスを提供し、ユーザー自身の user units を開始、停止、有効化、無効化できるようにします。これは、mpd のような単一ユーザー向けに一般的に実行されるデーモンやサービス、またはメールの取得のような自動化されたタスクを実行するのに便利です。

利用方法

ユーザーがログインを行うと、systemd は自動的にユーザーサービスを管理する systemd --user インスタンスを起動します。このプロセスは、そのユーザーにセッションが残っているかぎり生存し、ユーザーの最後のセッションが閉じられた時に終了します。systemd のユーザーインスタンスを自動起動している場合、インスタンスはブート時に起動し、終了しません。ユーザーサービスを使うことで、ソケットアクティベーションやタイマー、システムの依存関係や cgroups によるプロセスの制限など、systemd の恩恵を最大限活用して、デーモンや自動化されたタスクを実行できます。

ユーザーユニットはシステムサービスと同じように以下のディレクトリに配置されます (優先度が低い順):

  • /usr/lib/systemd/user/: インストールしたパッケージに含まれているサービス
  • ~/.local/share/systemd/user/: ホームディレクトリにインストールしたパッケージのユニット
  • /etc/systemd/user/: システムの管理者が配置する、全てのユーザーが使えるユーザーサービス
  • ~/.config/systemd/user/: ユーザーが配置する、そのユーザーのサービス

systemd のユーザーインスタンスが起動すると、default.target ターゲットが立ち上がります。その後 systemctl --user を使って手動で systemd のユーザーインスタンスを制御できるようになります。

ノート
  • バージョン206から、systemd --user インスタンスはユーザーごとのプロセスであり、セッションごとのプロセスではないので注意してください。ソケットやステートファイルなどのユーザーサービスによって扱われるリソースはセッションごとではなくユーザーごとに割り振られる (ユーザーのホームディレクトリ上に存在する) というのが原則です。つまりユーザーサービスは全てセッションとは関係なく動作します。結果として、セッションの中で実行する必要があるプログラムはユーザーサービスの中で壊れてしまう可能性があります。systemd のユーザーセッションの扱い方は流動的であり確定していません。[1][2] が今後どうなるかのヒントになるでしょう。
  • systemd --usersystemd --system プロセスとは別のプロセスを起動します。ユーザーユニットからシステムユニットに依存したり参照することはできません。

基本設定

すべてのユーザー単位は ~/.config/systemd/user/ に配置されます。最初のログイン時に単位を起動したい場合は、起動したい任意の単位に対して systemctl --user enable unit を実行してください。

ヒント 特定のユーザーではなく、すべてのユーザーに対して単位を有効にしたい場合は、root として systemctl --global enable unit を実行してください。無効化する場合も同様です disable に変更

環境変数

systemd のユーザーインスタンスは .bashrc などに設定された環境変数を全く継承しません。systemd インスタンスに環境変数を設定する方法は複数存在します:

  • $HOME ディレクトリが存在するユーザーの場合、~/.config/environment.d/ ディレクトリに NAME=VAL という形式で環境変数を記述した .conf ファイルを作成する。ユーザーのユニットファイルにのみ影響します。詳しくは environment.d(5) を見てください。
  • /etc/systemd/user.confDefaultEnvironment オプションを使う。全てのユーザーユニットに影響します。
  • /etc/systemd/system/user@.service.d/ に設定ファイルを追加する。全てのユーザーユニットに影響します。#サービス例を参照。
  • /etc/systemd/system/user@.service.d/ にドロップイン設定ファイルを追加する (すべてのユーザーに影響)、詳細は #サービス例を参照。
  • 適宜、systemctl --user set-environmentsystemctl --user import-environment を使う。環境変数を設定した後に起動したユーザーユニットには影響しますが、既に起動しているユニットには影響しません。
  • D-Busdbus-update-activation-environment --systemd --all コマンドを使う。systemctl --user import-environment と同じ効果があり、D-Bus セッションに適用されます。コマンドはシェルの初期化ファイルの末尾に追加できます。
  • ユーザー環境全体の環境変数は systemd のジェネレータによって読み込まれる environment.d ディレクトリを使用できます。詳しくは environment.d(5) を参照。
  • ユーザーごとに異なる環境変数を生成できる systemd.environment-generator(7) スクリプトを作成することもできます。ユーザーごとの環境が必要な場合(XDG_RUNTIME_DIRDBUS_SESSION_BUS_ADDRESS などが該当)、これはおそらく最適な方法です。

設定するべき変数としては PATH などが考えられます。

設定後、コマンド systemctl --user show-environment を使用して値が正しいことを確認できます。変更を即座に反映させるには、systemctl --user daemon-reload を実行する必要があるかもしれません。

Systemd ユーザーインスタンス

上記はユーザー単位のデフォルト環境変数についてのみ説明しています。しかし、systemd ユーザーインスタンス自体もいくつかの環境変数の影響を受けます。特に、特定の指定子 (systemd.unit(5) § SPECIFIERS を参照) は XDG 変数 の影響を受けます。

ただし、systemd ユーザーインスタンスは 起動時に設定された環境変数のみ を使用します。特に、ファイルを解析しようとはしません (upstream bug #29414 (closed WONTFIX) を参照) そのため、このような環境変数が必要な場合は、ドロップイン設定ファイルで設定する必要があります (#サービス例を参照)

systemd はこれらの値を確認するための内部ツールを提供していませんが、指定子が期待通りに展開されることを確認するために、次のようなサービスを使用できます。

$XDG_CONFIG_HOME/systemd/user/test-specifiers.service
[Service]
Type=oneshot
ExecStart=printf '(systemd)=(envvar)\n'
ExecStart=printf '%%s=%%s\n' %C "${XDG_CACHE_HOME}"
ExecStart=printf '%%s=%%s\n' %E "${XDG_CONFIG_HOME}"
ExecStart=printf '%%s=%%s\n' %L "${XDG_STATE_HOME}"/log
ExecStart=printf '%%s=%%s\n' %S "${XDG_STATE_HOME}"
ExecStart=printf '%%s=%%s\n' %t "${XDG_RUNTIME_DIR}"

サービス例

ドロップインディレクトリ /etc/systemd/system/user@.service.d/ を作成して、その中に拡張子が .conf のファイルを作成します (例: local.conf):

/etc/systemd/system/user@.service.d/local.conf
[Service]
Environment="PATH=/usr/lib/ccache/bin:/usr/local/bin:/usr/bin:/bin"
Environment="EDITOR=nano -c"
Environment="BROWSER=firefox"
Environment="NO_AT_BRIDGE=1"

DISPLAY と XAUTHORITY

DISPLAY は X アプリケーションがどのディスプレイを使えばいいのか知るために使用されます。XAUTHORITY はユーザーの .Xauthority ファイルのパスと、X サーバーにアクセスするのに必要な cookie を指定します。systemd ユニットから X アプリケーションを起動する場合、これらの変数を設定する必要があります。バージョン 219 から、セッションが開始したときに DISPLAYXAUTHORITYsystemd --user デーモンの環境にアップロードする X11 セッションアプレット /etc/X11/xinit/xinitrc.d/50-systemd-user.sh が systemd に付属するようになりました。これによって、X を標準の方法で起動しているかぎり、ユーザーサービスは DISPLAYXAUTHORITY を使用することができるようになっています。

PATH

PATH 変数を .bashrc.bash_profile で設定しても systemd では使うことができません。PATH をカスタマイズしており、それを利用するアプリケーションを systemd ユニットから起動する予定があるなら、systemd 環境に PATH を設定する必要があります。PATH.bash_profile に設定している場合、以下を PATH 変数を設定した後の .bash_profile に追加するのが systemd で PATH を設定する一番簡単な方法です:

~/.bash_profile
systemctl --user import-environment PATH

pam_environment

pam_env.so モジュールを利用することで環境変数を設定できます。~/.pam_environment ファイルを以下のように作成してください:

~/.pam_environment
XDG_CONFIG_HOME DEFAULT=@{HOME}/.local/config
XDG_DATA_HOME   DEFAULT=@{HOME}/.local/data

.pam_environment ファイルの構文に関する詳細は環境変数#pam_env を使うを見てください。systemctl --user show-environment コマンドを実行することで設定が上手くいっているか確認できます:

$ systemctl --user show-environment
...
XDG_CONFIG_HOME=/home/user/.local/config
XDG_DATA_HOME=/home/user/.local/data
...

systemd のユーザーインスタンスを自動起動

systemd のユーザーインスタンスはデフォルトでユーザーの最初のログイン時に実行され、ユーザーのセッションが閉じられた時に終了します。しかしながら、ブートした直後にインスタンスを起動して、セッションが閉じられてもユーザーインスタンスを実行し続ける方が、例えばセッションが開いてない時もユーザープロセスを実行したいときなどには便利です。linger を利用してこれを行います。特定のユーザーで linger を有効にするには以下のコマンドを使用:

# loginctl enable-linger username
警告 systemd のサービスはセッションではありません。サービスは logind の外で動作します。linger を利用して自動ログインを有効にするとセッションが破壊されるので注意してください。

ユーザーユニットを書く

普通の systemd ユニットファイルの書き方は systemd#ユニットファイルを見てください。

サンプル

以下は mpd サービスのユーザーバージョンの例です。

~/.config/systemd/user/mpd.service
[Unit]
Description=Music Player Daemon

[Service]
ExecStart=/usr/bin/mpd --no-daemon

[Install]
WantedBy=default.target

変数を使用したサンプル

以下は sickbeard.service のユーザーバージョンの例で、SickBeard が特定のファイルを見つけられるようホームディレクトリの変数を使っています:

~/.config/systemd/user/sickbeard.service
[Unit]
Description=SickBeard Daemon

[Service]
ExecStart=/usr/bin/env python2 /opt/sickbeard/SickBeard.py --config %h/.sickbeard/config.ini --datadir %h/.sickbeard

[Install]
WantedBy=default.target

man systemd.unit に書かれているように、%h 変数はサービスを実行しているユーザーのホームディレクトリに置き換えられます。他にも systemd のマニュアルページで示されている変数が存在します。

ジャーナルを読み込む

ユーザーのジャーナルも同じようなコマンドで読みことができます:

$ journalctl --user

ユニットを指定する場合:

$ journalctl --user -u myunit.service

ユーザーユニットを指定する場合:

$ journalctl --user --user-unit myunit.service

ときどきユーザーサービスからの出力がサービスユニットに割り当てられないというバグが存在します。-u を使って出力をフィルタリングするとサービスユニットからの出力が見れなくなる可能性があります。

一時ファイル

systemd-tmpfiles を使うことでシステム全体と同じように一時ファイル・ディレクトリを管理することができます (systemd#一時ファイルを参照)。ユーザー個別の設定ファイルは ~/.config/user-tmpfiles.d/~/.local/share/user-tmpfiles.d/ からこの順番で読み込まれます。設定ファイルを使うには、必要な systemd ユーザーユニットを使用しているユーザーで有効にする必要があります:

$ systemctl --user enable systemd-tmpfiles-setup.service systemd-tmpfiles-clean.timer

設定ファイルの構文はシステムユニットと同じです。詳しくは man ページの systemd-tmpfiles(8)tmpfiles.d(5) を見てください。

Xorg と systemd

systemd ユニットの中で Xorg を実行する方法は複数存在します。以下の2つは、xorg プロセスで新しいユーザーセッションを起動するというのと、systemd のユーザーサービスから xorg を起動するという方法です。

ディスプレイマネージャを使わずに Xorg に自動ログイン

こちらを選択した場合、xorg サーバーでユーザーセッションを起動してその後ウィンドウマネージャなどを起動するために通常の ~/.xinitrc を実行するシステムユニットを起動します。

D-Bus を適切に設定して xlogin-gitAUR をインストールする必要があります。

雛形のファイルから xinitrc を設定して、/etc/X11/xinit/xinitrc.d/ のファイルが読み込まれるようにしてください。~/.xinitrc は実行したときに戻ってこないようにする必要があるため、最後のコマンドとして wait を記述するか、(ウィンドウマネージャなど) exec を追加して戻ってこないようにしてください。

セッションは自らの dbus デーモンを使用しますが、systemd ユーティリティは dbus.service インスタンスに自動的に接続します。

最後に、(root で) xlogin を有効にして起動時に自動ログインするようにしてください:

# systemctl enable xlogin@username

ユーザーセッションが systemd の範囲の中に収まるようになってユーザーセッションの全てが問題なく動作するはずです。

systemd のユーザーサービスとしての Xorg

また、systemd のユーザーサービスの中から Xorg を実行することもできます。他の X 関連のユニットを Xorg などに依存させることができるという点では有利な一方、以下で説明するようにいくつか欠点が存在します。

バージョン 1.16 から xorg-server は systemd とのより良い統合を2つの手段で提供しています:

  • デバイス管理を logind に委託することで、非特権でも実行することが可能に (このコミット など Hans de Goede のコミットを参照)。
  • ソケットによってサービスを有効化することが可能に (このコミット を参照)。これによって systemd-xorg-launch-helper-gitAUR が不要に。

残念ながら、非特権モードで xorg を実行できるようにするには、セッションの中で xorg を実行する必要があります。そのため、今のところユーザーサービスとして xorg を実行するのには (1.16 以前と同じように) root 権限で実行する必要があるというハンディキャップがあり、1.16 で導入された非特権モードを活用することはできません。

ノート 上記の制約は logind によるものではなく、xorg にどのセッションを引き継ぐか知らせる必要があるのが理由です。現在の実装では自己の pid を引数として logindGetSessionByPID を呼び出すことで情報を取得しています。このスレッドxorg のソース を参照してください。アタッチする tty からセッションを取得するように xorg を修正すれば、セッションの外でもユーザーサービスから非特権で実行することができます。
警告 xorg 1.18 ではソケットアクティベーションが壊れているようです。クライアントからアクティベーションが実行されるとデッドロックが発生します。上流のバグレポート [3] を参照してください。回避策としては、ソケットアクティベーションを使わないで xorg サーバーを起動することで、サーバーが完全に起動するまでクライアントの接続を待機させることができます。X サーバーの起動を通知する機構は存在しません。

以下がユーザーサービスから xorg を起動する方法です:

1. /etc/X11/Xwrapper.config を編集して、xorg が root 権限を使ってどのユーザーでも動作するようにします:

/etc/X11/Xwrapper.config
allowed_users=anybody
needs_root_rights=yes

2. 以下のユニットを ~/.config/systemd/user に追加:

~/.config/systemd/user/xorg@.socket
[Unit]
Description=Socket for xorg at display %i

[Socket]
ListenStream=/tmp/.X11-unix/X%i
~/.config/systemd/user/xorg@.service
[Unit]
Description=Xorg server at display %i

Requires=xorg@%i.socket
After=xorg@%i.socket

[Service]
Type=simple
SuccessExitStatus=0 1

ExecStart=/usr/bin/Xorg :%i -nolisten tcp -noreset -verbose 2 "vt${XDG_VTNR}"

${XDG_VTNR} は xorg が起動する仮想コンソールで、サービスユニットでハードコードするか、以下のコマンドを使って systemd 環境で設定します:

$ systemctl --user set-environment XDG_VTNR=1
ノート xorg はユーザーがログインしたのと同じ仮想コンソールで起動する必要があります。そうでないと logind はセッションがアクティブになっていないと認識します。

3. で説明されているように DISPLAY 環境変数を設定します。

4. 次に、display 0 と tty 2 で xorg のソケットアクティベーションを有効化します:

$ systemctl --user set-environment XDG_VTNR=2     # So that xorg@.service knows which vt use
$ systemctl --user start xorg@0.socket            # Start listening on the socket for display 0

これで X アプリケーションを実行すると仮想コンソール 2 で自動的に xorg が起動します。

XDG_VTNR 環境変数は .bash_profile から systemd 環境に設定することができ、ウィンドウマネージャなど、あらゆる X アプリケーションを xorg@0.socket に依存する systemd ユニットとして起動することが可能です。

警告 現在、ユーザーサービスとしてウィンドウマネージャを実行するとセッションの外でウィンドウマネージャが実行されることになり次の問題が引き起こされます: セッションの破壊。ただし、systemd の開発者はウィンドウマネージャの実行を可能にする何かを作る予定があるようです。[4][5] を見て下さい。

ユースケース

永続的なターミナルマルチプレクサ

ウィンドウマネージャのセッションにログインする代わりに、ユーザーセッションでデフォルトで GNU ScreenTmux などのターミナルマルチプレクサをバックグラウンドで実行したいということもあるでしょう。X ログインとログインを分割するのはディスプレイマネージャの代わりに TTY で起動したい場合にのみ有用です (その場合すべてを myStuff.target で起動するように記述できます)。

上述のようなタイプのユーザーセッションを作成するには、wm.target を作成する代わりに、multiplexer.target を作成します:

[Unit]
Description=Terminal multiplexer
Documentation=info:screen man:screen(1) man:tmux(1)
After=cruft.target
Wants=cruft.target

[Install]
Alias=default.target

上記の mystuff.target と同じように、cruft.target は tmux や screen が起動する前に実行する必要があるサービス (もしくは起動時に即座に起動させたいサービス)、例えば GnuPG デーモンのセッションなどを起動します。

次にマルチプレクサセッションのサービスを作成してください。以下は tmux を使用して /tmp/gpg-agent-info に情報を書き込む gpg-agent セッションを読み込むサービスの例です。また、DISPLAY を設定することで X を起動したときに X プログラムが実行できるようにしています。

[Unit]
Description=tmux: A terminal multiplixer
Documentation=man:tmux(1)
After=gpg-agent.service
Wants=gpg-agent.service

[Service]
Type=forking
ExecStart=/usr/bin/tmux start
ExecStop=/usr/bin/tmux kill-server
Environment=DISPLAY=:0
EnvironmentFile=/tmp/gpg-agent-info

[Install]
WantedBy=multiplexer.target

上記の設定が完了したら、systemctl --user enabletmux.service, multiplexer.targetcruft.target で実行させるサービスを有効化してください。後は同じように user-session@.service を有効化しますが、ユーザーセッションは TTY を引き継がないので user-session@.service から Conflicts=getty@tty1.service は削除してください。これで、起動時にターミナルマルチプレクサなどのプログラムを実行できるようになります。

ウィンドウマネージャ

systemd のサービスとしてウィンドウマネージャを実行するには、まず Xorg を systemd のユーザーサービスとして実行する必要があります。以下では awesome を例として使います:

~/.config/systemd/user/awesome.service
[Unit]
Description=Awesome window manager
After=xorg.target
Requires=xorg.target

[Service]
ExecStart=/usr/bin/awesome
Restart=always
RestartSec=10
 
[Install]
WantedBy=wm.target
ノート [Install] セクションに WantedBy を記述することで、systemctl --user enable を使用した際に ~/.config/systemd/user/wm.target.wants/window_manager.service としてリンクが作成され、ログイン時に起動できるようになります。リンクは手動で作成するのではなく、サービスを有効化することを推奨します。

ログアウト時にユーザープロセスを終了

systemd パッケージのデフォルトは KillUserProcesses=no となっており、ユーザーが完全にログアウトしたときでもユーザープロセスは終了しません。ユーザーがログアウトしたときに全てのユーザープロセスが終了するようにしたい場合、/etc/systemd/logind.confKillUserProcesses=yes と設定してください。

ただし、この設定を変更すると tmuxscreen などのターミナルマルチプレクサが動作しなくなります。以下のように systemd-run を使ってターミナルマルチプレクサを利用してください:

$ systemd-run --scope --user command args

例えば、screen を実行する場合:

$ systemd-run --scope --user screen -S foo

systemd-run を使うと、ログアウト後も、ユーザがシステム内のどこかで少なくとも 1 回はログインしていて、user@.service がまだ実行されている間だけ、プロセスが実行され続けます。

ユーザがすべてのセッションからログアウトした後、user@.service は、そのユーザが「lingering」を有効にしていない限り、デフォルトで終了します [6]。ユーザーが完全にログアウトしていても、長時間のタスクを効果的に実行できるようにするには、そのユーザーに対してリンガリングを有効にする必要があります。詳しくは #Automatic start-up of systemd user instancesloginctl(1) を参照してください。

トラブルシューティング

ランタイムディレクトリ '/run/user/1000' は、UID1000 によって所有されていません

systemd[1867]: pam_systemd(systemd-user:session): Runtime directory '/run/user/1000' is not owned by UID 1000, as it should.
systemd[1867]: Trying to run as user instance, but $XDG_RUNTIME_DIR is not set

このようなエラーが表示され、ログインセッションが壊れている場合、システム上の別のシステム(ユーザーではない)サービスがこのフォルダを作成している可能性があります。例えば、/run/user/1000 にバインドマウントしている docker コンテナを使用している場合、このようなことが起こる可能性があります。これを解決するには、マウントを削除することでコンテナを修正するか、ドッカーサービスを無効化/遅延させるかのいずれかです。

"A stop job is running for User Manager for UID 1000"

シャットダウン中にこのメッセージが表示される場合、通常は2分のタイムアウトが設定されていることが多いですが、これはユーザーサービスの1つがタイムリーに停止しなかったことを意味します。これには、以前に一時的なサービスを生成した問題のあるアプリケーションが原因である可能性があります。タイムアウトが切れるのを待つこともできますが、これが気になる場合は、問題のあるサービスにオーバーライドを作成するか、すべてのユーザーサービスに対してグローバルタイムアウトを短縮することができます。

問題のあるサービスを特定してオーバーライドする

この問題をトラブルシューティングするために、systemd デバッグシェル を起動します:

systemctl start debug-shell

その後、システムを再起動するかシャットダウンします。問題が発生したときに、Ctrl+Alt+F9 を使用してデバッグシェルに切り替えます。シャットダウンを妨げているサービスを特定するには、次のコマンドを実行します:

systemctl --user list-jobs

ほとんどのオープンソースアプリケーションでは、この問題は該当するメンテナに報告されるべきであり、オーバーライドは必要ありません。しかし、クローズドソースアプリケーションの場合は、次のようにオーバーライドを作成できます:

$ systemctl --user edit --force name@.service 
[Service]
TimeoutStopSec=1s

これにより、その特定のサービスのタイムアウトが1秒に短縮されます。--force パラメータは、一時的なサービスでディスク上に .service ファイルを作成しない場合にのみ必要です。オーバーライドはそれに関係なく機能します。タイムアウトの代わりに KillSignal=SIGKILL を使用することもできます。これにより、ユーザーマネージャが停止したときにサービスが即座に強制終了されます。この方法は、そのサービスがそれを処理できると確信している場合にのみ使用してください。

タイムアウト値の変更

どのサービスがシャットダウンを妨げているか気にしない場合は、次のようにしてすべてのユーザーサービスのグローバルタイムアウトを変更できます:

# systemctl edit user@.service 
[Service]
TimeoutStopSec=10s

このタイムアウト後、優雅に停止していないすべてのユーザーサービスは強制終了されます。これは突然の電源断と同じです。この値は特定の使用ケースに合わせて調整してください。タイムアウトを低すぎる値に設定すると、アプリケーションによってはデータ損失が発生する可能性があります。

参照