#きょうのsystemd : ルートを変える (systemd for Administrators, Part 6)
これは
の翻訳
ルートを変える
管理者であれ開発者であれ、遅かれ早かれ、chroot()
環境に出会うことになる。
chroot()
システムコールは単純にプロセスおよびその子がルートディレクトリ /
と考えるものをシフトさせ、そのプロセスが見ることができるファイルの階層をそのサブツリーに制限することができる。
主としてchroot()
環境には2つの用途がある:
- セキュリティ目的:
この用途では、特定の独立したデーモンがプライベートなサブディレクトリに
chroot()
され、デーモンが悪用された場合でも、攻撃者は完全なOSの階層ではなく、サブディレクトリしか見ることができないようにすることができる。 - 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 enable
とsystemctl 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
のどこが偉大なのかもう一度まとめよう
- 簡単に使える。手動で
/proc
や/sys
をchroot()環境にマウントする必要がないこと。このツールはこれをやってくれ、コンテナが終了するときにはカーネルが自動でこれをきれいにしてくれる。 - 隔離がより完全で、コンテナ内部空の偶発的な変更からホストOSを守ってくれること。
- コンテナの中に完全なOSをブートさせることができること。たった1つの寂しいシェルではない。
- 実際に小さく、systemdがインストールされているならどこにでもインストールされていること。複雑なインストールやセットアップは不要だ。
systemd自体もこのようなコンテナでうまく動作するための修正が加えられている。 例えば、シャットダウンするときにコンテナで動いていることを検知すれば、systemdは最後の段階で、reboot()の代わりにexit()だけをコールする。
systemd-nspawn
は完全なコンテナソリューションでないことに注意してほしい。
それが必要ならLXCがより良い選択だ。
LXCは同じ基礎となるカーネルテクノロジーを利用しているが、ネットワーク仮想化を含むより多くを提供している。
もし望むなら、systemd-nspawn
はコンテナソリューション(分野で)のGNOME 3である。滑らかでほとんど努力せずに簡単に使える -- しかし、設定オプションはほとんどない。
一方で、LXCはよりKDEのようである。コードの行数以上の設定オプションがある。
systemd-nspawn
を私が書いたのは、テスト、デバッグ、ビルド、インストール、リカバリーをカバーするためである。
これがsystemd-nspawn
を利用するべきものであるし、これが得意なものだし、これがchroot(1) のより良い代替となるところである。
さて、そろそろ終わりにしよう。すでに長くなってしまっている。 以下が、このブログストーリーから家に持って帰ってもらいたいものだ。
- セキュアなchroot()はプログラムのCのソースコードでネイティブにやるのが一番。
ReadOnlyDirectories=
、InaccessibleDirectories=
は完全なchroot()環境への適切な代替手段である。RootDirectory=
は特定のサービスをchroot()させたいなら、友となる。systemd-nspawn
はすごいことができる。- chroot()は不自由だが、ファイルシステム名前空間は全面的にエリートである。
Fedora15でこのすべてが利用できる。
さて、今日はここまで。次回また会おう。
きょうのsystemd: 3つのレベルの「オフ」
概要
これは
の翻訳。
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)
概要
これは
の翻訳。
サービスを停止させる
システムデーモンを停止させるのは簡単、本当?
確かに、デーモンが単一のプロセスで構成されている限り、これは確かにある程度は正しいだろう。
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のサービスファイルに換えられるのか?
伝統的にUnixとLinuxのサービス(デーモン)はSysV init スクリプト経由で起動されている。
これらはBourne Shellスクリプトであり、通常は/etc/rc.d/init.d
といったディレクトリに置かれている。
これらのスクリプトはstart
やstop
、restart
といった数個の標準化された引数(動詞)を与えられて呼び出されたときに、件のサービスの制御、すなわち、起動、停止、再起動をおこなう。
起動の場合、スクリプトは通常、デーモンのバイナリーの呼び出しに関わり、バックグラウンドのプロセスをフォークする(より正確にはデーモン化させる)。
これらは非常に柔軟性がある(なんといったって、これらは単なるコードである)が、いくつかのことはシェルスクリプトでは適切におこなうことが非常に難しい。
具体的には、実行をパラレル化するように並べる、適切にプロセスを監督する、あるいは、非常に細かいところまで実行のコンテクストを設定するといったことである。
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プロセスをスポーンするが、(訳注: 今度の)そのプロセスはもはやフォークしない(これはデーモンに与えられた-d
sスイッチで設定されている)し、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のようなもの)の暗黙的なスーパセットである。これは後者でサービスを起動させれば、前者でもそのサービスが上がってくることを意味する。
いろいろ新調した
昨日、今日とで色々新調した。
昨日はユニクロに服を買いに行った。最低限の冬の準備はできたと思う。
今日は友人に会いに秋葉原へ。 ついでに、つくもへSSDを見に行った。
これまで使っていたSSDはIntelの330で60GBと120GBで、前者を/
、後者を/home
としてマウントしていた。
とても調子よく動いているが、買ったのが5年以上前のため、そろそろ不安になってきた。
あと、データドライブで120GBは手狭になってきているので、60GBを外して、120GBを/
に、新しいSSDを/home
にとスライドさせることにした。
新しいSSDはこれ。
Crucial [Micron製] 内蔵SSD 2.5インチ MX300 525GB ( 3D TLC NAND /SATA 6Gbps /3年保証 )国内正規品 CT525MX300SSD1/JP
- 出版社/メーカー: Crucial(クルーシャル)
- 発売日: 2017/04/04
- メディア: Personal Computers
- この商品を含むブログを見る
TLCなのが気になるけど、まぁ、ええかということで。
rsync -av
で/home
をまるごと移したけど、SSD同士なので超早かった。
現役からは引退させる60GBも何らかの形で使いたいので、これをポチった。
透明でかっこいい。
インテル Gigabit CT Desktop Adapter EXPI9301CT
- 出版社/メーカー: インテル
- 発売日: 2012/08/16
- メディア: Personal Computers
- 購入: 26人 クリック: 199回
- この商品を含むブログ (15件) を見る
実は、デスクトップ機にもかかわらず、ずっと無線LANのPCI-Eカードを挿してネットに繋いでいた。 実家にいた頃はそのほうが便利だったから仕方がない。
ただ、
- 実家から出たので、無線である必要はなくなった
- systemd-nspawnで遊ぶのに、MACVLANをすると、無線ではちょっと困ることがわかった
ということで、以前より有線にしたいなとは思っていた。
ところが、IvyBridgeのCPU + H77のマザボなので、オンボのNICが蟹さん。 「なので」というのは、オンボNICがIntelはHaswell世代のマザボは多くがそうだったり、Ivy世代でもチップセットが最上位のZ77だとそうだったり、という印象・記憶。 色々惜しい。
ネットで見て記憶していた値段より安かったので、エイヤということで買った。若干早い気がする。気だけかも。
#きょうのsystemd: (翻訳) どのサービスがどのプロセスを動かしてる? (systemd for Administrators, Part 2)
概要
systemd for Administrators, Part II
の翻訳。
どのサービスがどのプロセスを動かしている?
ほとんどのLinuxシステムで、実質的にはデフォルトでかなりの数のプロセスが動かされている。
どのプロセスが動いていて、それが何の、どこに属しているのかを知ることはどんどん難しくなっている。
サービスのいくつかは1組のワーカープロセスを維持していることすらあり、これらは、たいてい簡単には見分けのつかない数多くの追加のプロセスでps
の出力を乱雑にしている。
デーモンが任意のサードパーティーのプロセスを、ApacheがCGIプロセスでやっていたり、cronがユーザージョブでやっているように、動かそうものなら、状況はさらに複雑になる。
この状況へのほんのわずかな対処になるのは、たいてい、プロセスの継承ツリーで、これはps xaf
によって表示されるようなものだ。
だが、これは普通、信頼できるものではない。
というのも、親プロセスが死んだプロセスはPID 1を親に設定し直すためであり、したがって、継承に関するすべての情報が失われてしまう。
プロセスが「ダブルフォーク」している場合、このプロセスはそれを開始したプロセスとの関係も失ってしまうこととなる。(これは実際には特徴であり、伝統的なUnixのデーモン化のロジックに依存している。)
さらに、プロセスはPR_SETNAME
やargv[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.service
cgroupにあることがわかるだろう。
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つの別々のサービス、auditd
、audisp
とsedispatch
をスポーンしている。
詳しく見ると気づくと思うが、多くのプロセスが/user/1
というcgroupに割り当てられている。
ここでは簡単に、systemdはサービスをcgroupsで管理しているだけでなく、ユーザーセッションのサービスも同じようにcgroupsで管理している、ということにしておいてほしい。
のちのちの投稿でこの件についてはもっと詳しく論じることとする。
ここでおしまい。次の投稿を楽しみに、すぐ戻ってきてね!