最近、ようやく朝夕が涼しくなってきました。今年の夏は全国的にも暑かったようですが、筆者の使っているPCには、最近の省電力志向のCPUが出てくる以前の、Xeon DualやAthlon64といった消費電力を気にしない世代のCPUが乗っているため、この夏の暑さは余計にこたえました。Plamo-4.5のリリースに向けた作業を進めているためPCを動かしている時間も長くなりがちで、余計に暑さが厳しかったように感じます。
筆者がまとめ役をやっているPlamo Linuxではパッケージ管理機能もごくシンプルなため、利用者自身が自前のバイナリパッケージを作成することも多いのですが、最近の大手ディストリビューションでは、洗練されたパッケージ管理システムが処理を自動的に行うため、ユーザがバイナリパッケージを調べたり、自前で作ったパッケージをインストールする機会はほとんど無いようです。
バイナリパッケージを表に出さないのは、一般ユーザへと裾野を広げるためには有効かも知れませんが、オープンソースソフトウェアの最大の魅力はソースコードが公開され、自由に設定を調整したり、改造したりできることなので、筆者のような人間から見ると、バイナリパッケージがブラックボックス化していくことには寂しさを感じます。
そこで今回は、代表的なバイナリパッケージの構造を簡単に紹介し、パッケージ管理ツールを介さずにバイナリパッケージの中身を調べる方法を紹介してみようと思います。
tgz形式
tgz は、Plamo Linuxやその元となったSlackwareが採用しているパッケージ形式で、パッケージの拡張子が .tgz になっていることからこの名前で呼ばれています。拡張子のtgzは、tar.gz の略で、tar コマンドで1つにまとめたファイルを、gzip コマンドで圧縮していることを示します。
tar (Tape ARchiver)とは、複数のファイルを一つにまとめる 書庫(アーカイブ)作成ソフトウェア の一種です。書庫作成ソフトウェアには ar (ARchiver)と呼ばれるソフトがあり、arを磁気テープにバックアップするために改造したバージョンがtarの起源のようです。tarはバックアップ用ソフトウェアとして広く普及し、gzipやbzipといった圧縮機能にも対応するようになる一方で、ar はバックアップの用途にはほとんど使われなくなり、最近では主に、複数のオブジェクトファイルを1つにまとめて静的ライブラリを作成する際に使われています。
なお、後述しますが、Debian系のディストリビューションが採用しているdeb形式では、パッケージをまとめる一番外側の枠組みとしてarを採用しています。
tgz形式は、インストールしたいファイルやディレクトリをtarコマンドで1つのファイルにまとめ、gzipコマンドで圧縮しただけの構造なので、「 パッケージ形式」と呼ぶのもおこがましい感がありますが、tarやgzipといった、たいていのLinux/UNIX環境で利用可能なコマンドのみで操作できるので、インストールする前に中身を調べたり、テストのために必要なファイルを取り出すことも容易です。
tgz形式では、実体の存在するファイルはそのまま展開されますが、そのまま展開すると意図しないファイルの書き替えが生じる恐れがあるシンボリックリンクはパッケージ内には含めず、/install/doinst.sh というシェルスクリプトで生成するようになっています。シンボリックリンクに限らず、インストール後にユーザの設定やサービスの追加といった処理が必要な場合も、/install/doinst.shスクリプトでそれらの処理を行うようになっています。
逆に言うと、tgz形式ではパッケージの展開がそのままインストールになっている ため、インストール(=展開)後に何らかの操作を行うことは可能なものの、パッケージをインストールする前に(パッケージに含まれている情報を使って)何らかの操作を行うことは面倒です。このあたりが後述するrpmやdebといった専用のパッケージ形式に比べてやや見劣りする点でしょう。
Slackware/Plamo Linuxでは、installpkg というコマンドでtgz形式のパッケージをインストールするようになっています。
installpkg は指定したパッケージを /(ルート)を基点に展開して、展開後に/install/doinst.shを実行するだけのコマンドですが、展開したパッケージの情報を/var/log/packages ディレクトリに記録しておき、後日、不要になったパッケージをremovepkg コマンドで容易に削除できるようにしています。
rpm形式
最初期のLinuxでは、前述のtgz形式でパッケージを配布していましたが、Linuxが広く使われるようになるにつれ、tgz形式の欠点が明かになってきました。たとえば、あるコマンドが動作するために必要なライブラリや外部コマンドが揃っているかは、本来は事前にチェックして、揃っていなければインストールできないようにすべきですが、tgz形式ではそのような事前処理を組み込むことが容易ではありません。
そこで考案されたのが rpm (RPM Program Manager)形式と呼ばれるパッケージ形式です。
元々rpmはRed Hat社が開発したパッケージ形式で、当初はRedhat Package Manager の略とされていましたが、後にこの形式を広めるために仕様を公開して自由に利用可能にし、名称もRPM Package Manager に変更されました。
rpm形式では、ソフトウェアのバージョンや提供する機能、必要とする外部ライブラリやコマンドといった管理情報は、インストールするファイル群と分離してヘッダー部に記録し、実際のファイルやディレクトリは cpio (CoPy I/O)という書庫ソフトウェアを使って1つにまとめて圧縮し、管理情報の後ろに収める、という形式を取っています。このような形式にすることで、パッケージのヘッダ部分を調べるだけで動作に必要な環境が整っているかをチェックできるようにしています。
cpio というのはar/tarと同等の機能を提供する書庫作成ソフトウェアです。現在ではar/tarもcpioもGNUプロジェクトが公開しているバージョンが広く用いられていますが、歴史的に見るとar/tarはBSD UNIXの世界でよく用いられ、cpioはSysV UNIXの世界でよく用いられていたという違いがあるようです。
機能的にはar/tarもcpioもそれほど大きな違いはありませんが、データの格納方式としてar/tarは書庫の先頭部分に管理情報をまとめて配置するのに対して、cpioではファイルごとに管理情報を分散配置するため、書庫ファイルが途中で壊れた場合はcpioの方が被害は少なくて済む、と言われています。もっとも、壊れていても何とかデータを取り出さねばならないバックアップとは異なり、インストール用のパッケージはたとえ一部でも壊れていれば使いものにならないので、両形式の差は無いといっても過言ではないでしょう。
なお、最近では起動時に使うinitrd (厳密にはinitramfs )にcpio形式が採用されているので、initrdを作成する際にもcpio の世話になります。
rpmパッケージではインストールするファイルやディレクトリこそcpio形式で収められていますが、パッケージの先頭部分には管理情報などがバイナリデータで記録されているため、通常のcpioコマンドでは読み書きすることはできず、パッケージの操作には専用のコマンド(rpm )が必要になります。
rpmコマンドは、パッケージ先頭部分の管理情報をBerkeley DBを用いたデータベースに記録し、依存関係のチェックやアンインストール処理を高速化しています。
もっとも、パッケージの先頭部分にある管理情報を取り除いてしまえば、残りはcpio形式の書庫ファイルなので、rpmコマンドが無くても操作することはできます。rpmパッケージの先頭部分の管理情報を取り除くにはrpm2cpio というコマンドを用います。rpm2cpioはPerlで書かれたスクリプトなので、rpmの無い環境でも利用可能です。
たとえば、以下のコマンドはrpm形式で配布されているOpenOffice.orgのパッケージを、rpmコマンドを使わずに展開した例です。
# rpm2cpio ../openoffice.org-base-2.4.1-9310.i586.rpm | cpio -ivd
CPIO archive found!
opt/openoffice.org2.4
opt/openoffice.org2.4/help
opt/openoffice.org2.4/help/ja
..
opt/openoffice.org2.4/share/xdg
opt/openoffice.org2.4/share/xdg/base.desktop
18453 blocks
cpioの-ivd オプションは、-i がコピーイン(取り出し)モード、-v が処理の冗長表示、-d が必要に応じてディレクトリを作成する、という指定です。
このようにして取り出したファイルは通常のファイルと変りありませんので、そのまま適切なディレクトリにコピーして使ったり、tgz形式にまとめ直してPlamo Linux用のパッケージにすることも可能です。
厳密に言うと、rpm のヘッダ部分に埋め込まれているファイルのパーミッション情報やインストール前後にすべき処理などは上記手法では再現できないので、そのような処理が必要な rpm ファイルは別途工夫する必要があります。
deb形式
deb 形式はDebian GNU/Linuxが採用しているパッケージ形式で、rpm同様、インストールすべきファイル群と管理情報を分離し、事前に必要なライブラリやコマンドが揃っているかをチェックできるようになっています。
rpm形式がインストールすべきファイル群をcpio でまとめて先頭に管理情報を付加するという独自の形式を採用しているのに対し、deb形式ではインストールすべきファイル群と管理情報をそれぞれtar.gz 形式でまとめた上で、それらを ar で1つのファイルにまとめる、という形になっています。
# ar xv openoffice.org-base_2.4.1-17_i386.deb
x - debian-binary
x - control.tar.gz
x - data.tar.gz
# ls
control.tar.gz data.tar.gz debian-binary
このように、1つのdebパッケージの中には、control.tar.gz とdata.tar.gz という2つのtar.gzファイルと、debian-binary というテキストファイルが含まれています。
このうち、debian-binary はdebパッケージのバージョン情報を示しており、最近のdebパッケージでは2.0になっています。
data.tar.gz はインストールすべきファイル群をまとめた書庫ファイルで、control.tar.gz にはその書庫ファイルが必要とするライブラリや外部コマンド、インストールやアンインストールの前後で行う処理を記したスクリプトが収められています。
# tar tvf data.tar.gz
drwxrwxr-x root/root 0 2008-05-30 02:49 ./
drwxr-xr-x root/root 0 2008-05-30 02:49 ./opt/
drwxr-xr-x root/root 0 2008-05-30 02:49 ./opt/openoffice.org2.4/
drwxr-xr-x root/root 0 2008-05-30 02:49 ./opt/openoffice.org2.4/share/
...
# tar tvf control.tar.gz
./
./conffiles
./postrm
./control
./postinst
control.tar.gzに含まれているファイルのうち、control がライブラリ等の依存関係情報、postinstall , postrm がインストール/アンインストールの後に実行すべき処理を記したスクリプト、conffilesは設定ファイルの置き場等を示すファイルです。
debパッケージはrpmパッケージに比べて汎用的なツール(ar, tar, gzip)のみで操作できるため、バイナリを取り出したり、インストール前後の処理を調べることも容易でしょう。
バイナリパッケージ展開の実例
取り出したバイナリファイルは必要なライブラリが揃っていないと動きませんので、まずは ldd で必要なライブラリを調べてみましょう。
たとえば、以下の例はrpm.livna.orgのFedora 9用のレポジトリから入手した xv パッケージ(xv-3.10a.jumbopatch.20070520-5.lvn9.i386.rpm)を展開し、必要なライブラリを確認している例です。
$ rpm2cpio xv-3.10a.jumbopatch.20070520-5.lvn9.i386.rpm | cpio -ivd
CPIO archive found!
./usr/bin/bggen
./usr/bin/vdcomp
./usr/bin/xcmap
./usr/bin/xv
...
2188 blocks
$ ldd ./usr/bin/xv
linux-gate.so.1 => (0xffffe000)
libtiff.so.3 => /usr/lib/libtiff.so.3 (0xb7f65000)
libjpeg.so.62 => /usr/lib/libjpeg.so.62 (0xb7f46000)
libpng12.so.0 => /usr/lib/libpng12.so.0 (0xb7f24000)
libz.so.1 => /usr/lib/libz.so.1 (0xb7f11000)
libjasper.so.1 => not found
libX11.so.6 => /usr/X11/lib/libX11.so.6 (0xb7e2b000)
...
lddでxvに必要なライブラリを調べたところ、libjasperというライブラリが見つかりませんでした。libjasperというのはどういうライブラリか知らなかったのでGoogleで調べたところ、jpeg2000形式の画像を表示するためのライブラリだそうです。
話のタネのつもりで、libjasperはDebian sarge用のバイナリパッケージを試してみましょう。
$ ar xv libjasper-1.701-1_1.701.0-2_i386.deb
x - debian-binary
x - control.tar.gz
x - data.tar.gz
$ tar xvf data.tar.gz
./
./usr/
./usr/lib/
./usr/lib/libjasper-1.701.so.1.0.0
./usr/share/
...
新しく展開したライブラリが見つかるように、LD_LIBRARY_PATH環境変数を設定します。
$ setenv LD_LIBRARY_PATH `pwd`/usr/lib
$ env | grep -i LIBRARY
LD_LIBRARY_PATH=/home/kojima/Xv/usr/lib
手元ではtcshを使っているからsetenvで環境変数を指定していますが、bashならばexport LD_LIBRARY_PATH=`pwd`/usr/lib になります。なお`pwd` は現在のワーキングディレクトリを絶対パスに変換する指定で、この部分を./usr/lib と相対パスで指定してしまうと、別のディレクトリに移動したらライブラリが見つからなくなります。
さて、これでxvは動くでしょうか?
$ ./usr/bin/xv
./usr/bin/xv: error while loading shared libraries: libjasper.so.1: cannot open shared object file: No such file or directory
残念、まだlibjasperが見つからない旨のエラーになってしまいました。エラーメッセージをよく見ると、インストールしたライブラリはlibjasper-1.701.so.1なのに、xvが要求しているライブラリはlibjasper.so.1です。この程度のバージョンの不一致はシンボリックリンクで解決するかも知れません。
$ cd usr/lib
$ ln -sf libjasper-1.701.so.1 libjasper.so.1
$ ../usr/bin/xv
図1 rpmから取り出したxvの起動画面
タイトルバーに表示されているように、ようやくFedora 9用のxvを動かすことができました。
この例では何とかなりましたが、ライブラリによってはバージョンのわずかな違いでも提供する機能が異なっていたり、新しいバージョンの glibc の機能を要求したりして動かせないことがあるので注意が必要です。
今回は手動でパッケージの中身を取り出して直接操作しましたが、異なるパッケージ間の相互変換をするalienというツール も開発されているので、必要に応じて試してみるのもいいでしょう。