coLinux

Dual-Boot challenges[]

Other pages on the Wiki, e.g. Converting Distributions, and external sources (such as this support page) describe how to switch from running Linux natively using a partition (or several partitions) to running coLinux using these partitions. But what if we wish to do both?

There are several significant differences between what we have to do when we boot our Linux in stand-alone mode and in coLinux mode:

  • The devices for the 'Linux-related' filesystems change (e.g. instead of `/dev/sda1` we may have `/dev/cobd0`)
  • Instead of being able to mount the 'Windows-related' partitions directly, as vfat or msdos or ntfs type partitions, we must mount them either using Samba or as COFS devices).
  • The coLinux system is to be considered as a different machine than the Windows host, e.g. it needs a distinct hostname and IP address (because if you boot Linux directly you usually use the same hostnmane and IP address as in Windows)
  • Some additional network-related settings may differ, depending on whether you're using a bridged or a NATed connection
  • You may want your X display manager (e.g. xdm, gdm or kdm) to act differently - when booting natively it would run an X server on the Linux machine, and when booting in coLinux mode, it might, say, listen for XDMCP queries. Or you may want an X display manager + X server when booting natively but a VNC X Server when running in coLinux mode, so that you may connect to it using a VNC client.


Making these configuration changes is, again, covered by other pages; the question is how, when our Linux is booting, do we determine whether we're running in coLinux or natively, and how do we automatically and dynamically modify the configuration files so that the rest of the boot process will reflect our required choice in each of the abovementioned issues.

Three possible solution schemes seem possible:

  • Setting symlinks, i.e. something like the Debian 'alternatives' system
  • Same as the above, but overwriting configuration files with relevant versions instead of symlinking
  • Parsing proto-configuration files, and dynamically generating the configuration files from them.

All of these are to be applied using the init script/s which run right after the basic boot process is complete and before entering the initial runlevel.

Alternatives[]

Here is how Debian defines its alternatives system:

> It  is  possible  for  several  programs fulfilling the same or similar
> functions to be installed on a single system at  the  same  time.   For
> example,  many  systems  have  several  text editors installed at once.
> This gives choice to the users of a system, allowing each to use a dif-
> ferent editor, if desired, but makes it difficult for a program to make
> a good choice of editor to invoke if the user has not specified a  par-
> ticular preference.
>
> Debian's  alternatives  system  aims  to solve this problem.  A generic
> name in the filesystem is shared by all files providing interchangeable
> functionality.   The  alternatives  system and the system administrator
> together determine which actual file  is  referenced  by  this  generic
> name.   For  example,  if  the  text  editors ed(1) and nvi(1) are both
> installed on the system, the alternatives system will cause the generic
> name  /usr/bin/editor  to refer to /usr/bin/nvi by default.  The system
> administrator can override this and cause it to  refer  to  /usr/bin/ed
> instead,  and the alternatives system will not alter this setting until
> explicitly requested to do so.
>
> The generic name is not a direct symbolic link to the selected alterna-
> tive.   Instead,  it  is  a symbolic link to a name in the alternatives
> directory, which in turn is a symbolic link to the actual  file  refer-
> enced.   This is done so that the system administrator's changes can be
> confined within the /etc directory: the FHS (q.v.)  gives  reasons  why
> this is a Good Thing.

This solution is not very robust, since if you upgrade your system in some way, you will have to manually update both versions of your configuration files (and possibly propagate some changes which were made automatically to your current configuration files).

The symlinks option has the added 'uncertainty factor' of relying on symlinks in general, and the possibility that some process or user will change the 'active' version of the configuration file, permanently changing one of the two 'master' versions.

Overwriting Scripts[]

Here is a sample startup script which overrides some configuration files with one of the two relevant versions. It is a Debian script; in Debian, have it run by linking to it from `/etc/rcS.d/` , and use a priority such that it runs after the root fs becomes writable, but before the rest of the local filesystems are mounted. With other distributions, you may need to make some adaptations to it.

/etc/init.d/colinux[]

#
# colinux       use either a set of coLinux-related configuration
#               files, or their direct-boot counterparts
#
#

if [[ $COLINUX ]]; then
SUFFIX=colinux
else
SUFFIX=non-colinux
fi

for conf_file in \
"/etc/fstab" \
"/etc/network/interfaces" \
"/etc/X11/gdm/gdm.conf" \
"/etc/hostname" \
"/etc/mailname" \
; do
cp -f $conf_file-$SUFFIX $conf_file
done

# the COLINUX environment variable is not inherited much further
# than the init scripts, so if you want user processes to be able
# to tell whether we're in coLinux or stand-alone mode, you could
# do something like

# echo $COLINUX > /var/local/colinux

# or have the file's existence be the thing to check:

#if [[ $COLINUX ]]; then
#touch /var/local/colinux
#else
#rm -f /var/local/colinux
#fi


: exit 0

Obviously, you're asking yourself who sets the `COLINUX` environment variable. Well, you do! In your coLinux configuration file, have a bootprompt line such as the following:

<bootparams>root=/dev/cobd0 COLINUX=1</bootparams>

(you may have other parameters, you just need to set the `COLINUX` value).

As for the settings files themselves - these will differ based on exactly what you're configuring, as well as difference between distributions.

Dynamic Parser/Generator Scripts[]

Using scripts to dynamically to generate configuration files has both potential pros and cons.

Cons:

  • If the structure of the file to be modified changes drastically, the parser script may fail
  • (Static) changes made during a system upgrade may conflict with the 'old' changes the scripts are trying perform dynamically
  • The parser/generator scripts may very well become quite lengthy, complex and difficult to read through

Pros:

  • The changes could be punctual, limited only to what differ when running stand-alone from when running from colinux.

We could also use a simple script that just calls 'patch' to apply some diffs, depending on the current running mode. But I think this would be less flexible (though simpler to do).

Implementing #IF, #ELSE, #ENDIF[]

The majority of the Unix-like configuration files use the pound symbol, #, to mark a line as a comment. This allows us to implement a very simple pre-processor to convert configuration files between coLinux and normal Linux kernels. The following examples show how such a pre-processor can be applied to /etc/fstab:

LABEL=/       /          ext3    defaults        1 1
/dev/hda8     swap       swap    defaults        0 0
#IF COLINUX
0             /mnt/d     cofs    defaults        0 0
#ELSE
#&@/dev/hda5  /mnt/d     ntfs    ro,umask=0227,gid=disk 0 0
#ENDIF
none          /dev/pts   devpts  gid=5,mode=620  0 0
none          /dev/shm   tmpfs   defaults        0 0
none          /proc      proc    defaults        0 0
none          /sys       sysfs   defaults        0 0

When pre-processed:

  • normal lines will not be modified,
  • lines in the disabled section are marked with the magic comment sequence #&@,
  • lines in the enabled section that start with #&@ have these characters removed.

The magic comment sequence is an improbable sequence of characters to allow you to continue to use the normal comment character within the pre-processor sections should you need to disable lines or add real comments.

A simple pre-processor can be implemented in python as follows:

#!/usr/bin/python

#
# Mini-language to enable and disable colinux adaptations
#

import sys, getopt

# named constants
COMMENT_STATE = 0
UNCOMMENT_STATE = 1
UNTOUCHED_STATE = 2

def help_issue(full = False):
        print "Usage: colinux.py [OPTIONS] <filename> ..."
        if False != full:
                print """
Options:
  -h, --help          Print help information, then exit.
  -c, --colinux       Switch into coLinux mode (default is to switch off coLinux)
"""
        else:
                print "(try --help)"

def parse_args(args):
        # set up the defaults defaults
        class struct: pass
        opts = struct()
        opts.colinux = False

        try:
                flags, args = getopt.gnu_getopt(sys.argv[1:], "hc", ["help", "colinux"])
        except:
                help_issue()
                return None

        for f, a in flags:
                if f in ("-h", "--help"):
                        help_issue(True)
                        return None
                elif f in ("-c", "--colinux"):
                        opts.colinux = True

        return opts, args

def select_and_print(l, a, b, c):
        print c
        if l:
                return a
        else:
                return b

def main():
        try:
                opts, args = parse_args(sys.argv)
        except TypeError:
                return 1


        state = UNTOUCHED_STATE

        for i in sys.stdin:
                i = i.strip('\r\n')

                if i.startswith('#IF COLINUX'):
                        state = select_and_print(opts.colinux, UNCOMMENT_STATE, COMMENT_STATE, i)
                elif i.startswith('#IF LINUX'):
                        state = select_and_print(opts.colinux, COMMENT_STATE, UNCOMMENT_STATE, i)
                elif i.startswith('#ELSE'):
                        assert(state != UNTOUCHED_STATE)
                        state = select_and_print(state == COMMENT_STATE, UNCOMMENT_STATE, COMMENT_STATE, i)
                elif i.startswith('#ENDIF'):
                        assert(state != UNTOUCHED_STATE)
                        state = select_and_print(True, UNTOUCHED_STATE, 0, i)
                elif state == COMMENT_STATE:
                        if i.startswith('#&@'):
                                print i
                        else:
                                print '%s%s' % ('#&@', i)
                elif state == UNCOMMENT_STATE:
                        if i.startswith('#&@'):
                                print i[3:]
                        else:
                                print i
                else:
                        print i

        return 0

if <u>name</u> == "<u>main</u>":
        try:
                sys.exit(main())
        except KeyboardInterrupt:
                sys.exit(5)

The following shell script can be used to run the pre-processor against whatever files need to be modified:

#!/bin/sh

COLINUX_PY=/root/colinux.py

# find out which kernel we are running on
if uname -r | grep -qe "-co-"
then
        COLINUX_PY_ARGS=-c
else
        COLINUX_PY_ARGS=
fi

for i in \
        /etc/modprobe.conf \
        /etc/fstab \
        /etc/sysconfig/network-scripts/ifcfg-eth1
do
        cp $i $i.bak
        python $COLINUX_PY $COLINUX_PY_ARGS < $i.bak > $i
done

Implementing Patch System[]

How to apply the patch scenario. Create two copies of fstab, one fstab.normal, and one fstab.colinux. Then create two diff files for them. One to patch fstab.normal so as to be colinux mode, and the same for fstab.colinux, so that it changes to normal

<Brillgene:> Updating patch system to allow multiple boots into the same kernel (colinux/normal). Uses Unified patches as well as Error Redirection to make output clean. This will soon be available as a set of init scripts. Watch this space!

# diff -u fstab.normal fstab.colinux > colinux-fstab.patch

Now we have the patch file. We now create a script to patch fstab depending on the kernel running.

#!/bin/sh

# find out which kernel we are running on, do the patch
# we put the last working directory into a variable so we can cd back into it after
OLDPWD=`pwd`
cd /etc
if uname -r | grep -qe "-co"
then
        # Patch to colinux fstab
        patch fstab -s -f -r /tmp/patch.reject < colinux-fstab.patch > /dev/null
else
        # Reverse patch to normal fstab
        patch fstab -s -f -R -r /tmp/patch.reject < colinux-fstab.patch > /dev/null
fi
# lets cd back into the last working directory
cd $OLDPWD

Put this script in your init.d folder, I called it fix.coLinux.fstab. In your boot script (Under SuSE 9.3, it's /etc/init.d/boot, it may be the same for you). I placed a line to call the script right after the console/splash initialization. The rootfs must be mounted read write at booting time. This method works for me.

Skipping some scripts under coLinux boot[]

You may also need to do the same thing for boot.clock, hwscan, kudzu, powerd and other power management daemons as well as any type of hardware scanning. This is needed only if coLinux crashes on startup, otherwise you may have some fun with coLinux with very little modification. Edit non-working scripts and add these lines in the top of script replacing sample with the service name:

#!/bin/sh
#
# One of the file /etc/init.d/sample
#
# +++ added to skiping these script under coLinux boot.
if uname -r | grep -qe "-co-"
then
        echo "sample skipped (Colinux)"
        exit 0
fi
#
# +++ continue with the default code of the script
...

See also[]


MassTranslated on 25 Dec 2004.

Manually Adjusted on 25 Dec 2004. - had to remove some anchor and links.

Major rewrite by Eyal Rozenberg 04 Apr 2005 ; comments are welcome: eyalroz@technion.ac.il


MassTranslated on Sun Apr 23 17:36:03 UTC 2006