As capacidades completas de empacotamento System V quando implementadas no sistema operacional Solaris oferecem uma poderosa ferramenta para a instalação de produtos de software. Como um criador de pacotes, você pode tirar vantagem dessas capacidades. Os pacotes que não fazem parte do sistema operacional Solaris (pacotes não incorporados) podem usar o mecanismo de classe para personalizar as instalações de servidor/cliente. Os pacotes relocáveis podem ser criados para satisfazer os desejos do administrador. Um produto complexo pode ser entregue como um conjunto de pacotes compostos que resolve automaticamente as dependências de pacote. As atualizações e patches podem ser personalizados pelo criador do pacote. Os pacotes com patch podem ser entregues da mesma forma que os pacotes sem patch, e os arquivos de backout também podem ser incluídos no produto.
A lista abaixo traz as informações gerais encontradas neste capítulo.
Você pode usar vários métodos para especificar onde um pacote será instalado e é importante poder alterar a base de instalação dinamicamente no tempo de instalação. Se isto for atingido satisfatoriamente, um administrador pode instalar várias versões e várias arquiteturas se complicações.
Esta seção trata primeiro dos métodos comuns, depois das abordagens que melhoram as instalações em sistemas heterogêneos.
Os administradores responsáveis pela instalação de pacotes podem usar arquivos de administração para controlar a instalação do pacote. No entanto, como criador de pacotes, você precisa conhecer os arquivos de administração e saber como um administrador pode alterar a instalação pretendida do pacote.
Um arquivo de administração diz ao comando pkgadd se ele deve realizar as verificações ou solicitações que normalmente realiza. Conseqüentemente, os administradores devem entender por completo o processo de instalação do pacote e os scripts envolvidos antes de usar os arquivos de administração.
Um arquivo de padrões administrativos é entregue com o sistema operacional SunOS em /var/sadm/install/admin/default. Trata-se do arquivo que estabelece o nível mais básico de regras administrativas com relação à instalação de produtos de software. O arquivo apresenta a seguinte aparência quando entregue:
#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 |
O administrador pode editar este arquivo para estabelecer novos comportamentos padrões, ou criar um arquivo de administração e especificar sua existência usando a opção -a no comando pkgadd.
Podem ser definidos onze parâmetros no arquivo de administração, mas nem todos precisam ser definidos. Para obter mais informações, consulte admin(4).
O parâmetro basedir especifica como o diretório base será obtido quando um pacote for instalado. A maioria dos administradores o mantém como default, mas basedir pode ser definido como:
ask, que significa sempre pedir que o administrador indique um diretório base
Um nome de caminho absoluto
Um nome de caminho absoluto contendo a construção $PKGINST, que significa instalar sempre em um diretório base obtido da instância do pacote
Se o comando pkgadd for chamado com o argumento - a none, ele sempre pede que o administrador indique um diretório base. Infelizmente, isso também define todos os parâmetros do arquivo com o valor padrão quit, que pode causar problemas adicionais.
Um administrador tem controle sobre todos os pacotes que estão sendo instalados em um sistema usando o arquivo de administração. Infelizmente, um arquivo de padrões administrativos alternativo é, com freqüência, fornecido pelo criador de pacotes, ignorando os desejos do administrador.
Os criadores de pacotes incluem, às vezes, um arquivo de administração alternativo para que eles, e não o administrador, controlem a instalação de um pacote. Por substituir todos os diretórios base, a entrada basedir do arquivo de padrões administrativos proporciona um método simples para selecionar o diretório base apropriado no tempo de instalação. Em todas as versões do ambiente operacional Solaris anteriores ao Solaris 2.5, isso era considerado o método mais simples para controlar o diretório base.
Entretanto, é necessário aceitar os desejos do administrador com relação à instalação do produto. Proporcionar um arquivo de padrões administrativos temporário para fins de controle de instalação leva a uma desconfiança por parte dos administradores. Você deve usar um script request e um script checkinstall para controlar estas instalações sob a supervisão do administrador. Se o script request envolver fielmente o administrador no processo, o empacotamento System V servirá tanto aos administradores como aos criadores de pacotes.
O arquivo pkginfo de qualquer pacote relocável deve incluir um diretório base padrão em forma de entrada como esta:
BASEDIR=absolute_path |
Este é o único diretório base padrão. Ele pode ser alterado pelo administrador durante a instalação.
Enquanto alguns pacotes requerem mais de um diretório base, a vantagem de usar este parâmetro para posicionar o pacote está no fato de que garante que o diretório base esteja na sua posição e seja gravável como um diretório válido no momento em que a instalação começar. O caminho correto para o diretório base do servidor e do cliente está disponível para todos os scripts de procedimento na forma de variáveis de ambiente reservadas, e o comando pkginfo -r SUNWstuf exibe a base de instalação atual do pacote.
No script checkinstall, BASEDIR é o parâmetro exatamente conforme definido no arquivo pkginfo (ele ainda foi condicionado). A fim de inspecionar o diretório de destino base, a construção ${PKG_INSTALL_ROOT}$BASEDIR é necessária. Isso significa que o script request ou checkinstall podem alterar o valor de BASEDIR no ambiente de instalação com resultados previsíveis. Neste momento o script preinstall é chamado, o parâmetro BASEDIR é o indicador completamente condicionado do diretório base real no sistema de destino, mesmo que o sistema for um cliente.
O script request utiliza o parâmetro BASEDIR de forma diferente em diferentes versões do sistema operacional SunOS. A fim de testar um parâmetro BASEDIR em um script request, o seguinte código deve ser usado para determinar o diretório base real em uso.
# request script constructs base directory if [ ${CLIENT_BASEDIR} ]; then LOCAL_BASE=$BASEDIR else LOCAL_BASE=${PKG_INSTALL_ROOT}$BASEDIR fi |
Se um pacote precisar de vários diretórios base, você pode estabelecê-los com nomes de caminho paramétricos. Este método tem se tornado muito popular, embora apresente as desvantagens seguintes.
Um pacote com nomes de caminho paramétricos se comporta geralmente como um pacote absoluto, mas é tratado pelo comando pkgadd como um pacote relocável. O parâmetro BASEDIR deve ser definido mesmo que não seja usado.
O administrador não pode verificar a base de instalação do pacote usando os utilitários System V (o comando pkginfo -r não funcionará).
O administrador não pode usar o método estabelecido para relocar o pacote (é denominado relocável, mas age como absoluto).
As instalações de várias arquiteturas e várias versões requerem plano de contingência para cada um dos diretórios de destino base, o que significa freqüentemente vários scripts de ação de classe complexos.
Embora os parâmetros que determinam os diretórios base sejam definidos no arquivo pkginfo, eles podem ser modificados pelo script request. Esta é uma das principais razões da popularidade desta abordagem. As desvantagens, entretanto, são crônicas e esta configuração deve ser levada em consideração em último caso.
# pkginfo file PKG=SUNWstuf NAME=software stuff ARCH=sparc VERSION=1.0.0,REV=1.0.5 CATEGORY=application DESC=a set of utilities that do stuff BASEDIR=/ EZDIR=/usr/stuf/EZstuf HRDDIR=/opt/SUNWstuf/HRDstuf VENDOR=Sun Microsystems, Inc. HOTLINE=Please contact your local service provider EMAIL= MAXINST=1000 CLASSES=none PSTAMP=hubert980707141632 |
: 1 1758 1 d none $EZDIR 0775 root bin 1 f none $EZDIR/dirdel 0555 bin bin 40 773 751310229 1 f none $EZDIR/usrdel 0555 bin bin 40 773 751310229 1 f none $EZDIR/filedel 0555 bin bin 40 773 751310229 1 d none $HRDDIR 0775 root bin 1 f none $HRDDIR/mksmart 0555 bin bin 40 773 751310229 1 f none $HRDDIR/mktall 0555 bin bin 40 773 751310229 1 f none $HRDDIR/mkcute 0555 bin bin 40 773 751310229 1 f none $HRDDIR/mkeasy 0555 bin bin 40 773 751310229 1 d none /etc ? ? ? 1 d none /etc/rc2.d ? ? ? 1 f none /etc/rc2.d/S70dostuf 0744 root sys 450 223443 1 i pkginfo 348 28411 760740163 1 i postinstall 323 26475 751309908 1 i postremove 402 33179 751309945 1 i preinstall 321 26254 751310019 1 i preremove 320 26114 751309865 |
Os pacotes disponíveis em várias versões ou de várias arquiteturas devem ser criados para deslocar o diretório base, se necessário. Deslocar um diretório base significa que, se já existir uma versão anterior ou uma arquitetura diferente do pacote que estiver sendo instalado no diretório base, o pacote resolve este problema, criando talvez um novo diretório base com um nome levemente diferente. Os scripts request e checkinstall do Solaris 2.5 e versões compatíveis têm a capacidade de modificar a variável de ambiente BASEDIR. Isso não é válido para as versões anteriores do sistema operacional Solaris.
Mesmo em versões mais antigas do sistema operacional Solaris, o script request tem a autoridade para redefinir os diretórios que estão na base de instalação. O script request pode fazê-lo de uma forma que ainda oferece suporte à maioria das preferências administrativas.
Embora você possa selecionar os diretórios base de vários pacotes que são exclusivos de uma arquitetura ou versão, esse procedimento cria níveis desnecessários de hierarquia de diretórios. Por exemplo, em um produto criado para processadores baseados em SPARC e em x86, você poderia organizar os diretórios base por processador e versão conforme ilustrado abaixo.
Diretório base |
Versão e processador |
---|---|
/opt/SUNWstuf/sparc/1.0 |
Versão 1.0, SPARC |
/opt/SUNWstuf/sparc/1.2 |
Versão 1.2, SPARC |
/opt/SUNWstuf/x86/1.0 |
Versão 1.0, x86 |
Esse procedimento está bem e funciona, mas você está tratando os nomes e os números como se eles tivessem algum significado para o administrador. Uma abordagem mais apropriada é fazê-lo automaticamente depois de explicar ao administrador e pedir permissão.
Isso significa que você pode realizar todo o trabalho no pacote sem pedir ao administrador que o faça manualmente. Você pode atribuir o diretório base arbitrariamente e, a seguir, estabelecer com clareza os links de cliente apropriados em um script postinstall. Você também pode usar o comando pkgadd para instalar todo o pacote ou parte dele nos clientes do script postinstall. Você pode, até mesmo, perguntar ao administrador quais os usuários ou clientes que precisam ter informações sobre este pacote e atualizar automaticamente as variáveis de ambiente PATH e os arquivos /etc. Isso é totalmente aceitável contanto que qualquer ação realizada pelo pacote na instalação seja desfeita durante a remoção.
Você pode tirar vantagem dos dois métodos para controlar o diretório base no momento da instalação. O primeiro é melhor para novos pacotes que serão instalados somente no Solaris 2.5 e versões compatíveis. Ele oferece dados muito úteis para o administrador e oferece suporte a várias versões e arquiteturas instaladas, além de exigir pouco trabalho especial. O segundo método pode ser usado por qualquer pacote e faz uso do controle inerente do script request sobre os parâmetros de construção para garantir que as instalações sejam bem-sucedidas.
O script checkinstall pode selecionar o diretório base apropriado no tempo de instalação, o que significa que o diretório base pode ser posicionado bem abaixo na árvore de diretórios. Este exemplo aumenta o diretório base seqüencialmente, levando a diretórios na forma /opt/SUNWstuf, /opt/SUNWstuf.1 e /opt/SUNWstuf.2. O administrador pode usar o comando pkginfo para determinar que arquitetura e versão são instalados em cada diretório base.
Se o pacote SUNWstuf (que contém um conjunto de utilitários que realizam ações) usasse este método, os arquivos pkginfo e pkgmap teriam a seguinte aparência.
# pkginfo file PKG=SUNWstuf NAME=software stuff ARCH=sparc VERSION=1.0.0,REV=1.0.5 CATEGORY=application DESC=a set of utilities that do stuff BASEDIR=/opt/SUNWstuf VENDOR=Sun Microsystems, Inc. HOTLINE=Please contact your local service provider EMAIL= MAXINST=1000 CLASSES=none daemon PSTAMP=hubert990707141632 |
: 1 1758 1 d none EZstuf 0775 root bin 1 f none EZstuf/dirdel 0555 bin bin 40 773 751310229 1 f none EZstuf/usrdel 0555 bin bin 40 773 751310229 1 f none EZstuf/filedel 0555 bin bin 40 773 751310229 1 d none HRDstuf 0775 root bin 1 f none HRDstuf/mksmart 0555 bin bin 40 773 751310229 1 f none HRDstuf/mktall 0555 bin bin 40 773 751310229 1 f none HRDstuf/mkcute 0555 bin bin 40 773 751310229 1 f none HRDstuf/mkeasy 0555 bin bin 40 773 751310229 1 d none /etc ? ? ? 1 d none /etc/rc2.d ? ? ? 1 f daemon /etc/rc2.d/S70dostuf 0744 root sys 450 223443 1 i pkginfo 348 28411 760740163 1 i postinstall 323 26475 751309908 1 i postremove 402 33179 751309945 1 i preinstall 321 26254 751310019 1 i preremove 320 26114 751309865 1 i i.daemon 509 39560 752978103 1 i r.daemon 320 24573 742152591 |
Suponha que a versão x86 de SUNWstuf já esteja instalada no servidor em /opt/SUNWstuf. Quando o administrador usa o comando pkgadd para instalar a versão SPARC, o script request precisa detectar a existência da versão x86 e interagir com o administrador em relação à instalação.
O diretório base pode ser deslocado sem a interação do administrador em um script checkinstall, mas se operações arbitrárias como essa ocorrerem constantemente, os administradores perdem a confiança no processo.
Os scripts request e checkinstall de um pacote que manipula esta situação podem ter a seguinte aparência.
# request script for SUNWstuf to walk the BASEDIR parameter. PATH=/usr/sadm/bin:${PATH} # use admin utilities GENMSG="The base directory $LOCAL_BASE already contains a \ different architecture or version of $PKG." OLDMSG="If the option \"-a none\" was used, press the \ key and enter an unused base directory when it is requested." OLDPROMPT="Do you want to overwrite this version? " OLDHELP="\"y\" will replace the installed package, \"n\" will \ stop the installation." SUSPEND="Suspending installation at user request using error \ code 1." MSG="This package could be installed at the unused base directory $WRKNG_BASE." PROMPT="Do you want to use to the proposed base directory? " HELP="A response of \"y\" will install to the proposed directory and continue, \"n\" will request a different directory. If the option \"-a none\" was used, press the key and enter an unused base directory when it is requested." DIRPROMPT="Select a preferred base directory ($WRKNG_BASE) " DIRHELP="The package $PKG will be installed at the location entered." NUBD_MSG="The base directory has changed. Be sure to update \ any applicable search paths with the actual location of the \ binaries which are at $WRKNG_BASE/EZstuf and $WRKNG_BASE/HRDstuf." OldSolaris="" Changed="" Suffix="0" # # Determine if this product is actually installed in the working # base directory. # Product_is_present () { if [ -d $WRKNG_BASE/EZstuf -o -d $WRKNG_BASE/HRDstuf ]; then return 1 else return 0 fi } if [ ${BASEDIR} ]; then # This may be an old version of Solaris. In the latest Solaris # CLIENT_BASEDIR won't be defined yet. In older version it is. if [ ${CLIENT_BASEDIR} ]; then LOCAL_BASE=$BASEDIR OldSolaris="true" else # The base directory hasn't been processed yet LOCAL_BASE=${PKG_INSTALL_ROOT}$BASEDIR fi WRKNG_BASE=$LOCAL_BASE # See if the base directory is already in place and walk it if # possible while [ -d ${WRKNG_BASE} -a Product_is_present ]; do # There is a conflict # Is this an update of the same arch & version? if [ ${UPDATE} ]; then exit 0 # It's out of our hands. else # So this is a different architecture or # version than what is already there. # Walk the base directory Suffix=`expr $Suffix + 1` WRKNG_BASE=$LOCAL_BASE.$Suffix Changed="true" fi done # So now we can propose a base directory that isn't claimed by # any of our other versions. if [ $Changed ]; then puttext "$GENMSG" if [ $OldSolaris ]; then puttext "$OLDMSG" result=`ckyorn -Q -d "a" -h "$OLDHELP" -p "$OLDPROMPT"` if [ $result="n" ]; then puttext "$SUSPEND" exit 1 # suspend installation else exit 0 fi else # The latest functionality is available puttext "$MSG" result=`ckyorn -Q -d "a" -h "$HELP" -p "$PROMPT"` if [ $? -eq 3]; then echo quitinstall >> $1 exit 0 fi if [ $result="n" ]; then WRKNG_BASE=`ckpath -ayw -d "$WRKNG_BASE" \ -h "$DIRHELP" -p "$DIRPROMPT"` else if [ $result="a" ] exit 0 fi fi echo "BASEDIR=$WRKNG_BASE" >> $1 puttext "$NUBD_MSG" fi fi exit 0 |
# checkinstall script for SUNWstuf to politely suspend grep quitinstall $1 if [ $? -eq 0 ]; then exit 3 # politely suspend installation fi exit 0 |
Esta abordagem não funcionaria muito bem se o diretório base fosse simplesmente /opt. Este pacote tem que chamar mais precisamente o BASEDIR visto que /opt seria difícil de deslocar. De fato, dependendo do esquema de montagem, isso pode não ser possível. O exemplo desloca o diretório base criando um novo diretório em /opt, que não apresenta nenhum problema.
Este exemplo usa um script request e um script checkinstall embora as versões do Solaris anteriores à versão 2.5 não possam executar o script checkinstall. O script checkinstall deste exemplo é usado a fim de interromper educadamente a instalação em resposta a uma mensagem privada na forma da seqüência quitinstall. Se este script for executado no Solaris 2.3, o script checkinstall é ignorado e o script request interrompe a instalação com uma mensagem de erro.
Lembre-se que nas versões anteriores ao Solaris 2.5 e versões compatíveis, o parâmetro BASEDIR é um parâmetro somente de leitura e não pode ser alterado pelo script request. Por essa razão, se uma versão antiga do sistema operacional SunOS for detectada (através do teste de uma variável de ambiente CLIENT_BASEDIR condicionada), o script request tem apenas duas opções — continuar ou sair.
Se o seu produto de software puder ser instalado em versões antigas do sistema operacional SunOS, o script request precisa realizar todas as ações necessárias. Esta abordagem também pode ser usada para manipular vários diretórios. Se forem necessários diretórios adicionais, estes ainda precisam ser incluídos em um único diretório base a fim de oferecer um produto facilmente administrável. Embora o parâmetro BASEDIR não ofereça o nível de granularidade disponível na última versão do Solaris, seu pacote ainda pode deslocar o diretório base usando o script request para manipular caminhos paramétricos. Os arquivos pkginfo e pkgmap podem ter a aparência seguinte.
# pkginfo file PKG=SUNWstuf NAME=software stuff ARCH=sparc VERSION=1.0.0,REV=1.0.5 CATEGORY=application DESC=a set of utilities that do stuff BASEDIR=/opt SUBBASE=SUNWstuf VENDOR=Sun Microsystems, Inc. HOTLINE=Please contact your local service provider EMAIL= MAXINST=1000 CLASSES=none daemon PSTAMP=hubert990707141632 |
: 1 1758 1 d none $SUBBASE/EZstuf 0775 root bin 1 f none $SUBBASE/EZstuf/dirdel 0555 bin bin 40 773 751310229 1 f none $SUBBASE/EZstuf/usrdel 0555 bin bin 40 773 751310229 1 f none $SUBBASE/EZstuf/filedel 0555 bin bin 40 773 751310229 1 d none $SUBBASE/HRDstuf 0775 root bin 1 f none $SUBBASE/HRDstuf/mksmart 0555 bin bin 40 773 751310229 1 f none $SUBBASE/HRDstuf/mktall 0555 bin bin 40 773 751310229 1 f none $SUBBASE/HRDstuf/mkcute 0555 bin bin 40 773 751310229 1 f none $SUBBASE/HRDstuf/mkeasy 0555 bin bin 40 773 751310229 1 d none /etc ? ? ? 1 d none /etc/rc2.d ? ? ? 1 f daemon /etc/rc2.d/S70dostuf 0744 root sys 450 223443 1 i pkginfo 348 28411 760740163 1 i postinstall 323 26475 751309908 1 i postremove 402 33179 751309945 1 i preinstall 321 26254 751310019 1 i preremove 320 26114 751309865 1 i i.daemon 509 39560 752978103 1 i r.daemon 320 24573 742152591 |
Este exemplo não é perfeito. Um comando pkginfo -r retorna /opt para a base de instalação, que é um pouco ambíguo. Muitos pacotes estão em /opt, mas pelo menos é um diretório significativo. Igual ao exemplo anterior, o próximo exemplo oferece suporte completo a várias arquiteturas e versões. O script request pode ser adaptado às necessidades de determinado pacote e resolver quaisquer dependências aplicáveis.
# 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 |
O conceito original do empacotamento System V pressupõe uma arquitetura por sistema. O conceito de um servidor não tinha um papel importante na criação. Agora, é claro, um único servidor pode oferecer suporte a várias arquiteturas, o que significa que pode haver várias cópias de um mesmo software em um servidor, cada uma de uma arquitetura diferente. Embora os pacotes do Solaris estejam isolados dentro dos limites do sistema de arquivos recomendado (por exemplo, / e /usr), com os bancos de dados do produto no servidor, bem como em cada cliente, nem todas as instalações oferecem necessariamente suporte a esta divisão. Determinadas implementações oferecem suporte a uma estrutura completamente diferente e abrangem um banco de dados comum do produto. Embora seja simples indicar aos clientes diferentes versões, instalar pacotes System V em diferentes diretórios base pode realmente causar complicações ao administrador.
Ao criar seu pacote, você deve também levar em consideração os métodos comuns usados pelos administradores para introduzir novas versões de software. Os administradores procuram freqüentemente instalar e testar a última versão lado-a-lado com a versão atualmente instalada. O procedimento envolve a instalação da nova versão em um diretório base diferente do diretório da versão atual e o envio de uma nova versão a um grupo de clientes não-críticos como teste. Assim como as construções de confiança, o administrador reenvia a mais e mais clientes a nova versão. Eventualmente, o administrador retém a versão antiga somente para emergências e, em seguida, finalmente a exclui.
Isso significa que os pacotes destinados a sistemas heterogêneos modernos devem oferecer suporte a relocações verdadeiras, o quer dizer que o administrador pode colocá-los em qualquer lugar razoável do sistema de arquivos e ainda obter total funcionalidade. O Solaris 2.5 e versões compatíveis oferecem várias ferramentas úteis que permitem a instalação perfeita de várias arquiteturas e versões no mesmo sistema. O Solaris 2.4 e versões compatíveis também oferecem suporte à relocação verdadeira, mas a conclusão das tarefas não é tão evidente.
A ABI do System V dá a entender que a intenção original do pacote relocável era tornar a instalação do pacote mais fácil para o administrador. Agora, a necessidade em ter pacotes relocáveis vai mais além. A facilidade não é o único fim, é mais apropriado dizer que é bem possível que, durante a instalação, um produto de software ativo já esteja instalado no diretório padrão. Um pacote que não está desenhado para lidar com este tipo de situação, ou substitui o produto existente ou falha ao instalar. Entretanto, um pacote desenhado para manipular várias arquiteturas e versões pode ser instalado tranqüilamente e oferece ao administrador uma grande variedade de opções totalmente compatíveis com as tradições administrativas existentes.
De alguma forma, o problema das arquiteturas e versões múltiplas é o mesmo. Deve ser possível instalar uma variante do pacote existente lado-a-lado com outras variantes, e conduzir os clientes e os consumidores independentes de sistemas de arquivos exportados a qualquer uma destas variantes sem perda de funcionalidade. Embora a Sun tenha estabelecido métodos para lidar com várias arquiteturas em um servidor, o administrador pode não se ater a tais recomendações. Todos os pacotes precisam ser capazes de satisfazer os desejos sensatos dos administradores em relação à instalação.
Este exemplo mostra a aparência de um pacote relocável tradicional. O pacote será colocado em /opt/SUNWstuf, e os arquivos pkginfo e pkgmap podem ter a seguinte aparência.
# pkginfo file PKG=SUNWstuf NAME=software stuff ARCH=sparc VERSION=1.0.0,REV=1.0.5 CATEGORY=application DESC=a set of utilities that do stuff BASEDIR=/opt VENDOR=Sun Microsystems, Inc. HOTLINE=Please contact your local service provider EMAIL= MAXINST=1000 CLASSES=none PSTAMP=hubert990707141632 |
: 1 1758 1 d none SUNWstuf 0775 root bin 1 d none SUNWstuf/EZstuf 0775 root bin 1 f none SUNWstuf/EZstuf/dirdel 0555 bin bin 40 773 751310229 1 f none SUNWstuf/EZstuf/usrdel 0555 bin bin 40 773 751310229 1 f none SUNWstuf/EZstuf/filedel 0555 bin bin 40 773 751310229 1 d none SUNWstuf/HRDstuf 0775 root bin 1 f none SUNWstuf/HRDstuf/mksmart 0555 bin bin 40 773 751310229 1 f none SUNWstuf/HRDstuf/mktall 0555 bin bin 40 773 751310229 1 f none SUNWstuf/HRDstuf/mkcute 0555 bin bin 40 773 751310229 1 f none SUNWstuf/HRDstuf/mkeasy 0555 bin bin 40 773 751310229 1 i pkginfo 348 28411 760740163 1 i postinstall 323 26475 751309908 1 i postremove 402 33179 751309945 1 i preinstall 321 26254 751310019 1 i preremove 320 26114 751309865 |
A isso se refere como método tradicional porque cada objeto de pacote é instalado no diretório base definido pelo parâmetro BASEDIR do arquivo pkginfo. Por exemplo, o primeiro objeto do arquivo pkgmap é instalado como o diretório /opt/SUNWstuf.
Um pacote absoluto é aquele que é instalado em um determinado sistema de arquivos raiz (/). É difícil lidar com estes pacotes do ponto de vista de arquiteturas e versões múltiplas. Como regra geral, todos os pacotes devem ser relocáveis. Há, no entanto, razões muito boas para incluir elementos absolutos em um pacote relocável.
Se o pacote SUNWstuf fosse um pacote absoluto, o parâmetro BASEDIR não deveria estar no arquivo pkginfo e o arquivo pkgmap teria a seguinte aparência.
: 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 |
Neste exemplo, se o administrador tivesse especificado um diretório base alternativo durante a instalação, ele seria ignorado pelo comando pkgadd. Este pacote é instalado sempre no /opt/SUNWstuf do sistema de destino.
O argumento -R do comando pkgadd funciona conforme o previsto. Por exemplo,
pkgadd -d . -R /export/opt/client3 SUNWstuf |
instala os objetos em /export/opt/client3/opt/SUNWstuf. Porém, isso é o mais perto que este pacote chega de ser um pacote relocável.
Observe o uso do ponto de interrogação (?) no diretório /opt do arquivo pkgmap. Isso indica que os atributos existentes não devem ser alterados. Não significa “criar o diretório com atributos padrão”, embora sob certas circunstâncias isso possa ocorrer. Qualquer diretório específico de um novo pacote deve especificar todos os atributos explicitamente.
Qualquer pacote que contenha objetos relocáveis é denominado de pacote relocável. Isso pode induzir a erros porque um pacote relocável pode conter caminhos absolutos em seu arquivo pkgmap. O uso de uma entrada raiz (/) em um arquivo pkgmap pode melhorar os aspectos relocáveis do pacote. Os pacotes que possuem entradas raiz e relocáveis são denominados pacotes compostos.
Suponha que um objeto do pacote SUNWstuf seja um script de início executado no nível de execução 2. O arquivo /etc/rc2.d/S70dostuf precisa ser instalado como parte do pacote, mas não pode ser colocado no diretório base. Pressupondo que um pacote relocável seja a única solução, os arquivos pkginfo e pkgmap podem ter a seguinte aparência.
# pkginfo file PKG=SUNWstuf NAME=software stuff ARCH=sparc VERSION=1.0.0,REV=1.0.5 CATEGORY=application DESC=a set of utilities that do stuff BASEDIR=/ VENDOR=Sun Microsystems, Inc. HOTLINE=Please contact your local service provider EMAIL= MAXINST=1000 CLASSES=none PSTAMP=hubert990707141632 |
: 1 1758 1 d none opt/SUNWstuf/EZstuf 0775 root bin 1 f none opt/SUNWstuf/EZstuf/dirdel 0555 bin bin 40 773 751310229 1 f none opt/SUNWstuf/EZstuf/usrdel 0555 bin bin 40 773 751310229 1 f none opt/SUNWstuf/EZstuf/filedel 0555 bin bin 40 773 751310229 1 d none opt/SUNWstuf/HRDstuf 0775 root bin 1 f none opt/SUNWstuf/HRDstuf/mksmart 0555 bin bin 40 773 751310229 1 f none opt/SUNWstuf/HRDstuf/mktall 0555 bin bin 40 773 751310229 1 f none opt/SUNWstuf/HRDstuf/mkcute 0555 bin bin 40 773 751310229 1 f none opt/SUNWstuf/HRDstuf/mkeasy 0555 bin bin 40 773 751310229 1 d none etc ? ? ? 1 d none etc/rc2.d ? ? ? 1 f none etc/rc2.d/S70dostuf 0744 root sys 450 223443 1 i pkginfo 348 28411 760740163 1 i postinstall 323 26475 751309908 1 i postremove 402 33179 751309945 1 i preinstall 321 26254 751310019 1 i preremove 320 26114 751309865 |
Não há muita diferença entre esta abordagem e a abordagem do pacote absoluto. De fato, estaria melhor como um pacote absoluto — se o administrador fornecesse um diretório base alternativo a este pacote, ele não funcionaria!
Na verdade, somente um arquivo deste pacote precisa estar relacionado à raiz, o restante pode ser movido para qualquer lugar. Como resolver este problema através do uso de um pacote composto é um tema tratado no restante desta seção.
A abordagem descrita nesta seção não se aplica a todos os pacotes, mas tem como resultado melhor desempenho durante a instalação em um ambiente heterogêneo. Uma pequena parte se aplica a pacotes entregues como parte do sistema operacional Solaris (pacotes incorporados). Entretanto, os pacotes avulsos podem praticar empacotamento não tradicional.
A razão pela qual se encoraja o uso de pacotes relocáveis é oferecer suporte a este requisito:
Quando um pacote é adicionado ou removido, os comportamentos desejáveis existentes dos produtos de software instalados não serão alterados.
Os pacotes avulsos devem estar em /opt de modo a garantir que o novo pacote não interfira nos produtos existentes.
Há duas regras a seguir ao construir um pacote composto funcional:
Estabelecer o diretório base baseado em aonde vai a grande maioria dos objetos de pacote.
Se um objeto de pacote for para um diretório comum que não é o diretório base (por exemplo, /etc), especifique-o como nome de caminho absoluto no arquivo prototype.
Em outras palavras, visto que “relocável” significa que o objeto pode ser instalado em qualquer parte e ainda funcionar, nenhum script de início executado por init no tempo de inicialização pode ser considerado relocável! Embora não exista nada de errado em especificar /etc/passwd como um caminho relativo no pacote entregue, há somente um lugar para o qual ele pode ir.
Se for construir um pacote composto, os caminhos absolutos devem operar de modo que não interfiram no software instalado. Um pacote que pode estar totalmente contido em /opt contorna este problema, já que não há arquivos existentes no meio do caminho. Quando um arquivo em /etc estiver incluído no pacote, você deve garantir que nomes de caminho absolutos se comportem da mesma forma que os nomes de caminho relativos. Leve em consideração os dois exemplos seguintes.
Uma entrada está sendo adicionada a uma tabela, ou o objeto é uma nova tabela que será provavelmente modificada por outros programas ou pacotes.
Defina o objeto como tipo de arquivo e e pertencente à classe build, awk ou sed. O script que realiza esta tarefa deve remover a si mesmo de maneira tão eficaz como quando se adiciona.
Uma entrada precisa ser adicionada a /etc/vfstab em apoio ao novo disco rígido de estado sólido.
A entrada no arquivo pkgmap pode ser
1 e sed /etc/vfstab ? ? ? |
O script request pergunta ao operador se /etc/vfstab deve ser modificado pelo pacote. Se o operador responder “não”, então o script request imprimirá as instruções sobre como realizar o trabalho manualmente e executará
echo "CLASSES=none" >> $1 |
Se o operador responder “sim”, então ele executa
echo "CLASSES=none sed" >> $1 |
que ativa o script de ação de classe que fará as modificações necessárias. A classe sed significa que o arquivo de pacote /etc/vfstab é um programa sed que contém as operações de instalação e remoção do arquivo com o mesmo nome no sistema de destino.
O objeto é um arquivo totalmente novo que provavelmente não será editado mais adiante ou está substituindo um arquivo de outro pacote.
Defina o objeto de pacote como tipo de arquivo f e o instale usando um script de ação de classe capaz de desfazer alterações.
Um novo arquivo de marca é requerido em /etc para proporcionar as informações necessárias para suportar o disco rígido de estado sólido, denominado /etc/shdisk.conf . A entrada no arquivo pkgmap pode ter esta aparência:
. . . 1 f newetc /etc/shdisk.conf . . . |
O script de ação de classe i.newetc é responsável pela instalação e por qualquer outro arquivo que precise ir para /etc. Ele faz a verificação para se certificar de que não exista outro arquivo lá. Se não existir, ele simplesmente copia o novo arquivo no local. Se já existir um arquivo no local, ele fará o backup do arquivo existente antes de instalar o novo arquivo. O script r.newetc remove estes arquivos e restaura os originais, se requerido Abaixo se encontra o fragmento principal do script de instalação.
# 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 |
Observe que este usa a variável de ambiente PKGSAV para armazenar o backup do arquivo que será substituído. Quando o argumento ENDOFCLASS é passado para o script, isso quer dizer que o comando pkgadd informa o script que estas são as últimas entradas desta classe, momento em que o script arquiva e compacta os arquivos salvos usando um programa privado de compactação armazenado no diretório de instalação do pacote.
Embora o uso da variável de ambiente PKGSAV não seja confiável durante a atualização de um pacote, se o pacote não for atualizado (através de um patch, por exemplo), o arquivo de backup é seguro. O seguinte script de remoção inclui o código para lidar com outros problemas — o fato de que as versões antigas do comando pkgrm não passem para os scripts o caminho correto da variável de ambiente PKGSAV.
O script de remoção pode ter esta aparência.
# 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 |
Este script usa um algoritmo privado desinstalado (unsquish) que está no diretório de instalação do banco de dados do pacote. Isso é feito automaticamente pelo comando pkgadd no tempo de instalação. Todos os scripts não reconhecidos especificamente como somente de instalação pelo comando pkgadd são deixados neste diretório para serem usados pelo comando pkgrm. Você não pode saber onde tal diretório está, mas pode ter certeza de que está fixo e contém todos os arquivos de informação e scripts de instalação necessários do pacote. O script encontra o diretório devido ao fato de que o script de ação de classe está com certeza sendo executado a partir do diretório que contém o programa unsquish.
Observe, também, que este script não apenas supõe que o diretório de destino é /etc. Ele pode realmente estar em /export/root/client2/etc . O diretório correto pode ser construído de duas formas.
Use a construção ${PKG_INSTALL_ROOT}/etc, ou.
Tome o nome do diretório de um arquivo passado pelo comando pkgadd (que é o que esse script faz).
Usando esta abordagem para cada objeto absoluto do pacote, você pode ter certeza de que o comportamento atual desejável é inalterado ou, pelo menos, recuperável.
Abaixo se encontra um exemplo dos arquivos pkginfo e pkgmap de um pacote composto.
PKG=SUNWstuf NAME=software stuff ARCH=sparc VERSION=1.0.0,REV=1.0.5 CATEGORY=application DESC=a set of utilities that do stuff BASEDIR=/opt VENDOR=Sun Microsystems, Inc. HOTLINE=Please contact your local service provider EMAIL= MAXINST=1000 CLASSES=none daemon PSTAMP=hubert990707141632 |
: 1 1758 1 d none SUNWstuf/EZstuf 0775 root bin 1 f none SUNWstuf/EZstuf/dirdel 0555 bin bin 40 773 751310229 1 f none SUNWstuf/EZstuf/usrdel 0555 bin bin 40 773 751310229 1 f none SUNWstuf/EZstuf/filedel 0555 bin bin 40 773 751310229 1 d none SUNWstuf/HRDstuf 0775 root bin 1 f none SUNWstuf/HRDstuf/mksmart 0555 bin bin 40 773 751310229 1 f none SUNWstuf/HRDstuf/mktall 0555 bin bin 40 773 751310229 1 f none SUNWstuf/HRDstuf/mkcute 0555 bin bin 40 773 751310229 1 f none SUNWstuf/HRDstuf/mkeasy 0555 bin bin 40 773 751310229 1 d none /etc ? ? ? 1 d none /etc/rc2.d ? ? ? 1 e daemon /etc/rc2.d/S70dostuf 0744 root sys 450 223443 1 i i.daemon 509 39560 752978103 1 i pkginfo 348 28411 760740163 1 i postinstall 323 26475 751309908 1 i postremove 402 33179 751309945 1 i preinstall 321 26254 751310019 1 i preremove 320 26114 751309865 1 i r.daemon 320 24573 742152591 |
Enquanto S70dostuf pertence à classe daemon, os diretórios que lidam com ele (que já estão no local no tempo de instalação) pertencem à classe none. Mesmo se os diretórios forem exclusivos deste pacote, você deve deixá-los na classe none. A razão disso é que os diretórios precisam ser criados primeiro e excluídos por último, e tal ação é sempre válida para a classe none. O comando pkgadd cria diretórios. Eles não são copiados do pacote e não são passados a um script de ação de classe a ser criado. Em vez disso, eles são criados pelo comando pkgadd antes que ele chame o script de ação de classe de instalação, e o comando pkgrm exclui os diretórios após a conclusão do script de ação de classe de remoção.
Isso significa que, se um diretório de uma classe especial contiver objetos na classe none, quando o comando pkgrm tenta remover o diretório, ele falha porque o diretório não estará vazio a tempo. Se um objeto de classe none estiver para ser inserido em um diretório de alguma classe especial, tal diretório não existirá a tempo para aceitar o objeto. O comando pkgadd não criará o diretório instantaneamente durante a instalação do objeto e pode não ser capaz de sincronizar os atributos de tal diretório quando exibir finalmente a definição de pkgmap.
Ao atribuir um diretório a uma classe, lembre-se sempre da ordem de criação e exclusão.
Todos os pacotes devem ser remotamente instaláveis. Instalável remotamente significa que você não pressupõe que o administrador que está instalando seu pacote esteja instalando no sistema de arquivos raiz (/) do sistema que está executando o comando pkgadd. Se, em um dos seus scripts de procedimento, você precisar obter o arquivo /etc/vfstab do sistema de destino, será necessário usar a variável de ambiente PKG_INSTALL_ROOT. Em outras palavras, o nome de caminho /etc/vfstab o levará ao arquivo /etc/vfstab do sistema que estiver executando o comando pkgadd, mas o administrador pode estar instalando em um cliente em /export/root/client3. O caminho ${PKG_INSTALL_ROOT}/etc/vfstab com certeza o leva ao sistema de arquivos de destino.
Neste exemplo, o pacote SUNWstuf está instalado no client3, que está configurado com /opt no sistema de arquivos raiz (/). Outra versão deste pacote já está instalada no client3, e o diretório base está definido como basedir=/opt/$PKGINST de um arquivo de administração, thisadmin. (Para obter mais informações sobre arquivos de administração, consulte O arquivo de padrões administrativos.) O comando pkgadd executado no servidor é:
# pkgadd -a thisadmin -R /export/root/client3 SUNWstuf |
A tabela abaixo lista as variáveis de ambiente e os valores passados aos scripts de procedimento.
Tabela 6–1 Valores passados aos scripts de procedimento
Variável de ambiente |
Valor |
---|---|
PKGINST |
SUNWstuf.2 |
PKG_INSTALL_ROOT |
/export/root/client3 |
CLIENT_BASEDIR |
/opt/SUNWstuf.2 |
BASEDIR |
/export/root/client3/opt/SUNWstuf.2 |
Para instalar em um sistema servidor ou em um sistema independente sob as mesmas circunstâncias do exemplo anterior, o comando é:
# pkgadd -a thisadmin SUNWstuf |
A tabela abaixo lista as variáveis de ambiente e os valores passados aos scripts de procedimento.
Tabela 6–2 Valores passados aos scripts de procedimento
Variável de ambiente |
Valor |
---|---|
PKGINST |
SUNWstuf.2 |
PKG_INSTALL_ROOT |
Não definido. |
CLIENT_BASEDIR |
/opt/SUNWstuf.2 |
BASEDIR |
/opt/SUNWstuf.2 |
Suponha que o pacote SUNWstuf crie e compartilhe um sistema de arquivos no servidor em /export/SUNWstuf/share. Quando o pacote for instalado no sistema cliente, seus arquivos /etc/vfstab precisam ser atualizados para montar este sistema de arquivos compartilhado. É neste caso que a variável CLIENT_BASEDIR pode ser usada.
A entrada do cliente precisa apresentar o ponto de montagem com relação ao sistema de arquivos do cliente. Esta linha deve ser construída corretamente se a instalação for do servidor ou do cliente. Suponha que o nome do sistema do servidor seja $SERVER. Você pode ir a $PKG_INSTALL_ROOT/etc/vfstab e, usando os comandos sed ou awk, construir a seguinte linha no arquivo /etc/vfstab do cliente.
$SERVER:/export/SUNWstuf/share - $CLIENT_BASEDIR/usr nfs - yes ro |
Por exemplo, para o servidor universe e o sistema cliente client9, a linha no arquivo /etc/vfstab do sistema cliente se assemelharia a:
universe:/export/SUNWstuf/share - /opt/SUNWstuf.2/usr nfs - yes ro |
Ao usar corretamente este parâmetro, a entrada sempre monta o sistema de arquivos do cliente, tanto se estiver sendo construído localmente quanto a partir do servidor.
Um patch de um pacote é apenas um pacote pequeno criado para substituir certos arquivos no original. Não há uma verdadeira razão para a distribuição de pacotes pequenos exceto para economizar espaço no meio de distribuição. Você também pode distribuir o pacote original completo com poucos arquivos alterados, ou fornecer acesso ao pacote modificado em uma rede. Contanto que somente estes novos arquivos sejam realmente diferentes (os outros arquivos não foram recompilados), o comando pkgadd instala as diferenças. Observe as seguintes diretrizes em relação a atualização com patches dos pacotes.
Se o sistema for bastante complexo, é sensato estabelecer um sistema de identificação de patches que garanta que dois patches não substituam o mesmo arquivo na tentativa de corrigir comportamentos anômalos. Por exemplo, os números de base do patch da Sun são atribuídos mutuamente a conjuntos exclusivos de arquivos pelos quais eles são responsáveis.
É necessário poder devolver um patch.
É vital que o número de versão do pacote de patch seja o mesmo do pacote original. Você deve rastrear o status do patch do pacote usando uma entrada diferente do arquivo pkginfo com a forma.
PATCH=patch_number |
Se a versão do pacote for alterada em um patch, você cria outra instância do pacote e fica extremamente difícil gerenciar o produto com patch. Este método de patch de instância progressiva oferecia certas vantagens nas primeiras versões do sistema operacional Solaris, mas torna chato o gerenciamento de sistemas mais complexos.
Todos os parâmetros de zona do patch devem corresponder aos parâmetros de zona do pacote.
No que diz respeito aos pacotes que formam o sistema operacional Solaris, deveria haver somente uma cópia do pacote no banco de dados do pacote, embora possa haver várias instâncias com patches. Para remover um objeto de um pacote instalado (usando o comando removef), você precisa saber que instâncias possuem tal arquivo.
Entretanto, se seu pacote (que não faz parte do sistema operacional Solaris) precisar determinar o nível do patch de um determinado pacote que faz parte do sistema operacional Solaris, isso se torna um problema que será resolvido aqui. Os scripts de instalação podem ser um pouco grandes sem causar impacto importante desde que não sejam armazenados no sistema de destino. Com o uso de scripts de ação de classe e vários outros scripts de procedimento, você pode salvar os arquivos alterados usando a variável de ambiente PKGSAV (ou em outro diretório mais permanente) a fim de possibilitar a devolução dos patches instalados. Você pode monitorar o histórico do patch definindo as variáveis de ambiente adequadas através de scripts request. Os scripts das seções seguintes supõem que pode haver vários patches cujo esquema de numeração tem um significado quando aplicado a um único pacote. Neste caso, os números de patches individuais representam um subconjunto de funcionalidades relacionado aos arquivos do pacote. Dois números diferentes de patch não podem alterar o mesmo arquivo.
Para criar um pacote pequeno regular em um pacote de patch, os scripts descritos nas seções seguintes podem ser simplesmente colocados no pacote. Todos eles podem ser reconhecidos como componentes de pacote padrão com exceção dos dois últimos denominados patch_checkinstall e patch_postinstall. Estes dois scripts podem ser incorporados no pacote de devolução se você quiser incluir a capacidade de devolver o patch. Os scripts são bastante simples e as suas tarefas são fáceis.
Este método de atualizar com patches pode ser usado para colocar patches nos sistemas clientes, mas os diretórios-raiz do cliente no servidor deve ter as permissões corretas para que o usuário install ou nobody possa ler.
O script checkinstall verifica se tal patch é apropriado para este pacote em particular. Uma vez confirmado, ele constrói a lista de patches e a lista de informações do patch e, em seguida, insere-as no arquivo de resposta para que sejam incorporadas no banco de dados do pacote.
Uma lista de patches é a lista de patches que afetam o pacote atual. Esta lista de patches está registrada no arquivo pkginfo do pacote instalado com uma linha que se assemelha a:
PATCHLIST=patch_id patch_id ... |
Uma lista de informações do patch é a lista de patches da qual o patch atual é dependente. Esta lista de patches também está registrada no arquivo pkginfo com uma linha que se assemelha a:
PATCH_INFO_103203-01=Installed... Obsoletes:103201-01 Requires: \ Incompatibles: 120134-01 |
Estas linhas (e seus formatos) são declaradas como interface pública. Qualquer empresa que distribua patches para pacotes Solaris deve atualizar esta lista adequadamente. Quando um patch é entregue, cada pacote dentro do patch contém um script checkinstall que realiza esta tarefa. Este mesmo script checkinstall também atualiza alguns parâmetros específicos do patch. Esta é a nova arquitetura do patch, que é denominada Direct Instance Patching
Neste exemplo, os pacotes originais e seus patches existem no mesmo diretório. Os dois pacotes originais são denominados SUNWstuf.v1 e SUNWstuf.v2, e seus patches são denominados SUNWstuf.p1 e SUNWstuf.p2. Isso significa que poderia ser muito difícil para um script de procedimento saber de que diretório vêm esses arquivos, visto que tudo no nome do pacote após o ponto (“.”) é extraído para o parâmetro PKG, e a variável de ambiente PKGINST faz referência à instância instalada, não à instância de origem. Para que os scripts de procedimento possam encontrar o diretório de origem, o script checkinstall (que é executado sempre a partir do diretório de origem) faz a pesquisa e repassa o local como a variável SCRIPTS_DIR. Se houvesse somente um pacote no diretório de origem denominado SUNWstuf, então os scripts de procedimento poderiam tê-lo encontrado usando $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 |
O script preinstall inicializa o arquivo prototype, os arquivos de informação e os scripts de instalação para que o pacote de devolução seja construído. Este script é muito simples e os scripts restantes deste exemplo permitem somente que um pacote de devolução descreva arquivos regulares.
Se quiser restaurar os links simbólicos, os links de disco rígido, os dispositivos e os pipes nomeados em um pacote de devolução, você pode modificar o script preinstall para que use o comando pkgproto para comparar o arquivo pkgmap entregue aos arquivos instalados e, então, criar uma entrada do arquivo prototype para cada não-arquivo a ser alterado no pacote de devolução. O método que você deve usar é semelhante ao método do script de ação de classe.
Os scripts patch_checkinstall e patch_postinstall são inseridos na árvore de origem do pacote do script preinstall. Estes dois scripts desfazem as ações do patch.
# 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 |
O script de ação de classe cria uma cópia de cada arquivo que substitui um arquivo existente e adiciona uma linha correspondente ao arquivo prototype do pacote de devolução. Tudo isso é feito com scripts nawk muito simples. O script de ação de classe recebe uma lista de pares origem/destino composta de arquivos comuns que não combinam com os arquivos instalados correspondentes. Os links simbólicos e outros não-arquivos devem ser tratados no script 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 |
O script postinstall cria o pacote de devolução usando as informações fornecidas pelos outros scripts. Visto que os comandos pkgmk e pkgtrans não requerem o banco de dados do pacote, eles podem ser executados na instalação de um pacote.
No exemplo, é permitido desfazer o patch construindo um pacote em formato de fluxo no diretório salvo (usando a variável de ambiente PKGSAV). Não é óbvio, mas este pacote não deve estar em formato de fluxo, porque o diretório salvo se desloca durante uma operação com pkgadd. Se o comando pkgadd for aplicado em um pacote no seu próprio diretório salvo, é muito arriscado supor onde a origem do pacote está em um dado momento. Um pacote em formato de fluxo é desempacotado em um diretório temporário e é instalado a partir de tal diretório. (Um pacote em formato de fluxo poderia começar a instalação a partir do diretório salvo e descobrir inesperadamente que foi relocado durante uma operação com pkgadd à prova de falha.)
Para determinar quais patches se aplicam a um pacote, use este comando:
$ pkgparam SUNWstuf PATCHLIST |
Com exceção de PATCHLIST, que é uma interface pública da Sun, não há nada significativo nos nomes de parâmetro deste exemplo. Em vez de PATCH, você pode usar o SUNW_PATCHID tradicional, e as outras listas, tais como PATCH_EXCL e PATCH_REQD, podem ser renomeadas do devido modo.
Se determinados pacotes de patches dependerem de outros pacotes de patches que estão disponíveis no mesmo meio, o script checkinstall pode determinar tal dependência e criar um script que será executado pelo script postinstall da mesma forma que no exemplo de atualização (consulte Atualizando pacotes).
# This script creates the backout package for a patch package # # directory format options. # # @(#) postinstall 1.6 96/01/29 SMI # # Copyright (c) 1995 by Sun Microsystems, Inc. # All rights reserved # # Description: # Set the TYPE parameter for the remote file # # Parameters: # none # # Globals set: # TYPE set_TYPE_parameter () { if [ ${PATCH_UNDO_ARCHIVE:?????} = "/dev" ]; then # handle device specific stuff TYPE="removable" else TYPE="filesystem" fi } # # Description: # Build the remote file that points to the backout data # # Parameters: # $1: the un/compressed undo archive # # Globals set: # UNDO, STATE build_remote_file () { remote_path=$PKGSAV/$Patch_label/remote set_TYPE_parameter STATE="active" if [ $1 = "undo" ]; then UNDO="undo" else UNDO="undo.Z" fi cat > $remote_path << EOF # Backout data stored remotely TYPE=$TYPE FIND_AT=$ARCHIVE_DIR/$UNDO STATE=$STATE EOF } PATH=/usr/sadm/bin:$PATH if [ "$PKG_INSTALL_ROOT" = "/" ]; then PKG_INSTALL_ROOT="" fi if [ -n "$PATCH_BUILD_DIR" -a -d "$PATCH_BUILD_DIR" ]; then BUILD_DIR="$PATCH_BUILD_DIR/$Patch_label.$PKGINST" else BUILD_DIR="$PKG_INSTALL_ROOT/var/tmp/$Patch_label.$PKGINST" fi if [ ! -n "$PATCH_UNDO_ARCHIVE" ]; then PATCH_UNDO_ARCHIVE="none" fi FILE_DIR=$BUILD_DIR/files RELOC_DIR=$FILE_DIR/reloc ROOT_DIR=$FILE_DIR/root BO_Deletes=$FILE_DIR/deletes THIS_DIR=`dirname $0` PROTO_FILE=$BUILD_DIR/prototype TEMP_REMOTE=$PKGSAV/$Patch_label/temp if [ "$PATCH_PROGRESSIVE" = "true" ]; then # remove the scripts that are left behind install_scripts=`dirname $0` rm $install_scripts/checkinstall \ $install_scripts/patch_checkinstall $install_scripts/patch_postinstall # If this is being used in an old-style patch, insert # the old-style script commands here. #XXXOld_CommandsXXX# exit 0 fi # # At this point we either have a deletes file or we don't. If we do, # we create a prototype entry. # if [ -f $BO_Deletes ]; then echo "i deletes=$BO_Deletes" >> $BUILD_DIR/prototype fi # # Now delete everything in the deletes list after transferring # the file to the backout package and the entry to the prototype # file. Remember that the pkgmap will get the CLIENT_BASEDIR path # but we have to actually get at it using the BASEDIR path. Also # remember that removef will import our PKG_INSTALL_ROOT # Our_Deletes=$THIS_DIR/deletes if [ -f $Our_Deletes ]; then cd $BASEDIR cat $Our_Deletes | while read path; do Reg_File=0 if valpath -l $path; then Client_Path="$CLIENT_BASEDIR/$path" Build_Path="$RELOC_DIR/$path" Proto_Path=$BASEDIR/$path else # It's an absolute path Client_Path=$path Build_Path="$ROOT_DIR$path" Proto_Path=$PKG_INSTALL_ROOT$path fi # Note: If the file isn't really there, pkgproto # doesn't write anything. LINE=`pkgproto $Proto_Path=$path` ftype=`echo $LINE | nawk '{ print $1 }'` if [ $ftype = "f" ]; then Reg_File=1 fi if [ $Reg_File = 1 ]; then # Add source file to the prototype entry if [ "$Proto_Path" = "$path" ]; then LINE=`echo $LINE | sed -e s@$Proto_Path@$Build_Path@2` else LINE=`echo $LINE | sed -e s@$Proto_Path@$Build_Path@` fi DirName=`dirname $Build_Path` # make room in the build tree mkdir -p $DirName cp -p $Proto_Path $Build_Path fi # Insert it into the prototype file echo $LINE 1>>$PROTO_FILE 2>/dev/null # Remove the file only if it's OK'd by removef rm `removef $PKGINST $Client_Path` 1>/dev/null 2>&1 done removef -f $PKGINST rm $Our_Deletes fi # # Unless specifically denied, make the backout package. # if [ "$PATCH_NO_UNDO" != "true" ]; then cd $BUILD_DIR # We have to build from here. if [ "$PATCH_UNDO_ARCHIVE" != "none" ]; then STAGE_DIR="$PATCH_UNDO_ARCHIVE" ARCHIVE_DIR="$PATCH_UNDO_ARCHIVE/$Patch_label/$PKGINST" mkdir -p $ARCHIVE_DIR mkdir -p $PKGSAV/$Patch_label else if [ -d $PKGSAV/$Patch_label ]; then rm -r $PKGSAV/$Patch_label fi STAGE_DIR=$PKGSAV ARCHIVE_DIR=$PKGSAV/$Patch_label mkdir $ARCHIVE_DIR fi pkgmk -o -d $STAGE_DIR 1>/dev/null 2>&1 pkgtrans -s $STAGE_DIR $ARCHIVE_DIR/undo $PKG 1>/dev/null 2>&1 compress $ARCHIVE_DIR/undo retcode=$? if [ "$PATCH_UNDO_ARCHIVE" != "none" ]; then if [ $retcode != 0 ]; then build_remote_file "undo" else build_remote_file "undo.Z" fi fi rm -r $STAGE_DIR/$PKG cd .. rm -r $BUILD_DIR # remove the scripts that are left behind install_scripts=`dirname $0` rm $install_scripts/checkinstall $install_scripts/patch_\ checkinstall $install_scripts/patch_postinstall fi # # Since this apparently worked, we'll mark as obsoleted the prior # versions of this patch - installpatch deals with explicit obsoletions. # cd ${PKG_INSTALL_ROOT:-/} cd var/sadm/pkg active_base=`echo $Patch_label | nawk ' { print substr($0, 1, match($0, "Patchvers_pfx")-1) } '` List=`ls -d $PKGINST/save/${active_base}*` if [ $? -ne 0 ]; then List="" fi for savedir in $List; do patch=`basename $savedir` if [ $patch = $Patch_label ]; then break fi # If we get here then the previous patch gets deleted if [ -f $savedir/undo ]; then mv $savedir/undo $savedir/obsolete echo $Patch_label >> $savedir/obsoleted_by elif [ -f $savedir/undo.Z ]; then mv $savedir/undo.Z $savedir/obsolete.Z echo $Patch_label >> $savedir/obsoleted_by elif [ -f $savedir/remote ]; then `grep . $PKGSAV/$patch/remote | sed 's/STATE=.*/STATE=obsolete/ ' > $TEMP_REMOTE` rm -f $PKGSAV/$patch/remote mv $TEMP_REMOTE $PKGSAV/$patch/remote rm -f $TEMP_REMOTE echo $Patch_label >> $savedir/obsoleted_by elif [ -f $savedir/obsolete -o -f $savedir/obsolete.Z ]; then echo $Patch_label >> $savedir/obsoleted_by fi done # If additional operations are required for this package, place # those package-specific commands here. #XXXSpecial_CommandsXXX# exit 0 |
# checkinstall script to validate backing out a patch. # directory format option. # # @(#)patch_checkinstall 1.2 95/10/10 SMI # # Copyright (c) 1995 by Sun Microsystems, Inc. # All rights reserved # PATH=/usr/sadm/bin:$PATH LATER_MSG="PaTcH_MsG 6 ERROR: A later version of this patch is applied." NOPATCH_MSG="PaTcH_MsG 2 ERROR: Patch number $ACTIVE_PATCH is not installed" NEW_LIST="" # Get OLDLIST . $1 # # Confirm that the patch that got us here is the latest one installed on # the system and remove it from PATCHLIST. # Is_Inst=0 Skip=0 active_base=`echo $ACTIVE_PATCH | nawk ' { print substr($0, 1, match($0, "Patchvers_pfx")-1) } '` active_inst=`echo $ACTIVE_PATCH | nawk ' { print substr($0, match($0, "Patchvers_pfx")+1) } '` for patchappl in ${OLDLIST}; do appl_base=`echo $patchappl | nawk ' { print substr($0, 1, match($0, "Patchvers_pfx")-1) } '` if [ $appl_base = $active_base ]; then appl_inst=`echo $patchappl | nawk ' { print substr($0, match($0, "Patchvers_pfx")+1) } '` result=`expr $appl_inst \> $active_inst` if [ $result -eq 1 ]; then puttext "$LATER_MSG" exit 3 elif [ $appl_inst = $active_inst ]; then Is_Inst=1 Skip=1 fi fi if [ $Skip = 1 ]; then Skip=0 else NEW_LIST="${NEW_LIST} $patchappl" fi done if [ $Is_Inst = 0 ]; then puttext "$NOPATCH_MSG" exit 3 fi # # OK, all's well. Now condition the key variables. # echo "PATCHLIST=${NEW_LIST}" >> $1 echo "Patch_label=" >> $1 echo "PATCH_INFO_$ACTIVE_PATCH=backed out" >> $1 # Get the current PATCH_OBSOLETES and condition it Old_Obsoletes=$PATCH_OBSOLETES echo $ACTIVE_OBSOLETES | sed 'y/\ /\n/' | \ nawk -v PatchObsList="$Old_Obsoletes" ' BEGIN { printf("PATCH_OBSOLETES="); PatchCount=split(PatchObsList, PatchObsComp, " "); for(PatchIndex in PatchObsComp) { Atisat=match(PatchObsComp[PatchIndex], "@"); PatchObs[PatchIndex]=substr(PatchObsComp[PatchIndex], \ 0, Atisat-1); PatchObsCnt[PatchIndex]=substr(PatchObsComp\ [PatchIndex], Atisat+1); } } { for(PatchIndex in PatchObs) { if (PatchObs[PatchIndex] == $0) { PatchObsCnt[PatchIndex]=PatchObsCnt[PatchIndex]-1; } } next; } END { for(PatchIndex in PatchObs) { if ( PatchObsCnt[PatchIndex] > 0 ) { printf("%s@%d ", PatchObs[PatchIndex], PatchObsCnt\ [PatchIndex]); } } printf("\n"); } ' >> $1 # remove the used parameters echo "ACTIVE_OBSOLETES=" >> $1 echo "Obsoletes_label=" >> $1 exit 0 |
# This script deletes the used backout data for a patch package # and removes the deletes file entries. # # directory format options. # # @(#)patch_postinstall 1.2 96/01/29 SMI # # Copyright (c) 1995 by Sun Microsystems, Inc. # All rights reserved # PATH=/usr/sadm/bin:$PATH THIS_DIR=`dirname $0` Our_Deletes=$THIS_DIR/deletes # # Delete the used backout data # if [ -f $Our_Deletes ]; then cat $Our_Deletes | while read path; do if valpath -l $path; then Client_Path=`echo "$CLIENT_BASEDIR/$path" | sed s@//@/@` else # It's an absolute path Client_Path=$path fi rm `removef $PKGINST $Client_Path` done removef -f $PKGINST rm $Our_Deletes fi # # Remove the deletes file, checkinstall and the postinstall # rm -r $PKGSAV/$ACTIVE_PATCH rm -f $THIS_DIR/checkinstall $THIS_DIR/postinstall exit 0 |
O processo de atualização de um pacote é muito diferente do processo de substituição de um pacote. Ao mesmo tempo em que há ferramentas especiais que oferecem suporte à atualização de pacotes padrão entregues como parte do sistema operacional Solaris, um pacote não incorporado pode estar destinado a oferecer suporte a sua própria atualização — vários exemplos anteriores descreveram os pacotes pensados para o futuro e que controlam o método exato de instalação sob a direção do administrador. Você também pode criar um script request para oferecer suporte à atualização direta de um pacote. Se o administrador optar por instalar um pacote de modo a substituir completamente o outro, sem deixar rastro de arquivos obsoletos, os scripts de pacote podem fazê-lo.
O script request e o script postinstall deste exemplo oferecem um pacote atualizável simples. O script request se comunica com o administrador e configura um arquivo simples no diretório /tmp para remover a instância de pacote antiga. (Embora o script request crie um arquivo (que é proibido), não há problema porque todos têm acesso ao /tmp).
O script postinstall executa, então, o script shell em /tmp, que executa o comando pkgrm necessário contra o pacote antigo e, em seguida, se auto-exclui.
Este exemplo ilustra uma atualização básica. Tem menos de cinqüenta linhas de código incluindo algumas mensagens bastante longas. Pode ser estendido para que devolva a atualização ou realize outras transformações principais no pacote conforme requerido pelo criador.
O design de uma opção de atualização da interface de usuário deve estar absolutamente segura de que o administrador está totalmente ciente do processo e solicitou ativamente a atualização em vez da instalação paralela. Não há nada de errado em realizar uma operação complexa como a atualização contanto que a interface de usuário permita que a operação seja realizada com clareza.
# request script control an upgrade installation PATH=/usr/sadm/bin:$PATH UPGR_SCRIPT=/tmp/upgr.$PKGINST UPGRADE_MSG="Do you want to upgrade the installed version ?" UPGRADE_HLP="If upgrade is desired, the existing version of the \ package will be replaced by this version. If it is not \ desired, this new version will be installed into a different \ base directory and both versions will be usable." UPGRADE_NOTICE="Conflict approval questions may be displayed. The \ listed files are the ones that will be upgraded. Please \ answer \"y\" to these questions if they are presented." pkginfo -v 1.0 -q SUNWstuf.\* if [ $? -eq 0 ]; then # See if upgrade is desired here response=`ckyorn -p "$UPGRADE_MSG" -h "$UPGRADE_HLP"` if [ $response = "y" ]; then OldPkg=`pkginfo -v 1.0 -x SUNWstuf.\* | nawk ' \ /SUNW/{print $1} '` # Initiate upgrade echo "PATH=/usr/sadm/bin:$PATH" > $UPGR_SCRIPT echo "sleep 3" >> $UPGR_SCRIPT echo "echo Now removing old instance of $PKG" >> \ $UPGR_SCRIPT if [ ${PKG_INSTALL_ROOT} ]; then echo "pkgrm -n -R $PKG_INSTALL_ROOT $OldPkg" >> \ $UPGR_SCRIPT else echo "pkgrm -n $OldPkg" >> $UPGR_SCRIPT fi echo "rm $UPGR_SCRIPT" >> $UPGR_SCRIPT echo "exit $?" >> $UPGR_SCRIPT # Get the original package's base directory OldBD=`pkgparam $OldPkg BASEDIR` echo "BASEDIR=$OldBD" > $1 puttext -l 5 "$UPGRADE_NOTICE" else if [ -f $UPGR_SCRIPT ]; then rm -r $UPGR_SCRIPT fi fi fi exit 0 |
# postinstall to execute a simple upgrade PATH=/usr/sadm/bin:$PATH UPGR_SCRIPT=/tmp/upgr.$PKGINST if [ -f $UPGR_SCRIPT ]; then sh $UPGR_SCRIPT & fi exit 0 |
Um pacote de arquivo de classe, que é um aperfeiçoamento da ABI (Application Binary Interface), é aquele no qual certos conjuntos de arquivos são combinados em arquivos simples, ou arquivos de dados, e são opcionalmente compactados ou criptografados. Os formatos de arquivo de classe aumentam a velocidade da instalação inicial em até 30% e melhoram a confiabilidade durante a instalação de pacotes e patches em sistemas de arquivos potencialmente ativos.
As seções seguintes oferecem informações sobre a estrutura do diretório, as palavras-chave e o utilitário faspac do pacote de arquivo.
A entrada do pacote mostrada na ilustração abaixo representa o diretório que contém os arquivos de pacote. Este diretório deve ser o mesmo do pacote.
O quadro abaixo lista as funções dos arquivos e diretórios contidos no diretório do pacote.
Item |
Descrição |
---|---|
pkginfo |
Arquivo que descreve o pacote como um todo incluindo as variáveis de ambiente especiais e as diretivas de instalação |
pkgmap |
Arquivo que descreve cada objeto que será instalado , como um arquivo, diretório ou pipe |
reloc |
Diretório opcional que contém os arquivos a serem instalados em relação ao diretório base (os objetos relocáveis) |
root |
Diretório opcional que contém os arquivos a serem instalados em relação ao diretório root (os objetos-raiz) |
install |
Diretório opcional que contém os scripts e outros arquivos auxiliares (exceto para pkginfo e pkgmap, todos os arquivos ftype i até aqui) |
O formato de arquivo de classe permite que o construtor do pacote combine arquivos dos diretórios reloc e root em arquivos de dados que podem ser compactados, criptografados ou, senão, processados de outra maneira a fim de aumentar a velocidade da instalação, diminuir o tamanho do pacote ou aumentar a segurança deste.
A ABI permite que os arquivos dentro de um pacote sejam atribuídos a uma classe. Todos os arquivos de uma classe específica podem ser instalados no disco usando um método personalizado definido por um script de ação de classe. Este método personalizado pode usar programas disponíveis no sistema de destino ou programas entregues com o pacote. A formato resultante se parece muito ao formato padrão da ABI. Como mostrado na ilustração seguinte, outro diretório é adicionado. Qualquer tipo de arquivos destinado ao arquivo de dados é simplesmente combinado em um arquivo único e colocado no diretório archive. Todos os arquivos arquivados são removidos dos diretórios reloc e root e um script de ação de classe de instalação é colocado no diretório install.
Para oferecer suporte a este novo formato de arquivo de classe, três novas interfaces em forma de palavras-chave têm especial significado no arquivo pkginfo. Estas palavras-chave são usadas para designar as classes que requerem tratamento especial. A instrução do formato de cada palavra-chave é: keyword=class1[class2 class3 ...]. Cada valor da palavra-chave está definido na tabela seguinte.
Palavra-chave |
Descrição |
---|---|
PKG_SRC_NOVERIFY |
Esta palavra-chave diz a pkgadd para não verificar a existência e as propriedades dos arquivos nos diretórios reloc ou root do pacote entregue se eles pertencerem à classe nomeada. É requerido por todas as classes arquivadas, porque tais arquivos não estão mais em um diretório reloc ou root. São um arquivo de formato privado do diretório archive. |
PKG_DST_QKVERIFY |
Os arquivos nessas classes são verificados após a instalação usando um algoritmo rápido com pouca ou nenhuma saída de texto. A verificação rápida primeiro define os atributos de cada arquivo corretamente e, então, realiza a verificação para comprovar se a operação foi bem-sucedida. Em seguida, há um teste do tamanho do arquivo e do tempo de modificação em comparação ao pkgmap. Não é realizada nenhuma verificação com checksum e a recuperação de erros é bem pior comparada com a oferecida pelo mecanismo padrão. No caso de blecaute ou de falha do disco durante a instalação, o arquivo do conteúdo pode ficar incompatível com os arquivos instalados. Esta incompatibilidade pode ser resolvida sempre com um comando pkgrm. |
PKG_CAS_PASSRELATIVE |
Normalmente o script de ação de classe recebe do stdin uma lista de pares de origem e destino constando quais arquivos instalar. As classes atribuídas a PKG_CAS_PASSRELATIVE não obtêm os pares de origem e destino. Em vez disso, elas recebem uma única lista, na qual a primeira entrada é a localização do pacote de origem e o restante são os caminhos de destino. Isso serve especificamente para simplificar a extração de um arquivo de dados. Da localização do pacote de origem, você pode encontrar o arquivo de dados no diretório archive. Os caminhos de destino são, então, passados para a função responsável pela extração do conteúdo do arquivo de dados. Cada caminho de destino fornecido é absoluto ou relativo do diretório base dependendo se o caminho estiver localizado originalmente em root ou reloc. Se esta opção for escolhida, pode ser difícil combinar caminhos absoluto e relativo em uma única classe. |
Para cada classe arquivada é necessário um script de ação de classe. É um arquivo que contém comandos do shell Bourne que é executado por pkgadd para realmente instalar os arquivos a partir do arquivo de dados. Se o script de ação de classe for encontrado no diretório install do pacote, o pkgadd passa toda a responsabilidade da instalação para este script. O script de ação de classe é executado com permissões de raiz e pode colocar seus arquivos em quase todos os locais do sistema de destino.
A única palavra-chave que é absolutamente necessária para implementar um pacote de arquivo de classe é PKG_SRC_NOVERIFY. As outras podem ser usadas para melhorar a velocidade da instalação ou conservar o código.
O utilitário faspac converte um pacote de ABI padrão em um formato de arquivo de classe usado em pacotes incorporados. Este utilitário arquiva usando cpio e compacta usando compress. O pacote resultante tem um diretório adicional na parte superior do diretório denominada archive. Neste diretório todos os arquivos de dados terão o nome de classe. O diretório install conterá os scripts de ação de classe necessários para desempacotar cada arquivo de dados. Os caminhos absolutos não são arquivados.
O utilitário faspac tem o seguinte formato:
faspac [-m Archive Method] -a -s -q [-d Base Directory] / [-x Exclude List] [List of Packages] |
Cada opção do comando faspac está descrita na tabela seguinte.
Opção |
Descrição |
---|---|
-m Archive Method
|
Indica um método para arquivar ou compactar. bzip2 é o utilitário de compactação padrão usado. Para passar para o método zip ou unzip use -m zip ou para cpio ou compress use - m cpio. |
-a |
Fixa atributos (deve ser raiz para fazê-lo). |
-s |
Indica a tradução do pacote do tipo ABI padrão. Esta opção toma o cpio ou o pacote compactado e o transforma em um formato de pacote compatível com a ABI padrão. |
-q |
Indica o modo silencioso. |
-d Base Directory |
Indica o diretório no qual todos os pacotes presentes agirão de acordo com o requerido pela linha de comando. É mutuamente exclusivo em relação à entrada List of Packages. |
-x Exclude List |
Indica uma lista de pacotes separados por vírgulas, espaços ou entre aspas que serão excluídos do processo. |
List of Packages |
Indica a lista de pacotes que serão processados. |