I do Love eselect!
For those who don’t know eselect is a ‘modular administration and configuration framework’. In simpler words it is a management tool shipped with Gentoo, able to switch between packages versions and configurations files, working with symbolic links and environmental variables.
Just to better clarify eselect job, it is able to make you switch between Java VM versions, Python releases , Gcc compilers and it makes it possible to choose your favorite editor.
Eselect is a modular tool, meaning that you just need to create a simple .eselect file to make it run your own commands. What I’m going to explain below is how to create a simple module to manage a symbolic link.
Aim of the tutorial
What I want to achieve in this little tutorial is to create a simple switch to quickly change my /etc/resolv.conf file. This file tells to any network interface where to look for DNS resolving.
As during last days I had problems with DNS, I was forced to switch very often from my default domain resolver (this case an internal server) to an external one ( OpenDns ). Using eselect for this purpose really simplified my life!
Preparation : create the ‘alternatives’
As we want to switch between two different configurations we need to create those alternatives. Open a terminal, cd to /etc and create the files. I created one called resolv.orig and one called resolv.opends, to be used when local DNS resolver is down.
# Going to /etc directory cd /etc # Copying original resolv.conf # to the first alternative cp resolv.conf resolv.orig # Creating new resolv.conf alternative # using OpenDNS IPs echo -e "nameserver 208.67.222.222\nnameserver 208.67.220.220" > resolv.prova
Now we have to delete the original file and replace it with a symlink, obviously to the .orig file
# Deleting physical file rm resolv.conf # Creating symlink ln -s resolv.orig resolv.conf
We are done with pre-configuration, let’s proceed with eselect module creation
Create the module
For a complete developer guide to eselect module creation, please refer to Gentoo Wiki official guide . This is just a ‘for examples’ guide for a quick start.
First of all all eselect modules should be located in your /usr/share/eselect/modules directory, so go there and create a new .eselect file
# Going into eselect modules directory cd /usr/share/eselect/modules # Creating a new module file touch resolv.eselect
Now use your favorite editor to edit it. In this example I’m using vim
# Opening file with vim vim resolv.eselect
Head section
As always the head section of the file contains meta informations about the module and the author, will just quickly review it below
# -*-eselect-*- vim: ft=eselect
# Copyright 1999-2009 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
# $Id: $
# This will appear when listing the available modules
DESCRIPTION="Manage the /etc/resolv.conf symlink"
# Maintainer email
MAINTAINER="personal@andreaolivato.net"
# Date, used to create a version
SVN_DATE='$Date: 2009-10-23 12:00:07 +0200 (Fri, 23 Oct 2009) $'
# Creating version from date
VERSION=$(svn_date_to_version "${SVN_DATE}")
Find the alternatives
The first important function we need to create is the one taking care of listing all the possible alternatives that we have for our symlink. This means that you have to list all the files which can be linked to resolv.conf . In our case, there are only two : resolv.orig and resolv.opendns.
The easiest procedure would be to write those files directly, but as we want to learn how to deal with eselect properly let’s create a bash function to list those two file. This way if in the future we would like to create a new alternative, we don’t need to modify our code.
In the below function I just listed (using ls command) all the files starting with resolv. in the /etc folder and then excludes (using grep -v) the original file, which is resolv.conf. This way we can add more alternative by creating resolv.xxx files. Here’s the code
find_targets() {
local p
for p in $(ls /etc/resolv.* | grep -v conf )
do
echo $p
done;
}
Create and remove the symlink
Proceeding with our code, we now need to be able to remove and create the symlink.
To remove it, we just need to use the rm command like this
remove_symlink() {
rm "/etc/resolv.conf"
}
To create the symlink, we need to deal with the user choice. This choice is represented by a number, identifying the file to switch the link to. This means that we need to associate a parameter passed to the function with the position of the choice in our file list.
set_symlink() {
# Get the parameter
local target=${1}
# Check it is a number
if is_number "${target}" ; then
# Get the list of the files via the previously created function
local targets=( $(find_targets) )
# Get the file associated with the parameter passed
target=${targets[target - 1]}
fi
# If the result file does not exist
if [[ -z "$target" ]] ; then
# We output an error
die -q "Target "${1}" doesn't appear to be valid!"
# While if it exists
else
# We create the link
ln -s "${target}" "/etc/resolv.conf"
fi
}
Put the things together
Now we got a function to list, one to remove and one to insert. We need to put them together and switch between them depending on the user choices. The following function receives the parameter from the user, checks it, remove the current link if it exists and the call the create function forwarding the parameter.
do_set() {
if [[ -z ${1} ]] ; then
# If no parameter was passed
die -q "You didn't tell me what to set the symlink to"
elif [[ -L /etc/resolv.conf ]] ; then
# If the links exists try to remove it
if ! remove_symlink ; then
# If can't remove it output error
die -q "Couldn't remove existing symlink"
# Try to create
elif ! set_symlink "${1}" ; then
# If can't create, output error
die -q "Couldn't set a new symlink"
fi
# If the link exists but is not a link but a physical file
elif [[ -e /etc/resolv.conf ]] ; then
# We have something strange, output error
die -q "/etc/resolv.conf exists but is not a symlink"
else
# Try to create
set_symlink "${1}" || die -q "Couldn't set a new symlink"
fi
}
The complete file
Remaining needed functions are mostly descriptive and I’m not going to review them in detail. Below is the complete code I used for the module.
# -*-eselect-*- vim: ft=eselect
# Copyright 1999-2009 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
# $Id: $
DESCRIPTION="Manage the /etc/resolv.conf symlink"
MAINTAINER="personal@andreaolivato.net"
SVN_DATE='$Date: 2009-09-20 22:26:07 +0200 (Sun, 20 Sep 2009) $'
VERSION=$(svn_date_to_version "${SVN_DATE}")
find_targets() {
local p
for p in $(ls /etc/resolv.* | grep -v conf )
do
echo $p
done;
}
remove_symlink() {
rm "/etc/resolv.conf"
}
set_symlink() {
local target=${1}
if is_number "${target}" ; then
local targets=( $(find_targets) )
target=${targets[target - 1]}
fi
if [[ -z "$target" ]] ; then
die -q "Target \"${1}\" doesn't appear to be valid!"
else
ln -s "${target}" "/etc/resolv.conf"
fi
}
describe_show() {
echo "Show the current resolv.conf symlink"
}
do_show() {
write_list_start "Current resolv.conf symlink:"
if [[ -L /etc/resolv.conf ]] ; then
local resolv=$(canonicalise "/etc/resolv.conf")
write_kv_list_entry "${resolv%/}" ""
else
write_kv_list_entry "(unset)" ""
fi
}
describe_list() {
echo "List available resolv.conf symlink targets"
}
do_list() {
local i targets=( $(find_targets) )
write_list_start "Available resolv.conf symlink targets:"
for (( i = 0; i < ${#targets[@]}; i++ )) ; do
[[ ${targets[i]} = \
$(basename "$(canonicalise "/etc/resolv.conf")") ]] \
&& targets[i]=$(highlight_marker "${targets[i]}")
done
write_numbered_list -m "(none found)" "${targets[@]}"
}
describe_set() {
echo "Set a new resolv.conf symlink target"
}
describe_set_parameters() {
echo ""
}
describe_set_options() {
echo "target : Target name or number (from 'list' action)"
}
do_set() {
if [[ -z ${1} ]] ; then
die -q "You didn't tell me what to set the symlink to"
elif [[ -L /etc/resolv.conf ]] ; then
if ! remove_symlink ; then
die -q "Couldn't remove existing symlink"
elif ! set_symlink "${1}" ; then
die -q "Couldn't set a new symlink"
fi
elif [[ -e /etc/resolv.conf ]] ; then
die -q "/etc/resolv.conf exists but is not a symlink"
else
set_symlink "${1}" || die -q "Couldn't set a new symlink"
fi
}
Usage
After you saved the file, you can start using the new module.
To list available files do
eselect resolv list
To set the original one
eselect resolv set 1
To set the OpenDNS one
eselect resolv set 2
To show the current choice
eselect resolv show

Pingback: Andrea Olivato (andreaolivato) 's status on Friday, 23-Oct-09 12:46:16 UTC - Identi.ca