Debian パッケージはインストールされるファイルのアーカイブというだけではありません。Debian パッケージには他の Debian パッケージとの関係性 (依存関係、衝突、提案) を表す情報が含まれています。さらに Debian パッケージにはスクリプトも含まれています。このスクリプトはパッケージライフサイクルのある時点 (インストール、削除、アップグレード) にコマンドを実行するためのものです。これらのデータはパッケージ管理ツールによって使われますが、パッケージングされたソフトウェアの一部ではありません。しかし、これらのデータはパッケージの「メタ情報」(ソフトウェア以外の情報のデータ) と呼ばれて、パッケージに含まれています。
control
ファイルは (RFC 2822 の定義する) 電子メールヘッダとよく似た構造を使っています。たとえば、apt の control
ファイルは以下のような内容を持っています。
$
apt-cache show apt
Package: apt
Version: 1.4.8
Installed-Size: 3539
Maintainer: APT Development Team <deity@lists.debian.org>
Architecture: amd64
Replaces: apt-utils (<< 1.3~exp2~)
Depends: adduser, gpgv | gpgv2 | gpgv1, debian-archive-keyring, init-system-helpers (>= 1.18~), libapt-pkg5.0 (>= 1.3~rc2), libc6 (>= 2.15), libgcc1 (>= 1:3.0), libstdc++6 (>= 5.2)
Recommends: gnupg | gnupg2 | gnupg1
Suggests: apt-doc, aptitude | synaptic | wajig, dpkg-dev (>= 1.17.2), powermgmt-base, python-apt
Breaks: apt-utils (<< 1.3~exp2~)
Description-ja: コマンドラインパッケージマネージャ
本パッケージは、パッケージを検索、管理したりパッケージの情報を照会できるコ
マンドラインツールを提供します。libapt-pkg ライブラリの全機能に低レベルアク
セスできます。
.
次のツールが含まれます。
* apt-get: 信頼されたソースからパッケージやパッケージの情報を取得したり、
パッケージとその依存関係をまとめてインストール、アップグレード、および削
除できます
* apt-cache: インストールしたパッケージやインストール可能なパッケージに関
して利用できる情報を検索できます
* apt-cdrom: リムーバブルメディアをパッケージの取得ソースとして利用できます
* apt-config: 構成設定へのインターフェース
* apt-key: 信頼できる鍵を管理するインターフェース
Description-md5: 9fb97a88cb7383934ef963352b53b4a7
Tag: admin::package-management, devel::lang:ruby, hardware::storage,
hardware::storage:cd, implemented-in::c++, implemented-in::perl,
implemented-in::ruby, interface::commandline, network::client,
protocol::ftp, protocol::http, protocol::ipv6, role::program,
scope::application, scope::utility, sound::player, suite::debian,
use::downloading, use::organizing, use::searching, works-with::audio,
works-with::software:package, works-with::text
Section: admin
Priority: important
Filename: pool/main/a/apt/apt_1.4.8_amd64.deb
Size: 1231676
MD5sum: 4963240f23156b2dda3affc9c0d416a3
SHA256: bc319a3abaf98d76e7e13ac97ab0ee7c238a48e2d4ab85524be8b10cfd23d50d
5.2.1.1. 依存関係、Depends
フィールド
依存関係はパッケージヘッダの Depends
フィールドで定義されています。依存関係はパッケージを正しく動かすために必要な条件を定義しています。すなわち apt
などのツールはこの情報を使ってインストールしたいパッケージの依存関係を満たすために必要なバージョンのライブラリをインストールします。それぞれの依存パッケージについて、要求を満たすパッケージのバージョン範囲を指定することが可能です。言い換えれば、バージョン「2.15」以上の libc6 パッケージが必要という条件を表現する (「libc6 (>= 2.15)
」と表記する) ことが可能です。バージョン比較演算子は次の通りです。
満足すべき条件リストの中で使われるコンマは条件同士の区切りです。このコンマは論理「and」に解釈されます。条件リストの中で使われる垂直棒 (「|」) は論理「or」に解釈されます (これは「包含的論理和」で、「排他的論理和」ではありません)。「or」は「and」より高い優先度を持っており、必要に応じて何度でも使えます。このため、「(A or B) and C」は
A | B, C
のように表記できます。これに対して、「A or (B and C)」は「(A or B) and (A or C)」のように表記してください。なぜなら、
Depends
フィールドでは括弧を使って論理演算子「or」と「and」の優先度の順位を変えることができないからです。このため、これは
A | B, A | C
のように表記できます。
依存関係システムはプログラムの動作を保証する良いメカニズムですが、「メタパッケージ」を使う手もあります。メタパッケージは依存関係を表記するだけの空のパッケージです。メタパッケージはメンテナが事前に選んだ一連のプログラムグループのインストールを楽にします。すなわち apt install meta-package
はメタパッケージが依存するすべてのプログラムを自動的にインストールします。gnome、kde-full、linux-image-amd64 パッケージはメタパッケージの例です。
5.2.1.2. 衝突、Conflicts
フィールド
Conflicts
フィールドでは、同時にインストールできないパッケージを指定します。このフィールドが使われるケースで最も多いのは、両方のパッケージが同名のファイルを含む場合、同種のサービスを同じ TCP ポートで提供する場合、互いの動作を妨げる場合です。
dpkg
は、あるパッケージがインストール済みのパッケージと衝突を引き起こす場合、新しいパッケージがインストール済みのパッケージを「置換」するものでない限り、そのパッケージのインストールを拒否するでしょう (「置換」するものの場合、dpkg
はインストール済みパッケージを新パッケージで置換します)。これに対して apt
は常にあなたの指示に従います。つまり、もし apt
に新しいパッケージをインストールするよう指示したのなら、apt
は新しいパッケージをインストールする際に障害となるインストール済みパッケージを自動的にアンインストールします。
5.2.1.3. 不適合性、Breaks
フィールド
Breaks
フィールドは Conflicts
フィールドとよく似た効果を持っていますが、特別な意味があります。すなわち、Breaks
フィールドを持つパッケージは Breaks
フィールドに指定された他のパッケージ (または他のパッケージの特定バージョン) を「破壊する」という意味があります。一般的に、このような 2 つのパッケージの不適合性は一時的なもので、Breaks
フィールドでは不適合性がある特定のバージョンだけを指定します。
dpkg
はインストール済みのパッケージを破壊するようなパッケージのインストールを拒否します。apt
は破壊されるパッケージを新しいバージョンに更新することで (新しいバージョンではこの問題が修正され、両パッケージが適合すると期待されます) この問題の解決を試みます。
この手の状況は更新によって後方互換性がなくなる場合に起こりうるかもしれません。具体的に言えば、新しいバージョンが古いバージョンと同時に動かない場合、特別な設定をしないと別のプログラムがうまく動かない場合です。Breaks
フィールドはユーザがこのような問題に遭遇することがないようにしています。
5.2.1.4. 提供されるアイテム、Provides
フィールド
Provides
フィールドはとても興味深い「仮想パッケージ」の構想を生み出しました。Provides
フィールドは多くの役割を持っていますが、特に重要な 2 つを説明します。最初の役割は、Provides
フィールドを持つパッケージは Provides
フィールドに指定された一般的なサービスを意味する仮想パッケージに関連付けられている (サービスを「提供する」のは Provides
フィールドを持つパッケージ自身です)、という意味を持たせる役割です。2 番目の役割は、Provides
フィールドを持つパッケージは Provides
フィールドに指定されたパッケージの機能を完全に置き換えており、Provides
フィールドに指定されたパッケージの代替として Provides
フィールドに指定されたパッケージに依存する他のパッケージの依存関係を満足できる、という意味を持たせる役割です。そのため、あるパッケージの機能を代替する別パッケージを必ずしも同じパッケージ名を使わずに作ることが可能です。
最初の場合について、例を挙げて詳細に議論しましょう。すなわち、すべてのメールサーバ、たとえば 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 (および Stretch に含まれる 5.24 などのその後のバージョン) では標準モジュールに組み込まれました。このため、perl パッケージはバージョン 5.8 から Provides: libdigest-md5-perl
を宣言しています。そうすれば、ユーザが Perl 5.8 (とそれより新しいバージョン) を持っている場合、libdigest-md5-perl に依存するパッケージの依存関係を満足させることができるからです。そして最終的に実体を持つ libdigest-md5-perl パッケージは削除されました。なぜなら、古い Perl バージョンが削除されたことで実体を持つ libdigest-md5-perl パッケージはもはや存在意義がなくなったからです。
この機能はとても役立ちます。なぜなら、開発方向性の変化を予測することは絶対に不可能ですし、パッケージ名を変更したり他の時代遅れのソフトウェアを自動に置き換えたりすることを可能な状態にしておくことが必要だからです。
かつて仮想パッケージにはいくつかの制限がありました。最も重要な制限はバージョン番号がなかったことでした。先に挙げた例に戻ると、Perl 5.10 が存在する場合、Depends: libdigest-md5-perl (>= 1.6)
という依存関係は満足されています (正しく言えば、十中八九は満足されています)。しかしながら、パッケージシステムはこの依存関係が満足されていることに気が付きませんでした。そして、パッケージシステムは指定されたバージョンが一致しないと仮定して最もリスクの低いオプションを選んでいました。
この制限は dpkg 1.17.11 で撤廃され、Stretch ではもはや関係のない話です。パッケージは自分自身が提供する仮想パッケージにバージョン番号を付けることが可能です。これを行うには Provides: libdigest-md5-perl (= 1.8)
などのようにします。
5.2.1.5. ファイルの置き換え、Replaces
フィールド
Replaces
フィールドは、Replaces
フィールドを持つパッケージが Replaces
フィールドに指定された他のパッケージからも提供されるファイルを含んでおり、合法的に Replaces
フィールドに指定されたパッケージから提供されたファイルを置き換える権利を持っていることを示すためのものです。Replaces
フィールドがなければ、dpkg
は別パッケージから提供されたファイルをインストール中のパッケージに含まれるファイルで置き換えることはできません。すなわちこれは他のパッケージのファイルは上書きできないことを意味しています (技術的に言えば、--force-overwrite
オプションを付けることで強制的に上書き可能ですが、これは一般に認められていません)。このような dpkg
の挙動により潜在的な問題を識別できるようになりますし、メンテナはこのフィールドを追加する前に問題の原因を追及できるようになります。
Replaces
フィールドはパッケージ名が変更された時やパッケージが別のパッケージに統合された時に使われます。この状況はメンテナが同じソースパッケージから複数のバイナリパッケージを作成し、各バイナリパッケージから異なるファイルを配布するように方針を決めた場合に発生します。つまり、古いパッケージに含まれていたファイルがソースパッケージは同じでもバイナリパッケージの名前が異なる新しいパッケージに含まれるようになった場合に発生します。
インストール済みパッケージのすべてのファイルが置き換えられたら、このパッケージは削除されたとみなされます。最後に、Replaces
フィールドは衝突がある場合に dpkg
に置き換えられたパッケージを削除させる際に使われます。
それぞれの Debian パッケージには control
ファイルだけでなく control.tar.gz
アーカイブが含まれており、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
スクリプトです。config
スクリプトはパッケージから提供され、debconf
を用いて設定に必要な情報をユーザに入力させるために使われます。ユーザからの情報は debconf
データベースに保存され、後から利用されます。このスクリプトは通常 apt
によって各パッケージインストールの前に実行され、処理が始まるとすべての質問をまとめてユーザに尋ねます。インストール前後に実行されるスクリプトは、ユーザの希望を反映させるために、debconf
データベースに保存された情報を利用します。
前の節で既に説明したメンテナスクリプトと管理情報に加えて、Debian パッケージの
control.tar.gz
アーカイブは興味深いファイルを含んでいる場合があります。1 つ目は
md5sums
です。
md5sums
にはパッケージに含まれる全ファイルの MD5 チェックサムが列挙されています。
md5sums
のおかげで
dpkg --verify
はインストール以降ファイルが変更されたか否かを判断できるようになります (詳しくは
第 14.3.3.1 節「dpkg --verify
を使ったパッケージ監視」を参照してください)。パッケージが
md5sums
を提供しない場合、
dpkg
がインストール時に動的に
md5sums
を生成します (そして他の管理情報ファイルと同様に dpkg データベースに内容を保存します)。
conffiles
では、設定ファイルとして取り扱われるべきパッケージファイルが指定されています。管理者は設定ファイルを変更でき、dpkg
はパッケージの更新中に設定ファイルの変更を保存しようとします。
実際のところ、システムに現存する設定ファイルをパッケージから提供された設定ファイルで更新する際に dpkg
はできるだけ賢明に振る舞います。以下に dpkg
のデフォルトの設定ファイル更新処理規則を述べます。パッケージの更新前後でパッケージから提供される標準設定ファイルの内容が同じ場合、dpkg
は何もしません。しかしながら、パッケージの更新前後でパッケージから提供される標準設定ファイルの内容が違う場合、dpkg
はシステムに現存する設定ファイルを更新しようとします。ここでさらに 2 つの場合が考えられます。システムに現存する設定ファイルと古いバージョンのパッケージから提供される標準設定ファイルの内容が同じ場合、dpkg
は自動的に新しいパッケージから提供される標準設定ファイルをパッケージ更新完了後の設定ファイルとして採用します。一方で、システムに現存する設定ファイルと古いバージョンのパッケージから提供される標準設定ファイルの内容が違う場合、dpkg
は管理者に対してシステムに現存する設定ファイルまたは新しいバージョンのパッケージから提供される標準設定ファイルのどちらを更新後の設定ファイルとして採用するかを尋ねます。この判断を手助けするために、dpkg
は「diff
」を使って 2 つの設定ファイルの内容の違いを表示します。管理者が現存する設定ファイルを選んだ場合、新しいバージョンのパッケージから提供される標準設定ファイルは同じ場所にファイル名の末尾に .dpkg-dist
を追加して保存されます。管理者が新しいバージョンのパッケージから提供される標準設定ファイルを選んだ場合、現存する設定ファイルは同じ場所にファイル名の末尾に .dpkg-old
を追加して保存されます。この段階では、一時的に dpkg
の処理を中断してファイルを編集したり、もう一度バージョン間の違いを表示したり (先と同様に diff
コマンドを実行する) することも可能です。