BigAdmin System Administration Portal
Community-Submitted Article
Print-friendly VersionPrint-friendly Version
This content is submitted by a BigAdmin user. It has not been reviewed for technical accuracy by Sun Microsystems, though it may have been lightly edited to improve readability. If you find an error or would like to comment on the article, please contact the submitter or use the comment field at the bottom of the article. Community submissions may not follow Sun trademark guidelines. For information on Sun trademarks, please see http://www.sun.com/suntrademarks/.
 
 

Using the pkgman Utility to Manage Packages on Solaris Hosts

Derek Olsen, January 2008

Introduction

This tech tip describes pkgman, a package management utility that can be used to maintain packages in a desired state on hosts that run the Solaris 8, 9, or 10 Operating System for SPARC or x86 platforms. This tech tip illustrates how to use pkgman by installing GTK+, a multi-platform toolkit for creating graphical user interfaces, which is available from Sunfreeware.com.

Note: The examples in this document use the Solaris 10 OS.

Contents

This article covers the following topics:

Background

Adding packages to or removing packages from a Solaris host is trivial. However, if you have a large number of hosts running different versions of the Solaris OS with unique package requirements, those tasks can be cumbersome.

What happens after the host has been jumpstarted and a request comes in to install some additional software package? Do you have an authoritative source you can check to see if you are already using that software somewhere? Or does the resolution to the request depend on who responds to it? How will you make sure the same version of the package instance is installed to limit drift in your hosts' configuration? What if you need to roll upgrades of, say, PostgreSQL through development, test, and production? Do you have a documented procedure for doing that? Maybe you do, and I've been wasting my time building pkgman.

From my experience as a Solaris administrator, I've personally struggled with a graceful solution to the previous questions, and I have met others with similar struggles. The problem with my prior approaches to managing packages was the complexity involved with the solution. This led to a situation where I was the only one who could easily roll out new packages, upgrade packages, or recall packages. Obviously, I wanted to be able to have a vacation or call in sick sometime, so I went back to the whiteboard.

The goal of this document is to demonstrate how to install and configure pkgman to manage packages. Installing the GTK+ (2.12.0) package from Sunfreeware.com is the core of our example. The GTK+ package installation is a nice, real-world example to give you an idea of whether pkgman is something worth using in your shop.

Requirements for pkgman

We will start with the requirements of pkgman, because every shop has certain parameters it needs to operate within.

  • Ruby version 1.8.X is required to run pkgman. (I plan to test against Ruby 1.9.X in January of 2008.)
  • The Solaris packages to be installed must be in data stream format.
  • The packages to be installed need to be available locally through HTTP.

Installing pkgman

If you have not been scared off by the requirements, let's move on to installing pkgman:

Download the tarball marked "latest stable version" from the download list at http://code.google.com/p/pkgman/downloads/list. The name for the tarball is pkgman-<release number>.tar.gz.

Unpack the tarball. You end up with a directory called pkgman-<release number>.

Alternatively, you can use subversion to check out the pkgman repository. The following command checks out the pkgman subversion repository into a directory called pkgman:

svn checkout http://pkgman.googlecode.com/svn/trunk/ pkgman

Change directory to the root of the pkgman directory.

The setup script uses /usr/local as the prefix for deploying pkgman. So the executable goes into /usr/local/bin, and the configuration file goes into /usr/local/etc. The setup script also changes the shebang line to match the path to Ruby on your host.

ruby setup.rb
---> bin
<--- bin
---> conf
<--- conf
---> bin
<--- bin
---> conf
<--- conf
rm -f InstalledFiles
---> bin
mkdir -p /usr/local/bin/
install pkgman.rb /usr/local/bin/
<--- bin
---> conf
mkdir -p /usr/local/etc/
install pkgman.yaml /usr/local/etc/
<--- conf

If you wanted to install pkgman into a different directory you would do something like the following. Here, I am changing the directory prefix from /usr/local to my home directory:

ruby setup.rb all --prefix=$HOME
---> bin
<--- bin
---> conf
<--- conf
---> bin
<--- bin
---> conf
<--- conf
rm -f InstalledFiles
---> bin
mkdir -p /users/deet/bin/
install pkgman.rb /users/deet/bin/
<--- bin
---> conf
mkdir -p /users/deet/etc/
install pkgman.yaml /users/deet/etc/
<--- conf

Installing GTK+ Packages

Now that we have pkgman available at /usr/local/bin/pkgman.rb (or wherever you put it), we can start to focus on the important work, which is installing packages!

You'll want to determine which sunfreeware.com mirror is the best for you to use for downloading your packages. Check the list of available mirrors.

To be considerate of bandwidth costs, you should download the packages to your local site and then make them available locally through HTTP. As of this writing (December 2007), the software packages and versions you'll need to download to get GTKk+ running are as follows:

atk-1.18.0
cairo-1.4.10
expat-1.95.5
fontconfig-2.4.2
freetype-2.3.1
glib-2.14.1
gtk+-2.12.0
jpeg-6b
libiconv-1.11
libpng-1.2.23
pango-1.18.2
render-0.8
renderproto-0.9.3
tiff-3.8.2
xrender-0.8.3
zlib-1.2.3

Please keep in mind that these packages require the libgcc shared object. In this example, I assume that the libgcc software is already installed.

If you need to install libgcc, you can add in the libgcc package record after you see how the config file is set up.

After you have downloaded the required packages, you need to make them available through HTTP. Additionally, the packages need to be decompressed because pkgman does not currently support compressed packages. An enhancement request exists to track handling of compressed files.

To decompress the gzipped packages, you can run the following command in the directory where the gzipped packages reside:

for f in `ls *gz`; do gunzip $f; done

Constructing the pkgman.yaml Configuration File

Now we are going to construct the pkgman.yaml configuration file.

I've stripped out the comments from the pkgman.yaml file that gets installed to keep this section of the document from getting cluttered. The configuration file contains the information about what packages should be installed or removed as well the script settings. The script settings consist of items such as the location of the packages and how to log activities.

Here is what the pkgman.yaml file script settings section looks like for my environment:

---
-
:http_host: jumpstart
:http_port: 1080
:http_uri: /jumpstart/pkgs/sfw
:logging: syslog
:syslog_facility: daemon
:syslog_level: notice

For most environments, all you need to change are the http_host, http_port, and http_uri keys.

For example, if the URL that you use to browse the package repository at your site is http://10.30.1.22/solaris/support/extras, then your pkgman.yaml settings section would look like the following:

---
-
:http_host: 10.30.1.22
:http_port: 80
:http_uri: /solaris/support/extras
:logging: syslog
:syslog_facility: daemon
:syslog_level: notice

At this point, I am not going to delve too deep into the details of the pkgman.yaml file, but you can find further information by reading the "CONFIG FILE" section of the README file at http://pkgman.googlecode.com/svn/trunk/README.

Now that the settings section is complete, we need to construct the records for each package we want to install. The information we need in order to construct an install record is the package name, package version, file name, and which hosts the record applies to.

Let's start with the atk software package. You can determine the package name by using the pkginfo command. Here, I am querying the package file atk-1.18.0-sol10-sparc-local to determine the package name and the package version:

[root@jumpstart /jumpstart/pkgs/sfw]$ pkginfo -l -d \
atk-1.18.0-sol10-sparc-local |egrep '(PKGINST|VERSION)'
PKGINST: SMCatk
VERSION: 1.18.0

You can also get the package version through the pkgparam command as follows:

[root@jumpstart /jumpstart/pkgs/sfw]$ pkgparam -f \
atk-1.18.0-sol10-sparc-local
VERSION
1.18.0

So now we know that the package name is SMCatk, the package version is 1.18.0, and the file name is atk-1.18.0-sol10-sparc-local. So we will put this information into a package record in the pkgman.yaml file.

The install record for the SMCatk package would look like this:

-
:action: install
:name: SMCatk
:file: atk-1.18.0-sol10-sparc-local
:version: 1.18.0
:hosts: default

We are going to use the value default for the hosts key. The supported values for the hosts key are detailed in the README file.

To tie the settings section and the package record together, let's look at what our current pkgman.yaml file looks like. In plain English, the following pkgman.yaml file indicates that the software package SMCatk version 1.18.0 should be installed on all hosts that run pkgman. The package file name is atk-1.18.0-sol10-sparc-local, and it's available for download from http://jumpstart:1080/jumpstart/pkgs/sfw/atk-1.18.0-sol10-sparc-local.

Additionally, all output would be logged by syslog using the priority daemon.notice. If logging is configured to "syslog," logs will go only to syslog, and nothing will be sent to your terminal.

---
-
:http_host: jumpstart
:http_port: 1080
:http_uri: /jumpstart/pkgs/sfw
:logging: syslog
:syslog_facility: daemon
:syslog_level: notice
-
:action: install
:name: SMCatk
:file: atk-1.18.0-sol10-sparc-local
:version: 1.18.0
:hosts: default

Now we need to add the remaining packages to the pkgman.yaml file.

Why don't you pick a package or two, construct the package records, and then compare your results to what I have in the following complete pkgman.yaml file? The order of the packages is unimportant because all the content ends up as an unsorted array.

---
-
:http_host: cfhost
:http_port: 80
:http_uri: /jumpstart/pkgs/sfw
:logging: syslog
:syslog_facility: daemon
:syslog_level: notice
-
:hosts: default
:file: atk-1.18.0-sol10-sparc-local
:version: 1.18.0
:name: SMCatk
:action: remove
-
:hosts: default
:file: cairo-1.4.10-sol10-sparc-local
:version: 1.4.10
:name: SMCcairo
:action: remove
-
:hosts: default
:file: expat-1.95.5-sol10-sparc-local
:version: 1.95.5
:name: SMCexpat
:action: remove
-
:hosts: default
:file: fontconfig-2.4.2-sol10-sparc-local
:version: 2.4.2
:name: SMCfontc
:action: remove
-
:hosts: default
:file: freetype-2.3.1-sol10-sparc-local
:version: 2.3.1
:name: SMCftype
:action: remove
-
:hosts: default
:file: glib-2.14.1-sol10-sparc-local
:version: 2.14.1
:name: SMCglib
:action: remove
-
:hosts: default
:file: gtk+-2.12.0-sol10-sparc-local
:version: 2.12.0
:name: SMCgtk
:action: remove
-
:hosts: default
:file: jpeg-6b-sol10-sparc-local
:version: 6b
:name: SMCjpeg
:action: remove
-
:hosts: default
:file: libiconv-1.11-sol10-sparc-local
:version: 1.11
:name: SMCliconv
:action: remove
-
:hosts: default
:file: libpng-1.2.23-sol10-sparc-local
:version: 1.2.23
:name: SMClibpng
:action: remove
-
:hosts: default
:file: pango-1.18.2-sol10-sparc-local
:version: 1.18.2
:name: SMCpango
:action: remove
-
:hosts: default
:file: render-0.8-sol10-sparc-local
:version: 0.8
:name: SMCrender
:action: remove
-
:hosts: default
:file: renderproto-0.9.3-sol10-sparc-local
:version: 0.9.3
:name: SMCrenpro
:action: remove
-
:hosts: default
:file: tiff-3.8.2-sol10-sparc-local
:version: 3.8.2
:name: SMCtiff
:action: remove
-
:hosts: default
:file: xrender-0.8.3-sol10-sparc-local
:version: 0.8.3
:name: SMCxrend
:action: remove
-
:hosts: default
:file: zlib-1.2.3-sol10-sparc-local
:version: 1.2.3
:name: SMCzlib
:action: remove

Using pkgman

So now that we have pkgman installed and an initial configuration file set up, let's see what we can do.

First, let me cover the pkgman command-line options and what they mean.

[root@jtest ~]$ which pkgman
/usr/local/bin/pkgman
[root@jtest ~]$ pkgman
Usage: /usr/local/bin/pkgman [ -n ] -f pkgman.yaml
-f [ /path/to/pkgman.yaml | http://server/path/to/pkgman.yaml ]
-n Just print what would happen without making any changes
-h print usage and exit

The -n argument is optional and indicates that you want to run pkgman in dry-run mode. In dry-run mode, no changes are made to the system. You can use dry-run mode to see what packages would be installed or removed from a host before you run pkgman for real.

The -f argument is required and is followed by the location of the pkgman.yaml file. The file can be on the local file system or available through HTTP. Some sites don't use cfengine or a similar tool to push out files to their hosts, so I thought having the pkgman.yaml file available through HTTP would be useful.

Now let's actually run pkgman and point it to our configuration file. Whooo!

In this first example I'm going to run pkgman in dry-run mode. I'm using the default logging configuration, which ends up sending everything to /var/adm/messages. We can see that all of our desired packages are flagged as needing to be installed.

[root@jtest /]$ pkgman -n -f \
http://jumpstart:1080/jumpstart/pkgs/sfw/pkgman.yaml
[root@jtest ~]$ tail -16 /var/adm/messages
Dec 11 17:28:38 jtest pkgman[10407]: [ID 702911 daemon.notice] \
dryrun mode -
Install pkg SMCatk
Dec 11 17:28:39 jtest pkgman[10407]: [ID 702911 daemon.notice] \
dryrun mode -
Install pkg SMCcairo
Dec 11 17:28:40 jtest pkgman[10407]: [ID 702911 daemon.notice] \
dryrun mode -
Install pkg SMCexpat
Dec 11 17:28:41 jtest pkgman[10407]: [ID 702911 daemon.notice] \
dryrun mode -
Install pkg SMCfontc
Dec 11 17:28:42 jtest pkgman[10407]: [ID 702911 daemon.notice] \
dryrun mode -
Install pkg SMCftype
Dec 11 17:28:48 jtest pkgman[10407]: [ID 702911 daemon.notice] \
dryrun mode -
Install pkg SMCglib
Dec 11 17:29:07 jtest pkgman[10407]: [ID 702911 daemon.notice] \
dryrun mode -
Install pkg SMCgtk
Dec 11 17:29:08 jtest pkgman[10407]: [ID 702911 daemon.notice] \
dryrun mode -
Install pkg SMCjpeg
Dec 11 17:29:09 jtest pkgman[10407]: [ID 702911 daemon.notice] \
dryrun mode -
Install pkg SMCliconv
Dec 11 17:29:09 jtest pkgman[10407]: [ID 702911 daemon.notice] \
dryrun mode -
Install pkg SMClibpng
Dec 11 17:29:11 jtest pkgman[10407]: [ID 702911 daemon.notice] \
dryrun mode -
Install pkg SMCpango
Dec 11 17:29:11 jtest pkgman[10407]: [ID 702911 daemon.notice] \
dryrun mode -
Install pkg SMCrender
Dec 11 17:29:11 jtest pkgman[10407]: [ID 702911 daemon.notice] \
dryrun mode -
Install pkg SMCrenpro
Dec 11 17:29:13 jtest pkgman[10407]: [ID 702911 daemon.notice] \
dryrun mode -
Install pkg SMCtiff
Dec 11 17:29:13 jtest pkgman[10407]: [ID 702911 daemon.notice] \
dryrun mode -
Install pkg SMCxrend
Dec 11 17:29:14 jtest pkgman[10407]: [ID 702911 daemon.notice] \
dryrun mode -
Install pkg SMCzlib

So in this example, let's go ahead and do the actual installations by running pkgman without the dry-run flag. From the logging output, everything looks like it was done correctly.

[root@jtest /]$ pkgman -f \
http://jumpstart:1080/jumpstart/pkgs/sfw/pkgman.yaml
[root@jtest ~]$ tail -16 /var/adm/messages
Dec 11 17:33:40 jtest pkgman[10494]: [ID 702911 daemon.notice] \
Installation of <SMCatk> was successful.
Dec 11 17:33:44 jtest pkgman[10494]: [ID 702911 daemon.notice] \
Installation of <SMCcairo> was successful.
Dec 11 17:33:45 jtest pkgman[10494]: [ID 702911 daemon.notice] \
Installation of <SMCexpat> was successful.
Dec 11 17:33:51 jtest pkgman[10494]: [ID 702911 daemon.notice] \
Installation of <SMCfontc> was successful.
Dec 11 17:33:54 jtest pkgman[10494]: [ID 702911 daemon.notice] \
Installation of <SMCftype> was successful.
Dec 11 17:34:08 jtest pkgman[10494]: [ID 702911 daemon.notice] \
Installation of <SMCglib> was successful.
Dec 11 17:35:00 jtest pkgman[10494]: [ID 702911 daemon.notice] \
Installation of <SMCgtk> was successful.
Dec 11 17:35:02 jtest pkgman[10494]: [ID 702911 daemon.notice] \
Installation of <SMCjpeg> was successful.
Dec 11 17:35:04 jtest pkgman[10494]: [ID 702911 daemon.notice] \
Installation of <SMCliconv> was successful.
Dec 11 17:35:06 jtest pkgman[10494]: [ID 702911 daemon.notice] \
Installation of <SMClibpng> was successful.
Dec 11 17:35:12 jtest pkgman[10494]: [ID 702911 daemon.notice] \
Installation of <SMCpango> was successful.
Dec 11 17:35:13 jtest pkgman[10494]: [ID 702911 daemon.notice] \
Installation of <SMCrender> was successful.
Dec 11 17:35:14 jtest pkgman[10494]: [ID 702911 daemon.notice] \
Installation of <SMCrenpro> was successful.
Dec 11 17:35:19 jtest pkgman[10494]: [ID 702911 daemon.notice] \
Installation of <SMCtiff> was successful.
Dec 11 17:35:21 jtest pkgman[10494]: [ID 702911 daemon.notice] \
Installation of <SMCxrend> was successful.
Dec 11 17:35:23 jtest pkgman[10494]: [ID 702911 daemon.notice] \
Installation of <SMCzlib> was successful.

Let's run pkgman one more time in dry-run mode just to see what happens.

This time I'm changing the "logging" entry in the pkgman.yaml file. The line previously read :logging: syslog. Now it reads :logging: console. So if any work needs to be done, that will be reported to the terminal.

[root@jtest ~]$ pkgman -n -f \
http://cfhost/jumpstart/pkgs/sfw/pkgman.yaml
[root@jtest ~]$

Nothing was reported because the desired packages are already installed. However, for the doubters in the crowd, we will double-check the package database to make sure the packages actually got installed.

[root@jtest ~]$ pkginfo |grep SMC
application SMCatk atk
application SMCcairo cairo
application SMCexpat expat
application SMCfontc fontconfig
application SMCftype freetype
application SMCglib glib
application SMCgtk gtk+
application SMCjpeg jpeg
application SMClibpng libpng
application SMCliconv libiconv
application SMCpango pango
application SMCrender render
application SMCrenpro renderproto
application SMCtiff tiff
application SMCxrend xrender
application SMCzlib zlib

Well, everything appears to be in place here. Now we have a host with GTK+ and the required, dependent packages installed.

Now you should be able to repeat the package installations easily on any other hosts that you want to install GTK+ on.

JumpStart Postinstallation Script

The following portion of my JumpStart (using Flash archives) postinstallation script demonstrates how I have integrated pkgman into JumpStart.

I said that I just install the desired OS cluster with JumpStart, but that's not totally true, because I add the Ruby package to the OS before creating my Flash archive.

I run pkgman after the installation reboot is complete, because not all packages have been created to honor an alternate root installation. Hard-coded paths in preinstallation or postinstallation scripts might not work.

Additionally, I do some other setup work in the postinstallation script, so it is the logical place for me to include the pkgman execution. During a Flash installation, any scripts located in /etc/flash/reboot are executed during the first boot. The process removes the reboot directory and its contents after script execution, which is nicer than creating temporary init scripts.

************* start postinstall **************
#!/bin/sh

create_reboot_script()
{
mkdir -p /a/etc/flash/reboot
cat >/a/etc/flash/reboot/firstboot.sh <<_EOF
#!/bin/sh

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

logger -p daemon.notice "Running firstboot script"

logger -p daemon.notice "Executing pkgman"
/usr/local/bin/pkgman -f /usr/local/etc/pkgman.yaml

logger -p daemon.notice "rebooting"
init 6
_EOF

chown root:root /a/etc/flash/reboot/firstboot.sh
chmod 755 /a/etc/flash/reboot/firstboot.sh
}

create_reboot_script
************* end postinstall ****************

Once the initial post-JumpStart pkgman execution is complete, future executions of pkgman are triggered by cfengine. I have cfengine configured to execute pkgman if a certain trigger file exists on the host (/etc/.run_pkgman).

When I have to deploy package modifications, the destination hosts tend to line up with my cfengine groups. For example, when I want to push out the GTK+ packages to the Solaris workstations, I use cfengine to touch the trigger file on all the hosts in my cfengine "workstations" group.

Here is the snippet of my cfengine file that controls the execution of pkgman:

classes:
solaris::
run_pkgman = ( FileExists(/etc/.run_pkgman) )

shellcommands:
run_pkgman::
"/usr/local/bin/pkgman -f /usr/local/etc/pkgman.yaml"
"/usr/bin/rm /etc/.run_pkgman"

I use a dedicated cfengine file for one-time jobs, such as touching trigger files. To trigger pkgman on all the workstations, I would add the following to the cfengine file used for one-time jobs. Remember to delete the job after you've pushed out the cfengine changes.

shellcommands:
workstations::
"/usr/bin/touch /etc/.run_pkgman"

Summary

That pretty much wraps up our whirlwind tour of pkgman. Our goal was to install the GTK+ packages from sunfreeware.com, which we accomplished.

I hope that through this example, I have provided enough information for you to determine if pkgman is worth further investigation. Please consult the README file for further information.

If you have any additional comments or questions please drop me a line at someword [at] gmail.com!

Thanks for your time...

About the Author

Derek is a long-time UNIX/Linux system administrator. Over the years, he has developed a special fascination for custom JumpStart installations, package management, and configuration management. He lives and works in Portland, Oregon (USA) and spends his free time mucking about with the Solaris OS, drinking beer, and convincing himself that he is in charge of his dog, not the other way around.

Discuss and comment on this resource in the BigAdmin Wiki


Unless otherwise licensed, code in all technical manuals herein (including articles, FAQs, samples) is provided under this License.


BigAdmin
  
 
 
 
 
Would you recommend this Sun site to a friend or colleague?
Contact About Sun News & Events Employment Site Map Privacy Terms of Use Trademarks Copyright Sun Microsystems, Inc.