前回まで2回に渡ってPlamo Linuxのビルドスクリプトの内部構造を紹介しました。今回は、このビルドスクリプトによって作成されるPlamo Linux用のパッケージと、そのパッケージを操作するパッケージ管理ツールについて紹介しましょう。
Plamo Linuxのパッケージ
Plamo Linuxのパッケージは、インストールしたいファイル類をルートディレクトリからの相対位置に配置した状態を作り、それらをtarコマンドで1つのアーカイブにまとめてgzipやxzで圧縮しただけのシンプルな形式になっています。
この形式は、アーカイバであるtarとgzipやxzのような圧縮ツールのみで操作できるため、Slackware Linuxに代表される最初期のLinuxディストリビューションでは広く使われていました。
Plamo Linuxでは、gzipで圧縮した場合の拡張子をtgz、xzで圧縮した場合の拡張子をtxzとしており、両者を合わせて「tgz/txz形式」と呼んでいます。これらは「パッケージ」とは呼んでいるものの、実質はtarアーカイブを圧縮したファイルに過ぎないので、圧縮したtar形式を認識できるless等を使えば、直接中身を見ることができます。
また、txzの拡張子をtar+xzだと認識するためのパッチをあてたEmacsならば、Emacsのdired機能を用いて、パッケージ内部のファイルを直接開くことも可能です。
このようにしてPlamo Linuxのパッケージをあれこれ眺めていると、複数のパッケージにinstall/doinst.shというファイルがあることに気づくでしょう。前述のように、Plamo Linuxのパッケージはインストールしたいファイルをtarで固めただけの単純な構造なので、同じ名前のファイルが複数のパッケージにあると、後からインストールしたパッケージが上書きしてしまうはずですし、そもそもinstall/doinst.shなんてファイルはソフトウェアをmake installした際には存在しなかったファイルです。
実はこのinstall/doinst.shというファイルは、Plamo Linuxのパッケージをインストールする際に、tarを使ってアーカイブを展開する以外の処理が必要な場合、その処理を記述するために用意されています。
このinstall/doinst.shは、パッケージを展開後にシェルによって実行されます。図1のEmacsの上のウィンドウに表示しているファイルが、"sysfsutils-2.1.0-x86_64-P2.txz"パッケージに含まれているinstall/doinst.shファイルで、この例では、libsysfs.so.2.0.1からlibsysfs.so.2とlibsysfs.soへのシンボリックリンクを作成する処理を行なっています。
実のところ、install/doinst.shファイルで行う処理は、9割以上、パッケージに含まれているシンボリックリンクの再作成です。
tarアーカイブの中にシンボリックリンクが含まれていると、展開する際に展開先のファイルを書き替えてしまったり、指定したディレクトリの外部にファイルを書き出してしまう場合があります。そのため、tarの実装によっては、明示的に指定しない限りシンボリックリンクをアーカイブに含めなかったり、アーカイブ中のシンボリックリンクは展開しないようになっていることがあります。
そのためtgz/txz形式では、シンボリックリンクを含むパッケージでは、安全のためにシンボリックリンクは後から張り直すことにしています。この処理は、パッケージを作成するmakepkgコマンドが担当しており、パッケージとしてまとめるディレクトリ階層内にシンボリックリンクがあれば、いったんそれを削除して、install/doinst.sh内で再作成するようになっています。
install/doinst.shには、シンボリックリンクを再作成する以外に、"initpkg"と呼ばれるもうひとつ重要な機能がありますが、 それについては回を改めて紹介しましょう。
Plamo Linuxのパッケージ管理ツール
前節で紹介した tgz/txz形式のパッケージをインストールしたり、インストール済みのパッケージを削除するためのコマンドが/sbin/installpkgと/sbin/removepkgです。
先に紹介したように、tgz/txz形式のパッケージはtarアーカイブを圧縮しただけの単純な形式なので、gzipやxz,tarといったLinux環境の基本コマンドのみで操作することができます。そのためinstallpkgもシェルスクリプトで書かれており、圧縮データの伸長やtarアーカイブの展開には外部コマンドを使っています。
installpkgやremovepkgは「パッケージ管理ツール」として、インストールされたパッケージの情報を管理する必要があります。Plamo Linuxでは、これらの管理情報は/var/log/packagesディレクトリや/var/log/scriptsディレクトリに記録されています。
installpkgがパッケージをインストールする際には、インストール済のパッケージのデータが記録されている/var/log/packagesディレクトリを調べ、同名のパッケージがすでにインストールされていないかをチェックします。このチェックの際には、パッケージの名前全体ではなく、basename部のみがチェックの対象となります。
Plamo Linuxのパッケージ名は"glibc-2.16.0-x86_64-P5.txz"のようにハイフン(-)で区切られた4つの部分と拡張子から構成されています。ハイフンで区切られた4つの部分はそれぞれbasename部、version部、arch部、build部と呼んでいます。
たとえば"glibc-2.16.0-x86_64-P5.txz"の場合、basenameが"glibc"、versionが"2.16.0"、archが"x86_64"でbuildが"P5"ということになります。通常、basenameにはそのソフトウェアの名称を、versionにはパッケージ化時に採用したバージョンを設定します。以下、archはi586やx86_64といったCPUアーキテクチャの種類、buildは同じバージョン内で設定を変えてビルドしたパッケージを区別するための番号です。
/var/log/packagesディレクトリに同じbasenameのファイルが存在しなければ、installpkgは拡張子に応じてgzipやxzを用いて指定されたパッケージを伸長し、tarを用いて指定された場所にファイルやディレクトリを展開していきます。その後、/var/log/packages/にパッケージのbasenameのファイルを作り、そこにパッケージの情報を記録します。
このファイルには、パッケージの名称やサイズ、インストール元の場所、パッケージの解説といった情報と共に、そのパッケージに含まれるすべてのファイルのリストが記録されます。たとえば"at-3.1.13-x86_64-P1.txz"というパッケージをインストールした際に記録される/var/log/packages/atは以下のような内容になっています。
/var/log/packages/以下に記録されるファイルの書式は、この例のように1から4行目がそのパッケージの名称、圧縮/展開時のサイズ、パッケージのインストール元の記録で、5行目から16行目がパッケージの解説、17行目以下がそのパッケージからインストールされるファイルやディレクトリのリストになっています。
一方、先に紹介したinstall/doinst.shはシェルスクリプトとして別途実行され、実行済みのファイルは/var/log/scripts/以下に保存されます。たとえば、上記"at-3.1.13-x86_64-P1.txz"パッケージのinstall/doinst.shスクリプトは/var/log/scripts/atに保存され、このパッケージが作成したシンボリックリンクを記録しています。
インストール済のパッケージをアンインストールするためのコマンドが/sbin/removepkgです。このコマンドもinstallpkg同様、シェルスクリプトで書かれており、cutやsedといった外部のUNIXの基本コマンドを駆使して、インストール時に記録された/var/log/packages/と/var/log/scripts/以下のファイルから、installpkgが作成したファイルやディレクトリ、シンボリックリンクの情報を抽出し、それらを削除します。
Plamo Linuxを使っていると、セキュリティアップデートなどで既存のパッケージを更新しなければいけないことがよくありますし、設定等を変更した独自パッケージで更新したくなることもあるでしょう。
そのような場合、installpkgとremovepkgだけでは、いったん既存のパッケージをremovepkgでアンインストールしてから、installpkgで新しいバージョンを再インストールする、という作業が必要になります。
このような作業をパッケージごとに繰り返すのは面倒なので、/sbin/updatepkgというコマンドを用意しました。
updatepkgは/var/log/packages/を見て指定されたパッケージがすでにインストールされているかをチェックし、インストールされていなければinstallpkgを起動してそのままインストールします。
すでに同名のパッケージがインストールされていた場合、インストール済のパッケージのバージョン番号やビルド番号を調べ、指定されたパッケージの方が新しそうなら、removepkgを起動して既存のパッケージをアンインストールしてから、installpkgを起動して指定されたパッケージをインストールします。逆に、既存パッケージの方が新しそうな場合はその旨を表示して処理を中断します。
updatepkgではx.y.z形式の標準的なバージョン番号を想定しているため、"openssh-6.1p1-x86_64-P1.txz"のようにバージョン番号に文字が入ったり、gitやsvn等のソースコード管理システムで配布されていて、日付やリビジョン番号などをバージョン番号にしているパッケージの場合は新旧を正しく判定できないことがあります。
そのような場合は、-fオプションを指定すると、バージョンチェックを飛ばして強制的にremovepkgとinstallpkgを連続して実行します。
パッケージ管理ツールと/sbin/installerディレクトリ
Plamo Linuxのパッケージ管理ツールはシェルスクリプトで書かれており、パッケージを操作する際には外部のコマンドを利用しています。
汎用的に設計された小さなコマンドをシェルスクリプトを用いて組み合わせ、最小の手間で最大の効果を得ようとするのはUNIXの伝統的な考え方(Software Tools)で、installpkg/removepkgもそれに基づいた設計になっています。しかし、パッケージ管理ツールの場合、この方法がうまく行かない場合があります。
たとえば、installpkgはtarやgzip,xz、removepkgはsedやcut,rmといった外部コマンドを利用していますが、これらのコマンドを含むパッケージを更新するために古いパッケージをアンインストールしてしまうと、installpkgやremovepkgが使うコマンドも削除されてしまって処理が続行できなくなってしまいます。
より深刻な問題は共有ライブラリの更新時に発生しがちです。特にLinux環境の標準Cライブラリであるglibcパッケージをアップデートしようとして古いバージョンをアンインストールすると、古いライブラリを削除した瞬間にglibcを利用しているすべてのコマンドが動かなくなってしまい、新しいパッケージをインストールするどころか、システム全体が動作しなくなってしまいます。
このような問題を防ぐためにPlamo Linuxでは、installpkgやremovepkgではシステムの標準とは異なる専用のバイナリファイルを使うようにしています。
具体的には/sbin/installer/というディレクトリを用意して、そこに静的リンクで作成したbusyboxとその別名としてハードリンクしたUNIXの基本コマンド類を置き、installpkg等のパッケージ管理ツールではパスを/sbin/installer/に変更して、それらパッケージ管理ツール専用のコマンドを利用することで、システム標準の基本コマンドや共有ライブラリも安全に更新できるようにしています。
もちろん今度は/sbin/installer/以下のファイルをアップデートする際に同様の問題が生じるわけですが、これらのコマンドは用途が限定されていてそう頻繁に更新するものではありませんし、removepkgやinstallpkgでは書き替えられないものの、/usr/bin/tarコマンドを使って上書き更新することは可能なので、それほど致命的な問題にはならないでしょう。
このような小細工を弄してまでtgz/txz形式を使い続けるよりも、より高機能なパッケージ専用形式に乗り替える方が楽ではないか、と思われる方もいるでしょう。しかし、UNIX環境に慣れ親しんだ人間にとっては、特定の用途にしか使えない高機能な専用形式やツールよりも、汎用的な形式やツールを創意と工夫で組み合わせて仕事をこなすSoftware Toolsの考え方の方が好きですし、便利だけど特定の使い方しかできない仕組みよりも、多少不便でもシンプルで平易な仕組みの方が何か問題が発生した時の解決や対策も容易だろうと思っています。