The BFU ACR tool inspired me to consider the following approach to
getting us off of SVR4 + LU / BFU/ACR faster, and to making the
WOS->OpenSolaris conversions/transition simpler.
Suppose we had an SMF service to receive pings from IPS SMF actions,
which service runs per-pkg upgrade actions that require scripting.
And suppose that where we have an SVR4 pkg with legacy CAS and post*
scripts we could automatically convert that pkg to IPS using that
service to run the pkg's upgrade actions?
I've prototyped the [attached] start method for such a service.
It can run class action scripts, postinstall and postremove scripts, but
in the context of an SMF service pinged by IPS pkgs which "register"
their legacy SVR4 scripting.
The service works as follows:
- pkgs register their upgrade needs installing files like so:
/var/acr/registratrions/<pkg-name>.reg
/var/acr/registratrions/<pkg-name>[,-.]*
where the .reg file lists postinstall, postremove and CAS scripts, as
well as sources and destinations for the CAS scripts.
- pkgs that register these upgrade needs use an IPS SMF action to
restart the service
- the service keeps a copy of all the registrations and uses that to:
- detect removals
- detect additions
- be able to run these scripts after removals
Note that SVR4 preinstall, preremove, checkinstall and request scripts
are not and cannot be supported in this manner.
TODO:
- extend the SVR4->IPS conversion tool to automatically create these
registrations given the SVR4 pkg's post* and CAS scripts and pkgmap
entries of classes with CAS scripts
- package the service and its start method
- extend nightly(1) to create an IPS repo and publish into it the pkgs
it builds, with legacy SVR4 pkgs auto-converted to IPS.
Given all that it should be possible to reduce the amount of work needed
to convert the WOS to OpenSolaris releases, and to get us off of BFU.
Instead of BFUing one would image update to a private repo built from
pkgs built by nightly as described above.
Q1: Is this approach worth pursuing?
Or would it be better to throw away all those SVR4 scripts and start
from scratch?
Q2: Is reuse of SVR4 scripts, as opposed to throwing them out and
starting from scrath, too icky to consider?
Of course, we might eventually still throw out those scripts, but
we'd buy ourselves time to do it if we could reuse them in the short
term.
Thanks,
Nico
--
#!/bin/ksh93
PROG=${0##*/}
dryrun=
verbose=
statedir=/var/acr
[[ -n "$DEBUG" ]] && set -x
[[ -n "$VERBOSE" ]] && verbose="print --"
[[ -n "$DRYRUN" ]] && dryrun=:
# Taken from the Solaris 10 Application Packaging Developer's Guide
#
# http://docs.sun.com/app/docs/doc/817-0406?l=en
#
PATH=/sbin:/usr/sbin:/usr/bin:/usr/sadm/install/bin
function usage {
${PAGER:-/bin/more} <<EOF
Usage: $PROG [-vnx] [-d directory]
-x debug
-v verbose
-n dry run
-d dir state directory (default: $statedir)
This program is intended to be used as the start method of
transient services whose purpose is to run legacy class action
scripts (CAS) from legacy SVR4 packaging in images where SVR4
packaging is no longer in use.
Software wishing to use this facility should deliver the
following files:
- $regdir/<pkg-name>.reg
This file should contain entries of these forms:
# This is a comment.
#
# Set the BASEDIR environment variable's value to
# desired value when running the package's legacy SVR4
# scripts. Defaults to '/'.
BASEDIR <basedir>
#
# Specify install CAS for some legacy SVR4 class.
i.class <source-filename> <classname>
# Specify remove CAS for some legacy SVR4 class.
r.class <source-filename> <classname>
#
# Run the CAS for install/removal of <destination> using
# the given <source-filename>, where <source-filename>
# is a filename in $regdir/ delivered by the pkg that
# delivered this .reg file.
<class-name> <source-filename> <destination>
...
# Run the named <source-filename> as a legacy SVR4
# packaging postinstall or postremove script after all
# class actions have been run.
postinstall <source-filename>
postremove <source-filename>
All these are optional.
- $regdir/<source1>
$regdir/<source2>
...
$regdir/<sourceN>
Where each file is a file named in a .reg file (see above).
When run this method will first discover removals of previously-
processed .reg files and will run the corresponding class action
scripts, then the postremove scripts, if any. Then it will
discover additions of new .reg files and will run the
corresponding class action scripts, then the postinstall
scripts, if any.
The environment used to run the legacy SVR4 packaging scripts is
as faithful to the Solaris Developer Packaging Guide as
possible.
Limitations:
- \$INST_DATA_DIR will reference an empty directory; no outputs
from checkinstall or request scripts can be used as there is
no direct IPS equivalent to those scripts.
- class action scripts for removal cannot rely on *any* part of
the package being removed still being on the system (in fact,
they will not be). The system saves a copy of all the
registrations and source files for running at removal time.
Original class action scripts must be installed as:
- $casdir/i.<class>
- $casdir/r.<class>
EOF
exit 1
}
while getopts vnxd: opt
do
case $opt in
x) DEBUG=:; set -x;;
v) verbose="print -r --";;
n) dryrun=:;;
d) statedir=$OPTARG;;
\?) usage;;
esac
done
[[ $# -eq 0 && -z "$SMF_FMRI" ]] && usage
# See usage message below
usrcasdir=/usr/sadm/install/scripts
casdir=$statedir/cas
regdir=$statedir/registrations
saveregdir=$statedir/saved-registrations
# INST_DATADIR is a temp directory and must be cleaned up; see below
INST_DATADIR=
trap '[[ -n "$INST_DATADIR" && -d $INST_DATADIR ]] && rmdir $INST_DATADIR' EXIT
. /lib/svc/share/smf_include.sh
function run_it {
if [[ "$1" = @(postinstall|postremove) ]]
then
$verbose "Running $1 for pkg $2: $3"
$dryrun "$3"
return $?
elif [[ "$1" = class && $# -eq 5 ]]
then
$verbose "Running class action script $3 for pkg $2 to install
$5 from $4"
$dryrun print -r -- "$4 $5" | "$3"
return $?
elif [[ "$1" = class && $# -eq 4 ]]
then
$verbose "Running class action script $3 for pkg $2
(ENDOFCLASS)"
$dryrun "$3" ENDOFCLASS < /dev/null
return $?
else
print -u2 "Internal error (run_it)"
exit 1
fi
}
# Check source file names from registrations
function check_src {
[[ "$1" = */* ]] && return 1
[[ "$1" != *${2}[.,_-]* ]] && return 1
return 0
}
unset CASloc
typeset -A CASloc
# Find a CAS script
function resolve_cas {
if [[ -n "${CASloc[$1]}" ]]
then
# Private per-pkg CAS registered by the pkg
if ${dryrun:-false}
then
print -r "$regdir/${CASloc[$1]}"
else
print -r "$saveregdir/${CASloc[$1]}"
fi
return 0
elif [[ -x $usrcasdir/$1 ]]
then
# Standard SVR4 classes
print -r $usrcasdir/$1
return 0
fi
# Classes shipped in $casdir by the pkg that delivers this
# self-same script
print -r $casdir/$1
return 0
}
# This function does all the hard work.
#
# It is called once to look for and process removals, and once to look
# for and process additions. Most of the code for both is the same.
function scan_and_exec {
typeset scan_dir chk_dir bkp rm cas key src dst j
typeset cas_prefix post_script bkp_verbose rm_verbose
if [[ "$1" = additions ]]
then
scan_dir=$2
chk_dir=$3
cas_prefix=i
post_script=postinstall
bkp="$dryrun cp -p"
bkp_verbose=$verbose
rm=true
rm_verbose=true
elif [[ "$1" = removals ]]
then
scan_dir=$3
chk_dir=$2
cas_prefix=r
post_script=postremove
bkp=true
bkp_verbose=true
rm="$dryrun rm -f"
rm_verbose=$verbose
fi
[[ -d $scan_dir/ ]] || return 0
mkdir -p "$saveregdir"
# Scan $scan_dir looking for changes
for reg in $scan_dir/*.reg
do
# Check for no registrations
[[ "$reg" = $scan_dir/\*.reg ]] && continue
# Skip ones we've done already
if [[ -s "$chk_dir/${reg##*/}" ]]
then
cmp "$reg" "$chk_dir/${reg##*/}" > /dev/null && continue
fi
# Derive a pkg name from the .reg file basename
client_pkg=${reg##*/}
client_pkg=${client_pkg%%.*}
$verbose "...pprocessing registration for $client_pkg"
# BASEDIR, CLIENT_BASEDIR and PKGSAV are specific to the
# package whose scripts we're running -- we don't know
# this until we get here.
grep BASEDIR "$reg"|read j BASEDIR
BASEDIR=${BASEDIR:-/}
PKGSAV=${statedir}/pkgsav/$client_pkg
CLIENT_BASEDIR=$BASEDIR
export BASEDIR CLIENT_BASEDIR PKGSAV
$verbose "......BASEDIR=$BASEDIR"
# For additions backup the file
$bkp_verbose "......saving $reg in $saveregdir"
$bkp "$reg" $saveregdir
typeset -A CASes
while read key src dst
do
[[ "$key" = @(BASEDIR|postinstall|postremove) ]] && \
continue
if [[ "$key" = [ir].class ]]
then
# Private per-pkg CAS registered by the pkg;
# save the script and loop.
check_src "$src" "$client_pkg" || continue
$bkp "$regdir/$src" $saveregdir
CASloc[${key%%.*}.${dst#[ir].}]=$src
continue
fi
$bkp_verbose "......saving $src in $saveregdir"
$bkp $regdir/$src $saveregdir
cas=$(resolve_cas ${cas_prefix}.$key)
[[ -x $cas ]] || continue
check_src $src "$client_pkg" || continue
CASes[$cas]=$cas
run_it class $client_pkg $cas $scan_dir/$src $dst
done < "$reg"
# Now run the CAS with ENDOFCLASS as the argument
for cas in "${!cas...@]}"
do
run_it class $client_pkg $cas ENDOFCLASS
done
# Run postinstall/postremove
while read key src dst
do
[[ "$key" = $post_script ]] || continue
$bkp_verbose "......saving $src in $saveregdir"
$bkp $regdir/$src $saveregdir
run_it $post_script $scan_dir/$src
done < "$reg"
# For removals also remove the saved files
$verbose "...removing saved registration and scripts for
$client_pkg"
[[ "$1" = removals ]] || continue
while read key src dst
do
[[ "$key" = BASEDIR ]] && continue
check_src $src "$client_pkg" || continue
$rm_verbose "......removing $saveregdir/$src"
$rm "$saveregdir/$src"
done < "$reg"
$rm_verbose "......removing $reg"
$rm "$reg"
done
}
[[ -n "$DEBUG" ]] && typeset -ft scan_and_exec check_src resolve_cas
if [[ -n "SETUP" ]]
then
$verbose mkdir -p $statedir $casdir $regdir $saveregdir
$dryrun mkdir -p $statedir $casdir $regdir $saveregdir
fi
# These are taken from the Solaris 10 Application Packaging Developer's
# Guide.
#
# BASEDIR, CLIENT_BASEDIR and PKGSAV are specific to each package whose
# scripts we'll be running. Those are set in scan_and_exec() (see
# above).
PKG_INSTALL_ROOT=/
PKG_CLIENT_OS=Solaris
PKG_CLIENT_VERSION=$(uname -r)
PKG_CLIENT_REVISION=$(uname -v)
# INST_DATADIR will be empty; sorry. That means i.initd can't be made
# to work via this scheme, not without modifications to i.initd. Given
# SMF it's better to avoid the need for i.initd altogether.
INST_DATADIR=$(mktemp -d /tmp/inst_datadir_XXXXX)
TMPDIR=$INST_DATADIR
unset PKG_NO_UNIFIED
unset UPDATE
export PKG_CLIENT_OS PKG_CLIENT_VERSION PKG_CLIENT_REVISION INST_DATADIR
export PKG_INSTALL_ROOT
# Process removals first, then additions
$verbose "Scanning registrations of added pkgs with legacy SVR4 scripts..."
scan_and_exec removals $regdir $saveregdir
$verbose "Scanning removals of pkgs with legacy SVR4 scripts..."
scan_and_exec additions $regdir $saveregdir
exit $SMF_EXIT_OK
_______________________________________________
pkg-discuss mailing list
[email protected]
http://mail.opensolaris.org/mailman/listinfo/pkg-discuss