Professional Documents
Culture Documents
Prerequisites:
First of all you need your compilation results as we want your files to be put in a package (PKG). There are two possibilities to get there:
Using standard compiler and Solaris make Using gcc and GNU Make
In the following chapters you will see how to operate on this. Afterwards, you have to do the compilation and installation again for the final location.
gmake (as I call it) will then perform an installation at the DESTDIR as it would do in /usr/local, including the creation of all sub-directories of the former install-root "/". If you then got all the files
again, and you are nearly done with the package. Unfortunately, not all Open Source source tarballs come with a proper Makefile.in file, where the DESTDIR variable has to be enabled during creation of the source tarball with GNU autoconf. But there is a way around, which is more the "hackers way". You will have to manually edit the Makefile and seek the "install:" section. Then add a
$(DESTDIR)/...
The variables have the following meaning: Variable PKG ARCH VERSION CATEGORY DESC VENDOR Description Package name (up to 10 characters) Solaris architecture (sparc/x86) The version of the package Kind of software (system/application/library/utility etc). Can be freely defined. This is the long description Self-explaining :)
VSTOCK HOTLINE EMAIL BASEDIR MAXINST INTONLY CLASSES ISTATES RSTATES PSTAMP ULIMIT ORDER
Number of product (stock number) Self-explaining (put a telephone number here) Self-explaining (put a valid email address for contact here) Describes the top-level directory for installing the packaged files from and for searching the files to be packaged Maximum number of simultaneously installed instances of the same package Interactive Only. (value not equal to "0" means user interaction required) Needed to define installation classes. Init States the package is allowed to be installed Init States the package is allowed to be removed Production time stamp (package creation time stamp) Parameter for the ulimit command for maximum file size. (equal to ulimit -f; value will be calculated in 512 byte blocks) Defines the installation order for the specified classes
Most variables mentioned here are required: PKG, ARCH, CATEGORY, DESC, BASEDIR, VERSION For instance, if you delete the VERSION variable, you will get an error like this during "pkgmk":
$ pkgmk -o -b / -r / ## Building pkgmap from package prototype file. ## Processing pkginfo file. WARNING: parameter <VERSION> set to "Dev Release 07/19/2002" ...
Of course you can define other variables as well. When creating the package, those variables come into action while the packaging process is running. Additionally, those variables can be used by installation or removal scripts, e.g. for doing dependancy checks. NOTE: If you don't specify PSTAMP with your own format, then a PSTAMP entry of the kind <hostname><YYMMDDhhmm> will be created for you and added to the pkginfo file.
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:
Typ e b c d e f i l p s v x Description block special device (usually a file system) character special device (usually a raw disk device) directory editable file (will be usually edited during install) file include file hard link named pipe (FIFO) symbolic link volatile file (file to be created which changes size later -> logfiles) directory only accessible for this package
4. 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: DependancyType P I R Description Prerequisite (required package) Incompatible PKG (actual package to be installed cannot be installed due to incompatibilities to the named package) Reverse Dependancy (tells us that the package currently being installed is a prerequisite 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>)
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.
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 execute checkinstall script and 3. 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
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.
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
...
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
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. 2. 3. 4. 5. 6. request checkinstall preinstall postinstall preremove postremove
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.
The base directory with respect to the target system. While BASEDIR is the variable CLIENT_BAS to use if referring to a package on the cdrom/ install server, this variable is the path EDIR to include in files placed on the client system. INST_DATADI The directory where the package now being read is located. R The search list used by sh to find commands on script invocation. PATH The instance identifier of the package being installed. PKGINST The directory where files can be saved for use by removal scripts or where PKGSAV previously saved files can be found. PKG_INSTAL 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. L_ROOT PKG_NO_UNI Is an env variable that gets set if the pkgadd/pkgrm command is invoked with the -R -M option. FIED 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 UPDATE architecture is already installed on the system or that the installing PKG will overwrite an installed PKG. The original BASEDIR is then used.
8. Installation scripts
The "request" script
There are certain restrictions which apply to the request script:
There can be only one request script for every PKG and it must be named "request" The "request" script cannot modify any 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 request 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 Do not perform any special analysis of the target system in a request script. Data system collection is to be done by the "checkinstall" script. The request script runs as user install or - if this user is not available - as user nobody.
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 noninteractive 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.
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.
9. 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":
Procedure scripts run as uid=root and gid=other Each script should be able to be executed more than once since it is executed for each volume in a package. Administrator interaction is not permitted during execution. Administrator interaction is restricted to the request script. Each procedure script installing a package object lot listed in the pkgmap file _must_ use the installf command to notify the package database that it is adding or modifying a path name. See installf(1M) for more information. Each script removing files that are not listed in the pkgmap _must_ use the removef command to notify the package database that it is removing a path name. See removef(1M) for details. Only postinstall and postremove scripts may install/modify package objects in that way.
#!/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
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.
Defining classes
Classes can be only defined in two files: the CLASSES variable in the pkginfo file and by assigning certain files to this predefined class in the prototype file. Then pkgadd/pkgrm will create a file list containing all these files during an installation/removal and invoke the class action scripts with the file list as 1st parameter. Class names can be freely defined except for the following exceptions: Class Description Provides a method for using sed instructions to edit files upon package installation sed and removal. Provides a method for using awk instructions to edit files upon package awk installation and removal. Provied a method to dynamically construct or modify files using Bourne shell build commands. preserv Provides a method to preserve files that should not be overwritten by future package installations. e There are two possibilities class action scripts can come into action:
The easiest way is using a default class, so we start off with this. Let's do this by choosing the awk system class.
What does this mean? The "e" tells us we have a editable file. The "awk" tells us we have a file which belongs to the class "awk". The third entry tells us it is a file located in /tmp which contains the awk commands. Irritating, isn't it? We want to _modify_ an existing file and not placing a new file with awk commands there! As the /tmp/dummy.conf can't contain any awk commands, we place a awk command file _over_ it. The commands in this file will be then applied to the file it covers. It's like a hat you put on your head. A _very important_ information I gave to you when explaining the prototype file syntax will now be useful for us. Of course we can't overwrite an existing configuration file for creating a package (imagine it would be a vital system file like /etc/syslog.conf). If you remember the link syntax <destination>=<source>, you can set the path to the final awk script to modify our configuration file like this:
e awk tmp/dummy.conf=/var/tmp/build-root/modify_config.awk 0755 root other
When you create the package then, the file modify_config.awk will be put in the package as tmp/dummy.conf and during installation/removal the script will be then applied to the dummy.conf file. Now we are half on our way to Chicago, folks :). The next part is the syntax of the script. This is quite simple and looks like this:
!install # awk program to install changes BEGIN { } END { print "Installing..."; } !remove # awk program to remove changes BEGIN { } { } print "Removing...";
As you can clearly see the script is divided into two parts: the "install" and the "remove" part. When our package is installed/removed, pkgadd/pkgrm creates a list of files which belong to this class and invokes the class action script with the file list. Here we only have one file so the script will be applied only to one file. There is no difference between creating scripts for the sed class or the awk class except that in the
first case any command is a sed command and in the second case we have awk commands only AND that the sed/awk classes _modify_ existing files only.
If the file to be installed does not already exist in the target directory, the file will be installed normally. If the file to be installed exists in the target directory, a message describing the file exists is displayed and the file is not installed.
else fi
You may ask, what "$src" and "$dest" mean here. Very easy: The default behaviour of pkgadd is to copy the packaged file from the installation medium (e.g. a package) to its final destination. In this example we just want to know whether the file already exists ($src set to /dev/null).
2. Modify the CLASSES variable in the pkginfo file to contain the newly defined class name, e.g.
CLASSES=manpage application none
3. Create the class scripts. An installation script for a certain class must be named i.<class>, e.g.
i.manpage
A removal class action script for a certain class must be named r.<class>, e.g.
r.manpage
4. Add the required class scripts separately to the prototype file, e.g.
i i.manpage i r.manpage
5. Create your package using pkgmk. NOTE: Remember, when a file is part of a class that has a class action script, the script must install the file. The pkgadd command does not install files for which a class action script exists, although it does verify the installation. And, if you define a class but do not deliver a class action script, the only action taken for that class is to copy components from the installation medium (a package) to the target system (the default pkgadd behaviour).
And our generic prototype file looks like this, as we created it by the pkgproto command :
d none usr ? ? ? d none usr/local ? ? ? d none usr/local/bin ? ? ? d none usr/local/info ? ? ? 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
REMEMBER: When adding files to the package, every directory leading to the destination has to be included as well! That's the reason why etc, etc/init.d and etc/rc3.d has to be listed here as well! There may be several reasons to omit a directory, e.g. let /opt be a symbolic lic to /var/opt due to less diskspace. Adding /opt as of type d then results in replacing the symbolic link /opt with a real directory called /opt, which will be created in "/" and your package may be not installed, if "/" has not enough disk space!
Transferring the file back to the package directory is also quite simple:
$ pkgtrans /tmp/ARbc-1.0-sol8-test /tmp ARbc Transferring package instance $ ls -ld /tmp/ARbc drwxr-xr-x 3 garex staff 306 Jul 22 19:29 /tmp/ARbc
Doing a testinstallation
Now try installing the packge first from the directory, then from the file:
$ cd /tmp $ pkgadd -d . ARbc ... $ pkgrm ARbc ... $ pkgadd -d ARbc-1.0-sol8-test ... $ pkgrm ARbc ...