#きょうのsystemd : ルートを変える (systemd for Administrators, Part 6)

これは

0pointer.de

の翻訳

ルートを変える

管理者であれ開発者であれ、遅かれ早かれ、chroot()環境に出会うことになる。 chroot()システムコールは単純にプロセスおよびその子がルートディレクト/ と考えるものをシフトさせ、そのプロセスが見ることができるファイルの階層をそのサブツリーに制限することができる。 主としてchroot()環境には2つの用途がある:

  1. セキュリティ目的: この用途では、特定の独立したデーモンがプライベートなサブディレクトリにchroot()され、デーモンが悪用された場合でも、攻撃者は完全なOSの階層ではなく、サブディレクトリしか見ることができないようにすることができる。
  2. OSイメージのデバッグ、テスト、ビルド、インストール、リカバリーをセットアップないしはコントロールするため: このためにゲストのOSの階層がホストOSのサブディレクトリにマウントないしはブートストラップされており、シェル(ないしはその他のアプリケーション)をその中で実行し、このサブディレクトリをその/にする。 例えば、異なるディストリビューションや異なるアーキテクチャ(例: ホスト側はx86_64でゲスト側がi386)であるかもしれない。 ホストOSの完全な階層はchroot()された環境から見ることはできない。

古典的なSystemVベースのOSの場合、chroot()環境を使うのは比較的簡単であった。 例えば、特定のデーモンをテストないしはその他の理由でchroot()ベースのゲストOSツリー内で実行するには、/proc/sysあるいはその他いくつかのAPIファイルシステムをツリーの内部にマウントし、それからchroot(1)を使ってchrootに入り、最終的にSysVスクリプトファイルを/sbin/service経由でchrootの中空実行すれば良い。

systemdベースのOSではそこまで簡単ではなくなってくる。 systemdの大きなアドバンテージの1つはすべてのデーモンはユーザーがサービスを開始させようとしたコンテクストとは一切関係がなく完全にクリーンで独立したコンテクストで実行されることが保証されていることにある。 SysVベースのシステムでは実行コンテクストの大きな部分(リソース制限や環境変数その他のようなもの)はinitスクリプトを実行したユーザーシェルから継承されたものである。 一方、systemdの場合、ユーザーはinitデーモンに通知をするだけであり、initデーモンはそれを受けて、デーモンを健全で良く定義された、きれいな実行コンテクストでフォークし、ユーザーコンテクストのパラメーターを一切継承させない。 これは驚くべき機能である一方、この機能により、サービスをchroot()環境で実行するための伝統的な手法は確かに破壊されてしまった。 というのも、実際のデーモンは常にPID1からスポーンされ、そこからchroot()の設定を継承するため、デーモンをスタートするように要求したクライアントがchroot()されたかどうかは関係なくなるのだ。 この最たるものとして、systemdは実際に自身のローカルコミュニケーションのソケットを/run/systemdに置いているため、chroot()環境にあるプロセスはinitシステムとトークすることすらできないのである(しかし、これはいいことかもしれないし、大胆な人間だったら、もちろんこれを、bindマウントを使うことで迂回することはできる)。

これで、当然のように、systemd環境でchroot()を適切に使うにはどうしたらいいのか?という問題が生じてくる。 そして、この記事がそれを説明するために思いついたものであり、この問題に対して徹底的かつ包括的に答えられたら嬉しいと思う。

最初のユースケースをまず考えよう。 セキュリティ目的でデーモンをchroot()監獄に閉じ込めるユースケースだ。 まずはセキュリティツールとしてのchroot()はかなり疑わしい。というのも、chroot()は一方通行ではないからだ。 manページですら指摘しているとおりchroot()環境から脱出するのは比較的簡単なのだ。 その他いくつかのテクニックを組み合わせた場合にのみ、chroot()はいくらかセキュアにすることができる。 そのため、これは通常はchroot()自体をタンパー防止な方法でサポートするアプリケーションで特定のサポートをする必要があるのだ。 そのてっぺんにあるのは、chroot()されたサービスの深い理解がないと、chroot()環境を設定することが通常できないということである。例えば、chroot()内でサービスが実際に要求するコミュニケーションチャンネルをすべて利用可能にするために、ホストのツリーからどのディレクトリをbindでマウントすべきか知っていることだ。 これらをまとめると、セキュリティ目的でサービスをchroot()することはデーモン自身のCコードの中でやってしまうことがほとんど常にベストな方法だ。 開発者はchroot()を適切にセキュアにするベストな方法、最小限のファイルのセット、chroot()内部でデーモンが必要とするファイルシステムディレクトリが何であるかを知っている(あるいは少なくとも知っているべきだ)。 今日では、多くのデーモンがこれをおこなうことができるが、不幸なことに、通常のFedoraのインストールでデフォルトで動いているデーモンのうちこれをやっているのはたったの2つである。 AvahiとRealtimeKitである。 どちらも同じ、本当にスマートなヤツによって明らかに書かれている。Chapeauだ! (これは ls -l /proc/*/root をシステムで実行したら簡単に確かめられる)

さて、systemdはもちろん特定のデーモンをchroot()させて、通常のツールでその他と同じように管理する方法を提供している。 これはsystemdサービスファイルのRootDirectory=オプションでサポートされている。 次のは例だ。

[Unit]
Description=A chroot()ed Service

[Service]
RootDirectory=/srv/chroot/foobar
ExecStartPre=/usr/local/bin/setup-foobar-chroot.sh
ExecStart=/usr/bin/foobard
RootDirectoryStartOnly=yes

この例では、RootDirectory=ExecStart=で指定されたデーモンのバイナリーを実行する前にどこでchroot()をすべきかを設定している。 ExecStart=で指定されるパスはchroot()内部のバイナリーを指定している必要があり、ホストツリーのバイナリーへのパスでないこと(つまり、この例の場合、実行されるバイナリーはホストOSからは/srv/chroot/foobar/usr/bin/foobardとして見えること)に注意してほしい。 デーモンが起動される前にsetup-foobar-chroot.shというシェルスクリプトが実行される。このスクリプトの目的は必要とされるchroot環境を設定することだ。例えば、/procや類似のファイルシステムchroot環境の中にマウントするなど、サービスが必要なものに応じて設定する。 RootDirectoryStartOnly=スイッチを使うことで、ExecStart=で指定されたデーモンだけをchrootさせ、ExecStartPre=スクリプトディレクトリをbindマウントするために完全なOS階層へのアクセスする必要があるために、chrootに入れないといったことを設定できる(これらのスイッチに関する詳細はmanページを見てほしい)。 このようなユニットファイルを/etc/systemd/system/foobar.serviceに置けば、systemctl start foobar.serviceとタイプすることで、chroot()されたサービスを起動することができる。 systemctl status foobar.serviceで中を見ることもできる。 他のサービスと同様にサービスへアクセス可能で、chroot()されたという事実は、--SysVの場合と異なり--その監視や制御ツールとの関わり方は変わらない。 比較的新しいLinuxカーネルファイルシステム名前空間をサポートしている。これらはchroot()と似ているが、さらにもっと強力で、chroot()と同じようなセキュリティ問題からの影響を受けない。 systemdはファイルシステム空間でできることのサブセットをユニットファイル自身にもそのまま適用する。 たいていの場合、サブディレクトリで完全なchroot()環境をセットアップすることの、便利でシンプルな代替案となる。 ReadOnlyDirectories=InaccessibleDirectories=と、2つのスイッチによって、サービスに対するファイルシステム名前空間の監獄を設定することができる。 最初は、これはホストOSのファイルシステム名前空間と全く同じである。 これらのディレクティブにディレクトリを列挙することで、ホストOSのディレクトリやマウントポイントをデーモンに対し読み取り専用ないしは完全にアクセス不能なものとしてさえ、マークすることができる。 例:

[Unit]
Description=A Service With No Access to /home

[Service]
ExecStart=/usr/bin/foobard
InaccessibleDirectories=/home

このサービスはホストOSのファイルシステムツリー全体へのアクセスを持つが、唯一例外がある。/homeはサービスからは見えず、潜在的な攻撃者からユーザーのデータは守られる(これらのオプションの詳細はこちらを見てほしい)。

ファイルシステム名前空間は実際に多くの方法でchroot()のより良い代替となる。 最終的にAvahiとRealtimeKitはchroot()で置き換える形で名前空間を利用するようにアップデートされるべきだろう。

セキュリティのユースケースはもう十分だろう。 さて、もう一つのユースケースを見てみよう。デバッグやテスト、ビルド、インストールあるいはリカバリーのためにOSイメージを設定し、管理する場合だ。

chroot()環境は比較的簡単なことである。つまり、ファイルシステム階層を仮想化しているに過ぎない。 サブディレクトリへchroot()しても、プロセスはすべてのシステムコールへの完全なアクセスは持ち、すべてのプロセスをkillすることができ、それの動いているホストとすべてを共有している。 したがって、chroot()の中でOS(ないしはOSの一部)を実行することは危険なことである。というのも、ホストとゲストの隔離はファイルシステムに限定されており、それ以外の全てはchroot()の中から自由にアクセスすることができてしまう。 例えば、もしchroot()内部のディストリビューションをアップグレードし、パッケージスクリプトがSIGTERMをPID1に送って、initシステムの再実行をトリガーした場合、これは実際、これがホストOSで起きてしまうのだ! その最たるものが、SysVは共有メモリー、抽象名前空間のソケット、その他のIPCプリミティブがホストとゲストの間で共有されている。 OSのテスト、デバッグ、ビルド、インストール、回復のために完全にセキュアな隔離はおそらく必要ないものの、chroot()環境内部からホストOSの偶発的な変更を避けるための基本的な隔離はあるに越したことはない。パッケージスクリプトが実行するホストOSに影響を与えるかもしれないコードを知ることは決してない。

この用途でchroot()セットアップを扱うために、systemdは2つの機能を提供している。

まず最初にsystemctlはそれがchrootで実行されていることを検知する。 もしchrootで実行されていることを検出した場合、systemctl enablesystemctl disableとを除いて、systemctlの操作はほとんどNOP(何もしない)になる。 もしパッケージインストールスクリプトがこれら2つのコマンドを実行した場合、ゲストOSの中でサービスは有効になる。 しかし、systemctl restartのようなコマンドをパッケージアップグレードプロセスに含んでいるパッケージインストールスクリプトがあるとして、これはchroot()環境では一切の効果を持たない。

さらに重要なことに、systemdはout-of-the-boxな状態(訳注: インストール直後の何も設定しない状態)でchroot(1)の強化版として機能するsystemd-nspawnが提供されている。これはファイルシステムおよびPID名前空間を使うことで、シンプルで軽量なコンテナをファイルシステムツリーでブートさせることができる。 これはchroot(1)とほとんど同じように使うことができる。違いはホストOSからの隔離がより完全であり、かなりセキュアで使いやすくもあることである。 事実、systemd-nspawn完全なsystemdないしはSysVinitのOSを1つのコマンドでコンテナ内にブートさせることが可能である。 PIDを仮想化しているため、コンテナ内のinitシステムはPID1として活動することが可能であり、そのため、その仕事を通常通りおこなうことができる。 chroot(1)と比較して、このツールは/proc/sysを黙示的にマウントする。

次は3つのコマンドでFedoraのマシンでnspawnコンテナ内にDebian OSをブートする例である。

# yum install debootstrap
# debootstrap --arch=amd64 unstable debian-tree/
# systemd-nspawn -D debian-tree/

これはOSディレクトリツリーをブートストラップし、単純にその中のシェルを起動させている。 もしコンテナ内に完全なシステムをブートしたい場合、このようなコマンドを使う。

# systemd-nspawn -D debian-tree/ /sbin/init

素早く起動したあと、コンテナにブートした完全なOSの内部のシェルプロンプトに移る。 コンテナはその外にあるプロセスは一切見ることができない。 ネットワーク設定は共有するが、変更することはできない(例外はブート時の2つのEPERMだが、これは致命的なものとはならない)。 /sys/proc/sysのようなディレクトリはコンテナ内でも利用可能だが、コンテナがカーネルやハードウェア設定を修正できないようにするため、読み取り専用でマウントされている。 しかし、注意してほしいのはこれはホストOSを偶発的なホストOSのパラメーターの変更から守るということである。 コンテナ内のプロセスは手動でファイルシステムを読み書き可能で再マウントし、加えたいだけの変更を加えることが可能である。

さて、systemd-nspawnのどこが偉大なのかもう一度まとめよう

  1. 簡単に使える。手動で/proc/syschroot()環境にマウントする必要がないこと。このツールはこれをやってくれ、コンテナが終了するときにはカーネルが自動でこれをきれいにしてくれる。
  2. 隔離がより完全で、コンテナ内部空の偶発的な変更からホストOSを守ってくれること。
  3. コンテナの中に完全なOSをブートさせることができること。たった1つの寂しいシェルではない。
  4. 実際に小さく、systemdがインストールされているならどこにでもインストールされていること。複雑なインストールやセットアップは不要だ。

systemd自体もこのようなコンテナでうまく動作するための修正が加えられている。 例えば、シャットダウンするときにコンテナで動いていることを検知すれば、systemdは最後の段階で、reboot()の代わりにexit()だけをコールする。

systemd-nspawnは完全なコンテナソリューションでないことに注意してほしい。 それが必要ならLXCがより良い選択だ。 LXCは同じ基礎となるカーネルテクノロジーを利用しているが、ネットワーク仮想化を含むより多くを提供している。 もし望むなら、systemd-nspawnはコンテナソリューション(分野で)のGNOME 3である。滑らかでほとんど努力せずに簡単に使える -- しかし、設定オプションはほとんどない。 一方で、LXCはよりKDEのようである。コードの行数以上の設定オプションがある。 systemd-nspawnを私が書いたのは、テスト、デバッグ、ビルド、インストール、リカバリーをカバーするためである。 これがsystemd-nspawnを利用するべきものであるし、これが得意なものだし、これがchroot(1) のより良い代替となるところである。

さて、そろそろ終わりにしよう。すでに長くなってしまっている。 以下が、このブログストーリーから家に持って帰ってもらいたいものだ。

  1. セキュアなchroot()はプログラムのCのソースコードでネイティブにやるのが一番。
  2. ReadOnlyDirectories=InaccessibleDirectories=は完全なchroot()環境への適切な代替手段である。
  3. RootDirectory=は特定のサービスをchroot()させたいなら、友となる。
  4. systemd-nspawnはすごいことができる。
  5. chroot()は不自由だが、ファイルシステム名前空間は全面的にエリートである。

Fedora15でこのすべてが利用できる。

さて、今日はここまで。次回また会おう。

きょうのsystemd: 3つのレベルの「オフ」

概要

これは

0pointer.de

の翻訳。

3つのレベルの「オフ」

systemdではサービス(ないしは、その他のユニット)を3つのレベルでオフにできる。 これについて見てみよう。

1.サービスを停止することができる。これは単純にサービスのインスタンスを停止させることであり、それ以外はほとんど何もない。 アクティベーションのいくつかの方法(例えば、手動によるアクティベーション、ソケットアクティベーション、バスアクティベーション、システム起動時のアクティベーション、あるいは、ハードウェアの抜き差しによるアクティベーション)によって、停止させた後に、サービスが再び要求されれば、そのサービスは起動する。 サービスを止めるということは非常に単純で、一時的なもので、表層的な操作なのである。 これはNTPサーバーを停止させる方法の例である:

$ systemctl stop ntpd.service

これはざっくり言うと、SysVで動いている多くのシステムで利用可能な、次の伝統的なコマンドと同等だ:

$ service ntpd stop

実際、Fedora15では、後者のコマンドを実行すれば、透過的に前者へと変換されるようになっている。

2.サービスを無効にすることができる。 これはサービスがそのアクティベーションのトリガーによりフックされないようにする。 これは、サービスによっては、起動時にアクティベーションされたり、ソケットやバス、ハードウェアの抜き差し(その他、そのサービスに適用されているトリガー)でサービスがもはやアクティベートされないことを意味する。 しかし、必要であれば、手動で起動させることはまだできる。 もしすでに起動済みのインスタンスがある場合、サービスを無効にしてもそれを止めるような効果はない。 これはサービスを無効にする方法の例である:

$ systemctl disable ntpd.service

伝統的なFedoraのシステムでは、これは、ざっくり言うと次のコマンドに等しい:

$ chkconfig ntpd off

ここでも、Fedora15では後者のコマンドは、必要に応じて、透過的に前者へと変換される。

しばしば、サービスの停止と無効化を一緒におこないたい場合がある。というのも、現在のインスタンスを取り除いて、(手動でトリガーする場合をのぞいて)確実にそれが再び起動しないようにするためである。

$ systemctl disable ntpd.service
$ systemctl stop ntpd.service

これらのコマンドは例えば、Fedora上のsystemdサービスのパッケージを取り除く(訳注: deinstall)ときに使われる。

サービスの無効化は永続的な変更である。 元の状態に戻すまで、再起動を繰り返しても、その状態は保たれる。

3.サービスをマスクすることができる。 これはサービスの無効化と似ているが、極端である。 これは、サービスが自動的にもはや起動しなくなるようにするだけでなく、手動でさえもサービスを起動できなくさせるようにする。 これはsystemdのちょっとした隠し機能である。というのも、一般に使うものでもないし、ユーザーを混乱させてしまうかもしれないためだ。 しかし、やり方はこうだ:

$ ln -s /dev/null /etc/systemd/system/ntpd.service
$ systemctl daemon-reload

サービスファイルを/dev/nullへのシンボリックリンクにすることで、systemdに件のサービスを決して起動させず、完全に実行をブロックするように伝えることができる。 /etc/systemd/systemにあるユニットファイルは/lib/systemd/systemにある同じ名前のファイルを上書きする。 前者のディレクトリは管理者の領域であり、後者はパッケージマネージャーの領域である。 /etc/systemd/system/ntpd.serviceシンボリックリンクを差し込むことで、systemdがアップストリームが同梱しているサービスファイル/lib/systemd/system/ntpd.serviceを決して使わないようにすることになる。

systemdは/dev/nullシンボリックリンクの張られたユニットを認識することができ、これをmaskedと表示する。 手動で(例えばsystemctl start経由で)サービスを起動させようとしても、エラーで失敗するだろう。

SysVのシステムでは(公式には)似たようなトリックは存在しない。 しかし、いくつかの非公式のハックは存在していて、例えば、initスクリプトを修正して、頭の方にexit 0を置いたり、実行ビットを取り除いたりする方法がある。 しかし、これらの解決方法は様々な欠点がある。例えば、これらの方法はパッケージマネージャーからの妨害を受けてしまう。

サービスのマスクは永続的な変化で、サービスの無効化とかなり似ている。

さて、3つのレベルでサービスをオフにする方法を学んだが、ただ1点、問題が残されている。つまり、どうやってオンに戻すのか? まぁ、これはかなり対称的なものだ。 systemctl startを使ってsystemctl stopを元に戻す。 systemctl enableを使って、systemctl disableを元に戻す。 rmを使ってlnを元に戻す。

今日はここまで。読んでくれてありがとう!

#きょうのsystemd : サービスを停止させる (systemd for Administrators, Part 4)

概要

これは

0pointer.de

の翻訳。

サービスを停止させる

システムデーモンを停止させるのは簡単、本当?

確かに、デーモンが単一のプロセスで構成されている限り、これは確かにある程度は正しいだろう。 killall rsyslogdとタイプすれば、syslogデーモンはどこかへ行く。 しかし、このようにプロセスを止めるのはちょっと汚いやりかただ。 というのも、このように呼び出されることになったプロセスをこれはすべて停止させ、そこには偶然そのような名前を付けてしまった不幸なユーザーのプロセスも含まれるかもしれないからだ。 ほんの少し正しいバージョンは.pidファイルを読み込む方法で、例えば

kill `cat /var/run/syslogd.pid`

というものだ。 この時点ですでにかなり進むことができたが、それでもまだ、これが本当に我々のやりたいことなのだろうか?

多くの場合で、実際にはそうではない。 Apacheやcrondあるいはatdといったサービスを考えよう。これらはたいていの操作の一部として、子プロセスをスポーンさせる。 任意の、ユーザー設定可能な子サービスであり、例えば、cronやatのジョブ、CGIスクリプト、完全なアプリケーションサーバーといった場合もある。 apache、crond、atdのメインプロセスを停止させると、子プロセスも一緒に落ちるかもしれなければ、そうでないこともあり、残り続けるか停止するかはそれらのプロセス次第なのである。 基本的に、Apacheをターミネートさせることは、そのCGIスクリプトを残留させ、initの子として再割り当てをし、追跡を困難なものにするのである。

systemdはこれをレスキューする。 systemctl killであるサービスのすべてのプロセスに対し、簡単にシグナルを送ることができる。 例:

# systemctl kill crond.service

これはSIGTERMがcrondサービスのメインプロセスのみならず、すべてのプロセスに届けられることを保証する。 もちろん、望むなら違うシグナルを送ることも可能である。 例えば、あなたのお行儀が悪いなら、SIGKILLをすぐに送ってしまおうと思うだろう。

# systemctl kill -s SIGKILL crond.service

これで望み通りに、サービスが何回フォークしようと、ダブルフォークやフォークボミングで監視下から逃れようと、サービスはその全体が虐殺される。

ときどき、サービスのメインプロセスに特定のシグナルを送ることだけが必要となることがある。例えば、SIGHUP経由でリロードをトリガーしたいといった理由によるものだ。 PIDファイル経由でおこなう代わりに、もっと簡単な方法がある。

# systemctl kill -s HUP --kill-who=main crond.service

そして再び、systemdでサービスを停止させることについて何がそんなに新しくてファンシーなのか? Linuxで初めて、我々がそれを実際、適切におこなうことができている。 以前のソリューションは常にデーモン頼みであり、デーモンが自分自身がターミネートするときに自分がスポーンしたすべてのものを停止させるよう実際に協力してくれるかに頼っていた。 しかし、普通、SIGTERMやSIGKILLを使いたい場合に、そのようにすることになる。それはデーモンが実際に適切に協力してくれないからなのだ。

これがsystemctl stopとどのように関係してくるのか? killはグループにあるすべてのプロセスに直行し、シグナルを送る。 しかし、stopはサービスをシャットダウンする公式の設定を通過する。例えば、サービスファイルのExecStop=で設定された停止コマンドを実行する。 通常はstopで十分なはずである。 killはより荒っぽいバージョンであり、実行するサービスの公式のシャットダウンコマンドを使いたくなかったり、サービスを他の方法できれいにしたりハングさせたりする場合に使われる。

(ところで、人に寄ってはSIGプレフィックスを-sスイッチに付けたり付けなかったりするが、どちらでも動く。)

Linuxでサービスを適切に停止させる方法なしでここまでやってきたのにはちょっと驚く。 systemdはこれを適切におこなうことを、初めて可能にするものなのである。

#きょうのsystemd: Surface Proを持ち歩いている人はサーバーを持ち歩いていると言おう

完全にネタ。

hostnamed needs a quirk database for chassis type · Issue #7390 · systemd/systemd · GitHub

レナートがタイトルを変えちゃってるけど、もともとは "hostnamectl reports chassis-type as server for a Surface Pro tablet(hostnamectlがSurface Proタブレットの筐体をサーバーと報告する)"

#きょうのsystemd : (翻訳) どうやったらSysV initのスクリプトをsystemdのサービスファイルに換えられるのか? (systemd for Administrators, Part 3)

概要

これは

systemd for Administrators, Part III

の翻訳。

どうやったらSysV initのスクリプトをsystemdのサービスファイルに換えられるのか?

伝統的にUnixLinuxのサービス(デーモン)はSysV init スクリプト経由で起動されている。 これらはBourne Shellスクリプトであり、通常は/etc/rc.d/init.dといったディレクトリに置かれている。 これらのスクリプトstartstoprestartといった数個の標準化された引数(動詞)を与えられて呼び出されたときに、件のサービスの制御、すなわち、起動、停止、再起動をおこなう。 起動の場合、スクリプトは通常、デーモンのバイナリーの呼び出しに関わり、バックグラウンドのプロセスをフォークする(より正確にはデーモン化させる)。 これらは非常に柔軟性がある(なんといったって、これらは単なるコードである)が、いくつかのことはシェルスクリプトでは適切におこなうことが非常に難しい。 具体的には、実行をパラレル化するように並べる、適切にプロセスを監督する、あるいは、非常に細かいところまで実行のコンテクストを設定するといったことである。 systemdはこれらのシェルスクリプトとの互換性を提供しているが、ここで指摘した短所ゆえに、インストールされているすべてのデーモンがネイティブなsystemdサービスファイルをインストールすることが推奨される。 また、ディストリビューションに合うよう調整しなければいけないSysV initスクリプトと比べると、systemdのサービスファイルはsystemdを動かしているディストリビューション(最近どんどん増えている)ならどの種類でも互換性がある。 となると、次に続くのは、どのようにSysV initスクリプトを取り上げ、ネイティブなsystemdサービスファイルに翻訳するかの完結なガイドということになる。 理想的には、上流のプロジェクトがそれらのtarballにsystemdサービスファイルを同梱しインストールすることだ。 もし、SysVスクリプトガイドラインに従ってうまく変換しているならば、そのファイルを上流にパッチとして送ってしまうのはいい考えだ。 このようなパッチを準備する方法は、後の投稿で論じるつもりだが、現時点で十分に言えることは、systemdに同梱されているdaemon(7)のマニュアルページには、これに関する有益な情報が多く含まれているということだ。

閑話休題。 一例として、ここではABRTデーモンのinitスクリプトをsystemdサービスファイルに変換することにしよう。 ABRTは各Fedoraのインストールの標準的なコンポーネントであり、Automatic Bug Reporting Toolの略だ。 何をするかはその名のとおりで、クラッシュダンプを集めるサービスである。 これのSysVスクリプトはもうここに上げてある。

こういったスクリプトを変換するときの第一歩はこれを読むこと(驚け、驚け!)で、たいていは長いこのスクリプトから有益な情報を抽出することだ。 ほとんどすべてのケースで、スクリプトはほとんど決まり文句のコードで構成されている。 このコードはすべてのinitスクリプトで一致ないしは少なくともかなり似ており、通常はあっちからこっちへのコピペである。 さて、上でリンクを張ったスクリプトから興味深い情報を抽出してみよう。

  • サービスの説明文は「クラッシュしたアプリを検出するデーモン」である。 見たらわかるとおり、ヘッダーのコメントには余分な数の説明の文字列が含まれている。 これらのいくつかは実際のサービスを説明しているというよりかは、それをスタートさせるinitスクリプトについての説明をしている。 systemdサービスも説明文は含んではいるが、それが説明すべきなのはサービスのことであり、サービスファイルのことではない。

  • LSBヘッダー*1には依存関係の情報が含まれている。 systemdはそのソケットベースのアクティベーションの設計のため、通常は手動で依存関係を設定する必要はまったく(ないしはほんの少しだけしか)ない(ソケットアクティベーションの詳細はオリジナルのアナウンスのブログ投稿を見てほしい)。 今回の場合は$syslogに依存(これはabrtdがsyslogデーモンを必要としているということがエンコードされている)することが唯一、価値のある情報である。 ヘッダーがその他の依存関係($local_fs)を挙げているが、これはsystemdには余分な情報である。 というのも、通常のシステムサービスは常にすべてのローカルファイルシステムが利用可能な状態で起動されるためだ。

  • LSBヘッダーはこのサービスがランレベル3(マルチユーザー)と5(グラフィカル)で起動するべきことを示唆している。

  • デーモンのバイナリーは/usr/sbin/abrtdである。

準備はととのった。 この115行のシェルスクリプトの残りの行は全部、単なる決まり文句か余分なコードだ。 このコードで、スタートアップの同期やシリアル化(つまり、ロックファイルを検討するコード)を取り扱ったり、状態メッセージを出力(つまり、echoを呼び出すコード)したり、単に動詞をパース(つまり大きなcaseブロック)したりしている。

上で抽出した情報から、もう、systemdサービスファイルを書くことができる。

[Unit]
Description=Daemon to detect crashing apps
After=syslog.target

[Service]
ExecStart=/usr/sbin/abrtd
Type=forking

[Install]
WantedBy=multi-user.target

このファイルの中身についてちょっと説明しよう。 [Unit]セクションにはサービスに関する汎用的な情報が含まれている。 systemdはシステムサービスを管理するだけでなく、デバイスやマウントポイント、タイマー、あるいはその他のシステムのコンポーネントも管理している。 systemdでぇあこれらのオブジェクトすべてに汎用的な用語を与えており、それはユニットという。 そして[Unit]セクションにはユニットに関して、サービスだけでなく、systemdが維持しているその他のユニットタイプでも適用可能かもしれない情報をまとめている。 今回の場合、次のようなユニットの設定をおこなっている。 説明の文字列を設定し、デーモンがSyslog*2の後に開始されるように設定した。これはオリジナルのinitスクリプトのLSBヘッダーに書かれているのと同じようなことだ。 このsyslog依存関係のために、After=という種類の依存関係をsystemdのユニットであるsyslog.targetに対し作った。 後者はsystemdでは特別なターゲットユニットであり、これはsyslog実装を制御するための標準化された名前である。 これら標準化された名前についての詳細はsystemd.special(7)を見てほしい。 After=という種類の依存関係は示唆された順番をエンコードしているにすぎず、実際にabrtdが起動しするときにsyslogを起動させようとするものではないことに注意してほしい。 そしてこれが実際に我々が求めていることあることにも注意してほしい。abrtdは実際にsyslogが動いていなかったとしてもうまく動くためだ。 とはいえ、2つとも起動していれば(そして、普通はそうだ)、この2つを含む順序はこの依存関係によって制御される。

次のセクションは[Service]であり、これにはサービス自身の情報が含まれている。 これにはサービスにだけ適用される、すべての情報が含まれており、systemdが維持している他の種類のユニット(マウントポイント、デバイス、タイマー、……)にはない。 ここでは2つの設定が使われている。 ExecStart=はサービスが起動したときに実行されるべきバイナリのパスが書かれている。 Type=では、サービスがどのようにしてinitシステムに自身が起動を終えたことを伝えるかが設定されている。 伝統的なUnixデーモンではバックグラウンドデーモンのフォークと初期化が終わってから、親プロセスに戻ることでこれを実現しているため、ここではforkingに設定している。 これはsystemdに対し、スタートアップバイナリーが戻ってくるまで待ち、その後、デーモンプロセスを動かしているプロセスを考慮するように伝えるのである。

最後のセクションは[Install]である。 これは示唆されるインストレーションがどのように見えるべきかについての情報を含んでいる。 つまり、どの環境とどのトリガーに寄ってサービスがスタートすべきかである。 今回の場合、簡単に、このサービスはmulti-user.targetユニットがアクティブになったら開始されるべきと言っている。 これは特別なユニット(既述)であり、基本的には、古典的なSysVのランレベル3*3の役割を引き受ける。 WantedBy=という設定は実行時のデーモンにはほとんど効果がない。 これはsystemctl enableコマンドによってのみ読み込まれる。 このコマンドはsystemdでサービスを有効にするのに推奨されている方法だ。 このコマンドは我々の小さいサービスが、すべての通常起動*4でオンになるmulti-user.targetが要求されればすぐに自動的にアクティブになるようにするものである。

中身の説明はここまでだ。 さて、もう機能する最小限のsystemdサービスファイルを手に入れた。 テストするため、これを/etc/systemd/system/abrtd.serviceにコピーし、systemctl daemon-reloadを実行しよう。 これでsystemdにこのサービスファイルの存在を気づかせ、systemctl start abrtd.serviceを使ってサービスを起動させられるようになる。 systemctl status abrtd.serviceを使って状態を確かめることができる。 systemctl stop abrtd.serviceでサービスを再び停止させられる。 最後に、systemctl enable abrtd.serviceこれを有効にし、次回以降の起動時にデフォルトでこのサービスがアクティブになるようにすることができる。

上のサービスファイルは、十分かつ、基本的な1対1のSysV initスクリプトの翻訳(機能その他)なのだが、まだ改善の余地がある。 ここで、ちょっとだけアップデートしてみる。

[Unit]
Description=ABRT Automated Bug Reporting Tool
After=syslog.target

[Service]
Type=dbus
BusName=com.redhat.abrt
ExecStart=/usr/sbin/abrtd -d -s

[Install]
WantedBy=multi-user.target

さて、何が変わっただろうか? 答えは2つだ。説明の文字列をちょっと改善した。 しかしもっと重要なのは、サービスの種類をdbusに変えて、サービスのD-Busバス名を設定したことだ。 なぜこうしたのか? すでに述べたとおり、伝統的なSysVサービスはスタートアップ後にデーモン化する。 これは通常ダブルフォークとすべてのターミナルからのデタッチが起こる。 これはデーモンがスクリプト経由で起動されるときには便利であり、必須なのだが、systemdのような適切なプロセスのベビーシッターが使われている場合、逆効果なのに加え、不必要(で遅い)のだ。 その理由はフォークされたデーモンプロセスは通常、systemdによって起動されたオリジナルのプロセスとほとんど関係を持たないからであり、それ故、systemdが、フォークが終わった後に、サービスに属しているプロセスのどれがメインプロセスでどれが単なる補助のサービスなのかの見分けるのが難しいためだ。 だが、この情報は高度なベビーシッター、つまり、プロセスの監督、異常終了時の自動的な再スポーン、クラッシュおよび終了コード情報の収集、などを実装するには重要なことだ。 systemdがデーモンのメインプロセスを見つけるのが簡単になるようにサービスタイプをdbusへ変更した。 このサービスタイプのセマンティクスは、サービスの初期化の最終段階でD-Busシステムバス上に名前を取るすべてのサービスに適切なものである*5。 ABRTはその1つである。 この設定によりsystemdはABRTプロセスをスポーンするが、(訳注: 今度の)そのプロセスはもはやフォークしない(これはデーモンに与えられた-dsスイッチで設定されている)し、systemdはバス上にcom.redhat.abrt`が現れたら、すぐにサービスが完全に起動したとみなすようになる。 この方法でsystemdがスポーンしたプロセスはデーモンのメインプロセスであり、systemdはデーモンが完全にスタートアップした時を知る信頼できる方法を手に入れ、systemdはそれを簡単に監督することができるのだ。

それだけのことだ。 オリジナルのSysV initスクリプトでは115行でエンコードされていた以上の情報を10行でエンコードした、簡単なsystemdのサービスファイルを手に入れた。 そして、実はまだsystemdが提供するより多くの機能を使って、さらに改善する余地がたくさんある。 例えば、Restart=restart-alwaysを設定し、systemdにサービスが死んでしまったときにこれを自動的に再起動するように伝えることができる。 あるいはOMMScoreAdjust=0500を使うことで、カーネルにお願いだから、このプロセスをOOM Killerが大損害を与えるときにこのプロセスだけは見逃してくれるよう頼むことができる。 あるいはCPUSchedulingPolicy=idleを使うことで、abrtdプロセスのクラッシュダンプをバックグラウンドでのみ取得し、カーネルに何であれ他に動いていて、CPU時間を必要としているものにパフォーマンスを与えることができる。

ここで言及した設定オプションについての詳細情報は対応するmanページ、systemd.unit(5)systemd.service(5)systemd.exec(5)を見てほしい。 あるいはsystemdのmanページすべてをブラウズするのでも良い。

もちろん、すべてのSysVスクリプトがこれみたいに簡単に変換できるわけではない。 だが、嬉しいことに、実際には大半が簡単に変換できることがわかっている。

今日はここまで。次回の投稿ですぐまた会おう。

*1:原文注: initスクリプトのLSBヘッダーはSysV initスクリプトのトップにあるコメントブロックでサービスについてのメタデータを含ませる慣習であり、Linux Standard Baseで定義されている。これはディストリビューション間でinitスクリプトを標準化することを意図したものである。ほとんどのディストリビューションはこのスキームを採用しているが、ヘッダーの取り扱い方はディストリビューション間でかなり違っており、実際、各ディストリビューションごとにinitスクリプトを調整する必要がある。このようにLSBの仕様は自分がした約束を決して守っていないのである。

*2:原文注: 厳密なことをいうと、この依存関係は実はここで書く必要はなかった。というのも、Syslogデーモンがソケットアクティベーション可能なシステムなら、これは余分なのだ。モダンなsyslogシステム(例えばv5)はソケットアクティベーション可能なよう上流でパッチが当てられている。もしinitシステムがAfter=syslog.targetの設定を使っているなら、依存関係は余分であり、暗黙のものとなる。だが、アップデートされていないsyslogサービスとの互換性維持のために、ここではこの依存関係を含ませた

*3:原文注: 少なくともFedoraではそのように定義されていたものだ。

*4:原文注: systemdではグラフィカルなブートアップ(graphical.targetつまり、SysVランレベル5の役目を引き受ける)はコンソールのみのブートアップ(multi-user.targetつまりランレベル3のようなもの)の暗黙的なスーパセットである。これは後者でサービスを起動させれば、前者でもそのサービスが上がってくることを意味する。

*5:実際、デフォルトのFedoraインストールのサービスの大多数はいまやスタートアップ後にバス上に名前を取る

いろいろ新調した

昨日、今日とで色々新調した。

昨日はユニクロに服を買いに行った。最低限の冬の準備はできたと思う。

今日は友人に会いに秋葉原へ。 ついでに、つくもへSSDを見に行った。

これまで使っていたSSDIntelの330で60GBと120GBで、前者を/、後者を/homeとしてマウントしていた。 とても調子よく動いているが、買ったのが5年以上前のため、そろそろ不安になってきた。 あと、データドライブで120GBは手狭になってきているので、60GBを外して、120GBを/に、新しいSSD/homeにとスライドさせることにした。

新しいSSDはこれ。

TLCなのが気になるけど、まぁ、ええかということで。

rsync -av/homeをまるごと移したけど、SSD同士なので超早かった。

現役からは引退させる60GBも何らかの形で使いたいので、これをポチった。

透明でかっこいい。

あと、IntelNICも買った。

インテル Gigabit CT Desktop Adapter EXPI9301CT

インテル Gigabit CT Desktop Adapter EXPI9301CT

実は、デスクトップ機にもかかわらず、ずっと無線LANPCI-Eカードを挿してネットに繋いでいた。 実家にいた頃はそのほうが便利だったから仕方がない。

ただ、

  • 実家から出たので、無線である必要はなくなった
  • systemd-nspawnで遊ぶのに、MACVLANをすると、無線ではちょっと困ることがわかった

ということで、以前より有線にしたいなとは思っていた。

ところが、IvyBridgeのCPU + H77のマザボなので、オンボのNICが蟹さん。 「なので」というのは、オンボNICIntelはHaswell世代のマザボは多くがそうだったり、Ivy世代でもチップセットが最上位のZ77だとそうだったり、という印象・記憶。 色々惜しい。

ネットで見て記憶していた値段より安かったので、エイヤということで買った。若干早い気がする。気だけかも。

#きょうのsystemd: (翻訳) どのサービスがどのプロセスを動かしてる? (systemd for Administrators, Part 2)

概要

systemd for Administrators, Part II

の翻訳。

どのサービスがどのプロセスを動かしている?

ほとんどのLinuxシステムで、実質的にはデフォルトでかなりの数のプロセスが動かされている。 どのプロセスが動いていて、それが何の、どこに属しているのかを知ることはどんどん難しくなっている。 サービスのいくつかは1組のワーカープロセスを維持していることすらあり、これらは、たいてい簡単には見分けのつかない数多くの追加のプロセスでpsの出力を乱雑にしている。 デーモンが任意のサードパーティーのプロセスを、ApacheCGIプロセスでやっていたり、cronがユーザージョブでやっているように、動かそうものなら、状況はさらに複雑になる。

この状況へのほんのわずかな対処になるのは、たいてい、プロセスの継承ツリーで、これはps xafによって表示されるようなものだ。 だが、これは普通、信頼できるものではない。 というのも、親プロセスが死んだプロセスはPID 1を親に設定し直すためであり、したがって、継承に関するすべての情報が失われてしまう。 プロセスが「ダブルフォーク」している場合、このプロセスはそれを開始したプロセスとの関係も失ってしまうこととなる。(これは実際には特徴であり、伝統的なUnixのデーモン化のロジックに依存している。) さらに、プロセスはPR_SETNAMEargv[0]をパッチすることにより、自由に名前を変えることができ、これもまた、プロセスを識別するのを難しくしている。 事実、プロセスはこの方法で管理者とかくれんぼを楽しくやっているのだ。

systemdではサービスから名前を取ったコントロールグループにスポーンされたすべてのプロセスを置いている。 コントロールグループ(あるいはcgroup)は基本の「き」として、簡単にプロセスのグループなのである。そして、プロセスのグループは階層的に、別個にラベル付けされて、整理されるのである。 プロセスが他のプロセスをスポーンしたら、その子プロセスは自動的に親プロセスのcgroupのメンバーとなる。 したがって、cgroupsはプロセスに対し、それが属しているサービスに沿ってラベルをつける効果的な方法の一つとして使うことができ、どれだけフォークしても名前を変えても、そのラベルからサービスを逃さないようにすることができるのである。 さらに、これは安全にサービスとそれが作り出したすべてのプロセスを安全に停止させるのに使うことができる。ここでも、プロセスは逃れることができない。

さて、今日の投稿では、2つのコマンドを紹介しようと思う。 これらはシステムのサービスとプロセスに関係して、使うことができるものだ。 最初はよく知られたpsコマンドなのだが、これはcgroupの情報をプロセスの他の詳細情報と合わせて表示することができるようにアップデートされてきてる。 こんな感じだ

$ ps xawf -eo pid,user,cgroup,args
  PID USER     CGROUP                              COMMAND
    2 root     -                                   [kthreadd]
    3 root     -                                    \_ [ksoftirqd/0]
[...]
 4281 root     -                                    \_ [flush-8:0]
    1 root     name=systemd:/systemd-1             /sbin/init
  455 root     name=systemd:/systemd-1/sysinit.service /sbin/udevd -d
28188 root     name=systemd:/systemd-1/sysinit.service  \_ /sbin/udevd -d
28191 root     name=systemd:/systemd-1/sysinit.service  \_ /sbin/udevd -d
 1096 dbus     name=systemd:/systemd-1/dbus.service /bin/dbus-daemon --system --address=systemd: --nofork --systemd-activation
 1131 root     name=systemd:/systemd-1/auditd.service auditd
 1133 root     name=systemd:/systemd-1/auditd.service  \_ /sbin/audispd
 1135 root     name=systemd:/systemd-1/auditd.service      \_ /usr/sbin/sedispatch
 1171 root     name=systemd:/systemd-1/NetworkManager.service /usr/sbin/NetworkManager --no-daemon
 4028 root     name=systemd:/systemd-1/NetworkManager.service  \_ /sbin/dhclient -d -4 -sf /usr/libexec/nm-dhcp-client.action -pf /var/run/dhclient-wlan0.pid -lf /var/lib/dhclient/dhclient-7d32a784-ede9-4cf6-9ee3-60edc0bce5ff-wlan0.lease -
 1175 avahi    name=systemd:/systemd-1/avahi-daemon.service avahi-daemon: running [epsilon.local]
 1194 avahi    name=systemd:/systemd-1/avahi-daemon.service  \_ avahi-daemon: chroot helper
 1193 root     name=systemd:/systemd-1/rsyslog.service /sbin/rsyslogd -c 4
 1195 root     name=systemd:/systemd-1/cups.service cupsd -C /etc/cups/cupsd.conf
 1207 root     name=systemd:/systemd-1/mdmonitor.service mdadm --monitor --scan -f --pid-file=/var/run/mdadm/mdadm.pid
 1210 root     name=systemd:/systemd-1/irqbalance.service irqbalance
 1216 root     name=systemd:/systemd-1/dbus.service /usr/sbin/modem-manager
 1219 root     name=systemd:/systemd-1/dbus.service /usr/libexec/polkit-1/polkitd
 1242 root     name=systemd:/systemd-1/dbus.service /usr/sbin/wpa_supplicant -c /etc/wpa_supplicant/wpa_supplicant.conf -B -u -f /var/log/wpa_supplicant.log -P /var/run/wpa_supplicant.pid
 1249 68       name=systemd:/systemd-1/haldaemon.service hald
 1250 root     name=systemd:/systemd-1/haldaemon.service  \_ hald-runner
 1273 root     name=systemd:/systemd-1/haldaemon.service      \_ hald-addon-input: Listening on /dev/input/event3 /dev/input/event9 /dev/input/event1 /dev/input/event7 /dev/input/event2 /dev/input/event0 /dev/input/event8
 1275 root     name=systemd:/systemd-1/haldaemon.service      \_ /usr/libexec/hald-addon-rfkill-killswitch
 1284 root     name=systemd:/systemd-1/haldaemon.service      \_ /usr/libexec/hald-addon-leds
 1285 root     name=systemd:/systemd-1/haldaemon.service      \_ /usr/libexec/hald-addon-generic-backlight
 1287 68       name=systemd:/systemd-1/haldaemon.service      \_ /usr/libexec/hald-addon-acpi
 1317 root     name=systemd:/systemd-1/abrtd.service /usr/sbin/abrtd -d -s
 1332 root     name=systemd:/systemd-1/getty@.service/tty2 /sbin/mingetty tty2
 1339 root     name=systemd:/systemd-1/getty@.service/tty3 /sbin/mingetty tty3
 1342 root     name=systemd:/systemd-1/getty@.service/tty5 /sbin/mingetty tty5
 1343 root     name=systemd:/systemd-1/getty@.service/tty4 /sbin/mingetty tty4
 1344 root     name=systemd:/systemd-1/crond.service crond
 1346 root     name=systemd:/systemd-1/getty@.service/tty6 /sbin/mingetty tty6
 1362 root     name=systemd:/systemd-1/sshd.service /usr/sbin/sshd
 1376 root     name=systemd:/systemd-1/prefdm.service /usr/sbin/gdm-binary -nodaemon
 1391 root     name=systemd:/systemd-1/prefdm.service  \_ /usr/libexec/gdm-simple-slave --display-id /org/gnome/DisplayManager/Display1 --force-active-vt
 1394 root     name=systemd:/systemd-1/prefdm.service      \_ /usr/bin/Xorg :0 -nr -verbose -auth /var/run/gdm/auth-for-gdm-f2KUOh/database -nolisten tcp vt1
 1495 root     name=systemd:/user/lennart/1             \_ pam: gdm-password
 1521 lennart  name=systemd:/user/lennart/1                 \_ gnome-session
 1621 lennart  name=systemd:/user/lennart/1                     \_ metacity
 1635 lennart  name=systemd:/user/lennart/1                     \_ gnome-panel
 1638 lennart  name=systemd:/user/lennart/1                     \_ nautilus
 1640 lennart  name=systemd:/user/lennart/1                     \_ /usr/libexec/polkit-gnome-authentication-agent-1
 1641 lennart  name=systemd:/user/lennart/1                     \_ /usr/bin/seapplet
 1644 lennart  name=systemd:/user/lennart/1                     \_ gnome-volume-control-applet
 1646 lennart  name=systemd:/user/lennart/1                     \_ /usr/sbin/restorecond -u
 1652 lennart  name=systemd:/user/lennart/1                     \_ /usr/bin/devilspie
 1662 lennart  name=systemd:/user/lennart/1                     \_ nm-applet --sm-disable
 1664 lennart  name=systemd:/user/lennart/1                     \_ gnome-power-manager
 1665 lennart  name=systemd:/user/lennart/1                     \_ /usr/libexec/gdu-notification-daemon
 1670 lennart  name=systemd:/user/lennart/1                     \_ /usr/libexec/evolution/2.32/evolution-alarm-notify
 1672 lennart  name=systemd:/user/lennart/1                     \_ /usr/bin/python /usr/share/system-config-printer/applet.py
 1674 lennart  name=systemd:/user/lennart/1                     \_ /usr/lib64/deja-dup/deja-dup-monitor
 1675 lennart  name=systemd:/user/lennart/1                     \_ abrt-applet
 1677 lennart  name=systemd:/user/lennart/1                     \_ bluetooth-applet
 1678 lennart  name=systemd:/user/lennart/1                     \_ gpk-update-icon
 1408 root     name=systemd:/systemd-1/console-kit-daemon.service /usr/sbin/console-kit-daemon --no-daemon
 1419 gdm      name=systemd:/systemd-1/prefdm.service /usr/bin/dbus-launch --exit-with-session
 1453 root     name=systemd:/systemd-1/dbus.service /usr/libexec/upowerd
 1473 rtkit    name=systemd:/systemd-1/rtkit-daemon.service /usr/libexec/rtkit-daemon
 1496 root     name=systemd:/systemd-1/accounts-daemon.service /usr/libexec/accounts-daemon
 1499 root     name=systemd:/systemd-1/systemd-logger.service /lib/systemd/systemd-logger
 1511 lennart  name=systemd:/systemd-1/prefdm.service /usr/bin/gnome-keyring-daemon --daemonize --login
 1534 lennart  name=systemd:/user/lennart/1        dbus-launch --sh-syntax --exit-with-session
 1535 lennart  name=systemd:/user/lennart/1        /bin/dbus-daemon --fork --print-pid 5 --print-address 7 --session
 1603 lennart  name=systemd:/user/lennart/1        /usr/libexec/gconfd-2
 1612 lennart  name=systemd:/user/lennart/1        /usr/libexec/gnome-settings-daemon
 1615 lennart  name=systemd:/user/lennart/1        /usr/libexec/gvfsd
 1626 lennart  name=systemd:/user/lennart/1        /usr/libexec//gvfs-fuse-daemon /home/lennart/.gvfs
 1634 lennart  name=systemd:/user/lennart/1        /usr/bin/pulseaudio --start --log-target=syslog
 1649 lennart  name=systemd:/user/lennart/1         \_ /usr/libexec/pulse/gconf-helper
 1645 lennart  name=systemd:/user/lennart/1        /usr/libexec/bonobo-activation-server --ac-activate --ior-output-fd=24
 1668 lennart  name=systemd:/user/lennart/1        /usr/libexec/im-settings-daemon
 1701 lennart  name=systemd:/user/lennart/1        /usr/libexec/gvfs-gdu-volume-monitor
 1707 lennart  name=systemd:/user/lennart/1        /usr/bin/gnote --panel-applet --oaf-activate-iid=OAFIID:GnoteApplet_Factory --oaf-ior-fd=22
 1725 lennart  name=systemd:/user/lennart/1        /usr/libexec/clock-applet
 1727 lennart  name=systemd:/user/lennart/1        /usr/libexec/wnck-applet
 1729 lennart  name=systemd:/user/lennart/1        /usr/libexec/notification-area-applet
 1733 root     name=systemd:/systemd-1/dbus.service /usr/libexec/udisks-daemon
 1747 root     name=systemd:/systemd-1/dbus.service  \_ udisks-daemon: polling /dev/sr0
 1759 lennart  name=systemd:/user/lennart/1        gnome-screensaver
 1780 lennart  name=systemd:/user/lennart/1        /usr/libexec/gvfsd-trash --spawner :1.9 /org/gtk/gvfs/exec_spaw/0
 1864 lennart  name=systemd:/user/lennart/1        /usr/libexec/gvfs-afc-volume-monitor
 1874 lennart  name=systemd:/user/lennart/1        /usr/libexec/gconf-im-settings-daemon
 1903 lennart  name=systemd:/user/lennart/1        /usr/libexec/gvfsd-burn --spawner :1.9 /org/gtk/gvfs/exec_spaw/1
 1909 lennart  name=systemd:/user/lennart/1        gnome-terminal
 1913 lennart  name=systemd:/user/lennart/1         \_ gnome-pty-helper
 1914 lennart  name=systemd:/user/lennart/1         \_ bash
29231 lennart  name=systemd:/user/lennart/1         |   \_ ssh tango
 2221 lennart  name=systemd:/user/lennart/1         \_ bash
 4193 lennart  name=systemd:/user/lennart/1         |   \_ ssh tango
 2461 lennart  name=systemd:/user/lennart/1         \_ bash
29219 lennart  name=systemd:/user/lennart/1         |   \_ emacs systemd-for-admins-1.txt
15113 lennart  name=systemd:/user/lennart/1         \_ bash
27251 lennart  name=systemd:/user/lennart/1             \_ empathy
29504 lennart  name=systemd:/user/lennart/1             \_ ps xawf -eo pid,user,cgroup,args
 1968 lennart  name=systemd:/user/lennart/1        ssh-agent
 1994 lennart  name=systemd:/user/lennart/1        gpg-agent --daemon --write-env-file
18679 lennart  name=systemd:/user/lennart/1        /bin/sh /usr/lib64/firefox-3.6/run-mozilla.sh /usr/lib64/firefox-3.6/firefox
18741 lennart  name=systemd:/user/lennart/1         \_ /usr/lib64/firefox-3.6/firefox
28900 lennart  name=systemd:/user/lennart/1             \_ /usr/lib64/nspluginwrapper/npviewer.bin --plugin /usr/lib64/mozilla/plugins/libflashplayer.so --connection /org/wrapper/NSPlugins/libflashplayer.so/18741-6
 4016 root     name=systemd:/systemd-1/sysinit.service /usr/sbin/bluetoothd --udev
 4094 smmsp    name=systemd:/systemd-1/sendmail.service sendmail: Queue runner@01:00:00 for /var/spool/clientmqueue
 4096 root     name=systemd:/systemd-1/sendmail.service sendmail: accepting connections
 4112 ntp      name=systemd:/systemd-1/ntpd.service /usr/sbin/ntpd -n -u ntp:ntp -g
27262 lennart  name=systemd:/user/lennart/1        /usr/libexec/mission-control-5
27265 lennart  name=systemd:/user/lennart/1        /usr/libexec/telepathy-haze
27268 lennart  name=systemd:/user/lennart/1        /usr/libexec/telepathy-logger
27270 lennart  name=systemd:/user/lennart/1        /usr/libexec/dconf-service
27280 lennart  name=systemd:/user/lennart/1        /usr/libexec/notification-daemon
27284 lennart  name=systemd:/user/lennart/1        /usr/libexec/telepathy-gabble
27285 lennart  name=systemd:/user/lennart/1        /usr/libexec/telepathy-salut
27297 lennart  name=systemd:/user/lennart/1        /usr/libexec/geoclue-yahoo

(この出力は短くなっていることに注意。このブログストーリーと関係がないため、カーネルスレッドのほとんどはここでは削除している。)

3つ目の列で、それぞれのプロセスをsystemdがどのcgroupに割り当てているかを確認することができる。 udevプロセス群がname=systemd:/systemd-1/sysinit.servicecgroupにあることがわかるだろう。 systemdがブートの初期段階をカバーするsysinit.serviceサービスで起動されたすべてのプロセスをここに配置している。

個人的におすすめなのが、シェルのエイリアスpscを上で示したようなpsコマンドラインに設定することだ。

alias psc='ps xawf -eo pid,user,cgroup,args'

これで、たった4回キーを押すだけで、サービスの情報が見られる!

同じ情報を表示する別の方法はsystemd-cglsで、これはsystemdに同梱されている。 出力はこんな感じだ

$ systemd-cgls
+    2 [kthreadd]
[...]
+ 4281 [flush-8:0]
+ user
| \ lennart
|   \ 1
|     +  1495 pam: gdm-password
|     +  1521 gnome-session
|     +  1534 dbus-launch --sh-syntax --exit-with-session
|     +  1535 /bin/dbus-daemon --fork --print-pid 5 --print-address 7 --session
|     +  1603 /usr/libexec/gconfd-2
|     +  1612 /usr/libexec/gnome-settings-daemon
|     +  1615 /ushr/libexec/gvfsd
|     +  1621 metacity
|     +  1626 /usr/libexec//gvfs-fuse-daemon /home/lennart/.gvfs
|     +  1634 /usr/bin/pulseaudio --start --log-target=syslog
|     +  1635 gnome-panel
|     +  1638 nautilus
|     +  1640 /usr/libexec/polkit-gnome-authentication-agent-1
|     +  1641 /usr/bin/seapplet
|     +  1644 gnome-volume-control-applet
|     +  1645 /usr/libexec/bonobo-activation-server --ac-activate --ior-output-fd=24
|     +  1646 /usr/sbin/restorecond -u
|     +  1649 /usr/libexec/pulse/gconf-helper
|     +  1652 /usr/bin/devilspie
|     +  1662 nm-applet --sm-disable
|     +  1664 gnome-power-manager
|     +  1665 /usr/libexec/gdu-notification-daemon
|     +  1668 /usr/libexec/im-settings-daemon
|     +  1670 /usr/libexec/evolution/2.32/evolution-alarm-notify
|     +  1672 /usr/bin/python /usr/share/system-config-printer/applet.py
|     +  1674 /usr/lib64/deja-dup/deja-dup-monitor
|     +  1675 abrt-applet
|     +  1677 bluetooth-applet
|     +  1678 gpk-update-icon
|     +  1701 /usr/libexec/gvfs-gdu-volume-monitor
|     +  1707 /usr/bin/gnote --panel-applet --oaf-activate-iid=OAFIID:GnoteApplet_Factory --oaf-ior-fd=22
|     +  1725 /usr/libexec/clock-applet
|     +  1727 /usr/libexec/wnck-applet
|     +  1729 /usr/libexec/notification-area-applet
|     +  1759 gnome-screensaver
|     +  1780 /usr/libexec/gvfsd-trash --spawner :1.9 /org/gtk/gvfs/exec_spaw/0
|     +  1864 /usr/libexec/gvfs-afc-volume-monitor
|     +  1874 /usr/libexec/gconf-im-settings-daemon
|     +  1882 /usr/libexec/gvfs-gphoto2-volume-monitor
|     +  1903 /usr/libexec/gvfsd-burn --spawner :1.9 /org/gtk/gvfs/exec_spaw/1
|     +  1909 gnome-terminal
|     +  1913 gnome-pty-helper
|     +  1914 bash
|     +  1968 ssh-agent
|     +  1994 gpg-agent --daemon --write-env-file
|     +  2221 bash
|     +  2461 bash
|     +  4193 ssh tango
|     + 15113 bash
|     + 18679 /bin/sh /usr/lib64/firefox-3.6/run-mozilla.sh /usr/lib64/firefox-3.6/firefox
|     + 18741 /usr/lib64/firefox-3.6/firefox
|     + 27251 empathy
|     + 27262 /usr/libexec/mission-control-5
|     + 27265 /usr/libexec/telepathy-haze
|     + 27268 /usr/libexec/telepathy-logger
|     + 27270 /usr/libexec/dconf-service
|     + 27280 /usr/libexec/notification-daemon
|     + 27284 /usr/libexec/telepathy-gabble
|     + 27285 /usr/libexec/telepathy-salut
|     + 27297 /usr/libexec/geoclue-yahoo
|     + 28900 /usr/lib64/nspluginwrapper/npviewer.bin --plugin /usr/lib64/mozilla/plugins/libflashplayer.so --connection /org/wrapper/NSPlugins/libflashplayer.so/18741-6
|     + 29219 emacs systemd-for-admins-1.txt
|     + 29231 ssh tango
|     \ 29519 systemd-cgls
\ systemd-1
  + 1 /sbin/init
  + ntpd.service
  | \ 4112 /usr/sbin/ntpd -n -u ntp:ntp -g
  + systemd-logger.service
  | \ 1499 /lib/systemd/systemd-logger
  + accounts-daemon.service
  | \ 1496 /usr/libexec/accounts-daemon
  + rtkit-daemon.service
  | \ 1473 /usr/libexec/rtkit-daemon
  + console-kit-daemon.service
  | \ 1408 /usr/sbin/console-kit-daemon --no-daemon
  + prefdm.service
  | + 1376 /usr/sbin/gdm-binary -nodaemon
  | + 1391 /usr/libexec/gdm-simple-slave --display-id /org/gnome/DisplayManager/Display1 --force-active-vt
  | + 1394 /usr/bin/Xorg :0 -nr -verbose -auth /var/run/gdm/auth-for-gdm-f2KUOh/database -nolisten tcp vt1
  | + 1419 /usr/bin/dbus-launch --exit-with-session
  | \ 1511 /usr/bin/gnome-keyring-daemon --daemonize --login
  + getty@.service
  | + tty6
  | | \ 1346 /sbin/mingetty tty6
  | + tty4
  | | \ 1343 /sbin/mingetty tty4
  | + tty5
  | | \ 1342 /sbin/mingetty tty5
  | + tty3
  | | \ 1339 /sbin/mingetty tty3
  | \ tty2
  |   \ 1332 /sbin/mingetty tty2
  + abrtd.service
  | \ 1317 /usr/sbin/abrtd -d -s
  + crond.service
  | \ 1344 crond
  + sshd.service
  | \ 1362 /usr/sbin/sshd
  + sendmail.service
  | + 4094 sendmail: Queue runner@01:00:00 for /var/spool/clientmqueue
  | \ 4096 sendmail: accepting connections
  + haldaemon.service
  | + 1249 hald
  | + 1250 hald-runner
  | + 1273 hald-addon-input: Listening on /dev/input/event3 /dev/input/event9 /dev/input/event1 /dev/input/event7 /dev/input/event2 /dev/input/event0 /dev/input/event8
  | + 1275 /usr/libexec/hald-addon-rfkill-killswitch
  | + 1284 /usr/libexec/hald-addon-leds
  | + 1285 /usr/libexec/hald-addon-generic-backlight
  | \ 1287 /usr/libexec/hald-addon-acpi
  + irqbalance.service
  | \ 1210 irqbalance
  + avahi-daemon.service
  | + 1175 avahi-daemon: running [epsilon.local]
  + NetworkManager.service
  | + 1171 /usr/sbin/NetworkManager --no-daemon
  | \ 4028 /sbin/dhclient -d -4 -sf /usr/libexec/nm-dhcp-client.action -pf /var/run/dhclient-wlan0.pid -lf /var/lib/dhclient/dhclient-7d32a784-ede9-4cf6-9ee3-60edc0bce5ff-wlan0.lease -cf /var/run/nm-dhclient-wlan0.conf wlan0
  + rsyslog.service
  | \ 1193 /sbin/rsyslogd -c 4
  + mdmonitor.service
  | \ 1207 mdadm --monitor --scan -f --pid-file=/var/run/mdadm/mdadm.pid
  + cups.service
  | \ 1195 cupsd -C /etc/cups/cupsd.conf
  + auditd.service
  | + 1131 auditd
  | + 1133 /sbin/audispd
  | \ 1135 /usr/sbin/sedispatch
  + dbus.service
  | +  1096 /bin/dbus-daemon --system --address=systemd: --nofork --systemd-activation
  | +  1216 /usr/sbin/modem-manager
  | +  1219 /usr/libexec/polkit-1/polkitd
  | +  1242 /usr/sbin/wpa_supplicant -c /etc/wpa_supplicant/wpa_supplicant.conf -B -u -f /var/log/wpa_supplicant.log -P /var/run/wpa_supplicant.pid
  | +  1453 /usr/libexec/upowerd
  | +  1733 /usr/libexec/udisks-daemon
  | +  1747 udisks-daemon: polling /dev/sr0
  | \ 29509 /usr/libexec/packagekitd
  + dev-mqueue.mount
  + dev-hugepages.mount
  \ sysinit.service
    +   455 /sbin/udevd -d
    +  4016 /usr/sbin/bluetoothd --udev
    + 28188 /sbin/udevd -d
    \ 28191 /sbin/udevd -d

(これも短くしている。同じ方法だ。)

見てわかったとおり、このコマンドはcgroupつまりサービスごとにプロセスを表示しており、systemdがサービスに従ってラベルを付けている。 例えば、簡単にわかるとおり、auditサービスのauditd.serviceは3つの別々のサービス、auditdaudispsedispatchをスポーンしている。

詳しく見ると気づくと思うが、多くのプロセスが/user/1というcgroupに割り当てられている。 ここでは簡単に、systemdはサービスをcgroupsで管理しているだけでなく、ユーザーセッションのサービスも同じようにcgroupsで管理している、ということにしておいてほしい。 のちのちの投稿でこの件についてはもっと詳しく論じることとする。

ここでおしまい。次の投稿を楽しみに、すぐ戻ってきてね!