SUN Packaging Guide -
The prototype file

Syntax of the prototype file

WARNING: The following chapter is big and is a little bit hard to read.

The prototype file is essentially important for the packaging process, as it defines _how_ the files should be included in your package.
In our example, it would look like this:

$ cd <temporarily installation root>
$ pkgproto * | sort
d none bin 0755 root other
d none info 0755 root other
d none man 0755 root other
d none man/man1 0755 root other
f none bin/bc 0755 root other
f none bin/dc 0755 root other
f none info/bc.info 0644 root other
f none info/dc.info 0644 root other
f none man/man1/bc.1 0644 root other
f none man/man1/dc.1 0644 root other

Explanation:

The syntax is:
<type> <class> <name of file> <permissions> <user> <group>

File Types:

b block special device (usually a file system)
c character special device (usually a raw disk device)
d directory
e editable file (will be usually edited during install)
f file
i include file
l hard link
p named pipe (FIFO)
s symbolic link
v volatile file (file to be created which changes size later -> logfiles)
x directory only accessible for this package

You can get additional information on a Solaris machine by

$ man -s4 prototype

Include options:

The include directive means that the specified file will be treated as a file vital to the package. The usual Options are:

i depend(=<path to dependancy file>)

This tells us, where the dependancy file is located. The syntax of depend is:

P     ARbash 	Bourne Again Shell - 2.05a

as in

<type> <pkg name> <full pkg name>

For the installation of our binary calculator this means that we would have ARbash first installed before we can install our ARbc package.

Types of dependancies are:

P Prerequisite (required package)
I Incompatible PKG (actual package to be installed cannot be installed due to incompatibilities to the named package)
R Reverse Dependancy (tells us that the package currently being installed is a pre-requisite dependancy to another package)

Package name: This is the package's name our dependancy lies on.

Full package name: This is the package's full name as it appears in "pkginfo". Not required, but useful during packaging.

i pkginfo(=<path to pkginfo file>)

Defines, where to look for the _required_ pkginfo file.

i version(=<path to version file>)

Tells "pkgmk" where to look for a version file. This is optional/ informational.

i preinstall(=<path to preinstall script>)

Tells "pkgmk" where to look for a script to be executed _before_ the package will be installed, e.g. to create application specific users.

i preremove(=<path to preremove script>)

Tells "pkgmk" where to look for a script to be executed _before_ the package will be removed, e.g. to stop a webserver.

i postinstall(=<path to postinstall script>)

Tells "pkgmk" where to look for a script to be executed _after_ the package has been installed, e.g. start a webserver with a default config.

i postremove(=<path to postremove script>)

Tells "pkgmk" where to look for a script to be executed _after_ the package has been removed, e.g. cleaning up some old temporary files.

i request(=<path to request script>)

Tells "pkgmk" where to look for a script which asks the user for certain information, e.g. license key.

i checkinstall(=<path to checkinstall script>)

Tells "pkgmk" where to look for a script which gathers installation data

The sequence of these scripts during package installation is:

1. display copyright
2. execute request script and ask user
3. execute checkinstall script and gather data
4. execute preinstall
5. execute postinstall
6. execute preremove
7. execute postremove

Additionally, there are the following directives:

! <command>

This means that the following string is a shell command, e.g. means

  !PATH=/usr/local/bin:/usr/local/sbin:/usr/bin:/bin

the binary path to be set.

! search <dir1> <dir2> <dir3> ...

This means that within the following directories the files to be packaged will be searched in.

! include <dir> 

This means that another prototype will be included in the mentioned directory.

! default <attributes>

This means that a list of default parameters like mode, owner and group will be defined, but they are required and not defined in the prototype file.

Back to our example

Now that we got all the files, we have to modify the newly created prototype file. First of all, save it to your temporarily installation root:

$ cd <temporarily installation root>
$ pkgproto * | sort > prototype

The only one absolutely _required_ file we have to add is the pkginfo file. So open the newly created prototype file with your favourite editor and add a

i pkginfo=<name/location of pkginfo file>

to the file. Assuming the pkinfo file is located in the same directory as the prototype file, a simple

i pkginfo

also does the job. Now we need to change the location of the files. Just do the second compilation and installation run with the final location. Then we modify our prototype file to this:

i pkginfo=pkginfo
d none usr ? ? ?
d none usr/local ? ? ?
d none usr/local/bin ? ? ?
d none usr/local/info ? ? ?
d none usr/local/man ? ? ?
d none usr/local/man/man1 ? ? ?
f none usr/local/bin/bc 0755 bin bin
f none usr/local/bin/dc 0755 bin bin
f none usr/local/info/bc.info 0644 bin bin
f none usr/local/info/dc.info 0644 bin bin
f none usr/local/man/man1/bc.1 0644 bin bin
f none usr/local/man/man1/dc.1 0644 bin bin

In the former document about the pkginfo file, the variable BASEDIR defines the packaging - and later the installation root - to be "/", but our files are located somewhere else. The only solution to come around this problem is to do two compilation runs (you remember, I told you in the introduction). The first run for catching all files in a temporarily install location, the second for the final installation path.
Although bc does really not need a bootskript as it is no daemon, we will include a dummy bootscript called bc_startup.sh, just as an example. But how will we be able to build a system PKG if we can't become superuser to copy the bootskript to /etc/init.d and create a symbolic link to /etc/rc3.d? The answer is quite simple. Within the prototype file we can work with include paths and symbolic links. Assuming our boot script is in the same directory as our pkginfo and prototype file, we simply add the following line to the prototype file:

f none etc/init.d/bc_startup=bc_startup 0755 root other

"pkgmk" will add our local bc_startup.sh to the package as it would reside in /etc/init.d/bc_startup. Finally we add another entry to the prototype file:

s none etc/rc3.d/S99bc_startup=../init.d/bc_startup

This will create a symbolic link from the central init skript directory in /etc/init.d to /etc/rc3.d/S99bc_startup The syntax for these include paths is:

<destination path>=<source path>

IMPORTANT NOTE: When adding a symbolic link, the file that the link refers to _HAS_ to be listed first! As a consequence it is the best solution to put all symbolic links at the end of the prototype file. Additionally, you will have noticed in the prototype file above that I listed the complete /usr/local directory structure and added "?" at the end of the line. Why that?

  1. Add all needed directories, also the parent directories
  2. Adding "? ? ?" after the filename will result in the Solaris defaults for permissions, user and group.
  3. Omit the trailing "/" to create relocatable PKG (later in this guide).

One word to Solaris init scripts:
There is a small but very important difference between bootscripts with or without a ".sh" ending.
Bootscripts ending with ".sh" are treated as shell includes and are not executed. Think of a "for-loop":

...
for script in <current runlevel>*.sh
do
	. $script
done
...

Bootscripts not ending with ".sh" are treated as shell skripts and are therefore executed. (If you are interested in, where in the init bootprocess this happens, take a look at /sbin/rcS and search for a similar text block:

...
        if [ -d /etc/rcS.d ]; then
                for f in /etc/rcS.d/S*; do
                        if [ -s $f ]; then
                                case $f in
                                        *.sh)   .        $f ;;
                                        *)      /sbin/sh $f start ;;
                                esac
                        fi
                done
        fi
...

Finally: So our final prototype file will look like:

i pkginfo=pkginfo
d none usr ? ? ?
d none usr/local ? ? ?
d none usr/local/bin ? ? ?
d none usr/local/info ? ? ?
d none usr/local/man ? ? ?
d none usr/local/man/man1 ? ? ?
f none usr/local/bin/bc 0755 bin bin
f none usr/local/bin/dc 0755 bin bin
f none usr/local/info/bc.info 0644 bin bin
f none usr/local/info/dc.info 0644 bin bin
f none usr/local/man/man1/bc.1 0644 bin bin
f none usr/local/man/man1/dc.1 0644 bin bin
f none etc/init.d/bc_startup=bc_startup 0755 root other
s none etc/rc3.d/S99bc_startup=../init.d/bc_startup