Solaris OS で実装されている System V のパッケージ化機能では、ソフトウェア製品をインストールするための強力なツールが提供されます。パッケージの設計者はこれらの機能を利用できます。Solaris OS の一部ではないパッケージ (アンバンドルのパッケージ) では、クラスメカニズムを使用してサーバーおよびクライアントのインストールをカスタマイズできます。再配置可能パッケージは、管理者の要件に応じて設計できます。複雑な製品は、パッケージの依存関係が自動的に解決される複合パッケージのセットとして配信できます。パッケージの設計者は、アップグレードとパッチをカスタマイズできます。パッチを適用したパッケージは、パッチを適用していないパッケージと同じ方法で配信できます。また、製品にバックアウトアーカイブを含めることもできます。
この章の内容は以下のとおりです。
パッケージがインストールされる場所はさまざまな方法を使用して指定することができ、インストール時に動的にインストールベースを変更できることは重要です。インストール時に動的にインストールベースを変更できる場合、管理者は複数のバージョンと複数のアーキテクチャーを容易にインストールできます。
このセクションでは、最初に一般的な方法について説明してから、異機種システムへのインストールを行うアプローチを説明します。
パッケージのインストールを行う管理者は、管理ファイルを使用してパッケージのインストールを制御できます。ただし、パッケージの設計者は、管理ファイルと、設計者が意図するパッケージのインストールを管理者がどのように変更できるかを理解する必要があります。
管理ファイルによって、通常行われるチェックやプロンプトを実行するかどうかが pkgadd コマンドに通知されます。そのため、管理者はパッケージのインストールプロセスと、関連するスクリプトについて理解してから管理ファイルを使用するようにしてください。
基本的な管理デフォルトファイルは、SunOS オペレーティングシステムの /var/sadm/install/admin/default に付属しています。これは、ソフトウェア製品のインストールに関して最も基本的な管理ポリシーを確立するファイルです。このファイルは、出荷時には次のようになっています。
#ident "@(#)default 1.4 92/12/23 SMI" /* SVr4.0 1.5.2.1 */ mail= instance=unique partial=ask runlevel=ask idepend=ask rdepend=ask space=ask setuid=ask conflict=ask action=ask basedir=default |
管理者は、このファイルを編集して新しいデフォルト動作を確立したり、異なる管理ファイルを作成し、pkgadd コマンドに -a オプションを使用してファイルを指定したりすることができます。
管理ファイルでは 11 個のパラメータを定義できますが、すべてのパラメータを定義する必要はありません。詳細については、admin(4) を参照してください。
basedir パラメータは、パッケージをインストールする場合にベースディレクトリを取得する方法を指定します。ほとんどの管理者はこのパラメータを default のままにしますが、basedir は次のいずれかに設定できます。
ask。常に管理者にベースディレクトリを確認します。
絶対パス名。
$PKGINST 構成を含む絶対パス名。常にパッケージインスタンスから派生したベースディレクトリにインストールします。
pkgadd コマンドに引数 -a none を付けて呼び出した場合、常に管理者にベースディレクトリを確認します。ただし、この場合にはファイル内のすべてのパラメータがデフォルト値の quit に設定されます。これにより別の問題が発生する可能性があります。
管理者は、管理ファイルを使用してシステム上のすべてのパッケージのインストールを制御できます。しかし、パッケージの設計者は、しばしば管理者の要望を無視して、代替の管理デフォルトファイルを組み込んでいます。
パッケージの設計者は、管理者ではなく設計者がパッケージのインストールを制御できるように代替管理ファイルを組み込む場合もあります。管理デフォルトファイルの basedir エントリはほかのすべてのベースディレクトリに優先するため、この方法でインストール時に簡単に適切なベースディレクトリを選択することができます。Solaris 2.5 リリースより前のすべてのバージョンの Solaris OS で、これがベースディレクトリを制御するもっとも簡単な方法だと考えられていました。
しかし、製品のインストールに関しては管理者の要望を受け入れる必要があります。インストールを制御するために一時的な管理デフォルトファイルを組み込む方法は、管理者の信用を失うことにつながります。request スクリプトと checkinstall スクリプトを使用して、管理者の監督の下でこれらのインストールを制御するようにしてください。request スクリプトを誠実に使用してプロセスに管理者を関与させることで、System V のパッケージ化は管理者とパッケージの設計者の双方にとって効果的に機能します。
すべての再配置可能パッケージの pkginfo ファイルに、次の形式のエントリでデフォルトベースディレクトリを含める必要があります。
BASEDIR=absolute_path |
これは唯一のデフォルトベースディレクトリです。管理者はインストール時に変更できます。
複数のベースディレクトリが必要なパッケージもありますが、このパラメータを使用してパッケージを配置する利点は、ベースディレクトリが適切に配置され、インストール開始時に有効なディレクトリとして書き込み可能であることが保証されることです。サーバーとクライアントのベースディレクトリの適切なパスが、予約された環境変数の形式ですべての手続きスクリプトで使用可能になります。また、pkginfo -r SUNWstuf コマンドを使用すると、パッケージの現在のインストールベースを表示できます。
checkinstall スクリプトでは、BASEDIR は pkginfo ファイルで定義されたままのパラメータです。まだ条件は付いていません。ターゲットベースディレクトリを検査するには、${PKG_INSTALL_ROOT}$BASEDIR 構成が必要です。つまり、request スクリプトまたは checkinstall スクリプトを使用して、インストール環境の BASEDIR の値を予測可能な結果に変更できます。システムがクライアントの場合でも、preinstall スクリプトを呼び出すときまでに、BASEDIR パラメータは、ターゲットシステム上の実際のベースディレクトリを指す条件付きポインタになります。
request スクリプトでは、SunOS オペレーティングシステムのリリースごとに異なる BASEDIR パラメータが使用されます。request スクリプトの BASEDIR パラメータをテストするには、次のコードを使用して、実際に使用されるベースディレクトリを判断するようにしてください。
# request script constructs base directory if [ ${CLIENT_BASEDIR} ]; then LOCAL_BASE=$BASEDIR else LOCAL_BASE=${PKG_INSTALL_ROOT}$BASEDIR fi |
パッケージに複数のベースディレクトリが必要な場合、パラメータ型パス名を使用して複数のベースディレクトリを確立できます。この方法は一般的になりましたが、次の欠点があります。
パラメータ型パス名を使用するパッケージは通常は絶対パッケージのように機能しますが、pkgadd コマンドでは再配置可能パッケージのように扱われます。BASEDIR パラメータは、使用しない場合でも定義する必要があります。
管理者は、System V ユーティリティーを使用してパッケージのインストールベースを確認することができません。pkginfo -r コマンドは機能しません。
管理者は、確立された方法を使用してパッケージを再配置することができません。これは再配置可能パッケージと呼ばれますが、絶対パッケージとして動作します。
複数のアーキテクチャーまたは複数のバージョンのインストールでは、ターゲットベースディレクトリごとに不測事態対応計画を作成する必要があります。これは、しばしば複数の複雑なクラスアクションスクリプトを指します。
ベースディレクトリを決定するパラメータは pkginfo ファイルで定義されますが、request スクリプトで変更できます。このことが、このアプローチが一般的である主な理由の 1 つです。ただし、欠点は長期にわたるものであるため、この構成は最後の手段として検討するようにしてください。
# pkginfo file PKG=SUNWstuf NAME=software stuff ARCH=sparc VERSION=1.0.0,REV=1.0.5 CATEGORY=application DESC=a set of utilities that do stuff BASEDIR=/ EZDIR=/usr/stuf/EZstuf HRDDIR=/opt/SUNWstuf/HRDstuf VENDOR=Sun Microsystems, Inc. HOTLINE=Please contact your local service provider EMAIL= MAXINST=1000 CLASSES=none PSTAMP=hubert980707141632 |
: 1 1758 1 d none $EZDIR 0775 root bin 1 f none $EZDIR/dirdel 0555 bin bin 40 773 751310229 1 f none $EZDIR/usrdel 0555 bin bin 40 773 751310229 1 f none $EZDIR/filedel 0555 bin bin 40 773 751310229 1 d none $HRDDIR 0775 root bin 1 f none $HRDDIR/mksmart 0555 bin bin 40 773 751310229 1 f none $HRDDIR/mktall 0555 bin bin 40 773 751310229 1 f none $HRDDIR/mkcute 0555 bin bin 40 773 751310229 1 f none $HRDDIR/mkeasy 0555 bin bin 40 773 751310229 1 d none /etc ? ? ? 1 d none /etc/rc2.d ? ? ? 1 f none /etc/rc2.d/S70dostuf 0744 root sys 450 223443 1 i pkginfo 348 28411 760740163 1 i postinstall 323 26475 751309908 1 i postremove 402 33179 751309945 1 i preinstall 321 26254 751310019 1 i preremove 320 26114 751309865 |
複数のバージョンまたは複数のアーキテクチャーで使用可能なパッケージは、必要に応じてベースディレクトリを調査して設計するようにしてください。ベースディレクトリの調査とは、以前のバージョンまたは異なるアーキテクチャーのパッケージがすでにベースディレクトリにインストールされている場合に、インストールされるパッケージがこの問題を解決することです。通常は、わずかに異なる名前で新しいベースディレクトリが作成されます。Solaris 2.5 および互換リリースの request スクリプトと checkinstall スクリプトには、BASEDIR 環境変数を変更する機能があります。これより前のバージョンの Solaris OS にはこの機能はありません。
古いバージョンの Solaris OS でも、request スクリプトにはインストールベース内のディレクトリを再定義する権限があります。request スクリプトは、ほとんどの管理設定をサポートするようにディレクトリを再定義できます。
さまざまなパッケージに対して、アーキテクチャーおよびバージョンごとに一意であることが保証されるベースディレクトリを選択できますが、これによって不要なディレクトリ階層が作成されます。たとえば、SPARC ベースおよび x86 ベースのプロセッサ用の製品では、次のようにプロセッサとバージョンごとにベースディレクトリを編成できます。
ベースディレクトリ |
バージョンおよびプロセッサ |
---|---|
/opt/SUNWstuf/sparc/1.0 |
バージョン 1.0、SPARC |
/opt/SUNWstuf/sparc/1.2 |
バージョン 1.2、SPARC |
/opt/SUNWstuf/x86/1.0 |
バージョン 1.0、x86 |
これでも問題なく動作しますが、名前と数値が管理者にとって意味を持つように扱っています。より適切なアプローチは、管理者に説明して許可された後、これを自動的に実行することです。
つまり、設計者がパッケージのすべてのジョブを実行できます。管理者が手動で実行する必要はありません。設計者は任意のベースディレクトリを割り当て、postinstall スクリプトで透過的に適切なクライアントリンクを確立できます。また、pkgadd コマンドを使用して、パッケージのすべてまたは一部を postinstall スクリプトでクライアントにインストールすることもできます。このパッケージについて通知する必要があるユーザーまたはクライアントを管理者に確認して、PATH 環境変数と /etc ファイルを自動的に更新することもできます。これは、パッケージのインストール時に行った操作が削除時にすべて元に戻される限り許容されます。
インストール時にベースディレクトリを制御する方法は 2 通りあります。1 つ目の方法は、Solaris 2.5 および互換リリースのみに新しいパッケージをインストールする場合に最適です。この方法では、管理者に非常に有用なデータを提供し、複数のバージョンおよびアーキテクチャーのインストールをサポートし、最小限の作業で実行できます。2 つ目の方法は任意のパッケージで使用でき、構築パラメータに対する request スクリプト固有の制御を使用してインストールを実行します。
checkinstall スクリプトは、インストール時に適切なベースディレクトリを選択できます。つまり、ベースディレクトリをディレクトリツリー内の低い位置に配置できます。この例では、/opt/SUNWstuf、/opt/SUNWstuf.1、/opt/SUNWstuf.2 という形式でベースディレクトリを順に増加させます。管理者は、pkginfo コマンドを使用して、各ベースディレクトリにインストールするアーキテクチャーおよびバージョンを決定できます。
SUNWstuf パッケージ (要素となるユーティリティーのセットが含まれる) でこの方法を使用する場合、pkginfo ファイルおよび pkgmap ファイルは次のようになります。
# pkginfo file PKG=SUNWstuf NAME=software stuff ARCH=sparc VERSION=1.0.0,REV=1.0.5 CATEGORY=application DESC=a set of utilities that do stuff BASEDIR=/opt/SUNWstuf VENDOR=Sun Microsystems, Inc. HOTLINE=Please contact your local service provider EMAIL= MAXINST=1000 CLASSES=none daemon PSTAMP=hubert990707141632 |
: 1 1758 1 d none EZstuf 0775 root bin 1 f none EZstuf/dirdel 0555 bin bin 40 773 751310229 1 f none EZstuf/usrdel 0555 bin bin 40 773 751310229 1 f none EZstuf/filedel 0555 bin bin 40 773 751310229 1 d none HRDstuf 0775 root bin 1 f none HRDstuf/mksmart 0555 bin bin 40 773 751310229 1 f none HRDstuf/mktall 0555 bin bin 40 773 751310229 1 f none HRDstuf/mkcute 0555 bin bin 40 773 751310229 1 f none HRDstuf/mkeasy 0555 bin bin 40 773 751310229 1 d none /etc ? ? ? 1 d none /etc/rc2.d ? ? ? 1 f daemon /etc/rc2.d/S70dostuf 0744 root sys 450 223443 1 i pkginfo 348 28411 760740163 1 i postinstall 323 26475 751309908 1 i postremove 402 33179 751309945 1 i preinstall 321 26254 751310019 1 i preremove 320 26114 751309865 1 i i.daemon 509 39560 752978103 1 i r.daemon 320 24573 742152591 |
x86 版の SUNWstuf がすでにサーバーの /opt/SUNWstuf にインストールされていると仮定します。管理者が pkgadd コマンドを使用して SPARC 版をインストールする場合、request スクリプトは x86 版を検出して、インストールに関して管理者と対話する必要があります。
checkinstall スクリプトでは管理者と対話しなくてもベースディレクトリを調査できますが、このような勝手な処理が頻繁に実行されると、プロセスに対する管理者の信頼を失います。
この状況を処理するパッケージの request スクリプトおよび checkinstall スクリプトは、次のようになっています。
# request script for SUNWstuf to walk the BASEDIR parameter. PATH=/usr/sadm/bin:${PATH} # use admin utilities GENMSG="The base directory $LOCAL_BASE already contains a \ different architecture or version of $PKG." OLDMSG="If the option \"-a none\" was used, press the \ key and enter an unused base directory when it is requested." OLDPROMPT="Do you want to overwrite this version? " OLDHELP="\"y\" will replace the installed package, \"n\" will \ stop the installation." SUSPEND="Suspending installation at user request using error \ code 1." MSG="This package could be installed at the unused base directory $WRKNG_BASE." PROMPT="Do you want to use to the proposed base directory? " HELP="A response of \"y\" will install to the proposed directory and continue, \"n\" will request a different directory. If the option \"-a none\" was used, press the key and enter an unused base directory when it is requested." DIRPROMPT="Select a preferred base directory ($WRKNG_BASE) " DIRHELP="The package $PKG will be installed at the location entered." NUBD_MSG="The base directory has changed. Be sure to update \ any applicable search paths with the actual location of the \ binaries which are at $WRKNG_BASE/EZstuf and $WRKNG_BASE/HRDstuf." OldSolaris="" Changed="" Suffix="0" # # Determine if this product is actually installed in the working # base directory. # Product_is_present () { if [ -d $WRKNG_BASE/EZstuf -o -d $WRKNG_BASE/HRDstuf ]; then return 1 else return 0 fi } if [ ${BASEDIR} ]; then # This may be an old version of Solaris. In the latest Solaris # CLIENT_BASEDIR won't be defined yet. In older version it is. if [ ${CLIENT_BASEDIR} ]; then LOCAL_BASE=$BASEDIR OldSolaris="true" else # The base directory hasn't been processed yet LOCAL_BASE=${PKG_INSTALL_ROOT}$BASEDIR fi WRKNG_BASE=$LOCAL_BASE # See if the base directory is already in place and walk it if # possible while [ -d ${WRKNG_BASE} -a Product_is_present ]; do # There is a conflict # Is this an update of the same arch & version? if [ ${UPDATE} ]; then exit 0 # It's out of our hands. else # So this is a different architecture or # version than what is already there. # Walk the base directory Suffix=`expr $Suffix + 1` WRKNG_BASE=$LOCAL_BASE.$Suffix Changed="true" fi done # So now we can propose a base directory that isn't claimed by # any of our other versions. if [ $Changed ]; then puttext "$GENMSG" if [ $OldSolaris ]; then puttext "$OLDMSG" result=`ckyorn -Q -d "a" -h "$OLDHELP" -p "$OLDPROMPT"` if [ $result="n" ]; then puttext "$SUSPEND" exit 1 # suspend installation else exit 0 fi else # The latest functionality is available puttext "$MSG" result=`ckyorn -Q -d "a" -h "$HELP" -p "$PROMPT"` if [ $? -eq 3]; then echo quitinstall >> $1 exit 0 fi if [ $result="n" ]; then WRKNG_BASE=`ckpath -ayw -d "$WRKNG_BASE" \ -h "$DIRHELP" -p "$DIRPROMPT"` else if [ $result="a" ] exit 0 fi fi echo "BASEDIR=$WRKNG_BASE" >> $1 puttext "$NUBD_MSG" fi fi exit 0 |
# checkinstall script for SUNWstuf to politely suspend grep quitinstall $1 if [ $? -eq 0 ]; then exit 3 # politely suspend installation fi exit 0 |
このアプローチは、ベースディレクトリが単に /opt だった場合には正常に機能しません。/opt を調査するのは困難であるため、このパッケージは BASEDIR をより正確に呼び出す必要があります。実際、マウントスキーマによっては不可能な場合もあります。この例では、/opt の下に新しいディレクトリを作成してベースディレクトリを調査しています。これによって問題が発生することはありません。
この例では request スクリプトと checkinstall スクリプトを使用しています。ただし、2.5 リリースより前のバージョンの Solaris では checkinstall スクリプトを実行することはできません。この例の checkinstall スクリプトは、quitinstall という文字列形式の非公開メッセージに応答して、インストールをていねいに停止するために使用されます。Solaris 2.3 リリースでこのスクリプトを実行する場合、checkinstall スクリプトは無視され、request スクリプトはエラーメッセージを表示してインストールを停止します。
Solaris 2.5 および互換リリースより前では、BASEDIR パラメータは読み取り専用パラメータであり、request スクリプトで変更することはできません。このため、(条件付きの CLIENT_BASEDIR 環境変数をテストして) 旧バージョンの SunOS オペレーティングシステムが検出された場合、request スクリプトには継続するか終了するかの 2 つのオプションしかありません。
ソフトウェア製品が旧バージョンの SunOS オペレーティングシステムにインストールされる可能性がある場合、request スクリプトで必要な作業をすべて実行する必要があります。このアプローチを使用すると、複数のディレクトリを操作することもできます。追加のディレクトリが必要な場合、簡単に管理できる製品を提供するには、単一のベースディレクトリの下にディレクトリを含める必要があります。BASEDIR パラメータでは最新の Solaris リリースで使用できる粒度のレベルは提供されませんが、request スクリプトを使用してパラメータ型パスを操作することで、パッケージからベースディレクトリを調査できます。pkginfo ファイルおよび pkgmap ファイルは次のようになります。
# pkginfo file PKG=SUNWstuf NAME=software stuff ARCH=sparc VERSION=1.0.0,REV=1.0.5 CATEGORY=application DESC=a set of utilities that do stuff BASEDIR=/opt SUBBASE=SUNWstuf VENDOR=Sun Microsystems, Inc. HOTLINE=Please contact your local service provider EMAIL= MAXINST=1000 CLASSES=none daemon PSTAMP=hubert990707141632 |
: 1 1758 1 d none $SUBBASE/EZstuf 0775 root bin 1 f none $SUBBASE/EZstuf/dirdel 0555 bin bin 40 773 751310229 1 f none $SUBBASE/EZstuf/usrdel 0555 bin bin 40 773 751310229 1 f none $SUBBASE/EZstuf/filedel 0555 bin bin 40 773 751310229 1 d none $SUBBASE/HRDstuf 0775 root bin 1 f none $SUBBASE/HRDstuf/mksmart 0555 bin bin 40 773 751310229 1 f none $SUBBASE/HRDstuf/mktall 0555 bin bin 40 773 751310229 1 f none $SUBBASE/HRDstuf/mkcute 0555 bin bin 40 773 751310229 1 f none $SUBBASE/HRDstuf/mkeasy 0555 bin bin 40 773 751310229 1 d none /etc ? ? ? 1 d none /etc/rc2.d ? ? ? 1 f daemon /etc/rc2.d/S70dostuf 0744 root sys 450 223443 1 i pkginfo 348 28411 760740163 1 i postinstall 323 26475 751309908 1 i postremove 402 33179 751309945 1 i preinstall 321 26254 751310019 1 i preremove 320 26114 751309865 1 i i.daemon 509 39560 752978103 1 i r.daemon 320 24573 742152591 |
この例は完璧ではありません。pkginfo -r コマンドではインストールベースに /opt が返されますが、これは非常にあいまいです。多くのパッケージが /opt にありますが、少なくともこれは意味のあるディレクトリです。前の例と同様、次の例でも複数のアーキテクチャーおよびバージョンをサポートしています。request スクリプトは、特定のパッケージの要件に合わせて、適切な依存関係を解決することができます。
# request script for SUNWstuf to walk a parametric path PATH=/usr/sadm/bin:${PATH} # use admin utilities MSG="The target directory $LOCAL_BASE already contains \ different architecture or version of $PKG. This package \ could be installed at the unused target directory $WRKNG_BASE." PROMPT="Do you want to use to the proposed directory? " HELP="A response of \"y\" will install to the proposed directory \ and continue, \"n\" will request a different directory. If \ the option \"-a none\" was used, press the <RETURN> key and \ enter an unused base directory when it is requested." DIRPROMPT="Select a relative target directory under $BASEDIR/" DIRHELP="The package $PKG will be installed at the location entered." SUSPEND="Suspending installation at user request using error \ code 1." NUBD_MSG="The location of this package is not the default. Be \ sure to update any applicable search paths with the actual \ location of the binaries which are at $WRKNG_BASE/EZstuf \ and $WRKNG_BASE/HRDstuf." Changed="" Suffix="0" # # Determine if this product is actually installed in the working # base directory. # Product_is_present () { if [ -d $WRKNG_BASE/EZstuf -o -d $WRKNG_BASE/HRDstuf ]; then return 1 else return 0 fi } if [ ${BASEDIR} ]; then # This may be an old version of Solaris. In the latest Solaris # CLIENT_BASEDIR won't be defined yet. In older versions it is. if [ ${CLIENT_BASEDIR} ]; then LOCAL_BASE=$BASEDIR/$SUBBASE else # The base directory hasn't been processed yet LOCAL_BASE=${PKG_INSTALL_ROOT}$BASEDIR/$SUBBASE fi WRKNG_BASE=$LOCAL_BASE # See if the base directory is already in place and walk it if # possible while [ -d ${WRKNG_BASE} -a Product_is_present ]; do # There is a conflict # Is this an update of the same arch & version? if [ ${UPDATE} ]; then exit 0 # It's out of our hands. else # So this is a different architecture or # version than what is already there. # Walk the base directory Suffix=`expr $Suffix + 1` WRKNG_BASE=$LOCAL_BASE.$Suffix Changed="true" fi done # So now we can propose a base directory that isn't claimed by # any of our other versions. if [ $Changed ]; then puttext "$MSG" result=`ckyorn -Q -d "a" -h "$HELP" -p "$PROMPT"` if [ $? -eq 3 ]; then puttext "$SUSPEND" exit 1 fi if [ $result="n" ]; then WRKNG_BASE=`ckpath -lyw -d "$WRKNG_BASE" -h "$DIRHELP" \ -p "$DIRPROMPT"` elif [ $result="a" ]; then exit 0 else exit 1 fi echo SUBBASE=$SUBBASE.$Suffix >> $1 puttext "$NUBD_MSG" fi fi exit 0 |
System V のパッケージ化の背後にあるもともとの概念では、システムごとに 1 つのアーキテクチャーを想定していました。サーバーの概念は設計に関与しませんでした。今では当然、単一のサーバーが複数のアーキテクチャーをサポートしています。つまり、1 つのサーバー上に同じソフトウェアが異なるアーキテクチャーごとに複数存在する場合があります。Solaris パッケージは推奨されるファイルシステムの境界内 (例: / および /usr) に隔離されていますが、サーバーおよび各クライアントの製品データベースでは、必ずしもすべてのインストールでこの分割がサポートされているわけではありません。特定の実装では、まったく異なる構造をサポートして、共通製品データベースを含んでいます。クライアントを異なるバージョンに向けることは簡単ですが、実際に System V のパッケージを異なるベースディレクトリにインストールした場合、管理者にとって複雑な状況をもたらす可能性があります。
パッケージを設計する場合、管理者が新しいバージョンのソフトウェアのインストールに使用する一般的な方法についても検討するようにしてください。管理者は、しばしば現在インストールされているバージョンと共存させて最新バージョンをインストールし、テストしようとします。その手順として、現在のバージョンとは異なるベースディレクトリに新しいバージョンをインストールして、少数の重要ではないクライアントをテストとして新しいバージョンにします。確信が得られたら、管理者はより多くのクライアントを新しいバージョンにします。最終的に、管理者は非常時用にのみ旧バージョンを保持して、最後には削除します。
つまり、現代の異機種システムで使用するパッケージは、管理者がファイルシステム上の任意の適切な場所に配置でき、正常に動作するという意味で本当の再配置をサポートする必要があります。Solaris 2.5 および互換リリースでは、多数の有用なツールが提供され、同じシステムに複数のアーキテクチャーおよびバージョンをクリーンにインストールできます。Solaris 2.4 および互換バージョンでも本当の再配置をサポートしていますが、そのための手順は明白ではありません。
System V ABI は、再配置可能パッケージの背後にある当初の目的は、パッケージのインストールを管理者にとってより便利にすることだったということを含意しています。今では、再配置可能パッケージの必要性はそれ以上のものになっています。利便性だけの問題ではなく、インストール時にアクティブなソフトウェア製品がすでにデフォルトディレクトリにインストールされている可能性が非常に高くなっています。この状況に対応できないパッケージは、既存の製品を上書きするか、インストールに失敗します。しかし、複数のアーキテクチャーおよび複数のバージョンに対応したパッケージは、スムーズにインストールでき、従来の管理方法と十分に互換性がある幅広いオプションを管理者に提供します。
ある意味で、複数のアーキテクチャーの問題と複数のバージョンの問題は同じです。既存のパッケージのバリアントは、ほかのバリアントと共存させてインストールできる必要があります。また、エクスポートされたファイルシステムのクライアントまたはスタンドアロンのコンシューマは、機能を低下させることなく、いずれかのバリアントに変更できる必要もあります。Sun はサーバー上で複数のアーキテクチャーに対応する方法を確立していますが、管理者はこれらの提案に従わない場合もあります。すべてのパッケージは、管理者のインストールに関する合理的な要望に応じることができるようにする必要があります。
この例では、従来の再配置可能パッケージを示します。パッケージは /opt/SUNWstuf に配置され、pkginfo ファイルおよび pkgmap ファイルは次のようになります。
# pkginfo file PKG=SUNWstuf NAME=software stuff ARCH=sparc VERSION=1.0.0,REV=1.0.5 CATEGORY=application DESC=a set of utilities that do stuff BASEDIR=/opt VENDOR=Sun Microsystems, Inc. HOTLINE=Please contact your local service provider EMAIL= MAXINST=1000 CLASSES=none PSTAMP=hubert990707141632 |
: 1 1758 1 d none SUNWstuf 0775 root bin 1 d none SUNWstuf/EZstuf 0775 root bin 1 f none SUNWstuf/EZstuf/dirdel 0555 bin bin 40 773 751310229 1 f none SUNWstuf/EZstuf/usrdel 0555 bin bin 40 773 751310229 1 f none SUNWstuf/EZstuf/filedel 0555 bin bin 40 773 751310229 1 d none SUNWstuf/HRDstuf 0775 root bin 1 f none SUNWstuf/HRDstuf/mksmart 0555 bin bin 40 773 751310229 1 f none SUNWstuf/HRDstuf/mktall 0555 bin bin 40 773 751310229 1 f none SUNWstuf/HRDstuf/mkcute 0555 bin bin 40 773 751310229 1 f none SUNWstuf/HRDstuf/mkeasy 0555 bin bin 40 773 751310229 1 i pkginfo 348 28411 760740163 1 i postinstall 323 26475 751309908 1 i postremove 402 33179 751309945 1 i preinstall 321 26254 751310019 1 i preremove 320 26114 751309865 |
これが従来の方法と呼ばれる理由は、すべてのパッケージオブジェクトが pkginfo ファイルの BASEDIR パラメータで定義されるベースディレクトリにインストールされるためです。たとえば、pkgmap ファイルの最初のオブジェクトは /opt/SUNWstuf ディレクトリにインストールされます。
絶対パッケージは、ファイルシステムの特定のルート (/) にインストールされるパッケージです。絶対パッケージは、複数のバージョンおよびアーキテクチャーの立場から対処することが困難です。原則的に、すべてのパッケージを再配置可能にしてください。ただし、再配置可能パッケージに絶対的な要素を含める合理的な理由もあります。
SUNWstuf パッケージが絶対パッケージの場合、BASEDIR パラメータは pkginfo ファイルでは定義されず、pkgmap ファイルは次のようになります。
: 1 1758 1 d none /opt ? ? ? 1 d none /opt/SUNWstuf 0775 root bin 1 d none /opt/SUNWstuf/EZstuf 0775 root bin 1 f none /opt/SUNWstuf/EZstuf/dirdel 0555 bin bin 40 773 751310229 1 f none /opt/SUNWstuf/EZstuf/usrdel 0555 bin bin 40 773 751310229 1 f none /opt/SUNWstuf/EZstuf/filedel 0555 bin bin 40 773 751310229 1 d none /opt/SUNWstuf/HRDstuf 0775 root bin 1 f none /opt/SUNWstuf/HRDstuf/mksmart 0555 bin bin 40 773 751310229 1 f none /opt/SUNWstuf/HRDstuf/mktall 0555 bin bin 40 773 751310229 1 f none /opt/SUNWstuf/HRDstuf/mkcute 0555 bin bin 40 773 751310229 1 f none /opt/SUNWstuf/HRDstuf/mkeasy 0555 bin bin 40 773 751310229 1 i pkginfo 348 28411 760740163 1 i postinstall 323 26475 751309908 1 i postremove 402 33179 751309945 1 i preinstall 321 26254 751310019 1 i preremove 320 26114 751309865 |
この例では、管理者がインストール時に代替ベースディレクトリを指定した場合、pkgadd コマンドでは無視されます。このパッケージは、常にターゲットシステムの /opt/SUNWstuf にインストールされます。
pkgadd コマンドの -R 引数は予期されるとおりに機能します。たとえば、次のように指定します。
pkgadd -d . -R /export/opt/client3 SUNWstuf |
オブジェクトは /export/opt/client3/opt/SUNWstuf にインストールされますが、これはこのパッケージが再配置可能になることとほぼ同じです。
pkgmap ファイルの /opt ディレクトリに疑問符 (?) を使用しています。これは、既存の属性を変更できないことを示します。これは「デフォルト属性でディレクトリを作成する」ということではありませんが、特定の状況ではそうなる場合もあります。新しいパッケージに固有のディレクトリは、すべての属性を明示的に指定する必要があります。
再配置可能オブジェクトを含むパッケージは、再配置可能パッケージと呼ばれます。再配置可能パッケージは pkgmap ファイルに絶対パスが含まれる場合があるため、これは誤解を招く可能性があります。pkgmap ファイルでルート (/) エントリを使用することで、パッケージの再配置可能な側面を強化できます。再配置可能なエントリとルートエントリの両方が含まれるパッケージは、複合パッケージと呼ばれます。
SUNWstuf パッケージのオブジェクトの 1 つが、実行レベル 2 で実行される起動スクリプトだと仮定します。/etc/rc2.d/S70dostuf ファイルはパッケージの一部としてインストールする必要がありますが、ベースディレクトリに配置することはできません。再配置可能パッケージが唯一の解決方法だとすると、pkginfo および pkgmap は次のようになります。
# pkginfo file PKG=SUNWstuf NAME=software stuff ARCH=sparc VERSION=1.0.0,REV=1.0.5 CATEGORY=application DESC=a set of utilities that do stuff BASEDIR=/ VENDOR=Sun Microsystems, Inc. HOTLINE=Please contact your local service provider EMAIL= MAXINST=1000 CLASSES=none PSTAMP=hubert990707141632 |
: 1 1758 1 d none opt/SUNWstuf/EZstuf 0775 root bin 1 f none opt/SUNWstuf/EZstuf/dirdel 0555 bin bin 40 773 751310229 1 f none opt/SUNWstuf/EZstuf/usrdel 0555 bin bin 40 773 751310229 1 f none opt/SUNWstuf/EZstuf/filedel 0555 bin bin 40 773 751310229 1 d none opt/SUNWstuf/HRDstuf 0775 root bin 1 f none opt/SUNWstuf/HRDstuf/mksmart 0555 bin bin 40 773 751310229 1 f none opt/SUNWstuf/HRDstuf/mktall 0555 bin bin 40 773 751310229 1 f none opt/SUNWstuf/HRDstuf/mkcute 0555 bin bin 40 773 751310229 1 f none opt/SUNWstuf/HRDstuf/mkeasy 0555 bin bin 40 773 751310229 1 d none etc ? ? ? 1 d none etc/rc2.d ? ? ? 1 f none etc/rc2.d/S70dostuf 0744 root sys 450 223443 1 i pkginfo 348 28411 760740163 1 i postinstall 323 26475 751309908 1 i postremove 402 33179 751309945 1 i preinstall 321 26254 751310019 1 i preremove 320 26114 751309865 |
このアプローチと絶対パッケージのアプローチには大きな違いはありません。実際、これは絶対パッケージよりも良い状態です。管理者がこのパッケージの代替ベースディレクトリを指定した場合、このパッケージは機能しません。
このパッケージのファイルは 1 つだけルートと相対的にする必要がありますが、残りは任意の場所に移動できます。複合パッケージを使用してこの問題を解決する方法は、この節の後半で説明します。
この節で説明するアプローチはすべてのパッケージには適用されませんが、異機種システム混在環境にインストールする場合にパフォーマンスを向上できます。このアプローチは、Solaris OS の一部として提供されるパッケージ (バンドル版のパッケージ) にはほとんど適用されませんが、アンバンドルのパッケージでは従来とは異なるパッケージ化を実行できます。
再配置可能パッケージを奨励する理由は、次の要件をサポートするためです。
パッケージを追加または削除しても、インストールされたソフトウェア製品の既存の動作は変わりません。
アンバンドルのパッケージは、新しいパッケージが既存の製品と干渉しないことを保証できるように、/opt の下に配置するようにしてください。
有効な複合パッケージを構築する場合、次の 2 つの規則に従います。
パッケージオブジェクトの大部分が配置される場所に基づいてベースディレクトリを確立します。
パッケージオブジェクトがベースディレクトリ以外の共通ディレクトリ (例: /etc) に配置される場合、prototype ファイルで絶対パス名として指定します。
つまり、「再配置可能」とはオブジェクトを任意の場所にインストールして実行できることを意味するため、ブート時に init で実行される起動スクリプトは再配置可能と見なすことができません。提供されるパッケージで相対パスとして /etc/passwd を指定することに問題はありませんが、配置できる場所は 1 つしかありません。
複合パッケージを構築する場合、インストールされた既存のソフトウェアに干渉しないように絶対パスを処理する必要があります。すべて /opt に含まれるパッケージでは、既存のファイルが存在しないためこの問題を回避できます。/etc のファイルがパッケージに含まれる場合、絶対パス名が相対パス名から予測されるのと同様に機能することを保証する必要があります。次の 2 つの例を考えてみましょう。
エントリがテーブルに追加されるか、オブジェクトがほかのプログラムまたはパッケージによって変更される可能性が高い新しいテーブルです。
オブジェクトを、build、awk、または sed クラスに属するファイルタイプ e として定義します。この作業を実行するスクリプトは、自身を追加するのと同程度効率的に自身を削除する必要があります。
新しいソリッドステートのハードディスクをサポートするには、エントリを /etc/vfstab に追加する必要があります。
pkgmap ファイルのエントリは次のようになります。
1 e sed /etc/vfstab ? ? ? |
request スクリプトは、パッケージで /etc/vfstab を変更するかどうかをオペレータに確認します。オペレータが "no" と答えると、このジョブを手動で実行する方法が表示され、次の処理が実行されます。
echo "CLASSES=none" >> $1 |
オペレータが "yes" と答えると、次の処理が実行されます。
echo "CLASSES=none sed" >> $1 |
これによって、必要な変更を行うクラスアクションスクリプトが起動されます。sed クラスは、パッケージファイル /etc/vfstab が、ターゲットシステム上の同じ名前のファイルに対するインストールおよび削除の両方の処理を含む sed プログラムであることを意味します。
オブジェクトはまったく新しいファイルで、後から編集される可能性は高くありません。または、別のパッケージが所有するファイルを置き換えます。
パッケージオブジェクトをファイルタイプ f として定義して、変更を取り消すことができるクラスアクションスクリプトを使用してインストールします。
ソリッドステートのハードディスクをサポートするために必要な情報を提供するには、/etc に /etc/shdisk.conf という名前の新しいファイルが必要です。pkgmap ファイルのエントリは、次のようになります。
. . . 1 f newetc /etc/shdisk.conf . . . |
クラスアクションスクリプト i.newetc は、/etc に配置する必要があるファイルのインストールに使用されます。このスクリプトは、所定の場所に別のファイルが存在するかどうかを確認します。存在しない場合は新しいファイルをコピーするだけです。所定の場所にファイルが存在する場合、そのファイルをバックアップしてから新しいファイルをインストールします。スクリプト r.newetc は、必要に応じてこれらのファイルを削除して、元のファイルを復元します。インストールスクリプトの重要な部分を次に示します。
# i.newetc while read src dst; do if [ -f $dst ]; then dstfile=`basename $dst` cp $dst $PKGSAV/$dstfile fi cp $src $dst done if [ "${1}" = "ENDOFCLASS" ]; then cd $PKGSAV tar cf SAVE.newetc . $INST_DATADIR/$PKG/install/squish SAVE.newetc fi |
このスクリプトでは、PKGSAV 環境変数を使用して置き換えるファイルのバックアップを格納しています。引数 ENDOFCLASS がスクリプトに渡された場合、これらがこのクラスの最後のエントリであることをスクリプトに通知するのは pkgadd コマンドです。この時点で、パッケージのインストールディレクトリに格納された非公開の圧縮プログラムを使用して、保存されたファイルがアーカイブおよび圧縮されます。
パッケージの更新時には PKGSAV 環境変数の使用は信頼性がありませんが、(たとえばパッチによって) パッケージが更新されない場合、バックアップファイルはセキュリティー保護されています。次の削除スクリプトには、旧バージョンの pkgrm コマンドがスクリプトに PKGSAV 環境変数への正しいパスを渡さないという別の問題に対応するコードが含まれています。
削除スクリプトは次のようになります。
# r.newetc # make sure we have the correct PKGSAV if [ -d $PKG_INSTALL_ROOT$PKGSAV ]; then PKGSAV="$PKG_INSTALL_ROOT$PKGSAV" fi # find the unsquish program UNSQUISH_CMD=`dirname $0`/unsquish while read file; do rm $file done if [ "${1}" = ENDOFCLASS ]; then if [ -f $PKGSAV/SAVE.newetc.sq ]; then $UNSQUISH_CMD $PKGSAV/SAVE.newetc fi if [ -f $PKGSAV/SAVE.newetc ]; then targetdir=dirname $file # get the right directory cd $targetdir tar xf $PKGSAV/SAVE.newetc rm $PKGSAV/SAVE.newetc fi fi |
このスクリプトでは、パッケージデータベースのインストールディレクトリの非公開アンインストールアルゴリズム (unsquish) が使用されます。これはインストール時に pkgadd コマンドによって自動的に実行されます。pkgadd コマンドによって明確にインストール専用として認識されないスクリプトはすべて、pkgrm コマンドで使用するためにこのディレクトリに残されます。このディレクトリの場所を知ることはできませんが、このディレクトリが平坦で、パッケージの適切な情報ファイルおよびインストールスクリプトがすべて含まれていることは信頼できます。このスクリプトでは、 unsquish プログラムが含まれるディレクトリからクラスアクションスクリプトが実行されることが保証されていることに基づいてディレクトリを検索します。
また、このスクリプトでは、ターゲットディレクトリが /etc だけであるとは仮定していません。実際には /export/root/client2/etc である場合もあります。正しいディレクトリは、2 通りの方法のいずれかで構成できます。
${PKG_INSTALL_ROOT}/etc 構成を使用する方法
pkgadd コマンドで渡されるファイルのディレクトリ名を取得する方法 (このスクリプトで実行)
パッケージ内の絶対オブジェクトごとにこのアプローチを使用することで、現在の望ましい動作が変わらないか、少なくとも回復可能であることを保証できます。
次に示すのは、複合パッケージに対して pkginfo ファイルと pkgmap ファイルを使用する例です。
PKG=SUNWstuf NAME=software stuff ARCH=sparc VERSION=1.0.0,REV=1.0.5 CATEGORY=application DESC=a set of utilities that do stuff BASEDIR=/opt VENDOR=Sun Microsystems, Inc. HOTLINE=Please contact your local service provider EMAIL= MAXINST=1000 CLASSES=none daemon PSTAMP=hubert990707141632 |
: 1 1758 1 d none SUNWstuf/EZstuf 0775 root bin 1 f none SUNWstuf/EZstuf/dirdel 0555 bin bin 40 773 751310229 1 f none SUNWstuf/EZstuf/usrdel 0555 bin bin 40 773 751310229 1 f none SUNWstuf/EZstuf/filedel 0555 bin bin 40 773 751310229 1 d none SUNWstuf/HRDstuf 0775 root bin 1 f none SUNWstuf/HRDstuf/mksmart 0555 bin bin 40 773 751310229 1 f none SUNWstuf/HRDstuf/mktall 0555 bin bin 40 773 751310229 1 f none SUNWstuf/HRDstuf/mkcute 0555 bin bin 40 773 751310229 1 f none SUNWstuf/HRDstuf/mkeasy 0555 bin bin 40 773 751310229 1 d none /etc ? ? ? 1 d none /etc/rc2.d ? ? ? 1 e daemon /etc/rc2.d/S70dostuf 0744 root sys 450 223443 1 i i.daemon 509 39560 752978103 1 i pkginfo 348 28411 760740163 1 i postinstall 323 26475 751309908 1 i postremove 402 33179 751309945 1 i preinstall 321 26254 751310019 1 i preremove 320 26114 751309865 1 i r.daemon 320 24573 742152591 |
S70dostuf は daemon クラスに属しますが、これにつながるディレクトリ (インストール時に配置済み) は none クラスに属します。ディレクトリがこのパッケージに固有の場合でも、none クラスのままにしてください。この理由は、最初にディレクトリを作成して、最後に削除する必要がありますが、これは none クラスの場合も常に該当するためです。pkgadd コマンドによってディレクトリが作成されます。このディレクトリは、パッケージからコピーされたり、作成されるクラスアクションスクリプトに渡されたりすることはありません。その代わり、pkgadd コマンドで作成してからインストールクラスアクションスクリプトが呼び出され、削除クラスアクションスクリプトの完了後に、pkgrm コマンドによってディレクトリが削除されます。
つまり、特殊クラスのディレクトリに none クラスのオブジェクトが含まれる場合、pkgrm コマンドでディレクトリを削除しようとすると、ディレクトリが時間内に空にならないので失敗します。none クラスのオブジェクトが特殊クラスのディレクトリに挿入される場合、時間内にオブジェクトを受け入れるディレクトリは存在しません。pkgadd コマンドにより、オブジェクトのインストール時にオンザフライでディレクトリが作成され、最終的に pkgmap 定義を確認した場合にそのディレクトリの属性を同期できない場合があります。
クラスにディレクトリを割り当てる場合は、常に作成と削除の順序を覚えておいてください。
すべてのパッケージをリモートでインストール可能にする必要があります。リモートでインストール可能とは、パッケージをインストールする管理者が、pkgadd コマンドを実行するシステムのルート (/) ファイルシステムにインストールすることを仮定できないということです。手続きスクリプトの 1 つで、ターゲットシステムの /etc/vfstab ファイルを取得する必要がある場合、PKG_INSTALL_ROOT 環境変数を使用する必要があります。つまり、パス名 /etc/vfstab を使用して pkgadd コマンドを実行するシステムの /etc/vfstab ファイルを取得できますが、管理者はクライアントの /export/root/client3 にインストールする場合もあります。パス ${PKG_INSTALL_ROOT}/etc/vfstab は、ターゲットファイルシステムを取得できることが保証されています。
この例では、SUNWstuf パッケージを、ルート (/) ファイルシステムの /opt で構成された client3 にインストールします。このパッケージのほかのバージョンの 1 つが、すでに client3 にインストールされています。また、ベースディレクトリは管理ファイル thisadmin から basedir=/opt/$PKGINST に設定されています。管理ファイルの詳細については、「管理デフォルトファイル」を参照してください。サーバーで実行する pkgadd コマンドは、次のとおりです。
# pkgadd -a thisadmin -R /export/root/client3 SUNWstuf |
次の表は、環境変数と、手続きスクリプトに渡される値の一覧です。
表 6–1 手続きスクリプトに渡される値
環境変数 |
値 |
---|---|
PKGINST |
SUNWstuf.2 |
PKG_INSTALL_ROOT |
/export/root/client3 |
CLIENT_BASEDIR |
/opt/SUNWstuf.2 |
BASEDIR |
/export/root/client3/opt/SUNWstuf.2 |
前の例と同じ状況でサーバーまたはスタンドアロンシステムにインストールするには、次のコマンドを使用します。
# pkgadd -a thisadmin SUNWstuf |
次の表は、環境変数と、手続きスクリプトに渡される値の一覧です。
表 6–2 手続きスクリプトに渡される値
環境変数 |
値 |
---|---|
PKGINST |
SUNWstuf.2 |
PKG_INSTALL_ROOT |
未定義 |
CLIENT_BASEDIR |
/opt/SUNWstuf.2 |
BASEDIR |
/opt/SUNWstuf.2 |
SUNWstuf パッケージにより、サーバーに /export/SUNWstuf/share を作成し、ファイルシステムを共有すると仮定します。パッケージをクライアントシステムにインストールする場合、/etc/vfstab ファイルを更新して、この共有ファイルシステムにマウントする必要があります。これは、CLIENT_BASEDIR 変数を使用できる場合です。
クライアントのエントリは、クライアントのファイルシステムを参照してマウントポイントを指定する必要があります。インストールをサーバーから行う場合でもクライアントから行う場合でも、この行を正確に構成するようにしてください。サーバーのシステム名は $SERVER であるとします。$PKG_INSTALL_ROOT/etc/vfstab に移動して、sed コマンドまたは awk コマンドを使用してクライアントの /etc/vfstab ファイルの次の行を構成します。
$SERVER:/export/SUNWstuf/share - $CLIENT_BASEDIR/usr nfs - yes ro |
たとえば、サーバー universe とクライアントシステム client9 の場合、クライアントシステムの /etc/vfstab ファイルは、次の行になります。
universe:/export/SUNWstuf/share - /opt/SUNWstuf.2/usr nfs - yes ro |
これらのパラメータを正確に使用して、ローカルで構成する場合でもサーバーから構成する場合でも常にクライアントのファイルシステムをマウントします。
パッケージのパッチは、元のパッケージの特定のファイルを上書きするためのスパースパッケージです。供給媒体の容量を節約することを除いて、スパースパッケージを出荷することに実質的な理由はありません。少数のファイルを変更して元のパッケージ全体を出荷したり、ネットワーク上で変更されたパッケージにアクセスできるようにしたりすることもできます。実際に異なっているのはこれらの新しいファイルだけである (その他のファイルは再コンパイルされていない) 場合に限り、pkgadd コマンドで差分をインストールできます。パッケージのパッチについては、次のガイドラインを確認してください。
複雑なシステムでは、パッチ識別システムを確立して、別々の異常な動作を訂正する場合に 2 つのパッチが同じファイルを置き換えることがないようにします。たとえば、Sun のパッチベース番号は、ターゲットファイルの相互排他的なセットに割り当てられます。
パッチをバックアウトできるようにする必要があります。
パッチパッケージのバージョン番号を、元のパッケージのバージョン番号と同じにすることが重要です。次の形式の pkginfo ファイルエントリを個別に使用して、パッケージのパッチステータスを管理するようにしてください。
PATCH=patch_number |
パッチのためにパッケージのバージョンが変わる場合、パッケージの別のインスタンスを作成します。パッチを適用された製品を管理することは極めて困難になります。この連続的なインスタンスパッチの方法は、Solaris OS の早期リリースで一定の利点をもたらしましたが、複雑なシステムの管理は単調なものとなります。
パッチ内のすべてのゾーンパラメータがパッケージ内のゾーンパラメータと一致している必要があります。
Solaris OS を構成するパッケージに関する限り、パッケージデータベースにはパッケージのコピーは 1 つしかありません。ただし、パッチを適用されたインスタンスは複数ある場合があります。インストールされたパッケージから (removef コマンドを使用して) オブジェクトを削除するには、そのファイルを所有するインスタンスを特定する必要があります。
ただし、(Solaris OS の一部ではない) パッケージが、Solaris OS の一部である特定のパッケージのパッチレベルを決定する必要がある場合は問題になります。この問題の解決方法をここで説明します。インストールスクリプトは、ターゲットファイルシステムに格納されないため、重大な影響を与えることなく大きくすることができます。クラスアクションスクリプトおよびその他のさまざまな手続きスクリプトにより、PKGSAV 環境変数 (またはその他の永続的なディレクトリ) を使用して、変更されたファイルを保存し、インストールされたパッチのバックアウトを可能にすることができます。また、request スクリプトで適切な環境変数を設定して、パッチの履歴を監視することもできます。次の節のスクリプトでは、複数のパッチが存在する可能性があることを仮定しています。パッチの番号付けスキーマは、単一のパッケージに適用する場合に意味を持ちます。この場合、個別のパッチ番号は、パッケージ内の機能的に関連したファイルのサブセットを表します。2 つのパッチ番号が異なる場合、同じファイルを変更することはできません。
通常のスパースパッケージをパッチパッケージにするには、次の節で説明するスクリプトをパッケージに組み込みます。最後の 2 つが patch_checkinstall および patch_postinstall という名前であることを除いて、すべてを標準パッケージコンポーネントとして認識できます。パッチのバックアウト機能が必要な場合には、これら 2 つのスクリプトをバックアウトパッケージに組み込むことができます。スクリプトは非常に単純で、さまざまな作業は明確です。
このパッチの方法は、クライアントシステムのパッチに使用できますが、サーバー上のクライアントルートディレクトリには、ユーザーの install または nobody で読み取りできる適切な権限が必要です。
checkinstall スクリプトを使用して、特定のパッケージについてパッチが適切であることを確認します。確認が終わると、パッチ一覧とパッチ情報一覧が作成され、レスポンスファイルに挿入してパッケージデータベースに組み込まれます。
パッチ一覧は現在のパッケージに影響するパッチの一覧です。このパッチ一覧は、インストールされたパッケージの pkginfo ファイルに次のような行で記録されます。
PATCHLIST=patch_id patch_id ... |
パッチ情報一覧は、現在のパッチが依存しているパッチの一覧です。パッチ情報一覧も、pkginfo ファイルに次のような行で記録されます。
PATCH_INFO_103203-01=Installed... Obsoletes:103201-01 Requires: \ Incompatibles: 120134-01 |
これらの行 (およびその形式) は、公開インタフェースとして宣言されています。Solaris パッケージのパッチを出荷する企業は、この一覧を適切に更新するようにしてください。パッチが提供される場合、パッチ内の各パッケージにはこの作業を実行する checkinstall スクリプトが含まれます。同じ checkinstall スクリプトにより、その他のパッチ固有のパラメータも更新されます。これは、直接インスタンスパッチと呼ばれる新しいパッチアーキテクチャーです。
この例では、元のパッケージとそのパッチの両方が同じディレクトリに存在します。2 つの元のパッケージは、SUNWstuf.v1 および SUNWstuf.v2 という名前です。パッチは、 SUNWstuf.p1 および SUNWstuf.p2 という名前です。これによって、手続きスクリプトでこれらのファイルの元のディレクトリを特定するのは非常に困難になる場合があります。PKG パラメータではパッケージ名の点 (.) の後ろがすべて削除され、PKGINST 環境変数はソースインスタンスではなくインストールされたインスタンスを参照するためです。手続きスクリプトでソースディレクトリを特定するには、checkinstall スクリプト (常にソースディレクトリから実行される) で照会を行い、SCRIPTS_DIR 変数として場所を渡します。SUNWstuf というソースディレクトリにパッケージが 1 つしかなかった場合には、手続きスクリプトで $INSTDIR/$PKG を使用して検出できました。
# checkinstall script to control a patch installation. # directory format options. # # @(#)checkinstall 1.6 96/09/27 SMI # # Copyright (c) 1995 by Sun Microsystems, Inc. # All rights reserved # PATH=/usr/sadm/bin:$PATH INFO_DIR=`dirname $0` INFO_DIR=`dirname $INFO_DIR` # one level up NOVERS_MSG="PaTcH_MsG 8 Version $VERSION of $PKG is not installed on this system." ALRDY_MSG="PaTcH_MsG 2 Patch number $Patch_label is already applied." TEMP_MSG="PaTcH_MsG 23 Patch number $Patch_label cannot be applied until all \ restricted patches are backed out." # Read the provided environment from what may have been a request script . $1 # Old systems can't deal with checkinstall scripts anyway if [ "$PATCH_PROGRESSIVE" = "true" ]; then exit 0 fi # # Confirm that the intended version is installed on the system. # if [ "${UPDATE}" != "yes" ]; then echo "$NOVERS_MSG" exit 3 fi # # Confirm that this patch hasn't already been applied and # that no other mix-ups have occurred involving patch versions and # the like. # Skip=0 active_base=`echo $Patch_label | nawk ' { print substr($0, 1, match($0, "Patchvers_pfx")-1) } '` active_inst=`echo $Patch_label | nawk ' { print substr($0, match($0, "Patchvers_pfx")+Patchvers_pfx_lnth) } '` # Is this a restricted patch? if echo $active_base | egrep -s "Patchstrict_str"; then is_restricted="true" # All restricted patches are backoutable echo "PATCH_NO_UNDO=" >> $1 else is_restricted="false" fi for patchappl in ${PATCHLIST}; do # Is this an ordinary patch applying over a restricted patch? if [ $is_restricted = "false" ]; then if echo $patchappl | egrep -s "Patchstrict_str"; then echo "$TEMP_MSG" exit 3; fi fi # Is there a newer version of this patch? appl_base=`echo $patchappl | nawk ' { print substr($0, 1, match($0, "Patchvers_pfx")-1) } '` if [ $appl_base = $active_base ]; then appl_inst=`echo $patchappl | nawk ' { print substr($0, match($0, "Patchvers_pfx")\ +Patchvers_pfx_lnth) } '` result=`expr $appl_inst \> $active_inst` if [ $result -eq 1 ]; then echo "PaTcH_MsG 1 Patch number $Patch_label is \ superceded by the already applied $patchappl." exit 3 elif [ $appl_inst = $active_inst ]; then # Not newer, it's the same if [ "$PATCH_UNCONDITIONAL" = "true" ]; then if [ -d $PKGSAV/$Patch_label ]; then echo "PATCH_NO_UNDO=true" >> $1 fi else echo "$ALRDY_MSG" exit 3; fi fi fi done # Construct a list of applied patches in order echo "PATCHLIST=${PATCHLIST} $Patch_label" >> $1 # # Construct the complete list of patches this one obsoletes # ACTIVE_OBSOLETES=$Obsoletes_label if [ -n "$Obsoletes_label" ]; then # Merge the two lists echo $Obsoletes_label | sed 'y/\ /\n/' | \ nawk -v PatchObsList="$PATCH_OBSOLETES" ' BEGIN { printf("PATCH_OBSOLETES="); PatchCount=split(PatchObsList, PatchObsComp, " "); for(PatchIndex in PatchObsComp) { Atisat=match(PatchObsComp[PatchIndex], "@"); PatchObs[PatchIndex]=substr(PatchObsComp[PatchIndex], \ 0, Atisat-1); PatchObsCnt[PatchIndex]=substr(PatchObsComp\ [PatchIndex], Atisat+1); } } { Inserted=0; for(PatchIndex in PatchObs) { if (PatchObs[PatchIndex] == $0) { if (Inserted == 0) { PatchObsCnt[PatchIndex]=PatchObsCnt\ [PatchIndex]+1; Inserted=1; } else { PatchObsCnt[PatchIndex]=0; } } } if (Inserted == 0) { printf ("%s@1 ", $0); } next; } END { for(PatchIndex in PatchObs) { if ( PatchObsCnt[PatchIndex] != 0) { printf("%s@%d ", PatchObs[PatchIndex], \ PatchObsCnt[PatchIndex]); } } printf("\n"); } ' >> $1 # Clear the parameter since it has already been used. echo "Obsoletes_label=" >> $1 # Pass it's value on to the preinstall under another name echo "ACTIVE_OBSOLETES=$ACTIVE_OBSOLETES" >> $1 fi # # Construct PATCH_INFO line for this package. # tmpRequire=`nawk -F= ' $1 ~ /REQUIR/ { print $2 } ' $INFO_DIR/pkginfo ` tmpIncompat=`nawk -F= ' $1 ~ /INCOMPAT/ { print $2 } ' $INFO_DIR/pkginfo ` if [ -n "$tmpRequire" ] && [ -n "$tmpIncompat" ] then echo "PATCH_INFO_$Patch_label=Installed: `date` From: `uname -n` \ Obsoletes: $ACTIVE_OBSOLETES Requires: $tmpRequire \ Incompatibles: $tmpIncompat" >> $1 elif [ -n "$tmpRequire" ] then echo "PATCH_INFO_$Patch_label=Installed: `date` From: `uname -n` \ Obsoletes: $ACTIVE_OBSOLETES Requires: $tmpRequire \ Incompatibles: " >> $1 elif [ -n "$tmpIncompat" ] then echo "PATCH_INFO_$Patch_label=Installed: `date` From: `uname -n` \ Obsoletes: $ACTIVE_OBSOLETES Requires: Incompatibles: \ $tmpIncompat" >> $1 else echo "PATCH_INFO_$Patch_label=Installed: `date` From: `uname -n` \ Obsoletes: $ACTIVE_OBSOLETES Requires: Incompatibles: " >> $1 fi # # Since this script is called from the delivery medium and we may be using # dot extensions to distinguish the different patch packages, this is the # only place we can, with certainty, trace that source for our backout # scripts. (Usually $INST_DATADIR would get us there). # echo "SCRIPTS_DIR=`dirname $0`" >> $1 # If additional operations are required for this package, place # those package-specific commands here. #XXXSpecial_CommandsXXX# exit 0 |
preinstall スクリプトは、構成されるバックアウトパッケージの prototype ファイル、情報ファイル、およびインストールスクリプトの初期化を行います。このスクリプトは非常に単純で、この例の残りのスクリプトではバックアウトパッケージに通常のファイルを記述することしかできません。
バックアウトパッケージのシンボリックリンク、ハードリンク、デバイス、および名前付きパイプを復元する場合、pkgproto コマンドを使用するように preinstall スクリプトを変更して、提供される pkgmap ファイルをインストールされたファイルと比較できます。その後、バックアウトパッケージの変更されないファイルごとに prototype ファイルエントリが作成されます。使用する方法は、クラスアクションスクリプトの方法と同様です。
スクリプト patch_checkinstall および patch_postinstall は、preinstall スクリプトからパッケージソースツリーに挿入されます。これら 2 つのスクリプトにより、パッチが取り消されます。
# This script initializes the backout data for a patch package # directory format options. # # @(#)preinstall 1.5 96/05/10 SMI # # Copyright (c) 1995 by Sun Microsystems, Inc. # All rights reserved # PATH=/usr/sadm/bin:$PATH recovery="no" if [ "$PKG_INSTALL_ROOT" = "/" ]; then PKG_INSTALL_ROOT="" fi # Check to see if this is a patch installation retry. if [ "$INTERRUPTION" = "yes" ]; then if [ -d "$PKG_INSTALL_ROOT/var/tmp/$Patch_label.$PKGINST" ] || [ -d \ "$PATCH_BUILD_DIR/$Patch_label.$PKGINST" ]; then recovery="yes" fi fi if [ -n "$PATCH_BUILD_DIR" -a -d "$PATCH_BUILD_DIR" ]; then BUILD_DIR="$PATCH_BUILD_DIR/$Patch_label.$PKGINST" else BUILD_DIR="$PKG_INSTALL_ROOT/var/tmp/$Patch_label.$PKGINST" fi FILE_DIR=$BUILD_DIR/files RELOC_DIR=$BUILD_DIR/files/reloc ROOT_DIR=$BUILD_DIR/files/root PROTO_FILE=$BUILD_DIR/prototype PKGINFO_FILE=$BUILD_DIR/pkginfo THIS_DIR=`dirname $0` if [ "$PATCH_PROGRESSIVE" = "true" ]; then # If this is being used in an old-style patch, insert # the old-style script commands here. #XXXOld_CommandsXXX# exit 0 fi # # Unless specifically denied, initialize the backout patch data by # creating the build directory and copying over the original pkginfo # which pkgadd saved in case it had to be restored. # if [ "$PATCH_NO_UNDO" != "true" ] && [ "$recovery" = "no" ]; then if [ -d $BUILD_DIR ]; then rm -r $BUILD_DIR fi # If this is a retry of the same patch then recovery is set to # yes. Which means there is a build directory already in # place with the correct backout data. if [ "$recovery" = "no" ]; then mkdir $BUILD_DIR mkdir -p $RELOC_DIR mkdir $ROOT_DIR fi # # Here we initialize the backout pkginfo file by first # copying over the old pkginfo file and themn adding the # ACTIVE_PATCH parameter so the backout will know what patch # it's backing out. # # NOTE : Within the installation, pkgparam returns the # original data. # pkgparam -v $PKGINST | nawk ' $1 ~ /PATCHLIST/ { next; } $1 ~ /PATCH_OBSOLETES/ { next; } $1 ~ /ACTIVE_OBSOLETES/ { next; } $1 ~ /Obsoletes_label/ { next; } $1 ~ /ACTIVE_PATCH/ { next; } $1 ~ /Patch_label/ { next; } $1 ~ /UPDATE/ { next; } $1 ~ /SCRIPTS_DIR/ { next; } $1 ~ /PATCH_NO_UNDO/ { next; } $1 ~ /INSTDATE/ { next; } $1 ~ /PKGINST/ { next; } $1 ~ /OAMBASE/ { next; } $1 ~ /PATH/ { next; } { print; } ' > $PKGINFO_FILE echo "ACTIVE_PATCH=$Patch_label" >> $PKGINFO_FILE echo "ACTIVE_OBSOLETES=$ACTIVE_OBSOLETES" >> $PKGINFO_FILE # And now initialize the backout prototype file with the # pkginfo file just formulated. echo "i pkginfo" > $PROTO_FILE # Copy over the backout scripts including the undo class # action scripts for script in $SCRIPTS_DIR/*; do srcscript=`basename $script` targscript=`echo $srcscript | nawk ' { script=$0; } /u\./ { sub("u.", "i.", script); print script; next; } /patch_/ { sub("patch_", "", script); print script; next; } { print "dont_use" } '` if [ "$targscript" = "dont_use" ]; then continue fi echo "i $targscript=$FILE_DIR/$targscript" >> $PROTO_FILE cp $SCRIPTS_DIR/$srcscript $FILE_DIR/$targscript done # # Now add entries to the prototype file that won't be passed to # class action scripts. If the entry is brand new, add it to the # deletes file for the backout package. # Our_Pkgmap=`dirname $SCRIPTS_DIR`/pkgmap BO_Deletes=$FILE_DIR/deletes nawk -v basedir=${BASEDIR:-/} ' BEGIN { count=0; } { token = $2; ftype = $1; } $1 ~ /[#\!:]/ { next; } $1 ~ /[0123456789]/ { if ( NF >= 3) { token = $3; ftype = $2; } else { next; } } { if (ftype == "i" || ftype == "e" || ftype == "f" || ftype == \ "v" || ftype == "d") { next; } } { equals=match($4, "=")-1; if ( equals == -1 ) { print $3, $4; } else { print $3, substr($4, 0, equals); } } ' < $Our_Pkgmap | while read class path; do # # NOTE: If pkgproto is passed a file that is # actually a hard link to another file, it # will return ftype "f" because the first link # in the list (consisting of only one file) is # viewed by pkgproto as the source and always # gets ftype "f". # # If this isn't replacing something, then it # just goes to the deletes list. # if valpath -l $path; then Chk_Path="$BASEDIR/$path" Build_Path="$RELOC_DIR/$path" Proto_From="$BASEDIR" else # It's an absolute path Chk_Path="$PKG_INSTALL_ROOT$path" Build_Path="$ROOT_DIR$path" Proto_From="$PKG_INSTALL_ROOT" fi # # Hard links have to be restored as regular files. # Unlike the others in this group, an actual # object will be required for the pkgmk. # if [ -f "$Chk_Path" ]; then mkdir -p `dirname $Build_Path` cp $Chk_Path $Build_Path cd $Proto_From pkgproto -c $class "$Build_Path=$path" 1>> \ $PROTO_FILE 2> /dev/null cd $THIS_DIR elif [ -h "$Chk_Path" -o \ -c "$Chk_Path" -o \ -b "$Chk_Path" -o \ -p "$Chk_Path" ]; then pkgproto -c $class "$Chk_Path=$path" 1>> \ $PROTO_FILE 2> /dev/null else echo $path >> $BO_Deletes fi done fi # If additional operations are required for this package, place # those package-specific commands here. #XXXSpecial_CommandsXXX# exit 0 |
クラスアクションスクリプトにより、既存のファイルを置き換えるファイルごとにコピーを作成して、バックアウトパッケージの prototype ファイルに対応する行を追加します。この処理はすべて、非常に単純な nawk スクリプトを使用して実行します。クラスアクションスクリプトは、対応するインストール済みファイルに一致しない通常のファイルからなるソースとターゲットのペアの一覧を受け取ります。シンボリックリンクおよびその他のファイル以外のものは、preinstall スクリプトで対応する必要があります。
# This class action script copies the files being replaced # into a package being constructed in $BUILD_DIR. This class # action script is only appropriate for regular files that # are installed by simply copying them into place. # # For special package objects such as editable files, the patch # producer must supply appropriate class action scripts. # # directory format options. # # @(#)i.script 1.6 96/05/10 SMI # # Copyright (c) 1995 by Sun Microsystems, Inc. # All rights reserved # PATH=/usr/sadm/bin:$PATH ECHO="/usr/bin/echo" SED="/usr/bin/sed" PKGPROTO="/usr/bin/pkgproto" EXPR="/usr/bin/expr" # used by dirname MKDIR="/usr/bin/mkdir" CP="/usr/bin/cp" RM="/usr/bin/rm" MV="/usr/bin/mv" recovery="no" Pn=$$ procIdCtr=0 CMDS_USED="$ECHO $SED $PKGPROTO $EXPR $MKDIR $CP $RM $MV" LIBS_USED="" if [ "$PKG_INSTALL_ROOT" = "/" ]; then PKG_INSTALL_ROOT="" fi # Check to see if this is a patch installation retry. if [ "$INTERRUPTION" = "yes" ]; then if [ -d "$PKG_INSTALL_ROOT/var/tmp/$Patch_label.$PKGINST" ] || \ [ -d "$PATCH_BUILD_DIR/$Patch_label.$PKGINST" ]; then recovery="yes" fi fi if [ -n "$PATCH_BUILD_DIR" -a -d "$PATCH_BUILD_DIR" ]; then BUILD_DIR="$PATCH_BUILD_DIR/$Patch_label.$PKGINST" else BUILD_DIR="$PKG_INSTALL_ROOT/var/tmp/$Patch_label.$PKGINST" fi FILE_DIR=$BUILD_DIR/files RELOC_DIR=$FILE_DIR/reloc ROOT_DIR=$FILE_DIR/root BO_Deletes=$FILE_DIR/deletes PROGNAME=`basename $0` if [ "$PATCH_PROGRESSIVE" = "true" ]; then PATCH_NO_UNDO="true" fi # Since this is generic, figure out the class. Class=`echo $PROGNAME | nawk ' { print substr($0, 3) }'` # Since this is an update, $BASEDIR is guaranteed to be correct BD=${BASEDIR:-/} cd $BD # # First, figure out the dynamic libraries that can trip us up. # if [ -z "$PKG_INSTALL_ROOT" ]; then if [ -x /usr/bin/ldd ]; then LIB_LIST=`/usr/bin/ldd $CMDS_USED | sort -u | nawk ' $1 ~ /\// { continue; } { printf "%s ", $3 } '` else LIB_LIST="/usr/lib/libc.so.1 /usr/lib/libdl.so.1 \ /usr/lib/libw.so.1 /usr/lib/libintl.so.1 /usr/lib/libadm.so.1 \ /usr/lib/libelf.so.1" fi fi # # Now read the list of files in this class to be replaced. If the file # is already in place, then this is a change and we need to copy it # over to the build directory if undo is allowed. If it's a new entry # (No $dst), then it goes in the deletes file for the backout package. # procIdCtr=0 while read src dst; do if [ -z "$PKG_INSTALL_ROOT" ]; then Chk_Path=$dst for library in $LIB_LIST; do if [ $Chk_Path = $library ]; then $CP $dst $dst.$Pn LIBS_USED="$LIBS_USED $dst.$Pn" LD_PRELOAD="$LIBS_USED" export LD_PRELOAD fi done fi if [ "$PATCH_PROGRESSIVE" = "true" ]; then # If this is being used in an old-style patch, insert # the old-style script commands here. #XXXOld_CommandsXXX# echo >/dev/null # dummy fi if [ "${PATCH_NO_UNDO}" != "true" ]; then # # Here we construct the path to the appropriate source # tree for the build. First we try to strip BASEDIR. If # there's no BASEDIR in the path, we presume that it is # absolute and construct the target as an absolute path # by stripping PKG_INSTALL_ROOT. FS_Path is the path to # the file on the file system (for deletion purposes). # Build_Path is the path to the object in the build # environment. # if [ "$BD" = "/" ]; then FS_Path=`$ECHO $dst | $SED s@"$BD"@@` else FS_Path=`$ECHO $dst | $SED s@"$BD/"@@` fi # If it's an absolute path the attempt to strip the # BASEDIR will have failed. if [ $dst = $FS_Path ]; then if [ -z "$PKG_INSTALL_ROOT" ]; then FS_Path=$dst Build_Path="$ROOT_DIR$dst" else Build_Path="$ROOT_DIR`echo $dst | \ sed s@"$PKG_INSTALL_ROOT"@@`" FS_Path=`echo $dst | \ sed s@"$PKG_INSTALL_ROOT"@@` fi else Build_Path="$RELOC_DIR/$FS_Path" fi if [ -f $dst ]; then # If this is replacing something cd $FILE_DIR # # Construct the prototype file entry. We replace # the pointer to the filesystem object with the # build directory object. # $PKGPROTO -c $Class $dst=$FS_Path | \ $SED -e s@=$dst@=$Build_Path@ >> \ $BUILD_DIR/prototype # Now copy over the file if [ "$recovery" = "no" ]; then DirName=`dirname $Build_Path` $MKDIR -p $DirName $CP -p $dst $Build_Path else # If this file is already in the build area skip it if [ -f "$Build_Path" ]; then cd $BD continue else DirName=`dirname $Build_Path` if [ ! -d "$DirName" ]; then $MKDIR -p $DirName fi $CP -p $dst $Build_Path fi fi cd $BD else # It's brand new $ECHO $FS_Path >> $BO_Deletes fi fi # If special processing is required for each src/dst pair, # add that here. # #XXXSpecial_CommandsXXX# # $CP $src $dst.$$$procIdCtr if [ $? -ne 0 ]; then $RM $dst.$$$procIdCtr 1>/dev/null 2>&1 else $MV -f $dst.$$$procIdCtr $dst for library in $LIB_LIST; do if [ "$library" = "$dst" ]; then LD_PRELOAD="$dst" export LD_PRELOAD fi done fi procIdCtr=`expr $procIdCtr + 1` done # If additional operations are required for this package, place # those package-specific commands here. #XXXSpecial_CommandsXXX# # # Release the dynamic libraries # for library in $LIBS_USED; do $RM -f $library done exit 0 |
postinstall スクリプトにより、その他のスクリプトで提供される情報を使用して、バックアウトパッケージを作成します。pkgmk コマンドおよび pkgtrans コマンドではパッケージデータベースは必要ではないため、パッケージのインストール内で実行できます。
例では、(PKGSAV 環境変数を使用して) 保存ディレクトリにストリーム形式パッケージを作成することで、パッチの取り消しが可能になります。明白ではありませんが、pkgadd 処理の際に保存ディレクトリが移動されるため、このパッケージはストリーム形式にする必要があります。パッケージ自体の保存ディレクトリで pkgadd コマンドをパッケージに適用する場合、任意の時点でのパッケージソースの場所に関する想定の信頼性が非常に低くなります。ストリーム形式のパッケージは一時ディレクトリに展開され、一時ディレクトリからインストールされます。ディレクトリ形式のパッケージでは、保存ディレクトリからインストールが開始され、pkgadd 復旧処理時に突然再配置されます。
パッケージに適用するパッチを決定するには、次のコマンドを使用します。
$ pkgparam SUNWstuf PATCHLIST |
Sun の公開インタフェースである PATCHLIST を除いて、この例のパラメータ名には意味はありません。PATCH の代わりに、従来の SUNW_PATCHID および PATCH_EXCL などのほかの一覧を使用して、それに合うように PATCH_REQD の名前を変更することができます。
特定のパッチパッケージが、同じ媒体から利用できるほかのパッチパッケージに依存している場合、checkinstall スクリプトでこれを決定して、アップグレードの例 (「パッケージのアップグレード」を参照) と同じ方法で postinstall スクリプトから実行されるスクリプトを作成できます。
# This script creates the backout package for a patch package # # directory format options. # # @(#) postinstall 1.6 96/01/29 SMI # # Copyright (c) 1995 by Sun Microsystems, Inc. # All rights reserved # # Description: # Set the TYPE parameter for the remote file # # Parameters: # none # # Globals set: # TYPE set_TYPE_parameter () { if [ ${PATCH_UNDO_ARCHIVE:?????} = "/dev" ]; then # handle device specific stuff TYPE="removable" else TYPE="filesystem" fi } # # Description: # Build the remote file that points to the backout data # # Parameters: # $1: the un/compressed undo archive # # Globals set: # UNDO, STATE build_remote_file () { remote_path=$PKGSAV/$Patch_label/remote set_TYPE_parameter STATE="active" if [ $1 = "undo" ]; then UNDO="undo" else UNDO="undo.Z" fi cat > $remote_path << EOF # Backout data stored remotely TYPE=$TYPE FIND_AT=$ARCHIVE_DIR/$UNDO STATE=$STATE EOF } PATH=/usr/sadm/bin:$PATH if [ "$PKG_INSTALL_ROOT" = "/" ]; then PKG_INSTALL_ROOT="" fi if [ -n "$PATCH_BUILD_DIR" -a -d "$PATCH_BUILD_DIR" ]; then BUILD_DIR="$PATCH_BUILD_DIR/$Patch_label.$PKGINST" else BUILD_DIR="$PKG_INSTALL_ROOT/var/tmp/$Patch_label.$PKGINST" fi if [ ! -n "$PATCH_UNDO_ARCHIVE" ]; then PATCH_UNDO_ARCHIVE="none" fi FILE_DIR=$BUILD_DIR/files RELOC_DIR=$FILE_DIR/reloc ROOT_DIR=$FILE_DIR/root BO_Deletes=$FILE_DIR/deletes THIS_DIR=`dirname $0` PROTO_FILE=$BUILD_DIR/prototype TEMP_REMOTE=$PKGSAV/$Patch_label/temp if [ "$PATCH_PROGRESSIVE" = "true" ]; then # remove the scripts that are left behind install_scripts=`dirname $0` rm $install_scripts/checkinstall \ $install_scripts/patch_checkinstall $install_scripts/patch_postinstall # If this is being used in an old-style patch, insert # the old-style script commands here. #XXXOld_CommandsXXX# exit 0 fi # # At this point we either have a deletes file or we don't. If we do, # we create a prototype entry. # if [ -f $BO_Deletes ]; then echo "i deletes=$BO_Deletes" >> $BUILD_DIR/prototype fi # # Now delete everything in the deletes list after transferring # the file to the backout package and the entry to the prototype # file. Remember that the pkgmap will get the CLIENT_BASEDIR path # but we have to actually get at it using the BASEDIR path. Also # remember that removef will import our PKG_INSTALL_ROOT # Our_Deletes=$THIS_DIR/deletes if [ -f $Our_Deletes ]; then cd $BASEDIR cat $Our_Deletes | while read path; do Reg_File=0 if valpath -l $path; then Client_Path="$CLIENT_BASEDIR/$path" Build_Path="$RELOC_DIR/$path" Proto_Path=$BASEDIR/$path else # It's an absolute path Client_Path=$path Build_Path="$ROOT_DIR$path" Proto_Path=$PKG_INSTALL_ROOT$path fi # Note: If the file isn't really there, pkgproto # doesn't write anything. LINE=`pkgproto $Proto_Path=$path` ftype=`echo $LINE | nawk '{ print $1 }'` if [ $ftype = "f" ]; then Reg_File=1 fi if [ $Reg_File = 1 ]; then # Add source file to the prototype entry if [ "$Proto_Path" = "$path" ]; then LINE=`echo $LINE | sed -e s@$Proto_Path@$Build_Path@2` else LINE=`echo $LINE | sed -e s@$Proto_Path@$Build_Path@` fi DirName=`dirname $Build_Path` # make room in the build tree mkdir -p $DirName cp -p $Proto_Path $Build_Path fi # Insert it into the prototype file echo $LINE 1>>$PROTO_FILE 2>/dev/null # Remove the file only if it's OK'd by removef rm `removef $PKGINST $Client_Path` 1>/dev/null 2>&1 done removef -f $PKGINST rm $Our_Deletes fi # # Unless specifically denied, make the backout package. # if [ "$PATCH_NO_UNDO" != "true" ]; then cd $BUILD_DIR # We have to build from here. if [ "$PATCH_UNDO_ARCHIVE" != "none" ]; then STAGE_DIR="$PATCH_UNDO_ARCHIVE" ARCHIVE_DIR="$PATCH_UNDO_ARCHIVE/$Patch_label/$PKGINST" mkdir -p $ARCHIVE_DIR mkdir -p $PKGSAV/$Patch_label else if [ -d $PKGSAV/$Patch_label ]; then rm -r $PKGSAV/$Patch_label fi STAGE_DIR=$PKGSAV ARCHIVE_DIR=$PKGSAV/$Patch_label mkdir $ARCHIVE_DIR fi pkgmk -o -d $STAGE_DIR 1>/dev/null 2>&1 pkgtrans -s $STAGE_DIR $ARCHIVE_DIR/undo $PKG 1>/dev/null 2>&1 compress $ARCHIVE_DIR/undo retcode=$? if [ "$PATCH_UNDO_ARCHIVE" != "none" ]; then if [ $retcode != 0 ]; then build_remote_file "undo" else build_remote_file "undo.Z" fi fi rm -r $STAGE_DIR/$PKG cd .. rm -r $BUILD_DIR # remove the scripts that are left behind install_scripts=`dirname $0` rm $install_scripts/checkinstall $install_scripts/patch_\ checkinstall $install_scripts/patch_postinstall fi # # Since this apparently worked, we'll mark as obsoleted the prior # versions of this patch - installpatch deals with explicit obsoletions. # cd ${PKG_INSTALL_ROOT:-/} cd var/sadm/pkg active_base=`echo $Patch_label | nawk ' { print substr($0, 1, match($0, "Patchvers_pfx")-1) } '` List=`ls -d $PKGINST/save/${active_base}*` if [ $? -ne 0 ]; then List="" fi for savedir in $List; do patch=`basename $savedir` if [ $patch = $Patch_label ]; then break fi # If we get here then the previous patch gets deleted if [ -f $savedir/undo ]; then mv $savedir/undo $savedir/obsolete echo $Patch_label >> $savedir/obsoleted_by elif [ -f $savedir/undo.Z ]; then mv $savedir/undo.Z $savedir/obsolete.Z echo $Patch_label >> $savedir/obsoleted_by elif [ -f $savedir/remote ]; then `grep . $PKGSAV/$patch/remote | sed 's/STATE=.*/STATE=obsolete/ ' > $TEMP_REMOTE` rm -f $PKGSAV/$patch/remote mv $TEMP_REMOTE $PKGSAV/$patch/remote rm -f $TEMP_REMOTE echo $Patch_label >> $savedir/obsoleted_by elif [ -f $savedir/obsolete -o -f $savedir/obsolete.Z ]; then echo $Patch_label >> $savedir/obsoleted_by fi done # If additional operations are required for this package, place # those package-specific commands here. #XXXSpecial_CommandsXXX# exit 0 |
# checkinstall script to validate backing out a patch. # directory format option. # # @(#)patch_checkinstall 1.2 95/10/10 SMI # # Copyright (c) 1995 by Sun Microsystems, Inc. # All rights reserved # PATH=/usr/sadm/bin:$PATH LATER_MSG="PaTcH_MsG 6 ERROR: A later version of this patch is applied." NOPATCH_MSG="PaTcH_MsG 2 ERROR: Patch number $ACTIVE_PATCH is not installed" NEW_LIST="" # Get OLDLIST . $1 # # Confirm that the patch that got us here is the latest one installed on # the system and remove it from PATCHLIST. # Is_Inst=0 Skip=0 active_base=`echo $ACTIVE_PATCH | nawk ' { print substr($0, 1, match($0, "Patchvers_pfx")-1) } '` active_inst=`echo $ACTIVE_PATCH | nawk ' { print substr($0, match($0, "Patchvers_pfx")+1) } '` for patchappl in ${OLDLIST}; do appl_base=`echo $patchappl | nawk ' { print substr($0, 1, match($0, "Patchvers_pfx")-1) } '` if [ $appl_base = $active_base ]; then appl_inst=`echo $patchappl | nawk ' { print substr($0, match($0, "Patchvers_pfx")+1) } '` result=`expr $appl_inst \> $active_inst` if [ $result -eq 1 ]; then puttext "$LATER_MSG" exit 3 elif [ $appl_inst = $active_inst ]; then Is_Inst=1 Skip=1 fi fi if [ $Skip = 1 ]; then Skip=0 else NEW_LIST="${NEW_LIST} $patchappl" fi done if [ $Is_Inst = 0 ]; then puttext "$NOPATCH_MSG" exit 3 fi # # OK, all's well. Now condition the key variables. # echo "PATCHLIST=${NEW_LIST}" >> $1 echo "Patch_label=" >> $1 echo "PATCH_INFO_$ACTIVE_PATCH=backed out" >> $1 # Get the current PATCH_OBSOLETES and condition it Old_Obsoletes=$PATCH_OBSOLETES echo $ACTIVE_OBSOLETES | sed 'y/\ /\n/' | \ nawk -v PatchObsList="$Old_Obsoletes" ' BEGIN { printf("PATCH_OBSOLETES="); PatchCount=split(PatchObsList, PatchObsComp, " "); for(PatchIndex in PatchObsComp) { Atisat=match(PatchObsComp[PatchIndex], "@"); PatchObs[PatchIndex]=substr(PatchObsComp[PatchIndex], \ 0, Atisat-1); PatchObsCnt[PatchIndex]=substr(PatchObsComp\ [PatchIndex], Atisat+1); } } { for(PatchIndex in PatchObs) { if (PatchObs[PatchIndex] == $0) { PatchObsCnt[PatchIndex]=PatchObsCnt[PatchIndex]-1; } } next; } END { for(PatchIndex in PatchObs) { if ( PatchObsCnt[PatchIndex] > 0 ) { printf("%s@%d ", PatchObs[PatchIndex], PatchObsCnt\ [PatchIndex]); } } printf("\n"); } ' >> $1 # remove the used parameters echo "ACTIVE_OBSOLETES=" >> $1 echo "Obsoletes_label=" >> $1 exit 0 |
# This script deletes the used backout data for a patch package # and removes the deletes file entries. # # directory format options. # # @(#)patch_postinstall 1.2 96/01/29 SMI # # Copyright (c) 1995 by Sun Microsystems, Inc. # All rights reserved # PATH=/usr/sadm/bin:$PATH THIS_DIR=`dirname $0` Our_Deletes=$THIS_DIR/deletes # # Delete the used backout data # if [ -f $Our_Deletes ]; then cat $Our_Deletes | while read path; do if valpath -l $path; then Client_Path=`echo "$CLIENT_BASEDIR/$path" | sed s@//@/@` else # It's an absolute path Client_Path=$path fi rm `removef $PKGINST $Client_Path` done removef -f $PKGINST rm $Our_Deletes fi # # Remove the deletes file, checkinstall and the postinstall # rm -r $PKGSAV/$ACTIVE_PATCH rm -f $THIS_DIR/checkinstall $THIS_DIR/postinstall exit 0 |
パッケージのアップグレードのプロセスは、パッケージの上書きのプロセスとは異なっています。Solaris OS の一部として提供される標準パッケージのアップグレードをサポートする特殊ツールはありますが、アンバンドルのパッケージは独自のアップグレードをサポートするように設計できます。前のいくつかの例で、先を見越して、管理者の管理下でインストールの正確な方法を制御するパッケージについて説明しました。同様に、パッケージの直接アップグレードをサポートする request スクリプトを設計できます。管理者が、古いファイルが残らないように、あるパッケージをすべて置き換えて別のパッケージをインストールすることにした場合、パッケージスクリプトでこの処理を行うことができます。
この例の request スクリプトおよび postinstall スクリプトでは、単純なアップグレード可能パッケージを提供しています。request スクリプトは管理者と対話して、古いパッケージインスタンスを削除するための簡単なファイルを /tmp ディレクトリに設定します。request スクリプトはファイルを作成します (これは禁止されている) が、/tmp には全員がアクセスできるため、問題ありません。
postinstall スクリプトは、/tmp のシェルスクリプトを実行します。このスクリプトは、古いパッケージに対して必要な pkgrm コマンドを実行してから、自分自身を削除します。
この例では基本的なアップグレードについて説明します。いくつかの非常に長いメッセージを含めて、コードは 50 行未満です。設計者の要求により、アップグレードのバックアウトを拡張したり、パッケージにほかの大きな変換を行ったりすることができます。
アップグレードオプションのユーザーインタフェースの設計時には、管理者がプロセスについてはっきり認識して、並列インストールではなくアップグレードを積極的に要求していることを確実に確認する必要があります。ユーザーインタフェースによって操作が明確になっている限りは、アップグレードのような汎用的で複雑な処理を実行しても問題はありません。
# request script control an upgrade installation PATH=/usr/sadm/bin:$PATH UPGR_SCRIPT=/tmp/upgr.$PKGINST UPGRADE_MSG="Do you want to upgrade the installed version ?" UPGRADE_HLP="If upgrade is desired, the existing version of the \ package will be replaced by this version. If it is not \ desired, this new version will be installed into a different \ base directory and both versions will be usable." UPGRADE_NOTICE="Conflict approval questions may be displayed. The \ listed files are the ones that will be upgraded. Please \ answer \"y\" to these questions if they are presented." pkginfo -v 1.0 -q SUNWstuf.\* if [ $? -eq 0 ]; then # See if upgrade is desired here response=`ckyorn -p "$UPGRADE_MSG" -h "$UPGRADE_HLP"` if [ $response = "y" ]; then OldPkg=`pkginfo -v 1.0 -x SUNWstuf.\* | nawk ' \ /SUNW/{print $1} '` # Initiate upgrade echo "PATH=/usr/sadm/bin:$PATH" > $UPGR_SCRIPT echo "sleep 3" >> $UPGR_SCRIPT echo "echo Now removing old instance of $PKG" >> \ $UPGR_SCRIPT if [ ${PKG_INSTALL_ROOT} ]; then echo "pkgrm -n -R $PKG_INSTALL_ROOT $OldPkg" >> \ $UPGR_SCRIPT else echo "pkgrm -n $OldPkg" >> $UPGR_SCRIPT fi echo "rm $UPGR_SCRIPT" >> $UPGR_SCRIPT echo "exit $?" >> $UPGR_SCRIPT # Get the original package's base directory OldBD=`pkgparam $OldPkg BASEDIR` echo "BASEDIR=$OldBD" > $1 puttext -l 5 "$UPGRADE_NOTICE" else if [ -f $UPGR_SCRIPT ]; then rm -r $UPGR_SCRIPT fi fi fi exit 0 |
# postinstall to execute a simple upgrade PATH=/usr/sadm/bin:$PATH UPGR_SCRIPT=/tmp/upgr.$PKGINST if [ -f $UPGR_SCRIPT ]; then sh $UPGR_SCRIPT & fi exit 0 |
クラスアーカイブパッケージは、アプリケーションバイナリインタフェース (ABI) の拡張機能です。これは、特定のファイルセットが単一のファイル (アーカイブ) に結合され、オプションで圧縮または暗号化されたパッケージです。クラスアーカイブ形式は、アクティブな可能性があるファイルシステムに対して、最初のインストールの速度を最大 30% 向上し、パッケージおよびパッチのインストール時の信頼性が改善されます。
次の節では、アーカイブパッケージディレクトリの構造、キーワード、および faspac ユーティリティーについて説明します。
次の図に示すパッケージエントリは、パッケージファイルが含まれるディレクトリを表します。このディレクトリは、パッケージと同じ名前にする必要があります。
パッケージディレクトリに含まれるファイルおよびディレクトリの機能を次に示します。
項目 |
説明 |
---|---|
pkginfo |
特別な環境変数およびインストール命令を含めて、パッケージ全体を記述するファイル |
pkgmap |
インストールされる各オブジェクト (ファイル、ディレクトリ、パイプなど) を記述するファイル |
reloc |
ベースディレクトリから相対的にインストールされるファイル (再配置可能オブジェクト) が含まれる、任意のディレクトリ |
root |
root ディレクトリから相対的にインストールされるファイル (ルートオブジェクト) が含まれる、任意のディレクトリ |
install |
スクリプトおよびその他の補助的なファイルが含まれる、任意のディレクトリ (pkginfo および pkgmap を除き、すべての ftype i ファイルはここに含まれる) |
クラスアーカイブ形式では、パッケージビルダにより reloc および root の各ディレクトリからアーカイブにファイルを結合できます。アーカイブは、インストールの速度の向上、パッケージサイズの縮小、またはパッケージのセキュリティの向上のために、圧縮、暗号化、またはその他の任意の方法で処理できます。
ABI により、パッケージ内の任意のファイルをクラスに割り当てることができます。特定のクラス内のすべてのファイルは、クラスアクションスクリプトで定義されるカスタムメソッドを使用してディスクにインストールできます。このカスタムメソッドでは、ターゲットシステムで使用できるプログラムまたはパッケージとともに提供されるプログラムを使用できます。結果の形式は、標準 ABI 形式によく似ています。次の図に示すように、別のディレクトリが追加されます。アーカイブ用ファイルのすべてのクラスは、そのまま単一のファイルに結合され、archive ディレクトリに配置されます。アーカイブされたすべてのファイルが reloc および root の各ディレクトリから削除され、インストールクラスアクションスクリプトが install ディレクトリに配置されます。
この新しいクラスアーカイブ形式をサポートするために、pkginfo ファイル内で特殊な意味を持つキーワード形式の新しいインタフェースが 3 つあります。これらのキーワードを使用して、特殊な処理が必要なクラスを指定します。各キーワード文の形式は、 keyword=class1[class2 class3 ...] です。各キーワードの値は、次の表で定義されています。
キーワード |
説明 |
---|---|
PKG_SRC_NOVERIFY |
これは、提供されるパッケージの reloc または root の各ディレクトリ内のファイルが指定されたクラスに属する場合に、pkgadd でファイルの存在とプロパティーを確認しないように指定します。アーカイブされたクラスではファイルが reloc または root のディレクトリ内に存在しないため、このキーワードが必要です。それらのファイルは、archive ディレクトリの非公開形式のファイルです。 |
PKG_DST_QKVERIFY |
これらのクラスのファイルは、インストール後にテキスト出力がほとんどまたはまったくない簡易アルゴリズムを使用して確認されます。簡易確認では、最初に各ファイルの属性を正確に設定して、処理が正常に実行されたかどうかを確認します。pkgmap に対して、ファイルサイズと変更時間のテストも行われます。checksum 検証は実行されず、エラー復旧は標準の検証メカニズムのエラー復旧よりも非力です。インストール時に停電またはディスク障害が発生した場合には、コンテンツファイルがインストールされたファイルと一致しないことがあります。.この不一致は、どのような場合でも pkgrm で解決できます。 |
PKG_CAS_PASSRELATIVE |
通常、インストールクラスアクションスクリプトは、インストールするファイルを指定するソースとターゲットのペアの一覧を、stdin から受け取ります。PKG_CAS_PASSRELATIVE に割り当てられたクラスでは、ソースとターゲットのペアが取得されません。その代わりに単一の一覧を受け取ります。この一覧の最初のエントリはソースパッケージの場所で、一覧の残りはターゲットのパスです。これはアーカイブからの抽出を単純化するためです。ソースパッケージの場所から archive ディレクトリのアーカイブを検索できます。ターゲットのパスは、アーカイブのコンテンツを抽出する機能に渡されます。各ターゲットパスは、元のパスが root または reloc のどちらであったかに応じて、絶対パスまたはベースディレクトリからの相対パスのいずれかで指定されます。このオプションを選択すると、相対パスと絶対パスの両方を単一のクラスに結合することは困難な場合があります。 |
アーカイブされたクラスごとにクラスアクションスクリプトが必要です。これは Bourne シェルコマンドが含まれたファイルで、アーカイブから実際にファイルをインストールするために pkgadd から実行されます。パッケージの install ディレクトリにクラスアクションスクリプトが検出された場合、pkgadd からそのスクリプトにインストールのすべての責任が委任されます。クラスアクションスクリプトは root 権限で実行され、ターゲットシステムの任意の場所にファイルを配置できます。
クラスアーカイブパッケージを実装するために絶対に必要なキーワードは、PKG_SRC_NOVERIFY だけです。ほかのキーワードは、インストールの速度を向上したりコードを保護したりするために使用できます。
faspac ユーティリティーは、標準 ABI パッケージを、バンドル版のパッケージで使用されるクラスアーカイブ形式に変換します。このユーティリティーでは、cpio を使用してアーカイブを行い、compress を使用して圧縮を行います。結果のパッケージには、最上位のディレクトリに archive というディレクトリが追加されています。このディレクトリには、クラスごとに名前が付けられたすべてのアーカイブが含まれます。install ディレクトリには、各アーカイブの展開に必要なクラスアクションスクリプトが含まれます。絶対パスはアーカイブされません。
faspac [-m Archive Method] -a -s -q [-d Base Directory] / [-x Exclude List] [List of Packages] |
次の表で、faspac コマンドの各オプションについて説明します。
オプション |
説明 |
---|---|
-m Archive Method
|
アーカイブまたは圧縮の方法を示します。bzip2 はデフォルトで使用される圧縮ユーティリティーです。zip または unzip に切り替えるには、-m zip を使用します。cpio または compress の場合は、-m cpio を使用します。 |
-a |
属性を固定します (ルートで実行する必要がある)。 |
-s |
標準 ABI 型のパッケージ変換を示します。このオプションは、cpio または圧縮されたパッケージを取り、標準 ABI 準拠のパッケージ形式にします。 |
-q |
非出力モードを示します。 |
-d Base Directory |
コマンド行で要求される場合にすべてのパッケージの基準になるディレクトリを示します。これは List of Packages エントリとは相互排他的です。 |
-x Exclude List |
処理から除外するパッケージの、コンマ区切りまたは引用符付きスペース区切りの一覧を示します。 |
List of Packages |
処理されるパッケージの一覧を示します。 |