В ОС Solaris в полном объеме реализованы все возможности по созданию пакетов System V, которые представляют собой мощный инстумент для установки программных продуктов. Эти возможности доступны для использования разработчику пакетов. Для настройки установок типа клиент-сервер в пакетах, не являющихся частью ОС Solaris (несобранные пакеты), может использоваться механизм классов. Имеется возможность разработки перемещаемых пакетов для удобства работы администратора. Сложный продукт может поставляться в виде набора составных пакетов, которые автоматически вычисляют зависимости пакета. Разработчик пакета может настроить процесс его обновления и исправления. Исправленные пакеты могут поставляться так же, как и неисправленные пакеты. В продукт могут включаться архивы файлов отката.
Ниже следует общий перечень вопросов, рассмотренных в данной главе.
Существуют несколько методов, позволяющих указать место, куда будет установлен пакет. Важно иметь возможность изменять базовый каталог динамически во время установки. Если эта операция выполнена верно, администратор сможет без труда устанавливать разные версии и различные архитектуры компьютеров.
В начале этого раздела рассматриваются наиболее общие методы, а затем подходы, улучшающие процесс установки в неоднородных операционных средах.
Администраторы, ответственные за установку пакетов, могут использовать файлы администрирования для контроля над процессом установки пакета. Разработчику пакета следует знать о файлах администрирования и каким образом администратор может изменить предполагаемую установку пакета.
Файл администрирования сообщает команде 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 |
Администратор может редактировать этот файл и устанавливать новые значения по умолчанию. Он может также создать новый файл администрирования и указать на его наличие с помощью параметра -a команды pkgadd.
В файле администрирования можно при необходимости определить одиннадцать параметров. Для получения дополнительной информации см. страницу admin(4).
Параметр basedir указывает, каким образом будет извлечен базовый каталог при установке пакета. Большинство администраторов оставляют этот параметр со значением по умолчанию (default), однако для параметра basedir можно установить одно из следующих значений:
ask - всегда спрашивать администратора о расположении базового каталога
Абсолютное имя пути
Абсолютное имя пути, содержащее конструкцию $PKGINST, означающую, что следует всегда выполнять установку в базовый каталог, извлеченный из экземпляра пакета
При вызове команды pkgadd с аргументом - a none команда всегда запрашивает администратора о размещении базового каталога. К сожалению, при этом все параметры в файле устанавливаются на значение по умолчанию quit, что может создать дополнительные сложности.
Обычно администратор контролирует все пакеты, устанавливаемые в системе, с помощью файла администрирования. К сожалению, разработчики пакетов часто предоставляют альтернативные файлы административных значений по умолчанию, обходя тем самым пожелания администратора.
Разработчики пакетов иногда включают в поставку свой файл администрирования с тем, чтобы они, а не администраторы, контролировали установку пакета. Поскольку запись basedir в файле административных значений по умолчанию отменяет все другие базовые каталоги, это предоставляет простой способ выбора подходящего базового каталога во время установки. Во всех версиях ОС Solaris до выпуска Solaris 2.5 подобный способ контроля над базовым каталогом считался наиболее простым.
Однако следует принимать во внимание желания администраторов, которые хотят контролировать процесс установки продукта. Предоставление временного файла с административными значениями по умолчанию для контроля над установкой приводит к недоверию со стороны администраторов. Следует использовать сценарии 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 использует параметр BASEDIR по-разному в разных выпусках операционной системы SunOS. Чтобы осуществить проверку параметра BASEDIR в сценарии request, следует использовать следующий программный код, позволяющий определить фактический используемый базовый каталог.
# 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. Это одна из главных причин популярности данного подхода. Тем не менее, перечисленные выше недостатки являются неисправимыми, и данная структура должна рассматриваться лишь как последнее средство.
# 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 |
Любой пакет, существующий в нескольких версиях или для нескольких архитектур, должен, при необходимости, иметь возможность увода базового каталога. Увод базового каталога означает, что если в базовом каталоге уже существует предыдущая версия или другая архитектура устанавливаемого пакета, устанавливаемый пакет решает эту проблему, создавая, например, новый базовый каталог со слегка отличающимся именем. Сценарии request и checkinstall в Solaris 2.5 и совместимых выпусках имеют возможность изменять переменную среды BASEDIR. Однако такая возможность отсутствует в более ранних версиях ОС Solaris.
Даже в более старых версиях операционного окружения Solaris сценарию 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. Это вполне приемлемо при условии, что какие бы действия пакет ни выполнял при установке, он отменит их при удалении.
Можно воспользоваться преимуществами двух методов контроля базового каталога в период установки. Первый метод лучше всего подходит для новых пакетов, которые устанавливаются только на Solaris 2.5 и совместимые выпуски. Этот метод предоставляет администратору очень полезные данные и поддерживает большое количество установленных версий и архитектур при минимальных усилиях. Второй метод может применяться в любом пакете. Он использует внутренний контроль сценария 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 |
Предположим, что SUNWstuf для процессора архитектуры x86 уже установлен на сервере в каталоге /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. Данный пакет должен вызывать BASEDIR более конкретно, поскольку осуществить увод в каталоге /opt достаточно сложно. На самом деле, в зависимости от схемы монтирования, это может оказаться невозможным. В приведенном примере увод базового каталога осуществляется путем создания нового каталога, который не представляет никаких проблем, под каталогом /opt.
В примере используются сценарии request и checkinstall несмотря на то, что версии Solaris до выпуска 2.5 не допускали выполнения сценария checkinstall. Сценарий checkinstall в данном примере используется для приостановки процесса установки в ответ на частное сообщение в форме строки quitinstall. Если этот сценарий выполняется в Solaris 2.3, сценарий checkinstall игнорируется, и сценарий request приостанавливает установку и выдает сообщение об ошибке.
Помните, что в выпусках до Solaris 2.5 и совместимых выпусках параметр BASEDIR был параметром только для чтения и не мог быть изменен сценарием request. По этой причине при обнаружении (путем проверки обусловленной переменной среды CLIENT_BASEDIR) старой версии операционной системы SunOS сценарий request имеет только две возможности: продолжить работу и завершить ее.
Если программный продукт необходимо установить на старые версии операционной системы 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 предполагало наличие одной архитектуры на систему. В этом проектном решении концепция сервера не играла никакой роли. В настоящее время один сервер может осуществлять поддержку нескольких архитектур, то есть на сервере могут присутствовать несколько копий одного и того же программного обеспечения, разработанного для разных архитектур. Несмотря на то, что пакеты Solaris изолированы в рекомендованных рамках файловой системы (например, / и /usr) с размещением баз данных продукта на сервере и на каждом из клиентов, не все типы установок поддерживают это разделение. Некоторые разработки поддерживают совершенно другую структуру и предполагают наличие общей базы данных продуктов. Хотя направление клиента на другие версии представляется самым простым решением, в действительности установка пакетов System V в другие базовые каталоги может создать сложности для администратора.
При разработке пакета необходимо принять во внимание те методы, которые используют администраторы при внедрении новых версий ПО. Часто администраторы хотят устанавливать и тестировать новейшие версии наряду с уже установленной версией. Данная процедура включает в себя установку новой версии в базовый каталог, отличный от каталога, в котором расположена существующая версия, и направление нескольких некритических клиентов на новую версию в качестве теста. По мере нарастания уверенности в работе новой версии, администратор перенаправляет на нее все большее и большее число клиентов. В конечном итоге администратор оставляет старую версию только для чрезвычайных ситуаций, а позднее и полностью ее удаляет.
Все это подводит нас к тому, что пакеты, разработанные для современных неоднородных систем, должны поддерживать истинное перемещение в том смысле, что администратор может поместить их в любое допустимое место в файловой системе и при этом получить полную функциональность. Solaris 2.5 и совместимые выпуски предлагают ряд полезных средств, которые позволяют осуществлять чистую установку нескольких архитектур и версий на одну систему. Solaris 2.4 и совместимые версии также поддерживают истинное перемещение, однако решение этой задачи не совсем очевидно.
Двоичный интерфейс приложений System V предполагает, что первоначальным намерением при разработке перемещаемых пакетов было облегчить труд администратора по установке. В настоящее время потребности в перемещаемых пакетах идут гораздо дальше. Удобство уже не является единственной проблемой, поскольку вполне возможно, что в процессе установки активный программный продукт уже установлен в каталоге по умолчанию. Пакет, не спроектированный на решение данной ситуации, либо перезаписывает существующий продукт, либо установка не удается. С другой стороны пакет, разработанный для работы с многими архитектурами и версиями, может легко быть установлен и одновременно предлагает администратору широкий выбор возможностей, полностью совместимых с существующими традициями администрирования.
В какой-то степени проблема нескольких архитектур и проблема нескольких версий представляют собой одно и то же. Должна существовать возможность установки варианта существующего пакета наряду с другими вариантами и направления клиентов или автономных потребителей экспортированных файловых систем на любой из этих вариантов без потери функциональности. Несмотря на то, что компания 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 |
Этот метод называется традиционным потому что каждый объект пакета устанавливается в базовый каталог, определенный параметром BASEDIR в файле pkginfo. Например, первый объект в файле 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 целевой системы.
Параметр -R команды pkgadd работает как и ожидалось. Например,
pkgadd -d . -R /export/opt/client3 SUNWstuf |
устанавливает объекты в каталог /export/opt/client3/opt/SUNWstuf. Здесь пакет наиболее близко подходит к определению "перемещаемый".
Обратите внимание на использование знака вопроса (?) в каталоге /opt файла pkgmap. Он означает, что существующие атрибуты не должны изменяться. Он не означает "создать каталог с атрибутами по умолчанию", хотя при некоторых обстоятельствах это может случиться. Для любого каталога, относящегося к новому пакету, необходимо явно указывать все атрибуты.
Любой пакет, содержащий перемещаемые объекты, называется перемещаемым пакетом. Данное определение может ввести в заблуждение, поскольку в файле pkgmap перемещаемого пакета могут содержаться абсолютные пути. Использование корневой (/) записи в файле pkgmap может усилить перемещаемые аспекты пакета. Пакеты, содержащие одновременно перемещаемые и корневые записи, называются составными пакетами.
Предположим, что один объект в пакете SUNWstuf является сценарием запуска, выполняемым на уровне выполнения 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 |
Нет большой разницы между этим подходом и подходом, применяемым в абсолютном пакете. В действительности абсолютный пакет был бы здесь предпочтительнее - если администратор указал альтернативный базовый каталог для этого пакета, то он не будет работать!
На самом деле только один файл в этом пакете должен быть связан с корнем - остальные файлы можно было бы переместить в любое место. В оставшейся части раздела будет показано, как решить эту проблему с помощью составного пакета.
Подход, описанный в данном разделе, не применим ко всем пакетам, но он приводит к увеличению производительности при установке в неоднородную среду. Приведенная здесь информация практически не относится к пакетам, поставляемым как часть ОС Solaris (собранные пакеты); однако на несобранных пакетах можно попрактиковаться в нетрадиционных способах создания пакетов.
Причиной стимулирования использования перемещаемых пакетов является поддержка следующего требования:
При добавлении или удалении пакета существующее желательное поведение установленных программных продуктов не должно измениться.
Несвязанные пакеты должны размещаться в каталоге /opt с тем, чтобы гарантировать отсутствие влияния нового пакета на существующие программные продукты.
Существуют два правила, которым необходимо следовать при создании полнофункционального составного пакета:
Разместить базовый каталог там, где находится абсолютное большинство объектов пакета.
Если объект пакета размещается в обычном каталоге, не являющимся базовым каталогом (например, /etc), укажите этот объект в виде абсолютного имени пути в файле prototype.
Иными словами, поскольку "перемещаемый" означает, что объект может быть установлен в любом месте и по-прежнему работать, ни один стартовый сценарий, запускаемый с помощью init во время загрузки, не может считаться перемещаемым! И, хотя в поставляемом пакете вполне можно указать /etc/passwd в качестве относительного пути, есть только одно место, где он может быть размещен.
При создании составного пакета абсолютные пути должны работать так, чтобы не влиять на уже установленное программное обеспечение. Пакет, полностью размещаемый в каталоге /opt, обходит эту проблему, поскольку никакие существующие файлы ему не мешают. При включении файла из каталога /etc в пакет необходимо гарантировать, что абсолютные имена путей будут вести семя так же, как это ожидается от относительных имен путей. Рассмотрите следующие примеры.
В таблицу добавляется запись или объект представляет собой новую таблицу, которая скорее всего будет изменена другими программами или пакетами.
Определите объект как файловый типа e и относящийся к классу build, awk или sed. Сценарий, выполняющий эту задачу, должен удалять себя так же эффективно, как добавляет.
Необходимо добавить запись в /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 . Правильный каталог может быть составлен одним из двух способов.
Использовать конструкцию ${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. Если в одном из процедурных сценариев требуется добраться до файла /etc/vfstab на целевой системе, необходимо использовать переменную среды PKG_INSTALL_ROOT. Другими словами, имя пути /etc/vfstabприведет к файлу /etc/vfstab системы, в которой выполняется команда pkgadd, но администратор может осуществлять установку на клиентскую систему в /export/root/client3. Путь ${PKG_INSTALL_ROOT}/etc/vfstab гарантированно приведет на целевую файловую систему.
В этом примере пакет SUNWstuf устанавливается на client3, который настроен на /opt в своей корневой (/) файловой системе. Другая версия этого пакета уже установлена на client3, а базовый каталог установлен как basedir=/opt/$PKGINST в файле администрирования thisadmin. (Для получения дополнительной информации о файлах администрирования см. раздел Файл административных значений по умолчанию.) Команда 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 устанавливает эти отличающиеся файлы. Рассмотрите следующие рекомендации по внесению исправлений в пакеты:
Если система достаточно сложная, рекомендуется создать систему идентификации исправлений, которая будет следить за тем, чтобы два файла с исправлениями не заменили один и тот же файл в попытке скорректировать ошибочное поведение системы, вызванное разными причинами. Например, базовые номера исправлений Sun назначаются взаимоисключающим наборам файлов, за которые они ответственны.
Необходимо реализовать возможность отката исправления.
Очень важно, чтобы номер версии пакета исправлений совпадал с номером версии исходного пакета. Необходимо отслеживать состояние исправлений пакета с помощью отдельной записи в файле pkginfo в следующей форме:
PATCH=patch_number |
Если версия пакета заменена на версию исправления, создается еще один экземпляр пакета, и управлять таким исправленным продуктом становится чрезвычайно сложно. Метод последовательного применения исправлений к экземплярам имел определенные преимущества в ранних версиях ОС Solaris, но при работе на более сложных системах он сильно затрудняет управление.
Все параметры зоны в исправлении должны соответствовать параметрам зоны в пакете.
Что касается пакетов, составляющих ОС Solaris, то в базе данных пакетов должна быть только одна копия пакета, хотя там может находиться большое количество исправленных экземпляров. Для удаления объекта из установленного пакета (с помощью команды removef) необходимо выяснить, к каким экземплярам принадлежит этот файл.
Однако если для пакета (который не является частью операционной среды Solaris) требуется определить уровень исправления какого-либо другого конкретного пакета, который является частью операционной среды Solaris, возникает проблема, требующая решения. Установочные сценарии могут быть достаточно большими, не нанося какого-либо значительного ущерба целевой файловой системе, поскольку они там не хранятся. С помощью сценариев действий над классами и различных процедурных сценариев можно сохранять измененные файлы в переменную среды PKGSAV (или какой-то другой, более постоянный каталог), чтобы иметь возможность отменить изменения, произведенные установленными исправлениями. Наблюдение за журналом установки исправлений можно также осуществить, установив соответствующие переменные среды посредством сценариев request. В сценариях, приведенных в следующих разделах, предполагается, что может существовать множество исправлений, схема нумерации которых имеет определенный смысл применительно к одному пакету. В этом случае отдельные номера исправлений представляют поднабор функционально связанных файлов в пакете. Два исправления с разными номерами не могут изменять один и тот же файл.
Чтобы превратить обычный разреженный пакет в пакет исправлений, сценарии, описанные в следующих разделах, можно просто вложить в пакет в сжатом виде. Все они распознаются как стандартные компоненты пакета, за исключением двух последних: patch_checkinstall и patch_postinstall. Эти два сценария могут быть внедрены в пакет отмены исправлений, если есть необходимость предусмотреть такую возможность. Сценарии достаточно просты, а их различные задачи - прямолинейны.
Этот метод внесения исправлений может использоваться на клиентских системах, однако корневые каталоги клиента на сервере должны иметь соответствующие права доступа, позволяющие чтение данных пользователем 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 обновляет некоторые другие, относящиеся к исправлениям, параметры. Это новая архитектура исправлений, называемая прямым исправлением экземпляра.
В приводимом примере исходные пакеты и их исправления находятся в одном каталоге. Два исходных пакета называются SUNWstuf.v1 и SUNWstuf.v2, а их исправления называются SUNWstuf.p1 и SUNWstuf.p2. Это означает, что для процедурного сценария будет очень сложно обнаружить, из какого каталога пришли эти файлы, поскольку все символы в имени пакета, стоящие после точки (“.”), удаляются для параметра PKG, а переменная среды PKGINST ссылается на установленный экземпляр, а не на исходный экземпляр. Для того, чтобы процедурные сценарии смогли найти исходный каталог, сценарий checkinstall (который всегда выполняется из исходного каталога) делает запрос и передает расположение каталога дальше в виде переменной SCRIPTS_DIR. Если бы в исходном каталоге существовал только один пакет с именем SUNWstuf, тогда процедурные сценарии могли бы его найти с помощью $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, информационные файлы и установочные сценарии для создаваемого пакета отмены исправлений. Сценарий очень прост, и оставшиеся сценарии в данном примере позволяют пакету отмены исправлений только описывать обычные файлы.
Если требуется восстановить символьные ссылки, жесткие ссылки, устройства и именованные каналы в пакете отмены исправлений, необходимо изменить сценарий preinstall так, чтобы он использовал команду pkgproto для сравнения поставленного файла pkgmap с установленными файлами, а затем создать запись в файле prototype для каждого не являющегося файлом объекта, который будет изменен в пакете отмены исправлений. Метод, который необходимо использовать, похож на метод в сценарии действия над классом.
Сценарии patch_checkinstall и patch_postinstall вставляются в исходное дерево пакета из сценария preinstall. Эти два сценария отменяют выполненные исправления.
# 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 |
За исключением параметра PATCHLIST, который является общедоступным интерфейсом Sun, в данном примере нет ничего особенного в именах параметров. Вместо 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, несобранный пакет может быть разработан с поддержкой собственного обновления – в некоторых предыдущих примерах описывались пакеты, подготовленные к будущим обновлениям и контролирующие точный метод установки под руководством администратора. Кроме того, можно создать сценарий request, который будет поддерживать прямое обновление пакета. Если администратор решит установить один пакет так, чтобы полностью удалить другой, не оставляя лишних устаревших файлов, сценарии пакета могут справиться с этой задачей.
Сценарии request и postinstall в приводимом примере предоставляют простой обновляемый пакет. Сценарий request открывает диалог с администратором, а затем создает простой файл в каталоге /tmp, служащий для удаления экземпляра старого пакета. (Хотя сценарий request и создает файл (что запрещено), в нашем случае это неважно, поскольку у всех есть доступ к каталогу /tmp).
Затем сценарий postinstall запускает сценарий интерпретатора команд в каталоге /tmp, который выполняет необходимую команду pkgrm на старом пакете и после этого удаляет себя.
Данный пример иллюстрирует базовое обновление. В нем менее пятидесяти строк кода, включая несколько довольно длинных сообщений. Его функциональность можно расширить, например, чтобы отменить обновление или произвести другие важные изменения пакета по требованию разработчика.
Разработчик пользовательского интерфейса для обновления должен быть абсолютно уверен, что администратор полностью осознает процесс и действительно запросил обновление, а не параллельную установку экземпляра. Нет ничего страшного в выполнении хорошо понятой сложной операции, такой как обновление, при условии, что пользовательский интерфейс делает эту операцию понятной.
# 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. Эти ключевые слова используются для обозначения классов, требующих специального обращения. Формат каждой строки с ключевым словом таков: keyword=class1[class2 class3 ...]. Значения каждого ключевого слова определены в следующей таблице.
Ключевое слово |
Описание |
---|---|
PKG_SRC_NOVERIFY |
Сообщает команде pkgadd что не следует проверять существование и свойства файлов в поставляемых каталогах reloc или root пакета, если они принадлежат к именованному классу. Это требуется для всех заархивированных классов, поскольку эти файлы больше не содержатся в каталогах reloc или root. Они превратились в файл частного формата в каталоге archive. |
PKG_DST_QKVERIFY |
Файлы этого класса проверяются после установки с помощью быстрого алгоритма и почти не имеют текстового вывода. Функция быстрой проверки сначала правильно устанавливает атрибуты каждого файла, а затем проверяет, удачно ли прошла операция. Кроме того, существует тест на размер файла и время его изменения, результаты которого сравниваются с величинами, указанными в файле pkgmap. Проверка контрольной суммы не производится, и система восстановления после ошибки хуже, чем система, предлагаемая стандартным механизмом проверки. В случае отказа питания или сбоя диска в ходе установки содержимое может не соответствовать установленным файлам. Данное несоответствие всегда можно разрешить с помощью команды pkgrm. |
PKG_CAS_PASSRELATIVE |
Обычно установочный сценарий действия над классом получает из stdin (стандартного входного потока) список пар источник-адресат, который сообщает ему, какие файлы следует устанавливать. Классы, назначенные PKG_CAS_PASSRELATIVE, не получают пар источник-адресат. Вместо этого они получают список, первой записью в котором является расположение исходного пакета, а остальная часть списка - пути конечного каталога. Это сделано специально для упрощения процесса извлечения из архива. По положению исходного пакета можно найти архив в каталоге archive. После этого пути конечного каталога передаются в функцию, которая отвечает за извлечение содержимого архива. Каждый представленный путь конечного каталога является либо абсолютным, либо относительным по отношению к базовому каталогу в зависимости от того, располагался ли первоначально этот путь в каталоге root или reloc. При выборе этого варианта могут возникнуть сложности при объединении относительных и абсолютных путей в один класс. |
Для каждого заархивированного класса требуется сценарий действия над классом. Это файл, содержащий команды интерпретатора sh, который выполняется командой pkgadd для фактической установки файлов из архива. Если сценарий действия над классом находится в каталоге install пакета, команда pkgadd передает всю ответственность за установку этому сценарию. Сценарий действия над классом выполняется с полномочиями администратора (пользователя root) и может помещать свои файлы практически в любое место целевой системы.
Единственное ключевое слово, являющееся абсолютно обязательным для создания пакета с архивом класса, - это PKG_SRC_NOVERIFY. Оставшиеся ключевые слова могут использоваться для увеличения скорости установки или для сохранения программного кода.
Утилита faspac преобразует стандартный пакет интерфейса ABI в формат архива класса, используемый в связанных пакетах. Эта утилита осуществляет архивирование с помощью cpio и сжатие с помощью compress. В результирующем пакете появляется дополнительный каталог верхнего уровня с названием archive. В этом каталоге будут находиться все архивы, названные по имени класса. В каталоге install будут находиться сценарии действий над классами, необходимые для распаковки каждого архива. Абсолютные пути не архивируются.
Вызов служебной программы faspac имеет следующий формат:
faspac [-m Archive Method] -a -s -q [-d Base Directory] / [-x Exclude List] [List of Packages] |
Каждый параметр команды faspac описан в следующей таблице.
Параметр |
Описание |
---|---|
-m Способ архивирования
|
Указывает на способ архивирования или сжатия. По умолчанию для сжатия используется утилита bzip2. Для переключения на метод zip или unzip используйте параметр - m zip, либо (для cpio и compress) параметр -m cpio. |
-a |
Исправляет атрибуты (необходимы полномочия администратора (пользователя root)). |
-s |
Означает перевод пакета в стандартный тип ABI. Данный параметр берет пакеты cpio или compresssed и переводит их в стандартный формат пакета интерфейса ABI. |
-q |
Означает режим без выдачи сообщений. |
-d Базовый каталог |
Указывает каталог, в котором все имеющиеся пакеты будут обрабатываться в соответствии с требованиями, указанными в командной строке. Этот параметр является взаимоисключающим с параметром List of Packages. |
-x Список исключений |
Выдает разделенный запятыми, кавычками или пробелами список пакетов, которые следует исключить из процесса обработки. |
Список пакетов |
Выдает список пакетов, которые необходимо обработать. |