DæmonNews: News and views for the BSD community

October 2001 Get BSD New to BSD? Search BSD Submit News FAQ Contact Us Join Us
Search


Get BSD Stuff

Yet another approach to the laptop multi-home problem

Hellmuth Michaelis, <hm@hcs.de>

Introduction

For several years now, I've been using FreeBSD on my laptop at work. Since I am often at different customer sites on any given day, I must adjust my laptop settings according to their network, which means a new IP address, new name server, new default gateway and so on. Editing rc.conf, resolv.conf and friends by hand was tedious. I needed something that was easy to set up, use, develop and maintain.

My Requirements

After experimenting with shell scripts, it became clear that I needed a manageable method of entering parameters. When I arrive at a customer's site, they usually have a fairly urgent problem and expect me to start working immediately - not to take several minutes to connect to their LAN. Therefore, I wanted some form of menu-based interaction with a program that asks me to enter parameters, then does the rest for me. The key parameters to change are:

And, because you never know, I wanted to be able to specify the name of a site-specific shell script that should be executed to do any additional customization.

In addition, it should be possible to put all of the information in some form of a database. Once an entry was set up or chosen, the information should be persistent between reboots.

/etc/mobile

Well, I sat down one day and aggregated some curses menuing subroutines that I had used in earlier projects and wrote setnetparm using the curses library (for those who don't know, curses is a programming library for writing character-based, full-screen applications and is part of FreeBSD's base system) and some scripts needed to interface to the FreeBSD startup and boot system.

The data that setnetparm operates on, and most of the scripts, reside in a newly created directory, /etc/mobile.

laptop.menu

First, we need some form of database to store the information for the places that I regularly visit. Because I was in a hurry, I settled on a simple line-oriented, fixed-format text file database. It's not one of the brightest ideas I've had, but it has worked quite well until now. This database is called laptop.menu and contains all the setup information I need for a given site as well as a site name, which I can choose in a menu.

At the beginning of laptop.menu the user must set two parameters: (a) the time in seconds between the initial menu display to the continuation of the boot process with the currently configured values, (b) a list of the allowed network interface names.

The first line in each entry is a description or name of the entry. This line is displayed in the menu, and allows you to choose by this name a set of predefined values from the database.

laptop.curr

This file contains the current settings that are the result of the last run of setnetparm. It has the same format as one entry in the laptop.menu file. When the user has chosen an entry or entered new parameters, setnetparm saves this parameter set to the file laptop.curr where it is kept until the user decides to select a different set of parameters.

Setnetparms

Now it's time for a screen shot - it shows the setnetparm initial screen two seconds after it was invoked:

In the upper half of the window you see two blocks of information: the current values show the content of laptop.curr and the new values, which are empty because the user has not yet chosen anything else. The timeout counter counts down (currently 8 seconds left). In case the user does nothing at this point, setnetparm terminates in 8 seconds and the current values will be used to configure the laptop.

Any key press will stop the timeout, and then you are able to use the menu in the lower half of the screen. Items 1, 2 and 3 are special in that they allow you to exit setnetparm with the current values (item 1), or the new values (item 3), or to enter new values on the fly (item 2). Items 4 to 10 are symbolic names taken from the first line of each entry in the laptop.menu database file.

In case the user decides to enter new values on the fly, he chooses item 2: Enter new values. He is presented several editable entry fields to enter new values; this is how it looks:

New values have already been entered for the IP address, subnet mask, interface flags and host name, and the user is currently entering or editing the new value for the domain name. After the user has entered values for the remaining parameters, the main menu is displayed:

Usually one just presses Enter here to get the new values and leave the setnetparm program. The values displayed in the New Values column are saved in the laptop.curr file upon exit of setnetparm.

Integration into the FreeBSD boot mechanics

The main part of the FreeBSD boot process and initial machine configuration is done by the /etc/rcshell script, If you would like more information on this, have a look at the FreeBSD handbook and at the rc(8) manual page.

In order to call setnetparm at boot time, one must intercept the boot process, run setnetparm, change the machine configuration and then continue with the boot process. For this to work, /etc/rc has to be patched with a diff provided with the package.

/etc/rc

To understand how the patch to /etc/rc works, two additional files need to be mentioned: /etc/mobile/rc.special and /etc/mobile/rc.conf.mobile. The first one is a copy of the optional shell script, which can be executed to perform additional customization; the second one is used to help configure the host name, the default gateway and the ifconfig flags. The following code gets patched to /etc/rc to run at boot time right after the file systems are mounted and swap is configured:

1  if [ -r /etc/rc.mobile ]
2  then
3    # remove old rc.special if one exists
4    if [ -r /etc/mobile/rc.special ]; then
5    rm -f /etc/mobile/rc.special
6    fi
7    # remove old rc.conf.mobile if one exists
8    if [ -r /etc/mobile/rc.conf.mobile ]; then
9    rm -f /etc/mobile/rc.conf.mobile
10   fi
11   # execute rc.mobile
12   . /etc/rc.mobile
13   # execute new rc.conf.mobile if one exists
14   if [ -r /etc/mobile/rc.conf.mobile ]; then
15   . /etc/mobile/rc.conf.mobile
16   fi
17   # execute new rc.special if one exists
18   if [ -r /etc/mobile/rc.special ]; then
19   . /etc/mobile/rc.special
20   fi
21 fi

After a sanity check if /etc/rc.mobile (discussed below) does really exist, old copies of the just mentioned two files are deleted (lines 3-10). After that the script /etc/rc.mobile is executed (line 12) and if it produces new copies of /etc/mobile/rc.special and /etc/mobile/rc.conf.mobile they also get executed after that (lines 13-20). That's quite simple, isn't it?

/etc/rc.mobile

This is the shell script that gets called from the aforementioned patch to /etc/rc. It is somewhat more complicated and the real workhouse of the boot process integration of the setnetparms package:

1  MOBILE=/etc/mobile
2  DEFS=$MOBILE/laptop.curr
3  PROGPATH=/root/bin
4  RCCONF=$MOBILE/rc.conf.mobile
5  RCSPEC=$MOBILE/rc.special
6  # check for /etc/mobile/laptop.curr, if no available, create it
7  if [ ! -r $DEFS ]
8  then
9       echo "card"             >$DEFS
10      echo "192.168.168.168"  >>$DEFS
11      echo "255.255.255.0"    >>$DEFS
12      echo "link0 -link1"     >>$DEFS
13      echo "myname"           >>$DEFS
14      echo "mydomain.org"     >>$DEFS
15      echo "192.168.168.1"    >>$DEFS
16      echo "192.168.168.1"    >>$DEFS
17      echo "script.myname"    >>$DEFS
18 fi
19 # adjust permissions for /etc/mobile/laptop.curr
20 chown root:wheel $DEFS
21 chmod u=rw,og=r $DEFS
22 # prepare for running setnetparm
23 if [ -x $PROGPATH/setnetparm ]
24 then
25      # set TERM environment variable, curses needs it!
26      TERM=cons25
27      # check for pcvt (VT100/VT220 emulator). pcvt is runtime detectable!
28      if test -x /usr/sbin/ispcvt; then
29              if /usr/sbin/ispcvt; then
30                      TERM=pcvt25
31              fi
32      fi
33      export TERM
34      # init terminal
35 #    tput init
36      # run setnetparm
37      $PROGPATH/setnetparm
38      # process the resulting /etc/mobile/laptop.curr file
39      # first, suck the contents of /etc/mobile/laptop.curr into variables
40      index=1
41      exec 3<$DEFS
42      while read LINE 0<&3
43      do
44              case $index in
45                      1)      newiface=$LINE          ;;
46                      2)      newipaddr=$LINE         ;;
47                      3)      newsubnetmask=$LINE     ;;
48                      4)      newflags=$LINE          ;;
49                      5)      newhostname=$LINE       ;;
50                      6)      newdomainname=$LINE     ;;
51                      7)      newgwaddr=$LINE         ;;
52                      8)      newnameserver=$LINE     ;;
53                      9)      newscript=$LINE         ;;
54              esac
55              index=`expr $index + 1`
56      done
57      if [ $newipaddr = "DHCP" ]
58      then
59              # second, patch /etc/rc.conf and /etc/resolv.conf using the variables
60              exec 3<&-
61              cat $MOBILE/rc.conf.t | /usr/bin/sed \
62               -e "s/IFNAMETEMPLATE/\"$newiface\"/g" \
63               -e "s/IFNQNAMETEMPLATE/$newiface/g" \
64               -e "s/HOSTTEMPLATE/\"$newhostname.$newdomainname\"/g" \
65               -e "s/DEFGWTEMPLATE/\"$newgwaddr\"/g" \
66               -e "s/IFTEMPLATE/\"$newipaddr\"/g" \
67               > $RCCONF
68      else
69              # second, patch /etc/rc.conf and /etc/resolv.conf using the variables
70              exec 3<&-
71              cat $MOBILE/rc.conf.t | /usr/bin/sed \
72               -e "s/IFNAMETEMPLATE/\"$newiface\"/g" \
73               -e "s/IFNQNAMETEMPLATE/$newiface/g" \
74               -e "s/HOSTTEMPLATE/\"$newhostname.$newdomainname\"/g" \
75               -e "s/DEFGWTEMPLATE/\"$newgwaddr\"/g" \
76               -e "s/IFTEMPLATE/\"$newipaddr netmask $newsubnetmask $newflags\"/g" \
77               > $RCCONF
78              cat $MOBILE/resolv.conf.t | /usr/bin/sed \
79               -e "s/DOMAINTEMPLATE/$newdomainname/g" \
80               -e "s/NAMESERVERTEMPLATE/$newnameserver/g" \
81               > /etc/resolv.conf
82      fi
83      # cp special configuration script into place to get it executed
84      if [ -r $MOBILE/$newscript ]
85      then
86              cp $MOBILE/$newscript $RCSPEC
87      fi
88 fi

line description
1-5 assign values to some variables
7-18 in case /etc/mobile/laptop.curr does not (yet) exist, create the file and assign default values to it
20-21 set permissions for /etc/mobile/laptop.curr
23 check for the existence of the setnetparm program and terminate if it is not found
26-35 find out which console driver runs on the console and set up the environment variable TERM accordingly. Curses programs need this variable to run.
37 run the setnetparm program
40-56 read in /etc/mobile/laptop.curr and assign its contents to variables
60-67 in case user selected to use DHCP, use the variables from the previous step to set up a new /etc/mobile/rc.conf.mobile
70-81 for non-DHCP usage, use the variables from the previous step to setup a new /etc/mobile/rc.conf.mobile and a new /etc/resolv.conf
84-87 if there is a shell script available for further system customization, execute it.

Miscellaneous

Another thing to mention is that you must apply another patch to /etc/pccard_ether to make PCMCIA cards work together with the setnetparms package.

All the aforementioned programs and some optional scripts and examples are part of the setnetparms package. I released the first version in 1997 - since then it has seen many FreeBSD versions and quite some laptops. It started as a hack (and you see it all over the code), but it just suits my needs. The current version of setnetparms is 3.41, and you can download it from http://www.freebsd-support.de/misc/.

Have fun!




Author maintains all copyrights on this article.
Images and layout Copyright © 1998-2004 Dæmon News. All Rights Reserved.