Handling multiple versions of the same application with update-alternatives

In a previous article I shown how to rollback a package installation, just in case we get a buggy application after an update and want to go back to the previous version. This time I want to show how two or more versions of the same application can be installed without conflicts, and with the help of the small CLI application update-alternatives, how easy it is to switch from a version to another.

This application is usually installed by default and some packages already take advantage of that solution to provide for example:

  • different versions of the Ruby and Python interpreters and the related packages;
  • to handle different vendors of the Java Development Kit;
  • to switch between the classical gVim or the vim-nox11 version with no GUI.

All of that in a transparent manner for the end-user.

How it works

The tool itself is simple to use, but under the hood applies several steps to arrange the files.

In a few words update-alternatives keeps tracks of the available alternatives for every group of applications, identified with a name, within a file located under the folder /var/lib/alternatives/ having the same name.

The selected alternative will be linked, from time after time, under the /etc/alternatives/ folder. It in turn will be linked under one of the dedicated paths, like /usr/bin (or /usr/local/bin) for executables or /usr/man (or /usr/local/man) for man pages, to make them available to everyone in the user space.

Keep in mind that alternatives can be libraries, executables, man pages, or a mix of them for a given group. Installing multiple components at once is possible making them figure as slaves of the main file during the installation.

A real example

As I said this feature is usually well implemented by RPM packagers, but can also be useful to understand how to use it manually, in all those cases we have precompiled tar archives or we compile an application directly from source files.

I am going to try update-alternatives against the Crystal language compiler, picking up a couple of precompiled tar.gz archives under the Github releases page.

For completeness, I should mention that whoever is interested to use regularly this language, should consider to add the proper RPM repository, following the info at the openSUSE install page.

Arranging the folders

First of all, we download two different versions for the right architecture, let’s say the archives 0.35.1 and 0.36.0, and extract them.

The path dedicated to precompiled applications from tar.gz archives is /opt (switch -C), accessible only as privileged user:

$ sudo tar -xvf crystal-0.35.1-1-linux-x86_64.tar.gz -C /opt
$ sudo tar -xvf crystal-0.36.0-1-linux-x86_64.tar.gz -C /opt

The archive itself already contains a subfolder, so all the files will be properly stored respectively under the /opt/crystal-0.35.1-1 and /opt/crystal-0.36.0-1 paths, otherwise would be better to append a further subfolder to the tar command option.

At this point, after launching the crystal command, we get a Command not found error:

$ crystal

Although our system can tell us where to find the executables:

$ whereis crystal

To fix that situation update-alternatives come to the rescue.

Install the alternatives

Alternatives are grouped under a name, it works as identification and must be unique for each group, it won’t affect the name of the linked file, but it’s a good idea to keep it easy enough to remind the application itself, usually the filename is a good way to start.

So let’s check if crystal name is available:

$ sudo update-alternatives --get-selections | grep crystal

Now it’s time to install our alternatives using the switch --install for each alternative, let’s show first the simpler version:

$ sudo update-alternatives --install /usr/local/bin/crystal crystal /opt/crystal-0.35.1-1/bin/crystal 90

$ sudo update-alternatives --install /usr/local/bin/crystal crystal /opt/crystal-0.36.0-1/bin/crystal 90

The parameters are pretty easy to understand:

  1. the folder /usr/local/bin is one of the aforementioned paths where the file will be linked once the group is selected, and make it available to every user;
  2. the name is the id discussed above;
  3. the full path is where the file to link is actually stored;
  4. a number providing the priority level.

Probably you noticed that the priority level assigned was the same for both versions. This number is used for automatic selection when, for example, one of the alternatives is uninstalled and a new one must be selected. In our case, being all this process manual and not depending on any automatic update process, hasn’t much sense assign different preferences, although we will see how the system will put it in auto mode after the first install.

Being also man pages available in the same archive we might want to install them too, in that case we need define one or more slave components using the --slave switch:

$ sudo update-alternatives --install /usr/local/bin/crystal crystal /opt/crystal-0.35.1-1/bin/crystal 90 \
  --slave /usr/local/man/man1/crystal.1.gz crystal.1.gz /opt/crystal-0.35.1-1/share/man/man1/crystal.1.gz \
  --slave /usr/local/man/man1/shards.1.gz shards.1.gz /opt/crystal-0.35.1-1/share/man/man1/shards.1.gz \
  --slave /usr/local/man/man5/shard.5.gz shard.5.gz /opt/crystal-0.35.1-1/share/man/man5/shard.yml.5.gz

$ sudo update-alternatives --install /usr/local/bin/crystal crystal /opt/crystal-0.36.0-1/bin/crystal 90 \
  --slave /usr/local/man/man1/crystal.1.gz crystal.1.gz /opt/crystal-0.36.0-1/share/man/man1/crystal.1.gz \
  --slave /usr/local/man/man1/shards.1.gz shards.1.gz /opt/crystal-0.36.0-1/share/man/man1/shards.1.gz \
  --slave /usr/local/man/man5/shard.5.gz shard.5.gz /opt/crystal-0.36.0-1/share/man/man5/shard.yml.5.gz

The required parameters to create a slave component are quite easy to understand because identical to the previous ones:

  1. the path where build the link;
  2. the name which remembers the linked file;
  3. the full path of the filename to link.

The quick test below will prove whether or not everything went good:

$ crystal -v

$ man crystal

Display and Config

A resume about the new configurations added is available using the --display switch followed by the name:

$ sudo update-alternatives --display crystal

To enable a different version we can proceed in interactive mode using the --config switch:

$ sudo update-alternatives --config crystal

Entering the related number we set the new version as default.

Otherwise, there is the not interactive mode which is useful to use inside our scripts:

$ sudo update-alternatives --set crystal /opt/crystal-0.36.0-1/bin/crystal

The params to provide are:

  1. the name of our group;
  2. the path of the main file we provided before.

As the image shows, the configuration switched from auto to manual mode and all the paths are correctly aligned to our choice.

Another way to configure our alternatives make use of the yast2-alternatives package.

Like all the Yast modules it is ridiculous simply to understand just looking at its interface.

Remove alternatives

The day we decide to remove one or more alternative in the list, all we have to do is invoke the --remove switch:

sudo update-alternatives --remove crystal /opt/crystal-0.35.1-1/bin/crystal

The required options are pretty clear at this point:

  1. the name of our group of alternatives;
  2. the path to remove from the list.

Obviously, it will delete also the slave components.

To remove all the group instead, we can make use of the switch --remove-all:

$ sudo update-alternatives --remove-all crystal

It won’t delete the folders installed but only the internal reference, anyway like all the destructive commands use it carefully, to don’t mess up the whole setup!

Final thoughts

As seen this command is quite easy to understand and apply it to manage multiple versions is very smooth. In the past, I used it to provide a different version of the pdftk shell script that launches the Java application, the one coming from the original package had an annoying bug recently corrected.

There are also other options available, a full list can be obtained reading the man page:

$ man update-alternatives

while a good presentation of it is available at the SUSE documentation.

Definitively this is one of those system tools interesting to learn and use to keep our system in order and at the same time allowing us to experience different versions of our favorite software compilers, libraries or applications.


Creative Commons License This work is licensed under a Creative Commons Attribution-NoCommercial-ShareAlike 4.0 International License


Leave a Comment