Debian パッケージはインストールされるファイルのアーカイブというだけではありません。メタ情報はパッケージの一部で、他の Debian パッケージとの関係性 (依存関係、衝突、提案) を記述しています。また、メタ情報にはスクリプトも含まれています。スクリプトはパッケージライフサイクルのある時点 (インストール、削除、アップグレード) にコマンドを実行するためのものです。パッケージ管理ツールが使うこれらのデータは、パッケージ化されたソフトウェアの一部ではありませんが、パッケージの「メタ情報」(他の情報のデータ) と呼ばれて、パッケージに含まれています。
このファイルは (RFC 2822 の定義する) 電子メールヘッダとよく似た構造を使っています。例えば、apt の control ファイルは以下の内容です:
$ apt-cache show apt
Package: apt
Version: 0.9.7.9
Installed-Size: 3271
Maintainer: APT Development Team <deity@lists.debian.org>
Architecture: amd64
Replaces: manpages-pl (<< 20060617-3~)
Depends: libapt-pkg4.12 (>= 0.9.7.9), libc6 (>= 2.4), libgcc1 (>= 1:4.1.1), libstdc++6 (>= 4.6), debian-archive-keyring, gnupg
Suggests: aptitude | synaptic | wajig, dpkg-dev, apt-doc, xz-utils, python-apt
Conflicts: python-apt (<< 0.7.93.2~)
Description-en: commandline package manager
This package provides commandline tools for searching and
managing as well as querying information about packages
as a low-level access to all features of the libapt-pkg library.
.
These include:
* apt-get for retrieval of packages and information about them
from authenticated sources and for installation, upgrade and
removal of packages together with their dependencies
* apt-cache for querying available information about installed
as well as installable packages
* apt-cdrom to use removable media as a source for packages
* apt-config as an interface to the configuration settings
* apt-key as an interface to manage authentication keys
Description-md5: 9fb97a88cb7383934ef963352b53b4a7
Tag: admin::package-management, hardware::storage, hardware::storage:cd,
implemented-in::c++, interface::commandline, network::client,
protocol::ftp, protocol::http, protocol::ipv6, role::program,
suite::debian, use::downloading, use::searching,
works-with::software:package
Section: admin
Priority: important
Filename: pool/main/a/apt/apt_0.9.7.9_amd64.deb
Size: 1253524
MD5sum: 00a128b2eb2b08f4ecee7fe0d7e3c1c4
SHA1: 6a271487ceee6f6d7bc4c47a8a16f49c26e4ca04
SHA256: 3bba3b15fb5ace96df052935d7069e0d21ff1f5b496510ec9d2dc939eefad104
5.2.1.1. 依存関係: Depends フィールド
依存関係はパッケージヘッダの Depends フィールドで定義されています。依存関係はパッケージを正しく動かすために必要な条件を定義しています - apt などのツールは、この情報を使って、インストールしたいパッケージが要求するバージョンで必要なライブラリをインストールします。それぞれの依存パッケージについて、要求を満たすパッケージのバージョン範囲を指定することが可能です。言い換えれば、バージョン「2.3.4」以上の libc6 パッケージが必要という条件を表現する(「libc6 (>= 2.3.4)」と表記する) ことが可能です。バージョン比較演算子は次の通りです:
満足すべき条件リストの中で使われるカンマは条件同士の区切りです。このカンマは論理「and」に解釈されます。条件リストの中で、垂直棒 (「|」) は論理「or」に解釈されます (これは「包含的論理和」で、「排他的論理和」ではありません)。or は「and」より大きな優先順位を持っており、必要に応じて何度でも使えます。このため、「(A or B) and C」はn
A | B, C のように表記できます。これに対して、「A or (B and C)」は「(A or B) and (A or C)」のように表記してください。なぜなら、
Depends フィールドでは括弧を使って論理演算子「or」と「and」の優先度の順位を変えることができないからです。このため、これは
A | B, A | C のように表記できます。
依存関係システムはプログラムの動作を保証する良いメカニズムですが、「メタパッケージ」を使う手もあります。メタパッケージは依存関係を表記するだけの空のパッケージです。メタパッケージはメンテナが事前に選んだ一連のプログラムグループのインストールを楽にします; apt-get install meta-package はメタパッケージが依存する全てのプログラムを自動的にインストールします。gnome、kde-full、linux-image-amd64 パッケージはメタパッケージの例です。
5.2.1.2. 衝突: Conflicts フィールド
Conflicts フィールドでは、同時にインストールできないパッケージを指定します。このフィールドが使われるケースで最も多いのは、両方のパッケージが同じ名前のファイルを含む場合、同じ名前のサービスを同じ TCP ポートで提供する場合、互いの動作を妨げる場合です。
dpkg は、あるパッケージがインストール済みのパッケージと衝突を引き起こす場合、新しいパッケージがインストール済みのパッケージを「置換」する場合 (このような場合、dpkg が古いパッケージを新パッケージで置換します) を除き、そのパッケージのインストールを拒否するでしょう。apt-get は常に、あなたの指示に従います: もしあなたが新しいパッケージをインストールしたいのなら、問題を引き起こすインストール済みパッケージを自動的にアンインストールします。
5.2.1.3. 不適合性: Breaks フィールド
Breaks フィールドは Conflicts フィールドとよく似た効果を持っていますが、特別な意味があります。このフィールドはあるパッケージをインストールすることで他のパッケージ (または他のパッケージの特定バージョン) を「破壊する」という意味があります。一般的に、このような 2 つのパッケージの不適合性は一時的なもので、Breaks フィールドでは不適合性がある特定のバージョンだけを指定します。
dpkg はインストール済みのパッケージを破壊するようなパッケージのインストールを拒否します、apt-get は破壊されるパッケージを新しいバージョンに更新することで (新しいバージョンではこの問題が修正され、両パッケージが適合すると期待されます)、この問題の解決を試みます。
この手の状況は更新によって後方互換性が無くなる場合に起こりうるかもしれません: 新しいバージョンが古いバージョンと同時に動かない場合、特別な設定をしないと別のプログラムの機能不良が起きる場合です。Breaks フィールドはユーザがこのような問題に遭遇することがないようにしています。
5.2.1.4. 提供されるアイテム: Provides フィールド
このフィールドによって、とても興味深い「仮想パッケージ」の構想が生まれました。このフィールドは多くの役割を持っていますが、特に重要な 2 つを説明します。最初の役割は、仮想パッケージを使って一般的なサービスとパッケージを関連付ける (サービスを「提供する」のはパッケージです) ことを可能にする役割です。2 番目の役割は、このパッケージを他のパッケージを完全に置き換えること、このパッケージの依存関係を他のパッケージが満足するであろう依存関係を使って満足させることを可能にする役割です。このため、同じパッケージ名を使わずに、代替パッケージを作成することが可能です。
最初の場合について、例を挙げて詳細に議論しましょう: 全てのメールサーバ、例えば postfix や sendmail などは mail-transport-agent 仮想パッケージ「提供」するとされています。このため、動作にメールサービスを必要なパッケージ (例えば smartlist や sympa などのメーリングリストマネージャ) は、おそらくメールサービスを提供するであろうパッケージ (例えば、postfix | sendmail | exim4 | …の ように) をたくさん依存関係に宣言するのではなく、たった 1 つ mail-transport-agent を宣言するだけで十分です。さらに、1 台のマシンに 2 つのメールサーバをインストールすることは無駄なため、メールサーバの機能を提供するパッケージは mail-transport-agent 仮想パッケージとの衝突を宣言します。自分自身との衝突はシステムによって無視されます。この手法により、2 つのメールサーバを同時にインストールできなくなります。
パッケージの内容が巨大なパッケージに統合された場合に、Provides フィールドはさらに興味深い役割を果たします。例えば、libdigest-md5-perl Perl モジュールは Perl 5.6 では任意選択モジュールでしたが、Perl 5.8 (と Wheezy に含まれる 5.14 などのその後のバージョン) では標準モジュールに組み込まれました。このため、perl パッケージはバージョン 5.8 から Provides: libdigest-md5-perl を宣言しています。そうすれば、ユーザが Perl 5.8 (とそれより新しいバージョン) を持っている場合、このパッケージの依存関係が満足されるからです。libdigest-md5-perl パッケージ自体は、いずれ削除されます。なぜなら、古い Perl バージョンが削除されたらこのパッケージはもはや存在意義が無いからです。
この機能はとても便利です、なぜなら、開発方向性の変化を予測することは絶対に不可能ですし、名称を変更できたり、他の時代遅れのソフトウェアを自動に置き換えできるようにすることが必要だからです。
仮想パッケージにはいくつかの制限があります。最も重要な制限はバージョン番号が無いことです。先に挙げた例に戻ると、Perl 5.10 があるなら、Depends: libdigest-md5-perl (>= 1.6) という依存関係は満足されています (正しく言えば、十中八九満足されています) が、パッケージシステムはこの依存関係が満足されていることに気がつきません。パッケージシステムは依存関係が満足されていることに気がつきませんが、指定されたバージョンが一致しないと仮定して、最もリスクの低いオプションを選びます。
5.2.1.5. ファイルの置き換え: Replaces フィールド
Replaces フィールドは、そのパッケージが他のパッケージにも含まれるファイルを含んでおり、合法的にそのファイルを置き換える権利を持っていることを示すためのものです。この仕様が無かったとしても、dpkg はそのようなファイルを置き換える事はできません。これは他のパッケージのファイルは上書きできないことを意味しています (実際には、--force-overwrite オプションを付ければ強制的に上書き可能です)。このことにより潜在的な問題を識別できるようになりますし、メンテナはこのフィールドを追加する前に問題の原因を追及することができるようになります。
このフィールドは、パッケージ名が変更された時や、パッケージが別のパッケージに統合された時に使われます。これはまた、メンテナが同じソースパッケージから作成されたバイナリパッケージ間で異なるファイルを配布したい場合に使われます: 置き換えられたファイルはもはや古いパッケージではなく新しいパッケージに所属します。
インストール済みパッケージの全てのファイルが置き換えられたら、このパッケージは削除されたとみなされます。最後に、このフィールドは衝突がある場合に dpkg が置き換えられたパッケージを削除する際に使われます。
それぞれの Debian パッケージには、control ファイルだけでなく control.tar.gz アーカイブが含まれており、このアーカイブには、dpkg がパッケージ処理の各段階で呼び出す多数のスクリプトが含まれているかもしれません。Debian ポリシーでは、呼び出されるスクリプトとスクリプトが受け取る引数を明記することで、スクリプトの使われ方が詳しく説明されています。スクリプトが呼び出される順番はわかりにくいかもしれません。なぜなら、スクリプトの内 1 つでも失敗したら、dpkg は進行中のインストールを止めるか(可能ならば)削除することでシステムを一貫性のある状態に戻そうとするからです。
一般的に言って、preinst スクリプトはパッケージのインストール前に実行され、postinst はインストール後に実行されます。同様に、 prerm はパッケージの削除前に実行され、postrm は削除後に実行されます。パッケージの更新とは、パッケージの古いバージョンを削除して新しいバージョンをインストールすることと等価です。ここで全ての可能なシナリオを詳細に説明することは不可能なので、最も一般的なケースを 2 つだけ挙げます: インストール/更新と削除です。
5.2.2.1. パッケージのインストールとアップグレード
ここではパッケージのインストール中 (または更新中) に何が起きるかを説明します:
更新の際に、dpkg は old-prerm upgrade new-version を呼び出します。
更新する場合、dpkg は new-preinst upgrade old-version を実行します; 初めてインストールする場合、new-preinst install を実行します。過去にもしパッケージがインストールされてさらに削除されていた場合 (完全消去されていない場合、古い設定ファイルがまだ残っている場合)、最後の引数に古いバージョンを追加します。
そして新しいパッケージのファイルが解凍されます。あるファイルが既に存在した場合、そのファイルは置換されますが、一時的にバックアップコピーが作られます。
更新の場合、dpkg は old-postrm upgrade new-version を実行します。
dpkg はすべての内部データを (ファイルリスト、設定スクリプトなど) 更新し、置換されたファイルのバックアップを削除します。これ以降はもう後戻りできません: 前の状態に戻るために必要な情報が全て失われたため、dpkg は状態を復元できません。
最後に、dpkg は new-postinst configure last-version-configured を実行して、パッケージを設定します。
ここではパッケージの削除中に何が起こるかを説明します:
dpkg は prerm remove を呼び出します。
dpkg は設定ファイルと設定スクリプトを除く全てのパッケージのファイルを削除します。
dpkg は postrm remove を実行します。全ての設定スクリプトは postrm を除いて削除されます。ユーザが「purge」オプションを指定しない限り、作業はここで終了します。
パッケージを完全消去する (dpkg --purge または dpkg -P を実行した) 場合、設定ファイルおよびそのコピー (*.dpkg-tmp、*.dpkg-old、*.dpkg-new) と一時ファイルも削除されます; dpkg は postrm purge を実行します。
上で詳細を述べた 4 つのスクリプトの実行を補助するのが config スクリプトです。パッケージが提供するこのスクリプトは debconf を用いて設定に必要な情報をユーザに入力させるためのものです。ユーザからの情報は debconf データベースに保存され、後々利用されます。このスクリプトは通常 apt によって各パッケージインストールの前に実行され、処理が始まると全ての質問をまとめてユーザに尋ねます。インストール前後に実行されるスクリプトは、ユーザの希望を反映させるために、これらの情報を利用します。
前の節ですでに説明したメンテナスクリプトと管理情報に加えて、Debian パッケージの
control.tar.gz アーカイブに興味深いファイルが含まれている場合があります。1 つ目は
md5sums で、このファイルにはパッケージに含まれる全ファイルの MD5 チェックサムが列挙されています。これのおかげで、
debsums などのツール (詳しくは
「Auditing Packages: debsums and its Limits」を参照) はインストール以降ファイルが変更されたか否かを判断できるようになります。このファイルが存在しなければ、
dpkg がインストール時に動的にこのファイルを生成します (そして他の管理情報ファイルと同様に dpkg データベースに内容を保存します)。
conffiles では、設定ファイルとして取り扱われるべきパッケージファイルが指定されています。管理者は設定ファイルを変更でき、dpkg はパッケージの更新中に設定ファイルの変更を保存しようとします。
実際のところこの状況では、dpkg はできるだけ賢明に振舞います: パッケージに含まれる標準的な設定ファイルが 2 つのバージョン間で変更されていなければ、何もしません。しかしながら、標準的な設定ファイルが 2 つのバージョン間で変更されていたら、ファイルを更新しようとします。2 つの場合が考えられます: 管理者が設定ファイルに変更を加えていなければ、dpkg は自動的にパッケージに含まれる新しいバージョンをインストールします; 管理者が設定ファイルに変更を加えていれば、dpkg は管理者にどちらのバージョン(変更された古いバージョン、またはパッケージに含まれる新しいバージョン) を使うかを尋ねます。どちらのバージョンを使うかの判断を手助けするために、dpkg は「diff」を使って 2 つのバージョンの違いを表示します。ユーザが古いバージョンを選んだ場合、新しいバージョンは同じ場所にファイル名の末尾に .dpkg-distを追加して保存されます。ユーザが新しいバージョンを選んだ場合、古いバージョンはファイル名の末尾に .dpkg-old を追加して保存されます。他の利用可能な操作は、一旦 dpkg の処理を中断してファイルを編集する、または改めてバージョン間の違いを表示する (先と同じ様に diff コマンドを実行する) です。