Creating pkgadd Software Packages under Solaris

The software pointed to by my Sun Freeware Web page is archived in a format that can be read and installed by the pkgadd program that comes with Solaris 2.5, 2.6, 7, 8 and 9. I gave instructions on how to install our pkgadd software on a another page, but I did not discuss how we created the packages. A number of people have contacted me and said "How did you do that?!" The answer is "... with some struggle and hair pulling."

Michael Short at Berkeley first educated me on the basic steps to create a pkgadd format software package. Here I will add to his steps and try to make them as clear as I can. No doubt, there are more sophisticated or general ways to do this, but I will not attempt anything harder here. Comments are welcome.

There now exists a detailed discussion of the packaging process in the Answerbook 2 on the Solaris CD or on the Application Packaging Developer's Guide . See also updates to these basic processes here or on the FAQ as I learn them.

The Steps

Important: You will need root access on your machine for some of this.

Recently, Diethard Ohrt from Austria sent the following suggestions regarding the use of postinstall, which may be of some value to you:

Date: Mon, 12 Jul 1999 15:32:15 +0200
From: Diethard Ohrt <>
Subject: pkgmk -- prototype

I'd like to add a remark to your section "Creating Packages" --
especially the subsection about postinstall, regarding files to be
installed "elsewhere", i.e. NOT under /usr/local in your example.

If you do it your way, the problem will be:
"pkgadd" creates the files you are mentioning in /usr/local/etc/; then
"postinstall" moves them to /etc, but the installation process itself
does NOT control or notice that. So when you do a "pkgrm", you will
probably get some warning messages about files that have been removed
from /usr/local/etc. But, more important, you have to remove your files
from /etc YOURSELF, e.g. via a "preremove" or "postremove" file. This
additionally makes up another problem: You have to know yourself, which
files you have to remove from /etc ...

So, why not modify your "prototype" in a way that it installs the files
itself into the proper directories? This has the big advantage that the
files are removed by "pkgrm".

Suppose you have some files etc/file_1, etc/file_2, ... that have to be
installed in /etc; the appropriate "prototype" line would be:
f none /etc/file_1=etc/file_1 0644 bin bin
f none /etc/file_2=etc/file_2 0644 bin bin
All other "prototype" lines may be left unchanged.

      _/_/    _/    _/  _/_/_/    _/_/_/_/               _  __------ \
   _/    _/  _/    _/  _/    _/     _/                  | --          \
  _/    _/  _/_/_/_/  _/_/_/       _/     Diethard     |_              \
 _/    _/  _/    _/  _/   _/      _/        Ohrt         | ___-\   o   |
  _/_/    _/    _/  _/    _/     _/                     /_/     |      |
                                                                 |__---|       Steiermark - das gruene Herz Oesterreichs
SIEMENS AG / PSE TMN G3              Styria - the green heart of Austria
A-8054 Graz     Austria                Styrie - le coeur vert d'Autriche

Another method for making packages some from Jasper Aukes.

Date: Tue, 15 Feb 2000 12:09:54 +0100 (MET) From: Jasper Aukes <> Subject: make_package To: Hi Steve, I've create the following script that could help people creating packages. It creates the prototype file, builds the pkginfo file after asking some questions about the software and finally builds the package using pkgmk and pkgtrans. Afterwards it gzips the package, ready to be stored on your package-server. [You can download this file rather than copy and pasting by clicking make_package. ______________________________________________________________________________ #!/net/bin/perl # # Automated processes to create SUN packages # You can run this script after you did a 'make install' in the chrooted # environment. Run it from the <whatever>/packagename-1.0/usr/local/ directory # # JA: 06-01-2000 Initial release # JA: 25-01-2000 Beautified a little $find = "/usr/bin/find"; $pkgproto = "/usr/bin/pkgproto"; $pkgmk = "/usr/bin/pkgmk"; $pkgtrans = "/usr/bin/pkgtrans"; $temp = "/tmp/prototype$$"; $prototype = "prototype"; $pkginfo = "pkginfo"; # Sanitycheck $pwd = `pwd`; if ($pwd =~ '\/usr\/local') { $pwd = $`; } die "Wrong location, please cd to <PKGBASE>/usr/local/ and run again.\n" if ($pwd eq ""); system ("$find . -print | $pkgproto > $temp"); open (PREPROTO,"< $temp") || die "Unable to read prototype information ($!)\n"; open (PROTO,"> $prototype") || die "Unable to write file prototype ($!)\n"; print PROTO "i pkginfo=./$pkginfo\n"; while (<PREPROTO>) { # Read the prototype information from /tmp/prototype$$ chomp; $thisline = $_; if ($thisline =~ " prototype ") { # We don't need that line } elsif ($thisline =~ "^[fd] ") { # Change the ownership for files and directories ($dir, $none, $file, $mode, $user, $group) = split / /,$thisline; print PROTO "$dir $none $file $mode bin bin\n"; } else { # Symlinks and other stuff should be printed as well ofcourse print PROTO "$thisline\n"; } } close PROTO; close PREPROTO; # Clean up unlink $temp || warn "Unable to remove tempfile ($!)\n"; # Now we can start building the package # # First get some info $thispackage = `basename $pwd`; if ($thispackage =~ '-') { $default{"name"} = $`; $default{"version"} = $'; chomp $default{"version"}; } else { $default{"name"} = $thispackage; chomp $default{"name"}; $default{"version"} = "1.0"; } $default{"pkg"} = "UMC" . substr($default{"name"},0,4); $default{"arch"} = `uname -m`; chomp $default{"arch"}; $default{"category"} = "application"; $default{"vendor"} = "GNU"; $default{"email"} = "info@\"; $login = getlogin(); ($user, $passwd, $uid, $gid, $quota, $default{"pstamp"}, $userInfo, $userHome, $loginShell) = getpwnam ($login); $default{"pstamp"} = "Jasper Aukes" if ($default{"pstamp"} eq ""); $os = `uname -r`; $os =~ '\.'; $os = "sol$'"; chomp $os; $default{"basedir"} = "/usr/local"; # Check for correctness of guessed values by userinput %questions = ( pkg => "Please give the name for this package", name => "Now enter the real name for this package", arch => "What architecture did you build the package on?", version => "Enter the version number of the package", category => "What category does this package belong to?", vendor => "Who is the vendor of this package?", email => "Enter the email adress for contact", pstamp => "Enter your own name", basedir => "What is the basedir this package will install into?", packagename => "How should i call the packagefile?", ); @vars = qw(pkg name arch version category vendor email pstamp basedir packagename); foreach $varname (@vars) { $default{"$varname"} = "$name-$version-$os-$arch-local" if ($varname eq "packagename"); getvar($varname); } $classes = "none"; # Create the pkginfo file print "\nNow creating $pkginfo file\n"; open (PKGINFO,"> $pkginfo") || die "Unable to open $pkginfo for writingi ($!)\n"; print PKGINFO "PKG=\"$pkg\"\n"; print PKGINFO "NAME=\"$name\"\n"; print PKGINFO "ARCH=\"$arch\"\n"; print PKGINFO "VERSION=\"$version\"\n"; print PKGINFO "CATEGORY=\"$category\"\n"; print PKGINFO "VENDOR=\"$vendor\"\n"; print PKGINFO "EMAIL=\"$email\"\n"; print PKGINFO "PSTAMP=\"$pstamp\"\n"; print PKGINFO "BASEDIR=\"$basedir\"\n"; print PKGINFO "CLASSES=\"$classes\"\n"; close PKGINFO; print "Done.\n"; # Build and zip the package print "Building package\n"; system ("$pkgmk -r `pwd`"); system ("(cd /var/spool/pkg;$pkgtrans -s `pwd` /tmp/$packagename)"); system ("gzip /tmp/$packagename"); print "Done. (/tmp/$packagename.gz)\n"; # The subroutines sub getvar { my $questionname = "@_"; print "$questions{$questionname} [$default{\"$questionname\"}]: "; my $answer = <STDIN>; chomp $answer; $$questionname = $answer; $$questionname = $default{$questionname} if ($$questionname eq ""); } _________________________________________________________________________________ I also created a chrooted environment creator, to enable people to just run 'make install' in a clean environment. I think i mailed you about that some time ago, but perhaps something went wrong. The chrooted environment avoids problems with /usr/local/ in an environment where you just can't miss that directory (no way to quickly unmount it and remount it afterwards) Please tell me if you want that story (and script) again. When i have to create a package these days, i do the following: cd /tmp tar zxvf example-1.4.tar.gz cd example-1.4 ./configure make # Software is compiled here mkdir /tmp/example-1.4/tmp mv * /tmp/example-1.4/tmp setup_chroot /tmp/example-1.4 cd .. chroot example-1.4 /bin/sh # Get a chrooted environment (as root) make install # Install in the chrooted environment exit # Back to normal mode cd /tmp/example-1.4/usr/local make_package # Run the above script (as user) You got to have writepermission in /var/spool/pkg as user for this. Hope this is usefull. It helps me in building packages very much. Jasper -- Jasper Aukes | Unix system-administrator | Academic Medical Centre Utrecht Phone: +31 30 250 9283 | Bolognalaan 4, Utrecht Fax: +31 30 254 2028 | POBox 85500, 3508 GA Utrecht, NL Date: Wed, 16 Feb 2000 10:29:13 +0100 (MET) From: Jasper Aukes <; Subject: Re: make_package To: Hello Steve, Thanks for this. I have added it to my pkgadd page. I see. Please note this script only works if a 'make install' has been run that installed the compiled package in a _clean_ usr/local directory from a <whatever>/packagename-1.0/ directory. You will need my 'setup_chroot' script as well. I'll include it here: ______________________________________________________________________________ #!/bin/sh ## Original script by Dug Song <dugsong@UMICH.EDU>, used for a chrooted ## postfix environment, adapted by Jasper Aukes <> to be used for ## chrooted package creation. # # Location: /root/bin/setup_chroot # # Usage: # # First, create a chrooted dir with needed files (some may be left out (tcsh # etc), some might be missing on your system, some might be located elsewhere. # This script is just a dirty hack and by NO means intelligent. I should # improve it and write it in Perl when i have time. # # /root/bin/setup_chroot /tmp/PAKNAME # # Then, tar zxvf your package into: /tmp/PAKNAME/tmp/ # # cd /tmp/PAKNAME/tmp, configure and compile your package # DO NOT 'make install' just yet... # # Now, cd to /tmp and run: # # chroot PAKNAME /bin/sh # Or /bin/tcsh if you can and if you prefer it # # Check if you're really in the chrooted environment (f.e. cat /etc/passwd, it # shouldn't be there) :-) # # Now cd to your /tmp/package-version## # fill in the right name # and run a make install # # Exit your chrooted shell and start building the SUN package from the # directory /tmp/PAKNAME/usr/local/ # Or /usr, or even / # # A nice page to see how this stage could see a happy ending is to be found at # by Steven M. Christensen PATH=/usr/bin:/sbin:/usr/sbin # Create chroot'd area under Solaris 2.5.1 for postfix. # # Dug Song <dugsong@UMICH.EDU> if [ $# -ne 1 ]; then echo "Usage: `basename $0` <directory>, e.g.: /var/spool/postfix" ; exit 1 fi CHROOT=$1 # If CHROOT does not exist but parent does, create CHROOT if [ ! -d ${CHROOT} ]; then # lack of -p below is intentional mkdir ${CHROOT} fi if [ ! -d ${CHROOT} -o "${CHROOT}" = "/" -o "${CHROOT}" = "/usr" ]; then echo "$0: bad chroot directory ${CHROOT}" exit 2 fi for dir in etc/default etc/inet dev usr/bin usr/lib usr/share/lib/zoneinfo \ usr/local net \ tmp ; do if [ ! -d ${CHROOT}/${dir} ]; then mkdir -p ${CHROOT}/${dir} ; fi done ln -s usr/bin ${CHROOT}/bin ln -s usr/bin ${CHROOT}/net/bin # Set the right permissions chmod -R 755 ${CHROOT} # Copy some terminfo files for term in v x ; do if [ ! -d ${CHROOT}/usr/share/lib/terminfo/${term} ]; then \ mkdir -p ${CHROOT}/usr/share/lib/terminfo/${term} ; fi cp /usr/share/lib/terminfo/${term}/* ${CHROOT}/usr/share/lib/terminfo/${term} chmod 644 ${CHROOT}/usr/share/lib/terminfo/${term}/* done # AFS support. if [ "`echo $CHROOT | cut -c1-4`" = "/afs" ]; then echo '\tCreating memory resident /dev...' mount -F tmpfs -o size=10 swap ${CHROOT}/dev fi # Setup /etc files. cp /etc/nsswitch.conf ${CHROOT}/etc cp /etc/netconfig /etc/resolv.conf ${CHROOT}/etc cp /etc/default/init ${CHROOT}/etc/default cp /etc/inet/services ${CHROOT}/etc/inet/services ln -s /etc/inet/services ${CHROOT}/etc/services cp /usr/share/lib/termcap ${CHROOT}/usr/share/lib ln -s ${CHROOT}/usr/share/lib/termcap ${CHROOT}/etc/termcap find ${CHROOT}/etc -type f -exec chmod 444 {} \; # Most of the following are needed for basic operation, except # for,,, and which are # needed to resolve NIS names. cp /usr/lib/ /usr/lib/ ${CHROOT}/usr/lib for lib in libc libdl libintl libmp libnsl libsocket libw libkstat \ libcurses libkvm libelf libgen nss_nis nss_nisplus nss_dns nss_files; do cp /usr/lib/${lib}.so.1 ${CHROOT}/usr/lib rm -f ${CHROOT}/usr/lib/${lib}.so ln -s ./${lib}.so.1 ${CHROOT}/usr/lib/${lib}.so done for lib in straddr libmp; do cp /usr/lib/${lib}.so.2 ${CHROOT}/usr/lib rm -f ${CHROOT}/usr/lib/${lib}.so ln -s ./${lib}.so.2 ${CHROOT}/usr/lib/${lib}.so done chmod 555 ${CHROOT}/usr/lib/* # Copy timezone database. (cd ${CHROOT}/usr/share/lib/zoneinfo (cd /usr/share/lib/zoneinfo; find . -print | cpio -o) | cpio -imdu find . -print | xargs chmod 555 ) # Make device nodes. We need ticotsord, ticlts and udp to resolve NIS names. for device in zero tcp udp ticotsord ticlts; do line=`ls -lL /dev/${device} | sed -e 's/,//'` major=`echo $line | awk '{print $5}'` minor=`echo $line | awk '{print $6}'` rm -f ${CHROOT}/dev/${device} mknod ${CHROOT}/dev/${device} c ${major} ${minor} done chmod 666 ${CHROOT}/dev/* # Now copy some usefull binaries for bin in expr ls dirname cp chmod rm mv sed mkdir grep find cat \ true basename ln chown false cmp chgrp ; do cp /usr/bin/${bin} ${CHROOT}/usr/bin done cp /usr/ccs/bin/strip ${CHROOT}/usr/bin cp /bin/sh ${CHROOT}/usr/bin for bin in install tar tcsh make ; do cp /usr/local/bin/${bin} ${CHROOT}/usr/bin done chmod 755 ${CHROOT}/usr/bin/* exit 0 ______________________________________________________________________________ Greetings, Jasper -- Jasper Aukes | Unix system-administrator | Academic Medical Centre Utrecht Phone: +31 30 250 9283 | Bolognalaan 4, Utrecht Fax: +31 30 254 2028 | POBox 85500, 3508 GA Utrecht, NL

If you have any problems with these techniques, please study the man pages for the pkg programs above or contact your local UNIX guru. Please report any successes or failures in your understanding of these instructions.

Good luck and happy packaging.

Steve Christensen

Send comments or questions to envelope image

© Copyright 2001 Steven M. Christensen and Associates, Inc.
This page was last updated on July 5, 2001.