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

Reply via email to