Document
Creating Solaris Packages
Author
Mark <mark@ibiblio.org>

Introduction

Packages on Solaris allow easy installation and extraction of software. All the parts of a package are maintained in a table of contents. The package tools allow you to install, update and remove software easily without having to worry about "ghost files" left on the system or misconfigured or partly installed software.

The process for creation of a package is generally to install the software once and then to organise it into a package that can be quickly added to another host or removed quickly from the first host. This greatly lessens the time needed to add software to a host.

In this document we will use GNU bison 1.24 as the example software.

For a full complement of precompiled Solaris packages, look at the Solaris Package Archive I maintain at http://sunsite.unc.edu/pub/packages/solaris/sparc

Creation of the software

To package any software you need to know the files that are to be included in the package. This is usually easy if you created the software. If however it's a completely new release which you have no experience with then you will most likely need to install it in some unique location to see exactly what is in the kit. With the GNU software this is easy to do as you can simply `configure --prefix=/dir` where dir is the temporary install directory. For this example I chose to use /usr/local/pkg.

After compiling I did a `make install` and had GNUbison install itself into the /usr/local/pkg tree. Then I did a `find /usr/local/pkg -print > /tmp/files`. Since I now knew what was in the software kit I could blow away the temporary /usr/local/pkg directory.

Then it's simply a matter of compiling and installing the GNUbison package as normal. Since the file /tmp/files contains the names of the parts of GNUBison, nothing will get lost. We can begin to create our Solaris package now.

Creating package configuration files

Once the software is installed you need to create a table of contents suitable to be used for the package creation tool. First though you'll need to tidy up the /tmp/files to fix the pathnames of the files. (They still have /usr/local/pkg in front of them). Edit the file and fix the path names. Then run:
    % cat /tmp/files | pkgproto > /tmp/Prototype
This will create a /tmp/Prototype file with the following in it:
    d none /usr/local 0755 root root
    d none /usr/local/bin 0755 root root
    d none /usr/local/man 0755 root root
    d none /usr/local/man/man1 0755 root root
    d none /usr/local/lib 0755 root root
    d none /usr/local/etc 0755 root root
    d none /usr/local/info 0755 root root
    d none /usr/local/share 0755 root root
    f none /usr/local/bin/bison 0755 root root
    f none /usr/local/man/man1/bison.1 0644 root root
    f none /usr/local/info/bison.info 0644 root root
    f none /usr/local/info/bison.info-1 0644 root root
    f none /usr/local/info/bison.info-2 0644 root root
    f none /usr/local/info/bison.info-3 0644 root root
    f none /usr/local/info/bison.info-4 0644 root root
    f none /usr/local/info/bison.info-5 0644 root root
    f none /usr/local/share/bison.simple 0644 root root
    f none /usr/local/share/bison.hairy 0644 root root
After manually reviewing the /tmp/Prototype file you may notice the permissions are not to your liking. Simply edit the file to specify what permissions you want on each inode. I manually alter each file in the package archive to have all files world readable, root owned, root group and all directories read and execute. (drwx-r-xr-x or -rw-r--r--).

You now have to add a pointer to a pkginfo file which tells pkgmk(1) the necessary details of the package you are creating.

So to the front of the Prototype file I add:
    i pkginfo
If you want checkinstall, preinstall and postinstall scripts run simply include them at the top.

What are they I hear you say?

checkinstall is a script that is run at install time, by the user "nobody". With it you can check for dependancies and problems and if needed, exit the pkgadd gracefully. Care is needed when building the package that the permissions on the directories are set up so the user "nobody" can get to the script. You are NOT asked by pkgadd if you want to run this script.

preinstall is run as root when the package is actually installing. An abort here will require the user to pkgrm the half installed package. You are asked by pkgadd if you want to run this script.

postinstall is a script that runs after the rest of the package is installed. With it you can do things like utilize the just installed software, install kernel drivers, create devices and other post-install things. This script also runs as root. You are asked by pkgadd if you want to run this script.

To add them to the Prototype file put in a line like:
    i preinstall
    i postinstall
    i checkinstall
These scripts are run after installation starts, so if something goes wrong with the install in those scripts and they exit with non-zero then a pkgrm will be required to clean up the half installed package.

Finally our Prototype file looks like:
    i pkginfo
    i checkinstall
    d none /usr/local/bin 0755 root root
    d none /usr/local/man 0755 root root
    d none /usr/local/man/man1 0755 root root
    d none /usr/local/lib 0755 root root
    d none /usr/local/etc 0755 root root
    d none /usr/local/info 0755 root root
    d none /usr/local/share 0755 root root
    f none /usr/local/bin/bison 0755 root root
    f none /usr/local/man/man1/bison.1 0644 root root
    f none /usr/local/info/bison.info 0644 root root
    f none /usr/local/info/bison.info-1 0644 root root
    f none /usr/local/info/bison.info-2 0644 root root
    f none /usr/local/info/bison.info-3 0644 root root
    f none /usr/local/info/bison.info-4 0644 root root
    f none /usr/local/info/bison.info-5 0644 root root
    f none /usr/local/share/bison.simple 0644 root root
    f none /usr/local/share/bison.hairy 0644 root root
See the prototype(4) manual page for further information on this file.

Originally I used to be verbose and add in lines such as:
    d none /usr 0755 root root
    d none /usr/local 0755 root root
which told the system the modes of the parent directories on my system. But there is a problem with this since it assumes that the file system is always laid out the same way. If /usr/local was a symlink to /opt on your system for instance, having the above lines would break things. It's a gotcha you will need to watch out for. It isn't a bad thing to leave out the lines, the packager will complain, but you can ignore it. When the package is installed the system will automatically create /usr/local if it doesn't exist. A general rule is, don't assume your standard is the universal standard.

Next we need to create a pkginfo file to tell pkgmk(1) what you actually want things to be called. Here is the file I used:
    PKG="GNUbison"
    NAME="GNU bison 1.24"
    VERSION="1.24"
    ARCH="sparc"
    CLASSES="none"
    CATEGORY="utility"
    VENDOR="GNU"
    PSTAMP="4thSep95"
    EMAIL="request@gnu.ai.mit.edu"
    ISTATES="S s 1 2 3"
    RSTATES="S s 1 2 3"
    BASEDIR="/"
See the pkginfo(4) manual page for information on these fields.

Creating the package

Once the package configuration files are complete you can now run the package creation command from the directory containing the config files (/tmp).
    % pkgmk -o -r / -d /tmp -f Prototype
This command means make the package, overwrite any previous attempts, use '/' as the root directory for locating the files, build the package in the /tmp directory and finally, take the list of files and attributes from the Prototype file.

The output produced was:
    ## Processing pkginfo file.
    ## Attempting to volumize 19 entries in pkgmap.
    part  1 -- 990 blocks, 21 entries
    ## Packaging one part.
    /tmp/GNUbison/pkgmap
    /tmp/GNUbison/pkginfo
    /tmp/GNUbison/root/usr/local/bin/bison
    /tmp/GNUbison/root/usr/local/info/bison.info
    /tmp/GNUbison/root/usr/local/info/bison.info-1
    /tmp/GNUbison/root/usr/local/info/bison.info-2
    /tmp/GNUbison/root/usr/local/info/bison.info-3
    /tmp/GNUbison/root/usr/local/info/bison.info-4
    /tmp/GNUbison/root/usr/local/info/bison.info-5
    /tmp/GNUbison/root/usr/local/man/man1/bison.1
    /tmp/GNUbison/root/usr/local/share/bison.hairy
    /tmp/GNUbison/root/usr/local/share/bison.simple
    ## Packaging complete.
This created a directory in /tmp called GNUbison. Inside that directory were two files called pkgmap and pkginfo. The first, pkgmap, contains the pathnames to all the files and permissions and checksums for each of them. The second file, pkginfo, contains the same information you specified in your pkginfo file in /tmp. Also in the /tmp/GNUbison directory is a directory called root that has the tree of all your files laid out as they would appear on the real filesystem. If you included a checkinstall script, (or similar), they would be stored in a directory called install. You don't need to really be aware of what's under the top directory unless you need to fix something manually.

To allow easy storage and transportation of this package all you need to do is created a tar.gz file of the GNUbison directory and transport the package like that.
    cd /tmp
    tar -cf - GNUbison | gzip -9 -c > GNUbison.1.24.SPARC.pkg.tar.gz
In most cases I include the release of Solaris that the software was compiled on in the name of the package.

Unpacking and installing a package

To unpackage and install a prepared package simply type in:
    % gunzip -c GNUbison.1.24.SPARC.pkg.tar.gz | tar -xvf -
    % su -
    # pkgadd -d $PWD
You will be prompted to add in the GNUbison package; confirm this, answer appropriately to commence the install and it will self install. Afterwards you can remove the GNUbison directory to clean up.

Package Creation Files

When the package is created it's a good idea to keep the config files as you may choose to re-create the package or even update it to a later version. Having the files from other packages simplifies this. On my package creation machines I keep these files in /pkgs.
missi:/pkgs root# ls -aCF
./                  g77.0.5.21/         gzip.1.24/          perl5.004.04/
../                 gcc.2.7.2/          libstdc++-2.8.1.1/  preinstall*
X11R6.3/            gcc.2.7.2.1/        lsof.4.02/          rcs.5.7/
bison-1.25/         gcc.2.7.2.2/        mkpkg*              tcsh.6.07.02/
cmu-snmp.2.1.2/     gcc.2.7.2.3/        netpbm.1mar.94/     
cvs.1.9/            gcc.2.8.1/          perl5.003/
flex.2.5.4a/        gdb/                perl5.004.03/

missi:/pkgs/gzip.1.24 root# ls -aCF
./           ../          Prototype      checkinstall*    mkpkg*       pkginfo

pkginfo

PKG="GNUbison" NAME="GNU bison 1.24 SPARC Solaris 2.6" VERSION="1.24" ARCH="sparc" CLASSES="none" CATEGORY="utility" VENDOR="GNU" PSTAMP="12thAugust2004" EMAIL="request@gnu.ai.mit.edu" ISTATES="S s 1 2 3" RSTATES="S s 1 2 3" BASEDIR="/"
Prototype
i pkginfo i checkinstall d none /usr/local/bin 0755 root root d none /usr/local/man 0755 root root d none /usr/local/man/man1 0755 root root d none /usr/local/lib 0755 root root d none /usr/local/etc 0755 root root d none /usr/local/info 0755 root root d none /usr/local/share 0755 root root f none /usr/local/bin/bison 0755 root root f none /usr/local/man/man1/bison.1 0644 root root f none /usr/local/info/bison.info 0644 root root f none /usr/local/info/bison.info-1 0644 root root f none /usr/local/info/bison.info-2 0644 root root f none /usr/local/info/bison.info-3 0644 root root f none /usr/local/info/bison.info-4 0644 root root f none /usr/local/info/bison.info-5 0644 root root f none /usr/local/share/bison.simple 0644 root root f none /usr/local/share/bison.hairy 0644 root root
mkpkg
#!/bin/sh pkg=GNUbison pkgfile=GNUbison.1.24.SPARC.Solaris.2.6.pkg.tgz pkgmk -o -r / -d /tmp -f Prototype echo "Setting file permissions in /tmp/${pkg} tree to 644." find /tmp/${pkg} -type f -print | xargs chmod a+r find /tmp/${pkg} -type f -print | xargs chmod u+w echo "Setting directory permissions in /tmp/${pkg} tree to 755." find /tmp/${pkg} -type d -print | xargs chmod 755 if [ -f /tmp/${pkg}/install/preinstall ]; then chmod 755 /tmp/${pkg}/install/preinstall fi if [ -f /tmp/${pkg}/install/postinstall ]; then chmod 755 /tmp/${pkg}/install/postinstall fi if [ -f /tmp/${pkg}/install/preremove ]; then chmod 755 /tmp/${pkg}/install/preremove fi if [ -f /tmp/${pkg}/install/postremove ]; then chmod 755 /tmp/${pkg}/install/postremove fi if [ -f /tmp/${pkg}/install/request ]; then chmod 755 /tmp/${pkg}/install/request fi if [ -f /tmp/${pkg}/install/checkinstall ]; then chmod 755 /tmp/${pkg}/install/checkinstall fi cd /tmp echo Gzipping /tmp/$pkg into /tmp/$pkgfile... /usr/bin/tar -cf - $pkg | gzip -9c > /tmp/$pkgfile
checkinstall
#!/bin/sh # # expected_release="5.6" expected_platform="sparc" # release=`uname -r` platform=`uname -p` # if [ ${platform} != ${expected_platform} ]; then echo "\n\n\n\tThis package must be installed on a ${expected_platform} architecture\n" echo "\tAborting installation.\n\n\n" exit 1 fi # if [ ${release} != ${expected_release} ]; then # echo "\n\n\n\tThis package must be installed on a ${expected_release} machine\n" # echo "\tAborting installation.\n\n\n" # exit 1 # fi exit 0
The mkpkg script is mainly so I don't have to re-type the commands to build the package file. It's customized for every package. The checkinstall script above has been altered to be less stringent about the destination machine requirements. Most software doesn't require that the machine's Solaris revision be of a specific level. I leave the SPARC architechture check in though.