Cover V08, I12
Article
Table 1
Table 2

dec99.tar


Using Solaris Packages

Dave Zwieback

There are many ways of distributing software to systems, the most commonly used way is via NFS (often in combination with the automounter). However, there are situations when particular software must be physically installed on systems, and this is usually accomplished with tar files. This method has a number of shortcomings that become especially apparent in large installations of complex software. In such situations, Solaris packages clearly emerge as the preferred way of distributing software, specifically due to the following features:

• Uniform package installation and removal interfaces (pkgadd and pkgrm)

• Ability to see exactly which (versions of) packages are installed on the system (pkginfo)

• Ability to verify the integrity of the contents of the package (pkgchk)

• Ability to specify package dependencies and/or incompatibilities (depend, compver)

• Ability to specify additional space requirements for the package (space)

• Ability to create custom, dynamic package installation and removal scripts (request, checkinstall, preinstall, postinstall; preremove, postremove, and Class Action scripts)

I will not go into exhaustive detail about all of the powerful features of Solaris packages, which can be found in their full glory in the Application Packaging Developer's Guide. This article is intended as a primer that will allow you to start building functional packages right away.

Contents of a Package

Besides containing the installation files and directories ("package objects"), a package must also contain two "information files": pkginfo(4) and prototype(4). The pkginfo file includes the mandatory package abbreviation, name, architecture, version, and category, which are listed along with the most important optional parameters in Table 1.

The prototype file contains information about each package object, such as its source and destination pathnames, ownership, and permissions. It is a space-delimited file with up to nine fields, the meanings of which are listed in Table 2.

The package may also include optional package information files (compver(4), depend(4), space(4), and copyright(4)), and package installation and removal scripts. Collectively, all the information files and installation scripts are referred to as "control files". The prototype file must also specify the location of the pkginfo file, and other control files that are used in the package.

Building a Package

There are seven steps involved in building a package:

1. Create directory hierarchy and organize the object files.

2. Create the pkginfo file.

3. Create optional information files.

4. Create optional installation and removal scripts.

5. Create the prototype file.

6. Build the package.

7. Verify and transfer the package.

We will follow the above steps and build a (not so simple) sample package called INKtest.

Step 1: Create Directory Hierarchy and Organize the Object Files

The first step is to create working a directory in which the package objects and control files will be placed. In this case, because we abbreviate the name of the sample package to INKtest, We create the directory /home/test/INKtest for the package objects. In this example, We know that the package requires bin, doc, lib, man, and src directories, so we create them under /home/test/INKtest and place the appropriate files there.

We also have a symbolic link from /home/test/INKtest/lib/liblink to /usr/lib/libauth.a in order to demonstrate that we can also package links (and, for that matter, named pipes and devices). We also have a file (etcfile1) that should be placed in the /etc directory, and thus we create an /etc directory in our hierarchy. And don't forget the two Perl scripts (script[12].pl) in the scripts directory!

Finally, we create a directory named /home/test/control, in which to store all of the information files and installation scripts. In the end, the hierarchy looks like this:

unix# ls -R /home/test
/home/test:
INKtest          control

/home/test/INKtest:
bin      doc      etc      lib      man      scripts  src

/home/test/INKtest/bin:
binfile1  binfile2

/home/test/INKtest/doc:
docfile1  docfile2

/home/test/INKtest/etc:
etcfile1

/home/test/INKtest/lib:
libfile1  liblink1

/home/test/INKtest/man:
man1  man2

/home/test/INKtest/man/man1:
manfile1

/home/test/INKtest/man/man2:
manfile2

/home/test/INKtest/scripts:
script1.pl  script2.pl

/home/test/INKtest/src:
srcfile1  srcfile2

/home/test/control:

Step 2: Create the pkginfo File

The second step in creating a package is defining the required information about it. In this case, the pkginfo file will look as follows:

PKG="INKtest"
NAME="Sys Admin Test Package"
ARCH="sparc"
VERSION="0.1"
CATEGORY="application"
DESC="This is a test package for Sys Admin on the sparc platform"
VENDOR="inkcom Communications"
EMAIL="zwieback@inkcom.com"
CLASSES="none scripts"
PSTAMP="December 1, 1999"
BASEDIR="/opt "

It is customary for PKG (the abbreviated name of the package) to start with up to four letters uniquely identifying the company creating the package. Additionally, the PSTAMP parameter can be used to provide other information (namely, the date of the creation of the package); if not defined, the default of string of "machinenameYYMMDDHHMM" is used.

CLASSES are extremely useful means of grouping package objects, primarily because of Class Action scripts, which allow a series of actions to be performed on a group of objects at installation or removal. In INKtest, most of the package objects belong to the default class "none", except for the two Perl scripts that belong to the class "scripts". In Step 4, we will write a Class Action script for this class to modify the location of the Perl interpreter from /usr/bin/perl to /usr/local/bin/perl. In Step 5, we will add objects to a class using the prototype file. All the classes that are used in a package must be listed in the pkginfo file.

We chose /opt as the default installation directory for the "relocatable" files in this package -- because of the nature of the software, most of the package objects in INKtest can be installed in any directory, and are thus "relocatable". The one file in INKtest that is not relocatable is etcfile1, since it must go into /etc. In Step 5, we will create the prototype file that will specify which package objects are relocatable.

Now that I have defined some basic information about the package, we can specify what will go into the package.

Step 3: Create Optional Information Files

The third step is to create the information files, which are optional, but extremely useful nonetheless. Specifically, we want to specify package dependencies using a depend file, ensure that additional disk space for our log files is available with a space file, and finally print a copyright message.

Using a depend file allows us to establish three types of dependencies: prerequisite packages (P), reverse dependencies (R) (i.e., other packages depend on the existence of the current package -- a feature that is rarely used) and incompatible packages (I). In the simplest form, the format of the depend file includes a line for each package as follows:

type    PKG    NAME

PKG and NAME can be easily obtained using pkginfo(1). Since our Perl scripts rely on the existence of the Perl interpreter, we must verify that the Perl package is installed on the system:

unix# pkginfo | grep perl
utility perl5       perl 5.004.04 SPARC \
                      Solaris 2.6

Thus, our depend file looks as follows:

P perl5 perl 5.004.04 SPARC Solaris 2.6

Next, we create the space file. Since we need space for the INKtest log file, which will be placed in /var/adm and which will take at least 10 Mb of disk space, our space file will contain:

/var/adm    20480    1

The package INKtest will not get installed on a system unless it has 20480 512-byte blocks of disk space and at least 1 inode free on the partition where /var/adm resides.

Our last optional information file will be the copyright message:

Copyright (c) 1999 inkcom Communications
All Rights Reserved

Finally, here is a quote from the compver man page:

Since some packages may require installation of a specific version of another software package, compatibility information is extremely crucial. Consider, for example, a package called 'A', which requires version '1.0' of application 'B' as a prerequisite for installation. If the customer installing 'A' has a newer version of 'B' (version 1.3), the compver file for 'B' must indicate that '1.3' is compatible with version '1.0' in order for the customer to install package 'A'.

In our case, since this is the first version of the package, we don't have to worry about the condition described in the man page entry, but this is something to keep in mind when creating subsequent versions. For instance, if versions 0.1, 0.2, and 1.0.2 were all compatible, our compver file would look as follows:

0.1
0.2
1.0.2

Step 4: Create Optional Installation and Removal Scripts

One of the most powerful features of Solaris packages is the ability to use installation scripts. These scripts are Bourne shell (sh) scripts, and can be one of the following four types:

Request script -- Requests data from the administrator installing the package for assigning or redefining environment variables

Checkinstall script -- Examines the system for needed data, can set or modify package environment variables, and determines whether or not the installation proceeds (only in Solaris 2.5 and later)

Procedure scripts -- Preinstall, postinstall, preremove, and postremove scripts

Class Action scripts -- Actions applied to a class of package objects during installation or removal

For demonstration purposes, we will create a Class Action script to set the proper location of the Perl interpreter in the two Perl programs that belong to the class "scripts". In accordance with the Class Action script naming convention (i.classname and r.classname for installation and removal scripts, respectively), we call our script i.scripts:

while read src dest
do
        sed 's/\/usr\/bin/\/usr\/local\/bin/' $src > $dest || exit 2
done
exit 0

Note that the above script is responsible for installing the files into their ultimate destination. The sed command takes the source files (taken from the prototype file and passed as $src), replaces /user/bin with /usr/local/bin on the first line, and redirects the output into the destination files (passed as $dest), thus creating them or overwriting them if they already exist. In the absence of a Class Action script, pkgadd and pkgrm will simply copy or delete the file to or from its destination. As with other information files, their location is specified in the prototype file.The use of the installation scripts is discussed in exhaustive detail in the Application Packaging Developer's Guide.

Step 5: Create the Prototype File

The fifth step is to create the required prototype file. The easiest way is by using the pkgproto(1) command:

unix# cd /home/test
unix# pkgproto ./INKtest > control/prototype

(Alternatively, you may use find ./INKtest | pkgproto > control/prototype).

The resulting prototype file looks as follows:

d none INKtest 0755 root other
d none INKtest/bin 0755 root other
f none INKtest/bin/binfile2 0755 root other
f none INKtest/bin/binfile1 0755 root other
d none INKtest/doc 0755 root other
f none INKtest/doc/docfile2 0644 root other
f none INKtest/doc/docfile1 0644 root other
d none INKtest/lib 0755 root other
f none INKtest/lib/libfile1 0755 root other
s none INKtest/lib/liblink1=/usr/lib/libauth.a
d none INKtest/man 0755 root other
d none INKtest/man/man1 0755 root other
f none INKtest/man/man1/manfile1 0644 root other
d none INKtest/man/man2 0755 root other
f none INKtest/man/man2/manfile2 0644 root other
d none INKtest/src 0755 root other
f none INKtest/src/srcfile2 0644 root other
f none INKtest/src/srcfile1 0644 root other
d none INKtest/etc 0755 root other
f none INKtest/etc/etcfile1 0644 root other
d none INKtest/scripts 0755 root other
f none INKtest/scripts/script1.pl 0755 root other
f none INKtest/scripts/script2.pl 0755 root other

As listed in Table 2, the first column (since part number is optional and not used) is the ftype: d is a directory, f is a standard file (as opposed to special files), s stands for symbolic link. The other ftypes that we will use in this article are: i (installation script or information file), e (editable file), and v (volatile file, like the INKtest log file).

First, we modify the above prototype file, to include the location of the pkginfo file:

i pkginfo=/home/test/control/pkginfo

Since etcfile1 must reside in /etc, we need to change:

d none INKtest/etc 0755 root other
f none INKtest/etc/etcfile1 0644 root other

to:

d none /etc ? ? ?
f none /etc/etcfile1=INKtest/etc/etcfile1 0644 root other

The question marks in d none /etc ? ? ? simply mean that the file or directory already exists on the system and should not be changed.

We also need to modify the two lines referring to the Perl scripts: they must be of the type e, and belong to the "scripts" class in order for them to be modified by our Class Action script:

e scripts INKtest/scripts/script1.pl 0755 root other
e scripts INKtest/scripts/script2.pl 0755 root other
Of course, we need Perl to be installed on the system to run the scripts above. Thus, before we install INKtest, we should verify that it is indeed installed and specify the location of the depend file created in Step 3:

i depend=/home/test/control/depend

We also include a quick copyright(4) message:

i copyright=/home/test/control/copyright

Additionally, INKtest requires a log file /var/adm/INKtest (initially blank), and there is a special ftype of v (for volatile) used for just such files:

d none /var ? ? ?
d none /var/adm ? ? ?
v none /var/adm/INKtest=/dev/null 0600 root other

Since /var/adm/INKtest tends to get rather large, we need to make sure that this space is available on the system before installing the package. We will accomplish this in Step 4 when we create a space(4) file, which must be specified in the prototype file:

i space=/home/test/control/space

Finally, we specify the installation Class Action script location:

i i.scripts=/home/test/control/i.scripts

The new prototype file is listed below:

i pkginfo=/home/test/control/pkginfo
i depend=/home/test/control/depend
i copyright=/home/test/control/copyright
i space=/home/test/control/space
i i.scripts=/home/test/control/i.scripts
d none INKtest 0755 root other
d none INKtest/bin 0755 root other
f none INKtest/bin/binfile2 0755 root other
f none INKtest/bin/binfile1 0755 root other
d none INKtest/doc 0755 root other
f none INKtest/doc/docfile2 0644 root other
f none INKtest/doc/docfile1 0644 root other
d none INKtest/lib 0755 root other
f none INKtest/lib/libfile1 0755 root other
s none INKtest/lib/liblink1=/usr/lib/libauth.a
d none INKtest/man 0755 root other
d none INKtest/man/man1 0755 root other
f none INKtest/man/man1/manfile1 0644 root other
d none INKtest/man/man2 0755 root other
f none INKtest/man/man2/manfile2 0644 root other
d none INKtest/src 0755 root other
f none INKtest/src/srcfile2 0644 root other
f none INKtest/src/srcfile1 0644 root other
d none /etc ? ? ?
f none /etc/etcfile1=INKtest/etc/etcfile1 0644 root other
d none INKtest/scripts 0755 root other
e scripts INKtest/scripts/script1.pl 0755 root other
e scripts INKtest/scripts/script2.pl 0755 root other
d none /var ? ? ?
d none /var/adm ? ? ?
v none /var/adm/INKtest=/dev/null 0600 root other

The prototype file is extremely powerful, and only basic functionality has been covered in this article. Again, please refer to the man pages as well as the Developer's Guide for complete information.

Step 6: Build the Package

Next, we build the package using pkgmk(1):

unix# cd /home/test/control
unix# pkgmk -o -b /home/test
## Building pkgmap from package prototype file.
## Processing pkginfo file.
## Attempting to volumize 26 entries in pkgmap.
part  1 -- 267 blocks, 33 entries
## Packaging one part.
/var/spool/pkg/INKtest/pkgmap
/var/spool/pkg/INKtest/pkginfo
/var/spool/pkg/INKtest/root/etc/etcfile1
/var/spool/pkg/INKtest/root/var/adm/INKtest
/var/spool/pkg/INKtest/reloc/INKtest/bin/binfile1
/var/spool/pkg/INKtest/reloc/INKtest/bin/binfile2
/var/spool/pkg/INKtest/reloc/INKtest/doc/docfile1
/var/spool/pkg/INKtest/reloc/INKtest/doc/docfile2
/var/spool/pkg/INKtest/reloc/INKtest/lib/libfile1
/var/spool/pkg/INKtest/reloc/INKtest/man/man1/manfile1
/var/spool/pkg/INKtest/reloc/INKtest/man/man2/manfile2
/var/spool/pkg/INKtest/reloc/INKtest/scripts/script1.pl
/var/spool/pkg/INKtest/reloc/INKtest/scripts/script2.pl
/var/spool/pkg/INKtest/reloc/INKtest/src/srcfile1
/var/spool/pkg/INKtest/reloc/INKtest/src/srcfile2
/var/spool/pkg/INKtest/install/copyright
/var/spool/pkg/INKtest/install/depend
/var/spool/pkg/INKtest/install/i.scripts
/var/spool/pkg/INKtest/install/space
## Validating control scripts.
## Packaging complete.

The above command creates the directory /var/spool/pkg/INKtest, in which it copies the pkginfo file and creates the pkgmap file:

: 1 267
1 d none /etc ? ? ?
1 f none /etc/etcfile1 0644 root other 958 22646 936996453
1 d none /var ? ? ?
1 d none /var/adm ? ? ?
1 v none /var/adm/INKtest 0600 root other 0 0 937003138
1 d none INKtest 0755 root other
1 d none INKtest/bin 0755 root other
1 f none INKtest/bin/binfile1 0755 root other 5122 54390 936996395
1 f none INKtest/bin/binfile2 0755 root other 52620 14902 936996419
1 d none INKtest/doc 0755 root other
1 f none INKtest/doc/docfile1 0644 root other 1624 4378 936996434
1 f none INKtest/doc/docfile2 0644 root other 1041 11294 936996439
1 d none INKtest/lib 0755 root other
1 f none INKtest/lib/libfile1 0755 root other 26372 37864 936996532
1 s none INKtest/lib/liblink1=/usr/lib/libauth.a
1 d none INKtest/man 0755 root other
1 d none INKtest/man/man1 0755 root other
1 f none INKtest/man/man1/manfile1 0644 root other 8670 31098 936996550
1 d none INKtest/man/man2 0755 root other
1 f none INKtest/man/man2/manfile2 0644 root other 6460 35141 936996560
1 d none INKtest/scripts 0755 root other
1 e scripts INKtest/scripts/script1.pl 0755 root other 544 39584 936999236
1 e scripts INKtest/scripts/script2.pl 0755 root other 498 39260 936999245
1 d none INKtest/src 0755 root other
1 f none INKtest/src/srcfile1 0644 root other 1738 5997 936996612
1 f none INKtest/src/srcfile2 0644 root other 951 5682 936996629
1 i copyright 61 5425 936986213
1 i depend 40 2858 936986148
1 i i.scripts 97 8063 936999971
1 i pkginfo 261 22068 937003862
1 i space 17 1060 936986493

The contents of the pkgmap are quite similar to that of the prototype file. The differences are that the first column is the part number, and the last three are the size of the file, the checksum, and the datestamp of each object file. In addition, the first line specifies the number of parts and the maximum part size (in blocks).

It is also worthwhile to investigate the structure of the resulting directories in /var/spool/pkg/INKtest. The install directory contains our control files (copyright, depend, and space, and Class Action script i.scripts); the root directory contains package objects that cannot be relocated, while the reloc directory contains the relocatable ones.

Step 6: Verify and Transfer the Package

We use pkgchk(1) to verify the contents of the package:

unix# pkgchk -d /var/spool/pkg INKtest
Checking uninstalled directory format package <INKtest> from \
  </var/spool/pkg>
## Checking control scripts.
## Checking package objects.
## Checking is complete.

At this point, we can either tar and compress the INKtest directory, or use the pkgtrans(1) command to create a datastream package file:

unix# pkgtrans -s /var/spool/pkg /tmp/INKtest
The following packages are available:
  1  INKtest     Sys Admin Test Package
                 (sparc) 0.1
                 

Select package(s) you wish to process (or 'all' to process
all packages). (default: all) [?,??,q]: 1
Transferring <INKtest> package instance

To further verify that our package does what it's supposed to, we can actually install it:

unix# pkgadd

The following packages are available:

  1  INKtest     Sys Admin Test Package
                 (sparc) 0.1

Select package(s) you wish to process (or 'all' to process
all packages). (default: all) [?,??,q]: 1

Processing package instance <INKtest> from </var/spool/pkg>

Sys Admin Test Package
(sparc) 0.1
Copyright (c) 1999 inkcom Communications
All Rights Reserved
Using </opt> as the package base directory.
## Processing package information.
## Processing system information.
   4 package pathnames are already properly installed.
## Verifying package dependencies.
## Verifying disk space requirements.
## Checking for conflicts with packages already installed.
## Checking for setuid/setgid programs.

This package contains scripts which will be executed with super-user
permission during the process of installing this package.

Do you want to continue with the installation of <INKtest> [y,n,?] y

Installing Sys Admin Test Package as <INKtest>

## Installing part 1 of 1.
/etc/etcfile1
/opt/INKtest/bin/binfile1
/opt/INKtest/bin/binfile2
/opt/INKtest/doc/docfile1
/opt/INKtest/doc/docfile2
/opt/INKtest/lib/libfile1
/opt/INKtest/lib/liblink1 <symbolic link>
/opt/INKtest/man/man1/manfile1
/opt/INKtest/man/man2/manfile2
/opt/INKtest/src/srcfile1
/opt/INKtest/src/srcfile2
/var/adm/INKtest
[ verifying class <none> ]
[ verifying class <scripts> ]

Installation of <INKtest> was successful.

The package is now installed:

unix# pkginfo | grep INKtest
application INKtest        Sys Admin Test Package

unix# pkginfo -l INKtest
   PKGINST:  INKtest
      NAME:  Sys Admin Test Package
  CATEGORY:  application
      ARCH:  sparc
   VERSION:  0.1
   BASEDIR:  /opt
    VENDOR:  inkcom Communications
      DESC:  This is a test package for Sys Admin on the sparc platform
    PSTAMP:  December 1, 1999
  INSTDATE:  Sep 10 1999 17:56
     EMAIL:  zwieback@inkcom.com
    STATUS:  completely installed
     FILES:     26 installed pathnames
                 3 shared pathnames
                12 directories
                 5 executables
               214 blocks used (approx)

Conclusion

Solaris packages are one of the most effective ways of distributing software consistently across multiple systems. Additionally, packaging software is extremely flexible, aided by the fully functional installation and removal scripts. This article only begins to uncover the power of this method of software distribution, which have been used by almost all software vendors (and www.sunfreeware.com) for many years. I encourage those reader responsible for maintaining software in large environments to further investigate the tools by reading the Application Packaging Developer's Guide.

Bibliography

Sun Microsystems. 1997. Application Packaging Developer's Guide. http://docs.sun.com

Christensen, Steven M. 1999. Creating pkgadd Software Packages under Solaris. http://www.sunfreeware.com/pkgadd.html

In side Solaris. January 1997. Packaging groups of files for distribution. http://www.zdjournals.com/sun/9701/sun9712.htm

About the Author

Dave D. Zwieback is the Technical Director of inkcom (www.inkcom.com), a New Jersey-based consultancy specializing is Unix, Networks, Security, and Internet/Intranet development. He can be reached at zwieback@inkcom.com.