ONLamp.com    
 Published on ONLamp.com (http://www.onlamp.com/)
 See this if you're having trouble printing code examples


Big Scary Daemons

Running Commercial Linux Software on FreeBSD

by Michael W. Lucas
01/12/2006

One of the more intriguing capabilities of the BSD operating systems is their ability to run binaries for other Unix-like operating systems. I recently found myself requiring the commercial PGP Command Line for a project. Rather than install a Linux box just for this one piece of software, I jumped through some hoops and made it work perfectly on one of my existing FreeBSD systems. Getting a random piece of commercial Linux software running on a FreeBSD system isn't always as transparent as you might like, but you can do it with a minimum of fuss if you have a few extra troubleshooting skills.

Those of you familiar with OpenPGP are probably wondering why I wouldn't just use GnuPG. While GnuPG handles pure OpenPGP functions quite well, PGP Command Line has specific abilities that GnuPG lacks. For example, some government standards require that a company be able to decrypt data even if the private key owner is unwilling to provide the private key. PGP software uses an Additional Decryption Key (ADK) to support this. (After guarding my private keys for years, I freely admit that the idea of ADK makes my bowels churn--but as it's not mandatory, I can see where a corporation would need this.) PGP Command Line also has the ability to make self-extracting PGP Zip files, which is rather useful. There's always the IDEA patent issue--although if you need IDEA, you probably have other problems! PGP Command Line comes in a .zip file containing the binaries for all the supported platforms and a few instructions in PDF format. You also get license codes tied to your name, organization, and email address.

To run Linux software on FreeBSD, you must configure your FreeBSD system, use the RPM package management tool to install it in the Linux environment, and finally tune the installed software to match the requirements of the Linux subsystem. The first part is well documented, but the rest is murky to many BSD administrators.

Linux Compatibility Setup

Start by confirming that your FreeBSD system is configured to run Linux binaries. Your kernel must have the Linux mode kernel module loaded. The simplest way to check is with kldstat(8):

# kldstat | grep linux
7    1 0xc1c7b000 18000    linux.ko
#

If you don't get anything back, go to /etc/rc.conf and add an entry to enable Linux mode.

linux_enable="YES"

You also need a subset of a standard Linux userland under /usr/compat/linux. I used a Red Hat 9 userland for this article, as found in /usr/ports/emulators/linux_base-rh-9. If you have a dual-boot FreeBSD/Linux system and are willing to play around a little, you might want to mount your Linux userland under /usr/compat/. While you might encounter different issues if you use a different version of Linux userland, the general process given here will still be valid.

Many Linux programs also require a Linux-style procfs. FreeBSD has deprecated procfs in favor of sysctl(8), but many Linux programs still require it. If you install from a port, the port may configure a Linux procfs for you. If not, add the following line to /etc/fstab:

linproc    /compat/linux/proc      linprocfs     rw  0  0

You cannot mount this filesystem until your Linux userland is installed.

Once your kernel is configured, you have a Linux userland installed, and /etc/fstab lists a Linux procfs, you should reboot your computer. Confirm that everything works with kldstat(8) and df(1) before continuing.

Installing Linux RPMs on FreeBSD

If BSD's ports system has spoiled you, RPM may appear obtuse and arcane. It really isn't any worse than pkg_add--the headaches are simply in different spots. You must use some additional RPM features to install Linux packages on FreeBSD. First, the --ignoreos flag tells rpm(8) not to worry about the fact that you're not installing on a Linux box. The --root option gives the root of the filesystem you're installing in--in this case, /usr/compat/linux. This will prevent overwriting your native FreeBSD binaries!

# sudo rpm -i --ignoreos --dbpath /var/lib/rpm --root \
    /compat/linux PGPCommandLine902_Linux.rpm
error: failed dependencies:
        libxml2.so.2   is needed by pgpcmdln-9.0.2.560-1
#

PGP Command Line needs libxml2.so.2, and it's not available on the system. Did I mention that RPM had its own headaches? Dependency is one of them. The BSD ports system mostly eliminates dependency problems, so this kind of issue tends to give us fits. Linux users all have their favorite ways to solve dependency problems, but here's the method that works for me. First, confirm that this library really isn't on your system.

# locate xml | grep compat/linux
/usr/compat/linux/share/xml
/usr/compat/linux/usr/bin/xmlwf
/usr/compat/linux/usr/share/man/man1/xmlwf.1.gz
#

OK, it's really not there. Go to an RPM search site and find the appropriate RPM. I find that rpmfind.net gives me the best results, but you might have your own favorites. Choose only an RPM built for the Linux userland you have on your system. You wouldn't think of installing a FreeBSD 7 package on your FreeBSD 4 system, and Linux doesn't like that sort of thing any better. Once you download and install the RPM, try to install PGP Command Line again.

# sudo rpm -i --ignoreos --dbpath /var/lib/rpm --root /compat/linux \
    libxml2-2.5.4-3.rh9.i386.rpm
# sudo rpm -i --ignoreos --dbpath /var/lib/rpm --root /compat/linux \
    PGPCommandLine902_Linux.rpm
#

If rpm(8) returns silently, the install was successful.

Checking RPMs

I always wonder exactly what a precompiled package installs on my system, but this curiosity becomes vital when running programs in Linux mode. FreeBSD installs the smallest possible subset of Linux, but many programs expect to have a full Linux userland available, and these dependencies are not always recorded in the RPM file. You must confirm that the program has everything required and that your Linux environment is properly configured to support this software.

rpm(8) maintains a small database of installed packages and can tell you exactly what it installed, what files it contained, and which packages have which dependencies. Start by asking the RPM database for a list of all the installed packages. The list is in order by install date, so you want the very last entries.

# rpm --root /compat/linux --query -a
glibc-2.3.2-27.9.7
glibc-common-2.3.2-27.9.7
...
libxml2-2.5.4-3.rh9
pgpcmdln-9.0.2.560-1
#

RPM knows PGP Command Line as pgpcmdln-9.0.2.560-1. Now ask for the contents of that package with rpm's --query option. You might want to capture the output of this command, as you need it to finish configuring the software.

# rpm --root /compat/linux --query pgpcmdln -l
/usr/bin/pgp
/usr/bin/pgp-agent
/usr/lib
/usr/lib/debug
...

Configuring Software for Linux Mode

The two critical parts of installing a program to run under Linux mode are making sure that Linux's ldconfig(8) knows about any included libraries and binary branding.

Linux configures ldconfig(8) at boot time by reading /etc/ld.so.conf. PGP installs all of its libraries directly under /usr/lib, which is already listed. If this had changed, you would need to edit ld.so.conf and use Linux's ldconfig(8) program to update the shared library cache, /compat/linux/etc/ld.so.cache.

Now you need to check the branding on the executables. Branding a library identifies the operating system it is built to run on. FreeBSD uses this brand to determine whether an executable is a native FreeBSD program or whether it must treat the program as a foreign program. PGP Command Line includes two binaries, /usr/compat/linux/usr/bin/pgp and /usr/compat/linux/usr/bin/pgp-agent. Branding might or might not be necessary for the proper operation of any given piece of software, but it's worth it to be careful. The brandelf(1) program identifies the brand on a particular executable. PGP Command Line includes two executables, /usr/local/bin/pgp and /usr/local/bin/pgp-agent.

# cd /usr/compat/linux/usr/bin
# brandelf pgp*
File 'pgp' is of brand 'SVR4' (0).
File 'pgp-agent' is of brand 'SVR4' (0).
#

All programs run under Linux mode should have Linux brands. FreeBSD supports only SVR4, Linux, and native FreeBSD binaries, and will not let you assign unsupported brands to binaries. Use the -t flag to assign a brand to a binary.

# sudo brandelf -t Linux pgp*

Finally, I want these binaries available in my regular $PATH. Adding /compat/linux/usr/bin to $PATH is not highly recommended, as you generally want to execute native versions of your programs or at least know that you're using Linux mode. This is easy with symlinks:

# sudo ln -s /usr/compat/linux/usr/bin/pgp /usr/local/bin/
# sudo ln -s /usr/compat/linux/usr/bin/pgp-agent  /usr/local/bin/
# rehash

Once all this is done, you should be able to run the program:

# pgp --h
/home/mwlucas:unknown (3078:could not create directory, Permission denied)
#

Here's the good news: the program runs! The bad news is, it chokes on something. This error is listed in the PGP Command Line manual, but the description isn't exactly helpful. The trick here is to figure out what it wants to open.

Troubleshooting with truss

Once you learn how to use truss(1), you'll wonder how you ever solved any problems without it. truss watches a running program and lists the system calls it makes. Whenever I have a problem getting a program to run in Linux mode, I run it under truss and gather all sorts of details.

One drawback to truss is that it requires a FreeBSD-native procfs. While procfs is not part of the FreeBSD new world order, it is still required on rare occasions. I recommend adding an entry for /proc to /etc/fstab but marking it noauto so that it is not automatically mounted at boot. That way, you only have to remember to type mount /proc before running truss, instead of digging through the procfs(5) man page.

proc                /proc           procfs  rw,noauto       0       0

Once you have /proc mounted, run your Linux program under truss. This will generate a lot of output, so you might want to use script(1) to capture the output. Simply asking the version generates 308 lines of truss output to skim through for the error. The simplest thing to do is to search the truss output for directory. Near the end of the output are several lines like:

# truss pgp --version
...
linux_open("/home/mwlucas/.pgp/randseed.rnd",0x8002,00) ERR#2 'No such file or
    directory'
linux_open("/home/mwlucas/.pgp/randseed.rnd",0x80c1,0600) ERR#2 'No such file
    or directory'
linux_open("/home/mwlucas/.pgp/randseed.rnd",0x8002,00) ERR#2 'No such file or
    directory'
...

Aha! PGP Command Line can't open /home/mwlucas/.pgp directory, as the directory doesn't exist. Once I create a .pgp directory, I get:

# pgp --version
PGP Command Line 9.0.2 build 560
Copyright (C) 2005 PGP Corporation
All rights reserved.

A few more complicated tests show that the software runs exactly as documented in the PGP Command Line manual. While I'm not sure I could get official corporate support for this install, everything I've tried works perfectly. I get excellent results installing Linux programs on FreeBSD by configuring the environment properly, branding the software correctly, and using truss(1) to identify problems. Good luck!

Absolute OpenBSD

Related Reading

Absolute OpenBSD
UNIX for the Practical Paranoid
By Michael W. Lucas

Michael W. Lucas


Read more Big Scary Daemons columns.

Return to the BSD DevCenter.

Copyright © 2009 O'Reilly Media, Inc.