Article Listing 1 Listing 2 Listing 3 oct2006.tar

Managing Users' Environments with the Usepackage Environment Manager

Chandler Wilkerson

Sys admins who manage moderate to large mixed installations of Unix/Linux systems with local users know the headaches associated with keeping track of those users' environments across different operating systems. In this article, I will detail an under-sung utility called usepackage, which brings together environment management for diverse systems, software packages, user classes, and shell styles into a centralized configuration.

Creating centralized shell profiles by hand tends to involve large, complicated switch statements with scripts that must be ported to each shell style present on the systems. Other problems arise when dealing with the number of different places software can install itself. For instance, according to best practices standards, the use of /usr/local in Unix/Linux is being deprecated in favor of /opt. We know how long that lasts on a system as you install software packages. One of my colleagues once quipped that even on distributions that don't include a /usr/local directory by default, one will spontaneously generate if given enough time.

If you use /opt as recommended, you run into another problem with keeping track of packages. Rather than one /usr/local/bin directory to include in everybody's path, you get many /opt/package-name/bin directories, each with its own man path, libraries, and sometimes even custom environment variables, such as JAVA_HOME for Java.

One more common complication, just to make the job of environment management even tougher, is that sometimes multiple versions of packages will need to be installed side by side. This requires that environment variables be changed from package to package, usually by a complicated set of scripts that the systems administrators have to write by hand.

This article is not, however, about /usr/local, /opt, or best practice standards, but about the tough job of managing users' environments in a system where conflicting packages may install themselves in various, seemingly random locations.

Usepackage, written by Jonathan Hogg, is "...a tool for managing the Unix environment for a multi-tool, multi-platform site. It is designed to simplify login scripts and allow administrators to control users' environments centrally." (See http://usepackage.sourceforge.net/.) Usepackage consists of a central configuration file, a set of stubs to add to the system profiles, and a binary that parses the configuration and manipulates users' environment variables.

Using Use

The end-user interface to the usepackage system is through the use command. The use command is implemented as a shell function that evaluates the output of the usepackage binary. Scripts to create the use function are available in bash, csh, and ksh syntaxes. Usepackage can output commands to set relevant environment variables in each language by passing the corresponding script's flag: -b, -c, or -k.

Users interact with the use command in two basic modes. Typing just use -l will print out a list of all available packages, then use packagename will set environment variables for that particular package.

Getting Started

Installing usepackage is straightforward. You can get the latest tarball at http://sourceforge.net/projects/usepackage. From there, installation is a standard process of untar, configure, make, and sudo make install. (You didn't untar it as root, did you?) One change I like to make is to add --prefix=/opt/usepackage to the configure process, as usepackage installs itself by default to /usr/local.

Next, you should add the use function definition scripts to the system's user profile. You can find the scripts themselves under the "share/usepackage" directory. If your OS or distribution has an /etc/profile.d directory, you can create symbolic links to all the files in the share/usepackage directory to save time, for example:

# ln -s /opt/usepackage/share/usepackage/* /etc/profile.d 
# mv /etc/profile.d/use.bsh /etc/profile.d/use.sh 
            
If your system does not have a profile.d directory, you can just add source commands to the appropriate system login scripts. Now you should be able to log in as a regular user and try the use command:

$ use -l 
    
This should give you a list of available packages from the default configuration file. The default configuration file is quite functional for many setups. It includes path options such as none to empty paths and start over, system to get a typical set of system paths, and site to provide site-wide installed commands such as those residing in /usr/local. More advanced path setups are available for power users, such as bsd, which applies to Solaris systems to include the /usr/ucb path for those who prefer their ps command to work in the BSD way. The sysadm option includes the sbin directories for power users. Finally, there is an X11 group that adds /usr/X11R6/bin to allow X11 users to run X commands.

In addition to creating path lists for finding applications, usepackage is good for managing other environment variables. The packages mentioned above include settings for the MANPATH, INFOPATH, and LD_LIBRARY_PATH variables as well.

Once everything is configured, it is helpful to add a note to the system's MOTD file directing users to try running use -l.

Configuring Usepackage

The configuration file is found under the etc/usepackage.conf directory from wherever usepackage was installed. Entries in the configuration file consist of an optional label, for defining what appears in the use -l command, and a package definition. Comments start with a #.

An excerpt of the default configuration defining the none and system packages is shown in Listing 1.

The lines enclosed in angle brackets are labels that will appear in the use -l command. The lines starting with system add definitions to the system package. The first one sets common paths for most Unix/Linux operating systems, while the second two instances add OS-specific lines for, in this example, Mac OS X (Darwin) and Solaris versions 2.0 through 10.

The package definition format allows for matching of architecture, operating system, OS release version, hostname, and specific shell being used. Each subsequent option is optional, but requires that any previous option(s) be defined in order to identify which option to match. In the case of the Darwin match above, a wild card * is used as a placeholder for architecture. In addition to matching packages to certain subsets of systems and/or shells, a package definition may also require certain prerequisite packages be included in order to use a certain package using the <= construct.

A comma-separated list of variables may be defined or added to on the right-hand side of the definitions. The += token indicates that the defined value should be prepended to the existing value of the environment variable. An =, on the other hand, sets the value explicitly, and overwrites any preexisting values. One more syntactic distinction separates path-style values from regular values (as csh style shells handle paths differently). If the value is enclosed in quotes, it is a literal value to be set as-is. If it is not enclosed in quotes, it is interpreted as a colon-separated path list. For example, in the system definition above, all of the variables are paths, and those paths are to be prepended to existing values without overwriting. In the configuration for the none package, all the default variables are explicitly set to the empty string, "". This works as an exception to the syntax for path versus regular variable as the empty string means the same thing in either context.

In addition to packages consisting of variable definitions, usepackage supports groups of packages using a similar syntax to package definitions. Group definitions consist of a group name, a := and a comma-separated list of packages. Usepackage also supports an include command to reference other configuration files. The default example is (include ~/.packages), which allows users to define their own packages.

Managing Multiple Compilers

Although usepackage is originally designed to provide a centralized environment configuration for user accounts that may span multiple platforms, it is also of great utility in maintaining complex configurations on individual machines. Our department maintains an eight-processor machine for computational tasks. Multiple research groups use this machine, and several of our high-performance computing classes use it as an example of a symmetric multiprocessor (SMP) machine. Trying to maintain the user environment for different groups needing different tools would have been a mess through traditional /etc/profile hacks.

The system runs Gentoo Linux, and includes three versions of GCC, a commercial compiler, and two different MPI library versions. Dependencies between these packages can be complex, especially to a researcher or student who is just concerned with compiling code. For instance, one of the MPI libraries requires the commercial compiler, and because the commercial compiler is linked against version 3.3.5 of GCC found on Red Hat/Fedora systems, it requires access to the 32-bit GCC 3.3.5 library. The usepackage configuration for this system's compilers can be found in Listing 2.

Cross-Site Configuration

Although the previous example provides for a complex set of packages on just one system, the purpose of usepackage is more global. To that end, it makes sense to maintain a single instance of the configuration file, possibly on a shared filesystem like NFS. That configuration should closely match the default configuration that comes with the usepackage distribution, with a few tweaks for the local environment. For instance, I like to remove the dot package altogether, because usepackage prepends the most recently added package to the path, and having . as your first path element is one of the biggest no-nos in Unix.

It would be possible to include local configurations, such as the compilers and libraries above, in the central configuration file using host or OS filters. Unfortunately, there is no way in usepackage to prevent the labels for those packages from showing up in the use -l command on other systems. Thus, to avoid confusing users by listing packages that do not exist, it is best to save those definitions for a local configuration file. For local packages, create another usepackage configuration file and place it on a non-shared filesystem. Using a filename such as /etc/usepackage.local, specific configurations for individual systems can be created and referenced with an include command in the common usepackage.conf file.

Listing 3 provides a somewhat tweaked version of the default usepackage.conf that would be suitable for the setup detailed above.

Conclusion

Usepackage is a handy tool to manage users' environments across a combination of different shells, operating systems, and hardware. Due to the flexibility of its configuration format, it can be localized to specific tasks and machines, further customized by users, and is capable of supporting complex environments while offering users a simple interface. For more information, visit the usepackage home page at: http://usepackage.sourceforge.net/.

Chandler Wilkerson has a M.S. in Computer Science and is a LAN Administrator for the Department of Computer Science at the University of Houston. He is responsible for managing Unix and Linux servers, labs, and networking for the department. In his spare time, Chandler tempts fate by juggling semi-dangerous objects.