ソースコード・リテラシーのススメ

第17回バイナリパッケージを読む

最近、ようやく朝夕が涼しくなってきました。今年の夏は全国的にも暑かったようですが、筆者の使っているPCには、最近の省電力志向のCPUが出てくる以前の、Xeon DualやAthlon64といった消費電力を気にしない世代のCPUが乗っているため、この夏の暑さは余計にこたえました。Plamo-4.5のリリースに向けた作業を進めているためPCを動かしている時間も長くなりがちで、余計に暑さが厳しかったように感じます。

筆者がまとめ役をやっているPlamo Linuxではパッケージ管理機能もごくシンプルなため、利用者自身が自前のバイナリパッケージを作成することも多いのですが、最近の大手ディストリビューションでは、洗練されたパッケージ管理システムが処理を自動的に行うため、ユーザがバイナリパッケージを調べたり、自前で作ったパッケージをインストールする機会はほとんど無いようです。

バイナリパッケージを表に出さないのは、一般ユーザへと裾野を広げるためには有効かも知れませんが、オープンソースソフトウェアの最大の魅力はソースコードが公開され、自由に設定を調整したり、改造したりできることなので、筆者のような人間から見ると、バイナリパッケージがブラックボックス化していくことには寂しさを感じます。

そこで今回は、代表的なバイナリパッケージの構造を簡単に紹介し、パッケージ管理ツールを介さずにバイナリパッケージの中身を調べる方法を紹介してみようと思います。

tgz形式

tgzは、Plamo Linuxやその元となったSlackwareが採用しているパッケージ形式で、パッケージの拡張子が .tgzになっていることからこの名前で呼ばれています。拡張子のtgzは、tar.gzの略で、tarコマンドで1つにまとめたファイルを、gzipコマンドで圧縮していることを示します。

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形式では、ソフトウェアのバージョンや提供する機能、必要とする外部ライブラリやコマンドといった管理情報は、インストールするファイル群と分離してヘッダー部に記録し、実際のファイルやディレクトリは cpio(CoPy I/O)という書庫ソフトウェアを使って1つにまとめて圧縮し、管理情報の後ろに収める、という形式を取っています。このような形式にすることで、パッケージのヘッダ部分を調べるだけで動作に必要な環境が整っているかをチェックできるようにしています。

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用のパッケージにすることも可能です。

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.gzdata.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

さて、これで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の起動画面
図1 rpmから取り出したxvの起動画面

タイトルバーに表示されているように、ようやくFedora 9用のxvを動かすことができました。

この例では何とかなりましたが、ライブラリによってはバージョンのわずかな違いでも提供する機能が異なっていたり、新しいバージョンの glibc の機能を要求したりして動かせないことがあるので注意が必要です。

今回は手動でパッケージの中身を取り出して直接操作しましたが、異なるパッケージ間の相互変換をするalienというツールも開発されているので、必要に応じて試してみるのもいいでしょう。

おすすめ記事

記事・ニュース一覧