Руководство разработчика по пакетированию приложений

Глава 6 Дополнительные методы создания пакетов

В ОС 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 можно установить одно из следующих значений:


Примечание –

При вызове команды pkgadd с аргументом - a none команда всегда запрашивает администратора о размещении базового каталога. К сожалению, при этом все параметры в файле устанавливаются на значение по умолчанию quit, что может создать дополнительные сложности.


Устранение неопределенности

Обычно администратор контролирует все пакеты, устанавливаемые в системе, с помощью файла администрирования. К сожалению, разработчики пакетов часто предоставляют альтернативные файлы административных значений по умолчанию, обходя тем самым пожелания администратора.

Разработчики пакетов иногда включают в поставку свой файл администрирования с тем, чтобы они, а не администраторы, контролировали установку пакета. Поскольку запись basedir в файле административных значений по умолчанию отменяет все другие базовые каталоги, это предоставляет простой способ выбора подходящего базового каталога во время установки. Во всех версиях ОС Solaris до выпуска Solaris 2.5 подобный способ контроля над базовым каталогом считался наиболее простым.

Однако следует принимать во внимание желания администраторов, которые хотят контролировать процесс установки продукта. Предоставление временного файла с административными значениями по умолчанию для контроля над установкой приводит к недоверию со стороны администраторов. Следует использовать сценарии request и checkinstall с тем, чтобы установка происходила под контролем администратора. Если сценарий request вовлекает в процесс установки администратора, организация пакетов System V облегчит работу как разработчиков, так и администраторов.

Использование параметра BASEDIR

Файл 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

Использование параметрических базовых каталогов

Если для пакета требуется несколько базовых каталогов, их можно создать с помощью параметрических имен путей. Этот способ стал достаточно популярным, несмотря на то, что он имеет следующие ниже недостатки.

После того, как параметры, устанавливающие базовые каталоги, определены в файле pkginfo, их можно изменять с помощью сценария request. Это одна из главных причин популярности данного подхода. Тем не менее, перечисленные выше недостатки являются неисправимыми, и данная структура должна рассматриваться лишь как последнее средство.

Примеры. Использование параметрических базовых каталогов

Файл pkginfo

# 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

Файл pkgmap

: 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 над параметрами сборки пакета для обеспечения успешной установки.

Использование параметра BASEDIR

Сценарий checkinstall может выбрать соответствующий базовый каталог во время установки, что означает, что базовый каталог может быть размещен на очень низком уровне в дереве каталогов. В данном примере происходит последовательное увеличение номера базового каталога, что приводит к формированию каталогов вида /opt/SUNWstuf, /opt/SUNWstuf.1 и /opt/SUNWstuf.2. Администратор может воспользоваться командой pkginfo для определения установленной в каждом базовом каталоге архитектуры и версии.

Если в пакете SUNWstuf (содержащем набор служебных программ) используется данный метод, то его файлы pkginfo и pkgmap будут выглядеть следующим образом:

Файл pkginfo

# 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

Файл pkgmap

: 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

Пример. Аналитические сценарии, осуществляющие увод BASEDIR

Предположим, что SUNWstuf для процессора архитектуры x86 уже установлен на сервере в каталоге /opt/SUNWstuf. Когда администратор выполняет команду pkgadd для установки SPARC, сценарий request должен обнаружить существование версии x86 и начать диалог с администратором касательно данной установки.


Примечание –

В сценарии checkinstall увод базового каталога может осуществляться и без взаимодействия с администратором, однако если произвольные операции подобно этой случаются слишком часто, то администраторы начинают испытывать недоверие к происходящему процессу.


Сценарии request и checkinstall для пакета, который регулирует эту ситуацию, может выглядеть следующим образом:

Сценарий request

# 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

# 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

# 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

Файл pkgmap

: 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, осуществляющий увод по относительному параметрическому пути

# 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

# 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

Файл pkgmap

: 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 выглядел бы так:

Файл 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

# 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

Файл pkgmap

: 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 с тем, чтобы гарантировать отсутствие влияния нового пакета на существующие программные продукты.

Другой взгляд на составные пакеты

Существуют два правила, которым необходимо следовать при создании полнофункционального составного пакета:

Иными словами, поскольку "перемещаемый" означает, что объект может быть установлен в любом месте и по-прежнему работать, ни один стартовый сценарий, запускаемый с помощью 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 . Правильный каталог может быть составлен одним из двух способов.

Используя данный подход для каждого абсолютного объекта в пакете, можно быть уверенным, что существующее желаемое поведение не изменится или, по крайней мере, его можно восстановить.

Пример. Составной пакет

Ниже приведен пример файлов pkginfo и pkgmap для составного пакета.

Файл pkginfo

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

Файл pkgmap

: 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 устанавливает эти отличающиеся файлы. Рассмотрите следующие рекомендации по внесению исправлений в пакеты:

Очень важно, чтобы номер версии пакета исправлений совпадал с номером версии исходного пакета. Необходимо отслеживать состояние исправлений пакета с помощью отдельной записи в файле pkginfo в следующей форме:


PATCH=patch_number

Если версия пакета заменена на версию исправления, создается еще один экземпляр пакета, и управлять таким исправленным продуктом становится чрезвычайно сложно. Метод последовательного применения исправлений к экземплярам имел определенные преимущества в ранних версиях ОС Solaris, но при работе на более сложных системах он сильно затрудняет управление.

Все параметры зоны в исправлении должны соответствовать параметрам зоны в пакете.

Что касается пакетов, составляющих ОС Solaris, то в базе данных пакетов должна быть только одна копия пакета, хотя там может находиться большое количество исправленных экземпляров. Для удаления объекта из установленного пакета (с помощью команды removef) необходимо выяснить, к каким экземплярам принадлежит этот файл.

Однако если для пакета (который не является частью операционной среды Solaris) требуется определить уровень исправления какого-либо другого конкретного пакета, который является частью операционной среды Solaris, возникает проблема, требующая решения. Установочные сценарии могут быть достаточно большими, не нанося какого-либо значительного ущерба целевой файловой системе, поскольку они там не хранятся. С помощью сценариев действий над классами и различных процедурных сценариев можно сохранять измененные файлы в переменную среды PKGSAV (или какой-то другой, более постоянный каталог), чтобы иметь возможность отменить изменения, произведенные установленными исправлениями. Наблюдение за журналом установки исправлений можно также осуществить, установив соответствующие переменные среды посредством сценариев request. В сценариях, приведенных в следующих разделах, предполагается, что может существовать множество исправлений, схема нумерации которых имеет определенный смысл применительно к одному пакету. В этом случае отдельные номера исправлений представляют поднабор функционально связанных файлов в пакете. Два исправления с разными номерами не могут изменять один и тот же файл.

Чтобы превратить обычный разреженный пакет в пакет исправлений, сценарии, описанные в следующих разделах, можно просто вложить в пакет в сжатом виде. Все они распознаются как стандартные компоненты пакета, за исключением двух последних: patch_checkinstall и patch_postinstall. Эти два сценария могут быть внедрены в пакет отмены исправлений, если есть необходимость предусмотреть такую возможность. Сценарии достаточно просты, а их различные задачи - прямолинейны.


Примечание –

Этот метод внесения исправлений может использоваться на клиентских системах, однако корневые каталоги клиента на сервере должны иметь соответствующие права доступа, позволяющие чтение данных пользователем install или nobody.


Сценарий checkinstall

Сценарий 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

Сценарий 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

Сценарий 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

Сценарий patch_checkinstall

# 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

Сценарий patch_postinstall

# 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

# 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

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

Структура каталога архивного пакета

Элементы пакета, показанные на рисунке ниже, представляет собой каталог, содержащий файлы пакета. Этот каталог должен называться так же, как и пакет.

Рисунок 6–1 Структура каталога пакета

На рисунке представлены пять подкаталогов, расположенные непосредственно под каталогом пакета: pkginfo, pkgmap, reloc, root и install. Показаны также их подкаталоги.

В следующей таблице перечислены функции файлов и каталогов, содержащихся в каталоге пакета.

Элемент 

Описание 

pkginfo

Файл, описывающий пакет в целом, включая особые переменные среды и установочные команды 

pkgmap

Файл с описанием каждого устанавливаемого объекта, например, файла, каталога или канала 

reloc

Необязательный каталог, содержащий файлы, которые должны быть установлены относительно базового каталога (перемещаемые объекты) 

root

Необязательный каталог, содержащий файлы, которые будут установлены относительно каталога root (корневые объекты)

install

Необязательный каталог, содержащий сценарии и другие вспомогательные файлы (за исключением файлов pkginfo и pkgmap, все файлы типа ftype i находятся здесь)

Формат архива класса позволяет разработчику пакета объединить файлы из каталогов reloc и root в архивы, которые могут быть сжаты, зашифрованы или иным образом обработаны с тем, чтобы увеличить скорость установки, уменьшить размер пакета или увеличить его безопасность.

Интерфейс ABI позволяет назначить любой файл пакета какому-либо классу. Все файлы определенного класса могут быть установлены на диск с помощью особого метода, определенного сценарием действия над классом. Этот особый метод может использовать программы, доступные на целевой системе, или программы, поставляемые с пакетом. Результирующий формат похож на стандартный формат интерфейса ABI. На следующем рисунке видно, что добавлен еще один каталог. Любые классы файлов, предназначенных для архивирования, просто объединяется в один файл и помещаются в каталог archive. Все архивированные файлы удаляются из каталогов reloc и root, а в каталог install помещается установочный сценарий действия над классом.

Рисунок 6–2 Структура каталогов архивного пакета

На рисунке показана та же самая структура каталога, что и на рисунке 6-1, с добавлением подкаталога архива (archive).

Ключевые слова, используемые в пакетах с архивом классов

Для поддержки этого нового формата архива класса, трем новым интерфейсам в виде ключевых слов присвоено особое значение в файле 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

Утилита 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 Список исключений

Выдает разделенный запятыми, кавычками или пробелами список пакетов, которые следует исключить из процесса обработки. 

Список пакетов

Выдает список пакетов, которые необходимо обработать.