Simplify Your Life By Leveraging Source Packages In Your LINUX Environment
- Simplify Your Life By Leveraging Source Packages In Your LINUX Environment
- Making Your Package Match Your Environment
I am in blood
Stepp'd in so far that, should I wade no more,
Returning were as tedious as go o'er.
— Macbeth, Act III: Scene 4 (Shakespeare)
If you've worked with computers for a long time, you've probably encountered the situation where you need to upgrade foo, only to realize that it depends on a new version of bar, and upgrading bar means upgrading a dozen other things. Or even worse, you can't upgrade foo because it depends on bar-2.0, but frotz only works with bar-1.5… In the first case, your upgrade of foo is complicated by a heap of work that you didn't plan on; in the second, you're prevented from using the features in foo because you can't afford to break frotz.
Conflicting package requirements can be problematic, but strictly-enforced package dependencies also keep your system stable by forcing you to recognize that you have to choose between foo's new functionality and frotz. Consider the alternative where bar automatically upgrades foo for you and breaks frotz forever—you're performing the upgrade after hours and don't find out about it until the seething manager of the frotz users corners you in your cubicle on Monday morning. The cute mascot and geek cruises notwithstanding, package dependencies are one of the main reasons that Linux is popular with production folks who understand change control and how hard it is to implement without good tools. Package dependencies are not unique to Linux and technically are not even part of Linux—they're a function of the distribution of the operating system and that distribution's package-management toolset. However, the major distributions nowadays do enforce dependencies, which keeps administrators focused on productive work instead of reloading the operating system because software package X killed the box.
So there are safeguards in place to keep you from stepping into something that is just as murderous to finish as it is to undo (apologies to Shakespeare). This is useful, but often suboptimal. You may really really need the functionality in foo and the old frotz. Furthermore, you don't want to have to wade through a dozen seemingly unrelated upgrades during your short outage window, just to run the latest version of foo. This article discusses the nature of these dependencies and how you can—and why you should—use a distribution's package tools to keep your important packages current, without having to constantly upgrade to the latest release of your distribution. The same procedure can be used to apply security patches, fixes, and new functionality to systems running versions that are no longer supported by your distribution (also known as backporting).
The following examples are Debian specific, but the techniques are applicable to all distributions that support the notion of dependencies and source packages. This includes Red Hat and its derivatives.
Packages and Interdependencies
First, let's take a look at why dependencies prevent certain upgrades. The most prevalent type of dependency is a shared-library dependency based on the compile environment used to build the package. All binaries that are dynamically linked (that's most of them!) need to be able to find largely similar library versions to link against at run time in order to execute properly. I use the word "largely" because few programs use all the functions in a shared library, and new versions of shared libraries don't always update every function—or they update the functions in a way that breaks the calling interface. That being said, taking a binary compiled against library version X and moving it to a system with library version Y is about the quickest way I know to get a segmentation fault. If you want to see the list of libraries that a binary needs to execute, use the ldd command:
tony@bach:/home/tmancill$ ldd -v /bin/grep libc.so.6 => /lib/libc.so.6 (0x4001f000) /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000) Version information: /bin/grep: libc.so.6 (GLIBC_2.1) => /lib/libc.so.6 libc.so.6 (GLIBC_2.0) => /lib/libc.so.6 /lib/libc.so.6: ld-linux.so.2 (GLIBC_2.1.1) => /lib/ld-linux.so.2 ld-linux.so.2 (GLIBC_2.1) => /lib/ld-linux.so.2 ld-linux.so.2 (GLIBC_2.2) => /lib/ld-linux.so.2 ld-linux.so.2 (GLIBC_2.0) => /lib/ld-linux.so.2
Here we see that the /bin/grep executable only needs libc.so.6 and version 2 of the dynamic linker (ld-linux.so.2) to run. The -v switch gives us more information about the versions of glibc that grep is compatible with, and then the versions of glibc that our libc.so.6 is compatible with. Since there's a union to the set of these versions, grep should run with no problem. Now, ldd hasn't always been so generous with version information, and a glibc 2.0– or 2.1–based system may give results like these:
tony@dvorak:/home/tony$ ldd -v /bin/grep ldd: version 1.9.10 libc.so.6 => /lib/libc.so.6 (0x4000e000) /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
This will catch a glaring problem, such as a program linked against libc5 and the lack of libc5 libraries on the system, but it won't catch subtle glibc version issues. When you're uncertain, use the following rule, taken from the glibc 2.2 source package (and see the man pages for ldd, ld.so, and ldconfig):
The 2.2 release should be binary compatible with the 2.1 and earlier releases. All programs should continue to run. This does *not* mean that programs compiled on a system running glibc 2.2 will run on systems with only glibc 2.1. Compatibility is always in one direction. Systems with glibc 2.1 will not even attempt to run binaries generated with glibc 2.2 so there is not much to worry about.
When a developer builds the package foo, the package-creation tools note the libraries that the foo binary links against in the package dependencies. More often than not, developers are building their binaries on the latest versions of libraries because they're building packages for the upcoming release of their distribution. Hence, foo-2.0 may require libc-6.1 (also known as glibc-2.1), while your system is based on libc-6.0.
So you need foo-2.0. One option is to go find the source for the package on http://freshmeat.net/, download the tarball, and build it on your libc-6.0 system. If the program configures and compiles correctly, chances are it will run fine on your system. This does get you a working copy of foo-2.0 on your system, but it has a serious drawback. Because the package is not under the control of the package management system, you'll encounter the following problems:
-
It won't be taken into consideration the next time the other packages are upgraded. Thus, a future upgrade could break the package, either by replacing a library without foo's knowledge, or even replacing one of foo's files (oh please, don't let it be a configuration file!).
-
At some future date, you may want to use the package manager to generate a complete inventory of the software installed on your box. foo won't show up.
-
The package manager enforces a certain consistency across all packages, such as the location of startup and shutdown scripts, man pages, sample configuration files, and so on. The upstream (original) foo has no such limitations, and may or may not conform to the same filesystem standards as your distribution.