RPMパッケージを作成する時の注意点とか、RPMで"/"(root)ディレクトリを変更してRPMの使うDBの構築やパッケージのインストールを行う場合の自分用メモ。
なお動作検証は CentOS 5.2, RPM 4.4.2 で行っている。
参考資料:
CentOS 5.2 の場合、"/usr/src/redhat" 以下にRPMパッケージの作成やSRPMからのビルドに必要なディレクトリツリーが準備されている。
RPMパッケージを作成する場合は、誤ってシステム本体のファイルシステムやRPMデータベースに書き込んでしまわないよう、ユーザ環境でディレクトリツリーを準備する事が推奨されている。
そのための準備作業としては大まかに、以下のようになる。
実際にやってみる。まずディレクトリツリーの準備だが、幸いシステムのディレクトリツリーが空の状態だったので cpio を使ってごっそりコピーする。
$ pwd /usr/src/redhat $ find . -depth -print | cpio -pvd ~/in_vitro/rpmbuild/ /home/msakamoto/in_vitro/rpmbuild//./BUILD /home/msakamoto/in_vitro/rpmbuild//./SOURCES /home/msakamoto/in_vitro/rpmbuild//./RPMS/athlon /home/msakamoto/in_vitro/rpmbuild//./RPMS/i586 /home/msakamoto/in_vitro/rpmbuild//./RPMS/i686 /home/msakamoto/in_vitro/rpmbuild//./RPMS/noarch /home/msakamoto/in_vitro/rpmbuild//./RPMS/i486 /home/msakamoto/in_vitro/rpmbuild//./RPMS/i386 /home/msakamoto/in_vitro/rpmbuild//./RPMS /home/msakamoto/in_vitro/rpmbuild//./SPECS /home/msakamoto/in_vitro/rpmbuild//./SRPMS 0 blocks
続いて "$HOME/.rpmmacros"を次のように準備する。"%home"マクロについては、"/etc/rpm/macros" に入れておいても良いだろう。
$ cat .rpmmacros %home %{expand:%%(cd; pwd)} %_topdir %{home}/in_vitro/rpmbuild
確認してみる。
$ rpm --eval "%{home}" /home/msakamoto $ rpm --eval "%{_topdir}" /home/msakamoto/in_vitro/rpmbuild $ rpm --showrc | grep _topdir -14: _builddir %{_topdir}/BUILD -14: _rpmdir %{_topdir}/RPMS -14: _sourcedir %{_topdir}/SOURCES -14: _specdir %{_topdir}/SPECS -14: _srcrpmdir %{_topdir}/SRPMS -14: _topdir %{home}/in_vitro/rpmbuild
後は実際に "$HOME/in_vitro/rpmbuiild" ディレクトリの中でSPECファイルやソースファイル群を準備し、rpmbuildを実行すればよい。
なお今回検証したのはあくまでもspecファイルの作成からであり、SRPMのインストールからの作業は検証していない。SRPMのインストールでrpmコマンドを使って上記ユーザー環境にspecファイルやソースファイルを展開するのは不可能に思える。なぜなら、RPMコマンドの"--root"によりchrootして作業するのはroot権限で、一般ユーザではchroot(2)システムコールがエラーになってしまうからだ。
SRPMが手元にあり、そこからユーザー環境のディレクトリツリーでrpmbuildコマンドを実行したい場合は、一旦rpm2cpioでSPECファイルやソースファイル群を取り出した後手動で配置するべきかも知れない。
例:
$ ls sample-1.0-1.src.rpm $ rpm2cpio sample-1.0-1.src.rpm | cpio -t sample-1.0.tar.gz sample.spec 531 blocks $ rpm2cpio sample-1.0-1.src.rpm | cpio -iv sample-1.0.tar.gz sample.spec 531 blocks $ ls sample-1.0-1.src.rpm sample-1.0.tar.gz sample.spec
ちなみに自分は、当初勘違いして "$HOME/.rpmrc" に"%_topdir"設定を書いていて何度やってもエラーになってしまった。よくよくドキュメントを読み、"%"で始まるマクロ設定なので "$HOME/.rpmmacros" であることに気づくまで結構嵌ってしまった。
実験環境の構築などで、システムファイルやRPMデータベースの破壊を回避する為、一種のchroot環境を構築する場合を想定する。
rpmコマンドの "--root" オプションを使う事になるが、内部的に chroot(2) システムコールを使う為、root権限が必要になるので注意する。
基本的な流れとしては以下のようになる。なおファイル・ディレクトリパーミッション周りのトラブルを避ける為、基本的にroot権限で行う事を推奨する(*1)。
以降、パッケージのインストール・アップデート・削除などは "--root", "--dbpath" を指定する。
なお、"/var/lib/rpm"など元々のrpmコマンドが想定しているディレクトリをRPMのDB用に指定した場合は、"--dbpath"は不要。
以下、実際に試してみた例:"--root"が"/opt/rpmroot"で、"--dbpath"が"/var/lib/rpmtest"としてみた。
$ su - # mkdir -p /opt/rpmroot # mkdir -p /opt/rpmroot/var/lib/rpmtest # rpm --initdb -vv --root /opt/rpmroot --dbpath /var/lib/rpmtest D: opening db environment /opt/rpmroot/var/lib/rpmtest/Packages create:cdb:mpool D: opening db index /opt/rpmroot/var/lib/rpmtest/Packages create mode=0x42 D: locked db index /opt/rpmroot/var/lib/rpmtest/Packages D: closed db index /opt/rpmroot/var/lib/rpmtest/Packages D: closed db environment /opt/rpmroot/var/lib/rpmtest/Packages D: May free Score board((nil)) # find /opt/rpmroot/var/lib /opt/rpmroot/var/lib /opt/rpmroot/var/lib/rpmtest /opt/rpmroot/var/lib/rpmtest/__db.001 /opt/rpmroot/var/lib/rpmtest/Packages /opt/rpmroot/var/lib/rpmtest/__db.000 /opt/rpmroot/var/lib/rpmtest/__db.003 /opt/rpmroot/var/lib/rpmtest/__db.002
これでDBの初期化まで完了した。続いて、適当にでっち上げたRPMをインストールしてみる。
# rpm -ivh -vv --nodeps --dbpath /var/lib/rpmtest --root /opt/rpmroot --test sample-1.0-1.i386.rpm D: ============== sample-1.0-1.i386.rpm D: Expected size: 11976 = lead(96)+sigs(180)+pad(4)+data(11696) D: Actual size: 11976 D: sample-1.0-1.i386.rpm: Header SHA1 digest: OK (6c105c09750403c748aaaa321e442518e98663a6) D: added binary package [0] D: found 0 source and 1 binary packages D: ========== recording tsort relations D: ========== tsorting packages (order, #predecessors, #succesors, tree, depth, breadth) D: 0 0 0 0 1 0 +sample-1.0-1.i386 D: installing binary packages D: opening db environment /opt/rpmroot/var/lib/rpmtest/Packages joinenv D: opening db index /opt/rpmroot/var/lib/rpmtest/Packages rdonly mode=0x0 D: locked db index /opt/rpmroot/var/lib/rpmtest/Packages D: mounted filesystems: D: i dev bsize bavail iavail mount point D: 0 0x0000fd00 4096 1212587 2269294 / D: 1 0x00000003 4096 0 -1 /proc D: 2 0x00000000 4096 0 -1 /sys D: 3 0x0000000b 4096 0 -1 /dev/pts D: 4 0x00000801 1024 72066 26060 /boot D: 5 0x00000012 4096 64436 64435 /dev/shm D: 6 0x00000013 4096 0 -1 /proc/sys/fs/binfmt_misc D: 7 0x00000014 4096 0 -1 /var/lib/nfs/rpc_pipefs D: sanity checking 1 elements D: opening db index /opt/rpmroot/var/lib/rpmtest/Name create mode=0x0 D: running pre-transaction scripts D: computing 5 file fingerprints Preparing... D: computing file dispositions D: opening db index /var/lib/rpmtest/Basenames create mode=0x0 ########################################### [100%] D: ========== +++ sample-1.0-1 i386-linux 0x1 D: Expected size: 11976 = lead(96)+sigs(180)+pad(4)+data(11696) D: Actual size: 11976 D: sample-1.0-1: Header SHA1 digest: OK (6c105c09750403c748aaaa321e442518e98663a6) D: install: sample-1.0-1 has 5 files, test = 1 D: running post-transaction scripts D: closed db index /opt/rpmroot/var/lib/rpmtest/Basenames D: closed db index /opt/rpmroot/var/lib/rpmtest/Name D: closed db index /opt/rpmroot/var/lib/rpmtest/Packages D: closed db environment /opt/rpmroot/var/lib/rpmtest/Packages D: May free Score board((nil))
まだ"--test"付なので実際のインストールは行っていない。しかしこの段階でRPMデータベースディレクトリを覗いてみると、幾つかのインデックスファイルが作成されている事が分かる。
# find /opt/rpmroot/var/lib /opt/rpmroot/var/lib /opt/rpmroot/var/lib/rpmtest /opt/rpmroot/var/lib/rpmtest/__db.001 /opt/rpmroot/var/lib/rpmtest/Packages /opt/rpmroot/var/lib/rpmtest/__db.000 /opt/rpmroot/var/lib/rpmtest/__db.003 /opt/rpmroot/var/lib/rpmtest/Basenames /opt/rpmroot/var/lib/rpmtest/__db.002 /opt/rpmroot/var/lib/rpmtest/Name
実際にインストールしてみる。
# rpm -ivh -vv --nodeps --dbpath /var/lib/rpmtest --root /opt/rpmroot sample-1.0-1.i386.rpm D: ============== sample-1.0-1.i386.rpm D: Expected size: 11976 = lead(96)+sigs(180)+pad(4)+data(11696) D: Actual size: 11976 D: sample-1.0-1.i386.rpm: Header SHA1 digest: OK (6c105c09750403c748aaaa321e442518e98663a6) D: added binary package [0] D: found 0 source and 1 binary packages D: ========== recording tsort relations D: ========== tsorting packages (order, #predecessors, #succesors, tree, depth, breadth) D: 0 0 0 0 1 0 +sample-1.0-1.i386 D: installing binary packages D: opening db environment /opt/rpmroot/var/lib/rpmtest/Packages joinenv D: opening db index /opt/rpmroot/var/lib/rpmtest/Packages create mode=0x42 D: locked db index /opt/rpmroot/var/lib/rpmtest/Packages D: mounted filesystems: D: i dev bsize bavail iavail mount point D: 0 0x0000fd00 4096 1212494 2269292 / D: 1 0x00000003 4096 0 -1 /proc D: 2 0x00000000 4096 0 -1 /sys D: 3 0x0000000b 4096 0 -1 /dev/pts D: 4 0x00000801 1024 72066 26060 /boot D: 5 0x00000012 4096 64436 64435 /dev/shm D: 6 0x00000013 4096 0 -1 /proc/sys/fs/binfmt_misc D: 7 0x00000014 4096 0 -1 /var/lib/nfs/rpc_pipefs D: sanity checking 1 elements D: opening db index /opt/rpmroot/var/lib/rpmtest/Name create mode=0x42 D: running pre-transaction scripts D: computing 5 file fingerprints Preparing... D: computing file dispositions D: opening db index /var/lib/rpmtest/Basenames create mode=0x42 ########################################### [100%] D: ========== +++ sample-1.0-1 i386-linux 0x1 D: Expected size: 11976 = lead(96)+sigs(180)+pad(4)+data(11696) D: Actual size: 11976 D: sample-1.0-1: Header SHA1 digest: OK (6c105c09750403c748aaaa321e442518e98663a6) D: install: sample-1.0-1 has 5 files, test = 0 1:sample D: ========== Directories not explicitly included in package: D: 0 /usr/bin/ D: 1 /usr/share/doc/ D: 3 /usr/share/locale/ja/LC_MESSAGES/ D: ========== D: /usr directory created with perms 0755, no context. D: /usr/bin directory created with perms 0755, no context. D: /usr/share directory created with perms 0755, no context. D: /usr/share/doc directory created with perms 0755, no context. D: /usr/share/locale directory created with perms 0755, no context. D: /usr/share/locale/ja directory created with perms 0755, no context. D: /usr/share/locale/ja/LC_MESSAGES directory created with perms 0755, no context. D: fini 100755 1 ( 0, 0) 3276 /usr/bin/sample;4b050e8d D: fini 040755 2 ( 0, 0) 0 /usr/share/doc/sample-1.0 D: fini 100644 1 ( 0, 0) 18002 /usr/share/doc/sample-1.0/COPYING;4b050e8d D: fini 100644 1 ( 0, 0) 0 /usr/share/doc/sample-1.0/README;4b050e8d ########################################### [100%] D: fini 100644 1 ( 0, 0) 618 /usr/share/locale/ja/LC_MESSAGES/sample.mo;4b050e8d GZDIO: 3 reads, 22740 total bytes in 0.002939 secs D: +++ h# 1 Header SHA1 digest: OK (6c105c09750403c748aaaa321e442518e98663a6) D: adding "sample" to Name index. D: adding 5 entries to Basenames index. D: opening db index /var/lib/rpmtest/Group create mode=0x42 D: adding "Applications/Text" to Group index. D: opening db index /var/lib/rpmtest/Requirename create mode=0x42 D: adding 5 entries to Requirename index. D: opening db index /var/lib/rpmtest/Providename create mode=0x42 D: adding "sample" to Providename index. D: opening db index /var/lib/rpmtest/Dirnames create mode=0x42 D: adding 4 entries to Dirnames index. D: opening db index /var/lib/rpmtest/Requireversion create mode=0x42 D: adding 5 entries to Requireversion index. D: opening db index /var/lib/rpmtest/Provideversion create mode=0x42 D: adding "1.0-1" to Provideversion index. D: opening db index /var/lib/rpmtest/Installtid create mode=0x42 D: adding 1 entries to Installtid index. D: opening db index /var/lib/rpmtest/Sigmd5 create mode=0x42 D: adding 1 entries to Sigmd5 index. D: opening db index /var/lib/rpmtest/Sha1header create mode=0x42 D: adding "6c105c09750403c748aaaa321e442518e98663a6" to Sha1header index. D: opening db index /var/lib/rpmtest/Filemd5s create mode=0x42 D: adding 5 entries to Filemd5s index. D: opening db index /var/lib/rpmtest/Triggername create mode=0x42 D: running post-transaction scripts D: closed db index /opt/rpmroot/var/lib/rpmtest/Filemd5s D: closed db index /opt/rpmroot/var/lib/rpmtest/Sha1header D: closed db index /opt/rpmroot/var/lib/rpmtest/Sigmd5 D: closed db index /opt/rpmroot/var/lib/rpmtest/Installtid D: closed db index /opt/rpmroot/var/lib/rpmtest/Provideversion D: closed db index /opt/rpmroot/var/lib/rpmtest/Requireversion D: closed db index /opt/rpmroot/var/lib/rpmtest/Dirnames D: closed db index /opt/rpmroot/var/lib/rpmtest/Triggername D: closed db index /opt/rpmroot/var/lib/rpmtest/Providename D: closed db index /opt/rpmroot/var/lib/rpmtest/Requirename D: closed db index /opt/rpmroot/var/lib/rpmtest/Group D: closed db index /opt/rpmroot/var/lib/rpmtest/Basenames D: closed db index /opt/rpmroot/var/lib/rpmtest/Name D: closed db index /opt/rpmroot/var/lib/rpmtest/Packages D: closed db environment /opt/rpmroot/var/lib/rpmtest/Packages D: May free Score board((nil))
この段階で "/opt/rpmroot" 以下を見てみると、確かにディレクトリ階層とパッケージ中のファイルが配置された事が分かる。
# find /opt/rpmroot /opt/rpmroot /opt/rpmroot/usr /opt/rpmroot/usr/bin /opt/rpmroot/usr/bin/sample /opt/rpmroot/usr/share /opt/rpmroot/usr/share/locale /opt/rpmroot/usr/share/locale/ja /opt/rpmroot/usr/share/locale/ja/LC_MESSAGES /opt/rpmroot/usr/share/locale/ja/LC_MESSAGES/sample.mo /opt/rpmroot/usr/share/doc /opt/rpmroot/usr/share/doc/sample-1.0 /opt/rpmroot/usr/share/doc/sample-1.0/COPYING /opt/rpmroot/usr/share/doc/sample-1.0/README /opt/rpmroot/var /opt/rpmroot/var/lib /opt/rpmroot/var/lib/rpmtest /opt/rpmroot/var/lib/rpmtest/Providename /opt/rpmroot/var/lib/rpmtest/__db.001 /opt/rpmroot/var/lib/rpmtest/Sigmd5 /opt/rpmroot/var/lib/rpmtest/Requirename /opt/rpmroot/var/lib/rpmtest/Triggername /opt/rpmroot/var/lib/rpmtest/Packages /opt/rpmroot/var/lib/rpmtest/Group /opt/rpmroot/var/lib/rpmtest/__db.000 /opt/rpmroot/var/lib/rpmtest/__db.003 /opt/rpmroot/var/lib/rpmtest/Filemd5s /opt/rpmroot/var/lib/rpmtest/Installtid /opt/rpmroot/var/lib/rpmtest/Basenames /opt/rpmroot/var/lib/rpmtest/__db.002 /opt/rpmroot/var/lib/rpmtest/Sha1header /opt/rpmroot/var/lib/rpmtest/Requireversion /opt/rpmroot/var/lib/rpmtest/Name /opt/rpmroot/var/lib/rpmtest/Dirnames /opt/rpmroot/var/lib/rpmtest/Provideversion
"relocatable"なRPMパッケージを作成すれば、chrootの必要性はある程度回避できるようになるが、それもパッケージの作者に依存してしまうので常に有効とは限らない。そうした場合にこのテクニックでシステムから分離してRPMを操作できるようになる。
この記事を書こうとして"--initdb"周りの実験をやり始めた時、最初は一般ユーザでディレクトリを準備していた。
すると、どうしてもパッケージインストールのところでRPMデータベース中の "Basenames" ファイルの作成でエラーになってしまう。
$ mkdir $HOME/rpmroot $ mkdir -p $HOME/rpmroot/var/lib/rpmtest $ rpm --initdb -vv --root $HOME/rpmroot --dbpath /var/lib/rpmtest D: opening db environment /home/msakamoto/rpmroot/var/lib/rpmtest/Packages create:cdb:mpool D: opening db index /home/msakamoto/rpmroot/var/lib/rpmtest/Packages create mode=0x42 D: locked db index /home/msakamoto/rpmroot/var/lib/rpmtest/Packages D: closed db index /home/msakamoto/rpmroot/var/lib/rpmtest/Packages D: closed db environment /home/msakamoto/rpmroot/var/lib/rpmtest/Packages D: May free Score board((nil)) $ find $HOME/rpmroot/var/lib/rpmtest /home/msakamoto/rpmroot/var/lib/rpmtest /home/msakamoto/rpmroot/var/lib/rpmtest/__db.001 /home/msakamoto/rpmroot/var/lib/rpmtest/Packages /home/msakamoto/rpmroot/var/lib/rpmtest/__db.000 /home/msakamoto/rpmroot/var/lib/rpmtest/__db.003 /home/msakamoto/rpmroot/var/lib/rpmtest/__db.002
このように"--initdb"までは順調だが、実際にパッケージのインストールに進むと・・・
$ rpm -ivh -vv --nodeps --dbpath /var/lib/rpmtest --root $HOME/rpmroot --test sample-1.0-1.i386.rpm D: ============== sample-1.0-1.i386.rpm D: Expected size: 11976 = lead(96)+sigs(180)+pad(4)+data(11696) D: Actual size: 11976 D: sample-1.0-1.i386.rpm: Header SHA1 digest: OK (6c105c09750403c748aaaa321e442518e98663a6) D: added binary package [0] D: found 0 source and 1 binary packages D: ========== recording tsort relations D: ========== tsorting packages (order, #predecessors, #succesors, tree, depth, breadth) D: 0 0 0 0 1 0 +sample-1.0-1.i386 D: installing binary packages D: opening db environment /home/msakamoto/rpmroot/var/lib/rpmtest/Packages joinenv D: opening db index /home/msakamoto/rpmroot/var/lib/rpmtest/Packages rdonly mode=0x0 D: locked db index /home/msakamoto/rpmroot/var/lib/rpmtest/Packages D: mounted filesystems: D: i dev bsize bavail iavail mount point D: 0 0x0000fd00 4096 1212504 2269294 / D: 1 0x00000003 4096 0 -1 /proc D: 2 0x00000000 4096 0 -1 /sys D: 3 0x0000000b 4096 0 -1 /dev/pts D: 4 0x00000801 1024 72066 26060 /boot D: 5 0x00000012 4096 64436 64435 /dev/shm D: 6 0x00000013 4096 0 -1 /proc/sys/fs/binfmt_misc D: 7 0x00000014 4096 0 -1 /var/lib/nfs/rpc_pipefs D: sanity checking 1 elements D: opening db index /home/msakamoto/rpmroot/var/lib/rpmtest/Name create mode=0x0 D: running pre-transaction scripts D: computing 5 file fingerprints Preparing... D: computing file dispositions D: opening db index /var/lib/rpmtest/Basenames rdonly mode=0x0 D: closed db index /var/lib/rpmtest/Basenames error: cannot open Basenames index using db3 - No such file or directory (2) セグメンテーション違反です
Basenamesファイルのopenで "No such file or directory" になってしまう。
ファイル名をよくよく見ると、PackagesとNameまでは "/home/..." となっており、Basenamesから "--root" で指定したchrootパスを除去したパス名になっている。
そのため最初は「RPMのバグかな?」と思っていたのだが、色々漁っている内に次の記事に突き当たった。
Why not just run as root?
ということで、「あ、chroot(2)って特権じゃないとだめ?」と今更ながらに気づいた(*2)。
試しにstraceを使ってシステムコールとその結果を表示させてみたところ、確かにchroot(2)がEPERMを返している。
$ strace rpm -ivh -vv --nodeps --dbpath /var/lib/rpmtest --root $HOME/rpmroot \ --test sample-1.0-1.i386.rpm 2>strace.log Preparing... $ more strace.log ... open("/home/msakamoto/rpmroot/var/lib/rpmtest/Name", O_RDONLY|O_LARGEFILE) = 4 ... chdir("/") = 0 chroot("/home/msakamoto/rpmroot/") = -1 EPERM (Operation not permitted) ... stat64("/var/lib/rpmtest/Basenames", 0xbfcb189c) = -1 ENOENT (No such file or directory)
これでようやく、一般ユーザ権限だけのRPM環境を準備する事は不可能で、root権限が必要だと判明した次第。