SUN Packaging Guide -
Writing installation/procedure scripts

Creating installation/remove scripts

PROBLEM: During a Solaris installation - either by cdrom or jumpstart - the local harddisks/partitions are mounted at a read-writable mount point called "/a". The running Solaris itself is mounted read-only by NFS. Equally, the BASEDIR variable is also set to "/a". "/a" therefore is the location where all packages to be installed will be written to. If you have this in mind, then you will understand why I strongly recommend omitting the trailing "/" when creating a prototype file and why I _strongly_ recommend not using any kind of absolute pathnames. As the booted system is mounted read-only in memory, a PKG containing /usr/local in its pkgmap will fail to install as the files _cannot_ be installed in "/"!

Therefore any installation script using special shell commands like useradd, groupadd, mkdir or any other command should reference the commands like

CHROOTDIR="${BASEDIR}"
${BASEDIR}/usr/sbin/chroot ${CHROOTDIR} /usr/sbin/groupadd -g 80 httpd
${BASEDIR/usr/bin/mkdir $BASEDIR/tmp/foobar

Global environment variables _always_ override the definitions in the pkginfo file!

As you may remeber from the former chapter, there are six types of scripts you can create:

  1. request
  2. checkinstall
  3. preinstall
  4. postinstall
  5. preremove
  6. postremove

Differences between installation/procedure scripts

There are installation scripts and procedure scripts. Installation scripts are request and checkinstall. Procedure scripts are preinstall, postinstall, preremove, postremove.

Installation scripts cannot modify files. They can do global software dependancy checks, e.g. whether a certain version of a binary is installed or whether a needed file (database file) exists somewhere on the harddisk. They can gather data from the system (checkinstall) or the administrator (request). They can modify global variables like CLASSES, so only certain files will be installed.

Procedure scripts provide a mechanism to influence the package installation process. They can only do package-based dependancy checks. They can modify system resources like adding users or changing permissions. They cannot be used for gathering data interactively.

Package environment variables available to all scripts

CLIENT_BASEDIR The base directory with respect to the target system. While BASEDIR is the variable to use if referring to a package on the cdrom/ install server, this variable is the path to include in files placed on the client system.
INST_DATADIR The directory where the package now being read is located.
PATH The search list used by sh to find commands on script invocation.
PKGINST The instance identifier of the package being installed.
PKGSAV The directory where files can be saved for use by removal scripts or where previously saved files can be found.
PKG_INSTALL_ROOT The root file system on the target system where the package is being installed. Available for pkgadd/pkgrm only when these commands are invoked with -R option.
PKG_NO_UNIFIED Is an env variable that gets set if the pkgadd/pkgrm command is invoked with the -R -M option.
UPDATE This variable does not exist under most installation environments. If it does exist (with the value yes), it means that a PKG with the same name, version and architecture is already installed on the system or that the installing PKG will overwrite an installed PKG. The original BASEDIR is then used.

Installation scripts

The "request" script

There are certain restrictions which apply to the request script:

NOTE: When you include a request script in a package that you want to add to a jumpstart server, keep in mind that noone can answer the questions you may ask in the script. Therefore an automated package install will maybe result in a partially installed package or the jumpstart mechanism will halt until an administrator gives the right answer. The only solution is to provide default values for your questions and to define those default values in the pkginfo file. Additionally, you can check whether the installation is taking part in an interactive environment or in a non-interactive environment by checking the content of the ${BASEDIR} variable.
Remember: if you are running in a non-interactive environment, e.g. cdrom or jumpstart installation, ${BASEDIR} refers to /a.

Here is an example for a request script:

#!/bin/sh
REALNAME=""
echo "Enter your realname: "
read REALNAME

# export REALNAME to global PKG environment
cat >> $1 << EOT
REALNAME=${REALNAME}
EOT

Doing like this will make the variable REALNAME and its content available to the following installation/procedure scripts. If you add a

echo "installation response file: $1"

to your request script, then you will realize that pkgadd calls the request script with the name of the temporarily response file as first parameter.

The "checkinstall" script

The checkinstall script is executed shortly after the optional request script. The checkinstall script is _strictly_ a data gatherer. However, based on the information it gathers, it can create or modify environment variables in order to control the course of the resulting installation. It is also capable of halting cleanly the installation process.
The checkinstall script is intended to perform basic checks on a file system that would not be normal for the pkgadd command. For example, it can manage general software dependancies, in contradiction to the depend file of a package which manages only package-level dependencies.

  • There can be only one checkinstall script for every PKG and it must be named "checkinstall"
  • The "checkinstall" script cannot modify files.
  • Environment variable assignments have to be made public by adding them to the global response file, usually $1.
  • Only the CLASSES and the BASEDIR parameters can be modified by the script. This means that global system environment variables or installation PKG variables cannot be modified.
  • The checkinstall script is not executed during PKG removal.
  • Every environment variable that may be modified should be defined in the pkginfo file.
  • The output format of the modified variables _must_ be PARAM=value
  • Administrator interaction is not permitted during execution of a checkinstall script. This has to be done in the request script.
  • The checkinstall script runs as user install or - if this user is not available - as user nobody.

    Here is an example for a checkinstall script:

    #!/bin/sh
    # checkinstall script for ARbc
    
    # First find which bash package has been installed
    pkginfo -q ARbash1 # try the older one first
    
    if [ $? -ne 0 ]
    then
    	pkginfo -q ARbash # now the latest
    	if [ $? -ne 0 ]
    	then
    		echo "No bash package can be found. Please install the"	
    		echo "ARbash package and try this installation again."
    		exit 3 # Suspend
    	else
    		BASHROOT="`pkgparam ARbash BASEDIR`/usr/local/bin" # new one
    	fi
    else
    	BASHROOT="`pkgparam ARbash1 BASEDIR`/usr/local/bin" # old one
    fi
    
    # Now look for the bash executable we will need for our silly bootscript
    if [ $BASHROOT/bash ]
    then
    	exit 0 # all OK
    else
    	echo "No bash executable can be found. Please check your bash"
    	echo "package for integrity  and try this installation again."
    	exit 3 # Suspend
    fi
    

    Procedure scripts

    Procedure scripts provide a set of instructions to be performed at particular point in package installation or removal. Of course there are also restrictions which apply to procedure scripts as well as they did to the installation scripts "request" and "checkinstall":

    Here is an example for a preinstall script:

    #!/bin/sh
    
    CHROOTDIR="${BASEDIR}"
    
    # Create a group for our new service
    ${BASEDIR}/usr/sbin/chroot ${CHROOTDIR} /usr/sbin/groupadd -g 80 httpd
    
    # Create a user for our new service
    ${BASEDIR}/usr/sbin/chroot ${CHROOTDIR} /usr/sbin/useradd -u 80 -g 80 -d
     /data/www -m -s /bin/sh -c "Webserver User" httpd
    

    Here is an example for a postinstall script:

    #!/bin/sh
    
    # Create logfile directory for our webserver
    ${BASEDIR}/bin/mkdir -p ${BASEDIR}/data/logs/www
    

    Here is an example for a preremove script:

    #!/bin/sh
    
    # Stop our webserver first
    
    /etc/init.d/webserver stop
    

    Here is an example for a postremove script

    #!/bin/sh
    
    LOGDIR=/data/logs
    
    # collect all logfiles to a .tar. gz and trash the directory
    cd ${LOGDIR} 
    /usr/local/bin/gtar cvfz ${LOGDIR}/logfiles.tar.gz && \
    rm -rf ${LOGDIR}
    

    NOTE: You may have noticed that the preremove/postremove scripts do not use the relative commandline syntax, e.g. with a ${BASEDIR} as prefix. Honestly, I never saw a package removal during Solaris installation. Additionally I think that package removal _has_ to be done interactively.