8.2. パッケージ管理

パッケージ管理についての説明を LFS ブックに加えて欲しいとの要望をよく頂きます。 パッケージ管理ツールがあれば、インストールされるファイル類を管理し、パッケージの削除やアップグレードを容易に実現できます。 パッケージ管理ツールでは、バイナリファイルやライブラリファイルだけでなく、設定ファイル類のインストールも取り扱います。 パッケージ管理ツールをどうしたら・・・ いえいえ本節は特定のパッケージ管理ツールを説明するわけでなく、その利用を勧めるものでもありません。 もっと広い意味で、管理手法にはどういったものがあり、どのように動作するかを説明します。 あなたにとって最適なパッケージ管理がこの中にあるかもしれません。 あるいはそれらをいくつか組み合わせて実施することになるかもしれません。 本節ではパッケージのアップグレードを行う際に発生する問題についても触れます。

LFS や BLFS においてパッケージ管理ツールに触れていない理由には以下のものがあります。

ヒントプロジェクト (Hints Project) ページにパッケージ管理についての情報が示されています。 望むものがあるかどうか確認してみてください。

8.2.1. アップグレードに関する問題

パッケージ管理ツールがあれば、各種ソフトウェアの最新版がリリースされた際に容易にアップグレードができます。 全般に LFS ブックや BLFS ブックに示されている作業手順に従えば、新しいバージョンへのアップグレードを行っていくことはできます。 以下ではパッケージをアップグレードする際に注意すべき点、特に稼動中のシステムに対して実施するポイントについて説明します。

  • カーネルをアップグレードする必要がある場合 (たとえば 5.10.17 から 5.10.18 や 5.11.1 へ、など)、これ以外に再ビルドを必要とするものはありません。 カーネルとユーザー空間の境界が適切に定義されているため、システムは動作し続けるはずです。 特に Linux API ヘッダーは、カーネルに伴ってアップグレードする必要もありません (次に説明するように、アップグレードしてはなりません)。 アップグレードしたカーネルは、システムを再起動すれば利用できるようになります。

  • Linux API ヘッダーや Glibc を新しいバージョン (例えば glibc-2.31 から glibc-2.32) にアップグレードする必要が発生した場合は LFS を再構築することが安全です。 必要なパッケージの依存順を知っていれば再構築できるかもしれませんが、これはお勧めしません。

  • 共有ライブラリを提供しているパッケージをアップデートする場合で、そのライブラリ名が変更になったとします。 この場合は、このライブラリに動的リンクを行っていたパッケージは、新たなライブラリに向けてのリンクとなるように再コンパイルすることが必要になります。 (なおパッケージバージョンとライブラリ名には関連性はありません。) たとえば foo-1.2.3 というパッケージがあって、これが共有ライブラリ libfoo.so.1 をインストールしているとします。 そして新バージョン foo-1.2.4 が共有ライブラリ libfoo.so.2 を持っていて、これにアップグレードするものとします。 この場合 libfoo.so.1 に動的リンクを行っていたパッケージは、すべて新ライブラリバージョン libfoo.so.2 へのリンクを行うように再コンパイルしなければなりません。 そのように依存していたパッケージをすべて再コンパイルしてからでないと、古いバージョンのライブラリは削除するべきではありません。

  • 共有ライブラリを提供しているパッケージをアップデートする場合で、そのライブラリ名には変更がなかったとします。 ただしライブラリ名の変更はなくても、ライブラリファイルのバージョン番号が減らされたとします。 (たとえばライブラリ名 libfoo.so.1 はそのまま不変であったとして、ライブラリファイル名が libfoo.so.1.25 から libfoo.so.1.24 に変更となった場合です。) この場合、それまでインストールされていたバージョン(例では libfoo.so.1.25)のライブラリファイルは削除すべきです。 そうしておかないと、ldconfig を実行したときに(自分でコマンドライン実行したり、別のパッケージをインストールする際に実施されたりしたときに)、シンボリックリンク libfoo.so.1 がリセットされますが、それが指し示す先が古いライブラリファイルとなってしまいます。 なぜならバージョン番号がより大きい方なので、そのバージョンの方がより新しいと解釈されるためです。 こういった状況は、パッケージをダウングレードした場合や、パッケージにおけるバージョン番号づけの取り決めが突然変わってしまった場合に起こり得るものです。

  • 共有ライブラリを提供しているパッケージをアップデートする場合で、そのライブラリ名に変更はなかったとします。 ただしそこでは重大な問題(特にセキュリティぜい弱性)が解消されているような場合は、この共有ライブラリにリンクしている実行中プログラムは、すべて再起動してください。 アップグレードした後に、以下のコマンドを root で実行すると、どういったプログラムが古いバージョンのライブラリを利用しているかの一覧が表示されます。 (libfoo の部分は、目的のライブラリ名に置き換えてください。)

    grep -l  -e 'libfoo.*deleted' /proc/*/maps |
       tr -cd 0-9\\n | xargs -r ps u

    OpenSSH を利用してシステムにアクセスしている場合であって、これがリンクするライブラリがアップデートされたとします。 その場合は sshd サービスの再起動が必要です。 またシステムからはいったんログアウトしてログインし直し、その後に上のコマンドをもう一度実行して、削除されたライブラリを利用していないかどうかの確認を行ってください。

    systemd デーモンが(PID 1 として実行されていて)、アップデートしたライブラリにリンクされていた場合は、リブートするのではなく、root ユーザーになって systemctl daemon-reexec を実行すれば再起動できます。

  • バイナリや共有ライブラリが上書きされると、そのバイナリや共有バイナリ内のコードやデータを利用するプロセスがクラッシュすることがあります。 プロセスがクラッシュしないように、バイナリや共有ライブラリを正しく更新する方法は、まず初めに削除を行ってから、新たなものをそこにインストールすることです。 Coreutils が提供する install コマンドは、すでにこの処理が実装されているため、たいていのパッケージにおいて、バイナリやライブラリのインストールに利用されています。 したがってそのような問題に悩まされることは、これまでほとんどなかったはずです。 しかしパッケージの中には (特に BLFS にある Mozilla JS など)、すでにあるファイルを上書きする方式をとっているため、クラッシュするものがあります。 そこでパッケージ更新の前には、それまでの作業を保存して、不要な起動プロセスは停止することが安全です。

8.2.2. パッケージ管理手法

以下に一般的なパッケージ管理手法について示します。 パッケージ管理マネージャーを用いる前に、さまざまな方法を検討し特にそれぞれの欠点も確認してください。

8.2.2.1. すべては頭の中で

そうです。 これもパッケージ管理のやり方の一つです。 いろいろなパッケージに精通していて、どんなファイルがインストールされるか分かっている人もいます。 そんな人はパッケージ管理ツールを必要としません。 あるいはパッケージが更新された際にシステム全体を再構築しようと考えている人なら、やはりパッケージ管理ツールを必要としません。

8.2.2.2. 異なるディレクトリへのインストール

これは最も単純なパッケージ管理のやり方であり、パッケージ管理のためのツールを用いる必要はありません。 個々のパッケージを個別のディレクトリにインストールする方法です。 例えば foo-1.1 というパッケージを /usr/pkg/foo-1.1 ディレクトリにインストールし、この /usr/pkg/foo-1.1 に対するシンボリックリンク /usr/pkg/foo を作成します。 このパッケージの新しいバージョン foo-1.2 をインストールする際には /usr/pkg/foo-1.2 ディレクトリにインストールした上で、先ほどのシンボリックリンクをこのディレクトリを指し示すように置き換えます。

PATHLD_LIBRARY_PATHMANPATHINFOPATHCPPFLAGS といった環境変数に対しては /usr/pkg/foo ディレクトリを加える必要があるかもしれません。 もっともパッケージによっては、このやり方では管理できないものもあります。

8.2.2.3. シンボリックリンク方式による管理

これは一つ前に示したパッケージ管理テクニックの応用です。 各パッケージは同様にインストールします。 ただし先ほどのようなシンボリックリンクを生成するのではなく /usr ディレクトリ階層の中に各ファイルのシンボリックリンクを生成します。 この方法であれば環境変数を追加設定する必要がなくなります。 シンボリックリンクを自動生成することもできますが、パッケージ管理ツールの中にはこの手法を使って構築されているものもあります。 よく知られているものとして Stow、Epkg、Graft、Depot があります。

インストール時には意図的な指示が必要です。 パッケージにとっては /usr にインストールすることが指定されたものとなりますが、実際には /usr/pkg 配下にインストールされるわけです。 このインストール方法は単純なものではありません。 例えば今 libfoo-1.1 というパッケージをインストールするものとします。 以下のようなコマンドでは、このパッケージを正しくインストールできません。

./configure --prefix=/usr/pkg/libfoo/1.1
make
make install

インストール自体は動作しますが、このパッケージに依存している他のパッケージは期待どおりには libfoo を正しくリンクしません。 例えば libfoo をリンクするパッケージをコンパイルする際には /usr/lib/libfoo.so.1 がリンクされると思うかもしれませんが、実際には /usr/pkg/libfoo/1.1/lib/libfoo.so.1 がリンクされることになります。 正しくリンクするためには DESTDIR 変数を使って、パッケージのインストールをうまく仕組む必要があります。 この方法は以下のようにして行います。

./configure --prefix=/usr
make
make DESTDIR=/usr/pkg/libfoo/1.1 install

この手法をサポートするパッケージは数多く存在しますが、そうでないものもあります。 この手法を取り入れていないパッケージに対しては、手作業でインストールすることが必要になります。 またはそういった問題を抱えるパッケージであれば /opt ディレクトリにインストールする方が簡単かもしれません。

8.2.2.4. タイムスタンプによる管理方法

この方法ではパッケージをインストールするにあたって、あるファイルにタイムスタンプが記されます。 インストールの直後に find コマンドを適当なオプション指定により用いることで、インストールされるすべてのファイルのログが生成されます。 これはタイムスタンプファイルの生成の後に行われます。 この方法を用いたパッケージ管理ツールとして install-log があります。

この方法はシンプルであるという利点がありますが、以下の二つの欠点があります。 インストールの際に、いずれかのファイルのタイムスタンプが現在時刻でなかった場合、そういったファイルはパッケージ管理ツールが正しく制御できません。 またこの方法は一つのパッケージだけが、その時にインストールされることを前提とします。 例えば二つのパッケージが二つの異なる端末から同時にインストールされるような場合は、ログファイルが適切に生成されません。

8.2.2.5. インストールスクリプトの追跡管理

この方法はインストールスクリプトが実行するコマンドを記録するものです。 これには以下の二種類の手法があります。

インストールされるライブラリを事前にロードする場所を環境変数 LD_PRELOAD に定めておいてそれからインストールを行う方法です。 パッケージのインストール中には cpinstallmv など、さまざまな実行モジュールにそのライブラリをリンクさせ、ファイルシステムを変更するようなシステムコールを監視することで、そのライブラリがパッケージを追跡管理できるようにします。 この方法を実現するためには、動的リンクする実行モジュールはすべて suid ビット、sgid ビットがオフでなければなりません。 事前にライブラリをロードしておくと、インストール中に予期しない副作用が発生するかもしれません。 したがって、ある程度のテスト確認を行って、パッケージ管理ツールが不具合を引き起こさないこと、しかるべきファイルの記録を取っておくことが必要とされます。

二つめの方法は strace を用いるものです。 これはインストールスクリプトの実行中に発生するシステムコールを記録するものです。

8.2.2.6. パッケージのアーカイブを生成する方法

この方法では、シンボリックリンク方式によるパッケージ管理にて説明したのと同じように、パッケージが個別のディレクトリにインストールされます。 インストールの後は、インストールされたファイルのアーカイブが生成されます。 このアーカイブはローカルPCへのインストールに用いられたり、他のPCへのインストールに利用されたりします。

商用ディストリビューションが採用しているパッケージ管理ツールは、ほとんどがこの方法によるものです。 この方法に従ったパッケージ管理ツールの例に RPM があります。 (これは Linux Standard Base Specification が規定しています。) また pkg-utils、Debian の apt、Gentoo の Portage システムがあります。 このパッケージ管理手法を LFS システムに適用するヒント情報が https://www.linuxfromscratch.org/hints/downloads/files/fakeroot.txt にあります。

パッケージファイルにその依存パッケージ情報まで含めてアーカイブ生成することは、非常に複雑となり LFS の範疇を超えるものです。

Slackware は、パッケージアーカイブに対して tar ベースのシステムを利用しています。 他のパッケージ管理ツールはパッケージの依存性を取り扱いますが、このシステムは意図的にこれを行っていません。 Slackware のパッケージ管理に関する詳細は http://www.slackbook.org/html/package-management.html を参照してください。

8.2.2.7. ユーザー情報をベースとする管理方法

この手法は LFS に固有のものであり Matthias Benkmann により考案されました。 ヒントプロジェクト (Hints Project) から入手することが出来ます。 考え方としては、各パッケージを個々のユーザーが共有ディレクトリにインストールします。 パッケージに属するファイル類は、ユーザーIDを確認することで容易に特定出来るようになります。 この手法の特徴や短所については、複雑な話となるため本節では説明しません。 詳しくは https://www.linuxfromscratch.org/hints/downloads/files/more_control_and_pkg_man.txt に示されているヒントを参照してください。

8.2.3. 他システムへの LFS の配置

LFS システムの利点の一つとして、どのファイルもディスク上のどこに位置していても構わないことです。 他のコンピューターに対してビルドした LFS の複製を作ろうとするなら、それが同等のアーキテクチャーであれば容易に実現できます。 つまり tar コマンドを使って LFS のルートディレクトリを含むパーティション (LFS の基本的なビルドの場合、非圧縮で 250MB 程度) をまとめ、これをネットワーク転送か、あるいは CD-ROM を通じて新しいシステムにコピーし、伸張 (解凍) するだけです。 この場合でも、設定ファイルはいくらか変更することが必要です。 変更が必要となる設定ファイルは以下のとおりです。 /etc/hosts, /etc/fstab, /etc/passwd, /etc/group, /etc/shadow, /etc/ld.so.conf

新しいシステムのハードウェアと元のカーネルに差異があるかもしれないため、カーネルを再ビルドする必要があるでしょう。

[注記]

注記

類似するアーキテクチャーのシステム間にてコピーを行う際には問題が生じるとの報告があります。 例えばインテルアーキテクチャーに対する命令セットは AMD プロセッサーに対するものと完全に一致しているわけではないため、一方の命令セットが後に他方で動作しなくなることも考えられます。

最後に新システムを起動可能とするために 「GRUB を用いたブートプロセスの設定」を設定する必要があります。