前回 まで2回に渡ってPlamo Linuxのビルドスクリプトの内部構造を紹介しました。今回は、このビルドスクリプトによって作成されるPlamo Linux用のパッケージと、そのパッケージを操作するパッケージ管理ツールについて紹介しましょう。
Plamo Linuxのパッケージ
Plamo Linuxのパッケージは、インストールしたいファイル類をルートディレクトリからの相対位置に配置した状態を作り、それらをtar コマンドで1つのアーカイブにまとめてgzip やxz で圧縮しただけのシンプルな形式になっています。
この形式は、アーカイバであるtarとgzipやxzのような圧縮ツールのみで操作できるため、Slackware Linuxに代表される最初期のLinuxディストリビューションでは広く使われていました。
その後、Linuxが一般ユーザに広まるにつれ、ライブラリの依存関係の管理など、パッケージ管理に高度で複雑な機能が求められるようになり、専用ツールが必要なrpmやdebといったパッケージ形式が主流になっていきました。
Plamo Linuxでは、gzipで圧縮した場合の拡張子をtgz、xzで圧縮した場合の拡張子をtxzとしており、両者を合わせて「tgz/txz形式 」と呼んでいます。これらは「パッケージ」とは呼んでいるものの、実質はtarアーカイブを圧縮したファイル に過ぎないので、圧縮したtar形式を認識できるless等を使えば、直接中身を見る ことができます。
$ less zlib-1.2.6-x86_64-P1.txz
drwxr-xr-x root/root 0 2012-02-03 23:53:46 usr/
drwxr-xr-x root/root 0 2012-02-03 23:53:46 usr/lib64/
drwxr-xr-x root/root 0 2012-02-03 23:53:46 usr/lib64/pkgconfig/
-rw-r--r-- root/root 244 2012-02-03 23:53:46 usr/lib64/pkgconfig/zlib.pc
-rw-r--r-- root/root 125154 2012-02-03 23:53:46 usr/lib64/libz.a
-rwxr-xr-x root/root 97292 2012-02-03 23:53:46 usr/lib64/libz.so.1.2.6
drwxr-xr-x root/root 0 2012-02-03 23:53:46 usr/share/
drwxr-xr-x root/root 0 2012-02-03 23:53:46 usr/share/man/
drwxr-xr-x root/root 0 2012-02-03 23:53:46 usr/share/man/man3/
-rw-r--r-- root/root 1984 2012-02-03 23:53:46 usr/share/man/man3/zlib.3.gz
drwxr-xr-x root/root 0 2012-02-03 23:53:46 usr/share/doc/
drwxr-xr-x root/root 0 2012-02-03 23:50:20 usr/share/doc/zlib-1.2.6/
-rw-r--r-- root/root 25109 2012-01-30 03:13:15 usr/share/doc/zlib-1.2.6/ChangeLog.gz
-rw-r--r-- root/root 2474 2012-01-17 09:44:29 usr/share/doc/zlib-1.2.6/README.gz
-rwxr-xr-x root/root 2913 2012-02-03 23:53:46 usr/share/doc/zlib-1.2.6/PlamoBuild.zlib-1.2.6.gz
drwxr-xr-x root/root 0 2012-02-03 23:53:46 usr/include/
-rw-r--r-- root/root 86076 2012-02-03 23:53:46 usr/include/zlib.h
-rw-r--r-- root/root 14351 2012-02-03 23:53:46 usr/include/zconf.h
drwxr-xr-x root/root 0 2012-02-03 23:53:46 install/
-rw-r--r-- root/root 168 2012-02-03 23:53:46 install/doinst.sh
また、txzの拡張子をtar+xzだと認識するためのパッチをあてたEmacsならば、Emacsのdired機能を用いて、パッケージ内部のファイルを直接開く ことも可能です。
図1 Emacs-24.2からパッケージ内のファイルを開く
このようにして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内で再作成するようになっています。
このあたりのtarの動作については「標準」仕様は無いようで、最近のGNU tarでは、tarアーカイブの中のファイルやシンボリックリンクと同名のファイルが展開先のディレクトリにあった場合、まず展開先のディレクトリにある同名のファイルを削除してからシンボリックリンクを展開する、というのがデフォルトの動作になっています。
install/doinst.shには、シンボリックリンクを再作成する以外に、"initpkg"と呼ばれるもうひとつ重要な機能がありますが、 それについては回を改めて紹介しましょう。
Plamo Linuxのパッケージ管理ツール
前節で紹介した tgz/txz形式のパッケージをインストールしたり、インストール済みのパッケージを削除するためのコマンドが/sbin/installpkg と/sbin/removepkg です。
先に紹介したように、tgz/txz形式のパッケージはtarアーカイブを圧縮しただけの単純な形式なので、gzipやxz,tarといったLinux環境の基本コマンドのみ で操作することができます。そのためinstallpkgもシェルスクリプト で書かれており、圧縮データの伸長やtarアーカイブの展開には外部コマンド を使っています。
Plamo Linuxが採用しているinstallpkgやremovepkgは、Slackware Linux 3.xのころのinstallpkg/removepkgを元に、メッセージを日本語化したりPlamo用の改造を加えたりして使っています。
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 は以下のような内容になっています。
1 PACKAGE NAME: at-3.1.13-x86_64-P1
2 COMPRESSED PACKAGE SIZE: 45 K
3 UNCOMPRESSED PACKAGE SIZE: 130 K
4 PACKAGE LOCATION: /var/adm/mount/plamo/00_base/at-3.1.13-x86_64-P1.txz
5 PACKAGE DESCRIPTION:
6 at: 時限実行ツール
7 at:
8 at: at/atd は指定した時刻に登録したコマンドを実行するための
9 at: コマンドです。
10 at:
...
17 FILE LIST:
18 etc/
19 etc/at.deny
20 etc/pam.d/
21 etc/pam.d/atd
22 usr/
23 usr/bin/
24 usr/bin/at
...
55 var/spool/atjobs/.SEQ
56 install/
57 install/doinst.sh
/var/log/packages/以下に記録されるファイルの書式は、この例のように1から4行目がそのパッケージの名称、圧縮/展開時のサイズ、パッケージのインストール元の記録で、5行目から16行目がパッケージの解説、17行目以下がそのパッケージからインストールされるファイルやディレクトリのリストになっています。
パッケージの解説は、そのパッケージが置かれていたディレクトリのdescファイル(たとえば00_base/base.desc)から取り込まれるようになっており、descファイルに記載が抜けていたり、descファイルが無い場所でインストールしたりすると省略されることがあります。
一方、先に紹介したinstall/doinst.sh はシェルスクリプトとして別途実行され、実行済みのファイルは/var/log/scripts/ 以下に保存されます。たとえば、上記"at-3.1.13-x86_64-P1.txz"パッケージのinstall/doinst.shスクリプトは/var/log/scripts/at に保存され、このパッケージが作成したシンボリックリンクを記録しています。
1 ( cd usr/bin ; rm -rf atq )
2 ( cd usr/bin ; ln -sf at atq )
...
11 ( cd usr/share/man/man5 ; rm -rf at.deny.5.gz )
12 ( cd usr/share/man/man5 ; ln -sf at.allow.5.gz at.deny.5.gz )
インストール済のパッケージをアンインストールするためのコマンドが/sbin/removepkg です。このコマンドもinstallpkg同様、シェルスクリプトで書かれており、cutやsedといった外部のUNIXの基本コマンドを駆使して、インストール時に記録された/var/log/packages/と/var/log/scripts/以下のファイルから、installpkgが作成したファイルやディレクトリ、シンボリックリンクの情報を抽出し、それらを削除します。
Plamo Linuxでは、パッケージのインストール時は installpkg at-3.1.13-x86_64-P1.txz のようにパッケージのファイル名を指定しますが、アンインストール時はremovepkg at のようにbasenameだけを指定するのはこのような事情によります。
Plamo Linuxを使っていると、セキュリティアップデートなどで既存のパッケージを更新しなければいけないことがよくありますし、設定等を変更した独自パッケージで更新したくなることもあるでしょう。
そのような場合、installpkgとremovepkgだけでは、いったん既存のパッケージをremovepkgでアンインストール してから、installpkgで新しいバージョンを再インストール する、という作業が必要になります。
このような作業をパッケージごとに繰り返すのは面倒なので、/sbin/updatepkg というコマンドを用意しました。
updatepkgは/var/log/packages/を見て指定されたパッケージがすでにインストールされているかをチェックし、インストールされていなければinstallpkgを起動してそのままインストールします。
すでに同名のパッケージがインストールされていた場合、インストール済のパッケージのバージョン番号 やビルド番号 を調べ、指定されたパッケージの方が新しそうなら、removepkgを起動して既存のパッケージをアンインストールしてから、installpkgを起動して指定されたパッケージをインストールします。逆に、既存パッケージの方が新しそうな場合はその旨を表示して処理を中断します。
$ sudo updatepkg at-3.1.13-x86_64-P1.txz
パスワード:
old:1, new:1
same or newer vesion(at-3.1.13,P1) has been installed.
installation stopped for at-3.1.13-x86_64-P1.txz
updatepkgではx.y.z形式の標準的なバージョン番号 を想定しているため、"openssh-6.1p1-x86_64-P1.txz"のようにバージョン番号に文字が入ったり、gitやsvn等のソースコード管理システムで配布されていて、日付やリビジョン番号などをバージョン番号にしているパッケージの場合は新旧を正しく判定できないことがあります。
そのような場合は、-fオプション を指定すると、バージョンチェックを飛ばして強制的にremovepkgとinstallpkgを連続して実行します。
$ sudo updatepkg -f at-3.1.13-x86_64-P1.txz
Removing package at...
Removing files:
--> Deleting symlink usr/bin/atq
--> Deleting symlink usr/bin/atrm
...
--> Deleting empty directory usr/share/doc/at-3.1.13
--> Deleting empty directory usr/doc/at
at-3.1.13-x86_64-P1 ([required]) のインストール中
PACKAGE DESCRIPTION:
at: 時限実行ツール
at:
at: at/atd は指定した時刻に登録したコマンドを実行するための
at: コマンドです。
at:
at-3.1.13-x86_64-P1 のインストールスクリプトを実行中
パッケージ管理ツールと/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/に変更して、それらパッケージ管理ツール専用のコマンドを利用する ことで、システム標準の基本コマンドや共有ライブラリも安全に更新できるようにしています。
$ ls /sbin/installer
[* bzip2* comm* date* echo* gzip* mkdir* rm* sh* tee* wc*
basename* cat* cp* dialog* expr* ln* mv* rmdir* sort* touch* xz*
busybox* chmod* cut* dirname* grep* ls* paste* sed* tar* uniq* xzcat*
busybox とは、catやgrep、sedといったUNIXの基本的なコマンドをできるだけコンパクトに実装したツールで、組み込み環境などで広く利用されています。なお、busyboxだけでは対応できない機能は、共有ライブラリを使わないように静的リンクしたコマンドを用意しています。
もちろん今度は/sbin/installer/以下のファイルをアップデートする際に同様の問題が生じるわけですが、これらのコマンドは用途が限定されていてそう頻繁に更新するものではありませんし、removepkgやinstallpkgでは書き替えられないものの、/usr/bin/tarコマンドを使って上書き更新することは可能なので、それほど致命的な問題にはならないでしょう。
このような小細工を弄してまでtgz/txz形式を使い続けるよりも、より高機能なパッケージ専用形式に乗り替える方が楽ではないか、と思われる方もいるでしょう。しかし、UNIX環境に慣れ親しんだ人間にとっては、特定の用途にしか使えない高機能な専用形式やツールよりも、汎用的な形式やツールを創意と工夫で組み合わせて仕事をこなすSoftware Tools の考え方の方が好きですし、便利だけど特定の使い方しかできない仕組みよりも、多少不便でもシンプルで平易な仕組みの方が何か問題が発生した時の解決や対策も容易だろうと思っています。