Un parche para un paquete es simplemente un paquete suelto diseñado para sobrescribir determinados archivos del original. No hay un motivo real para la inclusión de un paquete suelto excepto guardar espacio en el medio de entrega. También es posible distribuir todo el paquete original con unos cuantos archivos cambiados, o bien proporcionar acceso al paquete modificado en una red. Siempre que esos archivos nuevos sean de hecho diferentes (los demás archivos no se recompilaron), el comando pkgadd instala las diferencias. Revise las directrices siguientes relacionadas con los paquetes de parches.
Si el sistema es lo suficientemente complejo, es aconsejable establecer un sistema de identificación de parches que asegure que dos parches no sustituyan al mismo archivo en un intento de corregir comportamientos aberrantes diferentes. Por ejemplo, a los números base de parches de Sun se les asignan conjuntos de archivos mutuamente excluyentes de los que son responsables.
Es necesario que sea posible anular un parche.
Es vital que el número de versión del paquete de parches sea el mismo que el del paquete original. Debe hacer un seguimiento del estado del parche del paquete mediante una entrada aparte del archivo pkginfo con el formato.
PATCH=patch_number |
Si se cambia la versión del paquete de un parche, cree otra instancia del paquete para que sea extremadamente difícil administrar el producto con el parche. Este método de parches de instancias progresivos comportaban ciertas ventajas en las primeras versiones del sistema operativo Solaris, pero hace que la administración de los sistemas más complicados sea tediosa.
Todos los parámetros de zona del parche deben coincidir con los parámetros de zona del paquete.
Por lo que respecta a los paquetes que componen el sistema operativo Solaris, debe haber una única copia del paquete en la base de datos de paquetes, aunque puede haber varias instancias de parches. Con el fin de suprimir un objeto de un paquete instalado (mediante el comando removef) debe imaginarse qué instancias son propietarias de ese archivo.
Sin embargo, si el paquete (que no forma parte del sistema operativo Solaris) debe determinar el nivel de parches de un paquete concreto que forma parte del sistema operativo Solaris, se convierte en un problema que debe resolverse aquí. Las secuencias de comandos de instalación pueden ser bastante grandes sin un impacto significativo puesto que no se almacenan en el sistema de archivos de destino. Gracias a las secuencias de comandos de acción de clase y otras secuencias de comandos de procedimientos, puede guardar archivos cambiados mediante la variable de entorno PKGSAV (o en otros directorios, más permanentes) con el fin de permitir la anulación de los parches instalados. También puede supervisar el historial de parches si configura las variables de entorno pertinentes a través de las secuencias de comandos request. Las secuencias de comandos de las secciones siguientes asumen que puede haber varios parches cuyo esquema de numeración conlleve algún significado al aplicarse a un único paquete. En este caso, el número de cada parche representa un subconjunto de archivos con funciones relacionadas en el paquete. Dos números de parches diferentes no pueden cambiar el mismo archivo.
Con el fin de convertir un paquete disperso regular en un paquete de parches, las secuencias de comandos descritas en las secciones siguientes pueden doblarse sencillamente en el paquete. Todas ellas son reconocibles como componentes de paquete estándar con la excepción de las dos últimas, patch_checkinstall y patch_postinstall. Esas dos secuencias de comandos pueden incorporarse al paquete de anulación, si desea incluir la posibilidad de anular el parche. Las secuencias de comandos son bastante sencillas y sus diversas tareas son sencillas.
Este método de parches se puede usar para sistemas cliente de parches, pero los directorios raíz cliente del servidor deben contar con los permisos correctos para permitir la lectura al usuario install o nobody.
La secuencia de comandos checkinstall verifica que el parche es el adecuado para este paquete concreto. Una vez que se haya confirmado, genera la lista de parches y la lista de información de parches; después las inserta en el archivo de respuesta para su incorporación a la base de datos de paquetes.
Una lista de parches es aquella donde se enumeran los que tienen afectado el paquete actual. Esta lista de parches se registra en el paquete instalado en el archivo pkginfo con una línea que podría tener este aspecto:
PATCHLIST=patch_id patch_id ... |
Una lista de información de parches es aquella de la que depende el parche actual. Esta lista de parches también se registra en el archivo pkginfo con una línea que podría tener este aspecto:
PATCH_INFO_103203-01=Installed... Obsoletes:103201-01 Requires: \ Incompatibles: 120134-01 |
Estas líneas (y su formato) se declaran como interfaz pública. Las empresas que distribuyan parches para los paquetes de Solaris deben actualizar esta lista de forma pertinente. Cuando se entrega un parche, cada paquete del parche contiene una secuencia de comandos checkinstall que lleva a cabo esta tarea. Esa misma secuencia de comandos checkinstall también actualiza otros parámetros específicos de los parches. Ésta es la nueva arquitectura de parches que recibe el nombre de Direct Instance Patching.
En este ejemplo, tanto los paquetes originales como sus parches existen en el mismo directorio. Los dos paquetes originales reciben el nombre de SUNWstuf.v1 y SUNWstuf.v2, y sus parches reciben el nombre de SUNWstuf.p1 y SUNWstuf.p2. Esto significa que puede ser muy difícil para una secuencia de comandos de procedimientos imaginarse de qué directorio vienen estos archivos, puesto que todo lo que sigue en el nombre del paquete al punto (“.”) se elimina en el parámetro PKG, y la variable de entorno PKGINST se refiere a la instancia instalada, no la instancia de origen. Por lo tanto, las secuencias de comandos de procedimientos pueden encontrar el directorio de origen, la secuencia de comandos checkinstall (que siempre se ejecuta desde el directorio de origen) lleva a cabo la consulta y pasa la ubicación como variable SCRIPTS_DIR. Si hubiese habido un solo paquete en el directorio de origen llamado SUNWstuf, las secuencias de comandos de procedimientos podrían haberla encontrado mediante $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 |
La secuencia de comandos preinstall inicializa el archivo prototype, los archivos de información y las secuencias de comandos de instalación para que se genere el paquete de anulación. Esta secuencia de comandos es muy sencilla y las secuencias restantes de este ejemplo sólo permiten un paquete de anulación para describir archivos regulares.
Si quisiera restaurar vínculos simbólicos, vínculos físicos, dispositivos y conducciones con nombre en un paquete de anulación, podría modificar la secuencia de comandos preinstall para usar el comando pkgproto con el fin de comparar el archivo pkgmap entregado con los archivos instalados, y después crear una entrada de archivo prototype para cada elemento que no sea un archivo que se deba cambiar en el paquete de anulación. El método que debe usar es parecido al de la secuencia de comandos de acción de clase.
Las secuencias de comandos patch_checkinstall y patch_postinstall se insertan en el árbol de origen del paquete desde la secuencia de comandos preinstall. Estas dos secuencias de comandos deshacen lo que el parche hace.
# 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 |
La secuencia de comandos de acción de clase crea una copia de cada archivo que sustituye a un archivo existente y agrega una línea correspondiente al archivo prototype para el paquete de anulación. Todo esto se lleva a cabo con secuencias de comandos nawk bastante sencillas. La secuencia de comandos de acción de clase recibe una lista de pares de origen/destino que se compone de archivos ordinarios que no coinciden con los archivos instalados correspondientes. Los vínculos simbólicos y otros elementos que no sean archivos deben gestionarse en la secuencia de comandos 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 |
La secuencia de comandos postinstall crea el paquete de anulación mediante la información proporcionada por las demás secuencias de comandos. Puesto que los comandos pkgmk y pkgtrans no precisan la base de datos de paquetes, se pueden ejecutar en la instalación de un paquete.
En este ejemplo se permite deshacer el parche mediante la construcción de un paquete de formato de flujo en el directorio para guardar (mediante la variable de entorno PKGSAV). No es obvio, pero este paquete debe tener formato de flujo, porque el directorio para guardar cambia de posición durante una operación de pkgadd. Si se aplica el comando pkgadd a un paquete en su propio directorio para guardar, las suposiciones sobre dónde está el origen del paquete en un momento concreto se vuelven muy poco fiables. Un paquete con formato de flujo se desempaqueta en un directorio temporal y se instala desde allí. (Un paquete con formato de directorio comenzaría la instalación desde el directorio para guardar y se encontraría repentinamente reubicado durante una operación a prueba de fallos de pkgadd.)
Para determinar qué parches se aplican a un paquete, use este comando:
$ pkgparam SUNWstuf PATCHLIST |
Con la excepción de PATCHLIST, una interfaz pública de Sun, no hay nada significativo en los nombres de parámetros en este ejemplo. En lugar de PATCH, podría usar la SUNW_PATCHID tradicional y se podría cambiar en consonancia el nombre de otras listas como PATCH_EXCL y PATCH_REQD.
Si determinados paquetes de parches dependen de otros que están disponibles en el mismo medio, la secuencia de comandos checkinstall podría determinar esto y crear una secuencia de comandos que se ejecutara mediante la secuencia de comandos postinstall del mismo modo que el ejemplo de actualización (consulte Actualización de paquetes).
# 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 |