Tag Archives: Linux

TT: Uni-directional file synchronization between different hosts with Unison

When you work with at least two computers on the same project on a daily basis you might have a problem. You need to get changed files from host A to host B and vice versa. The problem getting bigger when you work in addition on different operation systems or use more than two hosts. On UNIX/Linux the preferred tool for such a task is Rsync. Unfortunately Rsync synchronize only in one direction, it doesn’t work very well when more than two hosts are involved (and it isn’t really comfortable to set up on Windows) and can’t use a secure communication channel. Another approach is to check-in changed source files into a version control system, like CVS. On host A you check it in and on host B you check it out afterwards. But this means you always need a more or less stable variant of your code, so that other developer can, at least compile, or much better use it. That is not always the case (especially when you leave the office at 11:00 p.m.) and it also doesn’t cover files which aren’t handled by a version control system. Luckily there is a solution for all the problems mentioned which is called Unison. So here comes the second post in the ToolTips series, which covers an easy and portable way for file synchronization.

Installing Unison

Most modern Linux distributions include Unison in their package manage system. On Mac OS X you can use MacPorts. Alternatively you could download a binary version for Mac OS X or Windows here. To prevent surprises and unnecessary trouble it might be a good idea to make sure that every involved system use the same version of Unison. At least on Linux and Mac OS X it is relatively easy to compile Unison from the sources.

Setting up public/private key authentication for ssh

One advantage of Unison over Rsync is that you can use different communication channels for the file transfers. One is ssh. As I always prefer/demand encrypted communication this is a big plus of course. In the default setup you can just use ssh. But for a little bit more comfort I suggest to create a public/private key pair for the authentication.
The following creates public/private keys without a password. Although this is much more easier to use, it should be only used on hosts which are trusted. If you are in doubt, use the normal password approach or even better create a public/private key pair with a password. Create a new public/private key pair with the following command:

user@host-a ~ $ ssh-keygen -t rsa

When you are asked for a password just hit Enter twice. The command creates the private key in ~/.ssh/id_rsa and the public key in ~/.ssh/id_rsa.pub. Now copy the public key to host B:

user@host-a ~ $ scp ~/.ssh/id_rsa.pub host_B:.ssh/authorized_keys

If you already have some public keys on host B, make sure you append the new key and not overwrite the file by the above command. Make the file accessible by the user only with:

user@host-b ~ $ chmod 600 ~/.ssh/authorized_keys

Now you should be able to connect to host B without any interaction needed.

Configuring Unison

Like in the long UNIX tradition, Unison is configured using text files. The files are located in the ~/.unison directory. You can configure more than one synchronization target by choosing a meaningful name. There exists one default target which is configured in the file default.prf. Because I have more than one target I prefer to split the configuration into several files. You can include other project files with the include statement as shown here:

# directory on host a (this is where Unison will be executed)
root       = /mnt/data/projects
# directory on host b (this is the remote host)
root       = ssh://host-b//mnt/data/projects
# which directories to sync?
include projects_files.prf
# options
include options.prf
ignorecase = false
# unison executable on the server
servercmd  = /usr/local/bin/unison

We setup the root directories on both machines, including the configuration file for the project target and some generic option file. We also overwriting the default unison location, because this is a self compiled version. The file options.prf looks like this:

# No staled nfs and mac store files
ignore  = Name .nfs*
ignore  = Name .DS_Store
# options
log     = true
rsrc    = true
auto    = true
#debug   = verbose
#logfile = ~/.unison/unison.log

This just set some generic options which are valid for all my targets. For the specific target projects the file projects_files.prf contains mainly the directories and files which should be ignored:

# No ISOs
ignore    = Path vms/ISO
# Ignore VBox branches
ignore    = Path vbox-*
# No binary output from the other platforms
ignore    = Path vbox*/out/*
# One exception:
ignorenot = Path vbox/out/linux.amd64.additions
# No wine stuff
ignore    = Path vbox*/wine.*
# Tools
ignore    = Name vbox*/tools/{FetchDir,freebsd*,os2*}

So in general, you configure the directory to synchronize and later define directories or files which should be ignored. As you see, you can include or exclude paths as you like. Even simple bash wildcards are possible. As shown in this example I exclude all binary files of a VirtualBox build, because they are useless on another platform. Understanding how Unison decide which directories or files should be synchronized is sometimes difficult. So I suggest to carefully read the documentation and just use the “try and failure” approach ;). Another reason for splitting up the configuration files is you can synchronize these files as well. I have another target which synchronize several configuration files, e.g. .bashrc, .profile, .vim* and the sub-project files of Unison like the projects_files.prf. You can’t synchronize e.g. default.prf, cause the root directories are different from host to host, but the general configuration is always the same. My home target looks like this:

# Which directories/files to sync?
path = .bashrc
path = .ion3
path = .gdbinit
path = .cgdb
path = .valgrind-vbox.supp
path = .vim
path = .vimrc
path = .gvimrc
path = .Xdefaults
path = .gnupg
path = .unison/options.prf
path = .unison/home_files.prf
path = .unison/projects_files.prf
# Do not sync:
ignore = Path .vim/.netrwhist
ignore = Path .ion3/default-session*
ignore = Path .cgdb/readline_history.txt

One of the strengths over other synchronization tools is, you can do this for others host as well. So if you synchronize between host a and host b you can also synchronize between host c and host b. However, a little bit of discipline is necessary. There should be one host which all other host synchronize again.
If you now execute unison the project target will be used. If you execute unison home the files of the home target will be synchronized.

Conclusion

Unison is a very powerful tool. You can synchronize between more than two hosts (OS independent), in a secure way and uni-directional. Currently there is no better tool and I use it on a daily basis.

FRITZ!Box tuning part 4: Cross-building and installing additional applications

The articles I wrote about the FRITZ!Box are pretty popular. They are creating the most traffic on my website. I understand this, cause the FRITZ!Box is a really great piece of hardware and AVM is also a company which knows how to make their users happy by serving regular updates to the firmware. Although I didn’t tuned my FRITZ!Box any further, I updated it with the latest Labor firmware version regularly. At some point the sshd setup (with dropbear) doesn’t worked anymore and I decided it is the time to update my software as well. Beside that it didn’t work anymore it is always a good idea to update software which allow access to a host from everywhere very regularly. Anyway, it turned out this isn’t as simple as I initial thought. Therefore here is the next post in the FRITZ!Box tuning series, which shows how to cross-build software for the MIPS32 architecture used in the FRITZ!Box and in particular get the sshd software to life again. I use a FRITZ!Box Fon WLAN 7270 v2 and the firmware is 54.05.05. Please make sure you read the other FRITZ!Box articles as well, cause some of the information given there still applies.

As already mentioned in the other articles, there is no warranty that this will work on your side and I am not responsible for any damage which may happen. You have been warned!

Cross-building for the MIPS32 architecture

The FRITZ!Box itself doesn’t include any build tools. Why should it! It’s an end-consumer product which is usually never changed, beside the user is updating it with an official firmware update. So we have to do it ourself. Creating a build chain for another architecture is usually hard. There are several things you have to consider. This includes problems with the configuration, endianess differences between the host and the target, bit depths and depending shared libraries which have to be available in the target format. Fortunately this is a common problem, especially for embedded systems, which means there is a proper solution called Buildroot (look at the first line in dmsg, when the FRITZ!Box has booted and you know what I mean). Buildroot is a build system which allows, similar to the Linux kernel, configuring and generating complete embedded Linux systems. Regardless how configured, Buildroot builds a complete Linux system, always. This is of course a little bit overhead for us, but we just pick out the relevant files. First we need to know which architecture we target. You have to login into the FRITZ!Box either using telnet or a working ssh daemon. To get some information about the hardware used in your FRITZ!Box execute cat /proc/cpuinfo.

# cat /proc/cpuinfo
system type             : TI UR8 (7270)
processor               : 0
cpu model               : MIPS 4KEc V6.8
BogoMIPS                : 359.62
wait instruction        : yes
microsecond timers      : yes
tlb_entries             : 16
extra interrupt vector  : yes
hardware watchpoint     : no
ASEs implemented        :
shadow register sets    : 1
core                    : 0
VCED exceptions         : not available
VCEI exceptions         : not available

Here we learn that the FRITZ!Box is using a MIPS processor. In this case it uses the little-endian format. Next you need some information about the system C library. On normal Linux (desktop) systems the GNU C Library is used. This library is heavyweight and therefore the uClibc was invented for embedded systems. By executing ls /lib/libuClibc* you get the version installed. In my case it is 0.9.31. Now we have all information needed to configure Buildroot. Download and extract the source of Buildroot.

If you are working on a Mac like me (where Buildroot doesn’t work), VirtualBox with a Linux system as a guest is a good alternative ;).

By executing make menuconfig in the root directory of Buildroot you are able to configure it. The most important options are Target Architecture, set this to mipsel and Target Architecture Variant, set this to mips 32r2. In Toolchain -> uClibc C library Version select a version which is close to the one on your FRITZ!Box. Next you have to select which packages should be build in Package Selection for the target. Choose whatever you want but for the beginning Networking applications -> openssh might be enough. If you prefer a more lightweight ssh implementation dropbear may also an option. In the following we use OpenSSH. You can also tweak the build options, like the gcc version to use or set the gcc optimization level to -O3 in favor of size. After saving the configuration and executing make, it is time for a coffee. When the build has been finished all relevant files are in output/target/. We only pack the directories and files which are necessary. For example the libuClibc was built by Buildroot, but we will use the one from the FRITZ!Box. The following create a zip archive of the files for OpenSSH.

zip -r ../../local.zip "lib/libutil* lib/libnsl* lib/libresolv* 
 lib/libcrypt* usr/lib usr/share usr/sbin usr/bin etc/ssh_config 
 etc/sshd_config"

Creating overlay directories on the root filesystem

The following is based on ideas invented by the guys at http://www.spblinux.de/. Our aim is it to have as little persistent changes on the FRITZ!Box as possible. This allows us to go back to the original state with a simple reboot. Therefore all writeable directories will be on a tmpfs filesystem. tmpfs filesystems are created in RAM and the content will be removed when the filesystem is unmounted. Because the FRITZ!Box has only limited RAM, we will not copy our applications into the tmpfs filesystem, but just create symbolic links to the files on the USB stick (where usually much more space is available). Because we want the applications from the FRITZ!Box and our own one be accessible from the root filesystem, we need to merge both into the tmpfs directory. Therefore first symbolic links into the original filesystem and second symbolic links to the USB stick are created. The second step is only performed when there isn’t a file already. This makes sure the original files are always preferred over our own one. The last step is binding the directories to the root filesystem. Summarized the following will be done:

  1. remount / readonly
  2. mount / into /var/_ro_
  3. create tmpfs in /var/_overlay_
  4. symlink the files from /var/_ro_ into /var/_overlay_
  5. symlink the files from the USB stick into /var/_overlay_
  6. bind the directories in /var/_overlay_ to /

This script does the previous steps. It uses helper methods which are defined in common.sh. You will find all the necessary files in the package you can download at the end of this article.

#!/bin/sh
 
. common.sh
 
MNT=/var/_overlay_
BASE_RO=/var/_ro_
BASE_OVL="${BASE}/local"
 
DIRS="lib etc usr/bin usr/sbin usr/lib usr/share"
 
# remount root readonly
"${MOUNT}" | "${GREP}" "/dev/root" | "${GREP}" -q ro
[ $? -eq 1 ] && _lmt /dev/root / "-o remount -r"
# bind root into var
_lchkmnt "${BASE_RO}"
if [ $? -eq 1 ]; then
  _lmkdir "${BASE_RO}"
  _lmnt / "${BASE_RO}" "-o bind"
fi
 
# create our overlay dir
_lchkmnt "${MNT}"
if [ $? -eq 1 ]; then
  _lmkdir "${MNT}"
  _lmnt tmpfs "${MNT}" "-t tmpfs"
fi
 
# link/copy the base stuff
for i in ${DIRS}; do
  _lsymlnk_dir "${BASE_RO}" "${i}" "${MNT}"
done
 
# link/copy our own stuff
for i in ${DIRS}; do
  _lsymlnk_dir "${BASE_OVL}" "${i}" "${MNT}"
done
 
# now bind all overlay dirs to root
# (this is critical don't interupt)
for i in ${DIRS}; do
  _lmnt "${MNT}/${i}" "/${i}" "-o bind"
done
 
exit 0

Now copy the zip archive you have created above to the FRITZ!Box and unpack it on the USB stick in addons/local/. The scripts have to be placed into addons/. You need to configure the following in common.sh:

# the base of the usb stick goes here:
BASE="/var/media/ftp/FLASH-DISK-01/addons"
# add the encrypted root password here:
PASS=""

Point to the base directory within the USB stick in BASE. Also add your encrypted root password into PASS. This will create a root user in /etc/passwd automatically.

If you believe all is correct, you can start the installation by executing ./install.sh. If no errors are shown, you can try ssh. ssh -v should output something like this:

# ssh -v
OpenSSH_5.8p2, OpenSSL 1.0.0d 8 Feb 2011
...

Additional to the standard filesystems mounted, you should see the following when executing mount:

# mount
...
/dev/root on /var/_ro_ type squashfs (ro,relatime)
tmpfs on /var/_overlay_ type tmpfs (rw,relatime)
tmpfs on /lib type tmpfs (rw,relatime)
tmpfs on /etc type tmpfs (rw,relatime)
tmpfs on /usr/bin type tmpfs (rw,relatime)
tmpfs on /usr/sbin type tmpfs (rw,relatime)
tmpfs on /usr/lib type tmpfs (rw,relatime)
tmpfs on /usr/share type tmpfs (rw,relatime)

Configure and start the OpenSSH daemon

Now that we have our brand new OpenSSH on the FRITZ!Box working, we need some last steps to let the daemon running correctly. First create a private/public RSA host key pair for the server process by executing:

ssh-keygen -t rsa -f addons/config/ssh_host_rsa_key

addons/config/ have to be an existing directory below the USB stick base. Next create a file addons/config/sshd_config in the same directory with the following content:

Protocol               2
UsePrivilegeSeparation no
Subsystem              sftp /usr/lib/sftp-server

The following script could be use to start the sshd:

#!/bin/sh
 
. common.sh
 
SSHD="/usr/sbin/sshd"
SSH_SPKEY="${BASE}/config/ssh_host_rsa_key"
SSH_CFG="${BASE}/config/sshd_config"
SSH_TPKEY="/var/tmp/ssh_host_rsa_key"
 
# sanity check
[ ! -e "${SSHD}" ] && _lce $? "sshd not installed"
 
# start in tmp
cd /var/tmp   
 
mount -o remount devpts /dev/pts -t devpts
 
# cp key and make them user readable only
_lcp_file "${SSH_SPKEY}" "${SSH_TPKEY}"
_lchmod 400 "${SSH_TPKEY}"             
 
# start sshd
"${SSHD}" -f "${SSH_CFG}" -h "${SSH_TPKEY}"

Please note that you have to set up the root user first and maybe adjust your port forwarding rules to allow access from the Internet as described in this post. The package will contain a script startup.sh which will add the root user automatically.

Here is the obligatory screenshot which shows htop with color terminal support. You see OpenSSH and OpenVPN in the list of the running processes.

Conclusion

Thanks to Buildroot it is pretty easy to cross-build for embedded machines these days. With some tricky directory rebinding new applications could be injected into the FRITZ!Box root filesystem without overwriting the existing firmware. Now you should be able to build any software already bundled with Buildroot or even add new one to the build process. Happy cross-building!

You can download the scripts and binaries discussed in this post:
FRITZBox_OpenSSH_5.8p2.tar.gz (3.8MB, SHA1)

TT: Console navigation the easy way with Apparix

Today I will start a new series where I present small tools which I use on a daily basis and considered very useful. These tools haven’t to be killer applications, but doing the task they are written for, very well. Therefor also the name of this series: TT, which stands for ToolTips. Most of this applications are open source, so I will take the opportunity to say “Thank you” to all the people out there, which create such cool stuff in there free time.

We start with a tool called Apparix.

Console?

As you may know, I’m a keyboard addict. My preferred OS is Linux, I use Ion3 as the window manager, rxvt-unicode as terminal application and (g)Vim as the preferred editor. On the other side I have a MacBook Pro as laptop and love the possibilities of Mac OS X. How does this come together? Well, all these tools are available on Mac OS X as well. The X11 port of Mac OS X is getting better with each release and using it in fullscreen is good enough. Mac OS X is based on BSD and together with MacPorts, there is nothing I missed until today. As I have to work really platform independent, there is Windows. Vim is available, but working console based (read: command.com) isn’t really much fun. There is of course mingw, but I never managed it to create a work environment like on Linux/UNIX. Anyway, I work not that much on Windows, so I can live with other tools there as well (a really good free editor is Notepad++ and if you hate Explorer have a look at Altap Salamander). So the question remains: Console? I have the feeling using the keyboard within a console application is much faster than clicking around with the mouse. This doesn’t mean I like to type every single character when starting an application or changing the current working directory. Many modern command interpreters, like the Bash, have built in word expansion. This allows you to use the tabulator key for fast expansion of typed characters for a given set of commands. Of course this makes the console life much more easier. But there is more!

Bookmarking important directories

Usually, I’m working on projects. One project is VirtualBox. Other might be some private stuff, like personal letters or other development I do. As the regular user organize his projects in directory trees it make sense to bookmark them. Most people believe bookmarking is something Internet specific, but this isn’t true. Bookmarking means getting access to an important information very fast, without remembering the exact location of this information. This is exactly what Apparix does. It bookmarks directories and make these bookmarks easy accessible on the console. So I have bookmarks for sub-projects within VirtualBox, like the GUI or Main. Getting there is as easy as writing to vgui. It’s the same as writing cd /bin and it even provide word expansion of the available bookmarks. In this case the bookmark is vgui and it expand to the GUI directory within the VirtualBox source tree.

How does this work?

Installing Apparix is pretty simple. You will find all the information on the website. The important part is the integration within your shell. You need to extent your .bashrc. One way is to add the functions you need into .bashrc. Alternatively you can download the file .bash_apparix and source it in your .bashrc. When you have done this and start a new shell, you should navigate to a directory you like to bookmark. There you type bm to create your first bookmark. Afterwards, regardless what your current working directory is, you could execute to DIR_NAME to get there. Of course you can name the bookmark other than the current directory name, by using bm MY_NAME. By executing als all bookmarks are shown. There is much more possible of course. See the documentation for more information. Anyway, this is the main use case for Apparix, so there is not much more to say. Really?

Using the Apparix database for other applications

The cool thing on many Linux applications is, they are saving their data in simple text files. Also Apparix. The bookmark “database” is saved in a file called .apparixrc. It’s a simple text file, which is easy to parse. It looks like this:

j,vbox,/Users/poetzsch/projects/vbox
j,VirtualBox,/Users/poetzsch/projects/vbox/src/VBox/Frontends/VirtualBox
j,Main,/Users/poetzsch/projects/vbox/src/VBox/Main
j,Runtime,/Users/poetzsch/projects/vbox/src/VBox/Runtime
j,Installer,/Users/poetzsch/projects/vbox/src/VBox/Installer/darwin
j,cv,/Users/poetzsch/projects/private/office/latex/cv
j,dow,/Users/poetzsch/Downloads

This allows the usage of this information in other contexts as well. One example is Vim. As I mentioned earlier, this is my main editor. So it would be cool to use the bookmarks, I already have, there as well. To easy change the current directory in Vim, add the following to your .vimrc:

command! -complete=custom,BmCe -nargs=1 To :call BmTo('<args>')
fun BmCe(A,L,P)
 return system("awk 'BEGIN{FS=","}/j,/{print $2}' ~/.apparixrc")
endfun
fun BmLs(dir)
 return system('apparix '.a:dir.' | tr -d "n"')
endfun
fun BmTo(dir)
 execute ':cd '.BmLs(a:dir)
endfun

After restarting Vim, it is possible to navigate with :To MY_BOOKMARK to any directory known by Apparix.

Conclusion

One simple task, getting to a directory where you are often have to be, with one simple solution. Thats all to say about Apparix. It does this in a very simple way, which is fully integrated into the Bash and the saved information could be used by other applications as well.

Creating file shortcuts on three different operation systems

As you may know, developing for multiple platforms is one of my strengths. Strictly speaking, it’s a basic requirement if you are involved in such a product like VirtualBox, which runs on every major (and several minor) platform available today. Beside the GUI, which uses Qt and therewith is portable without any additional cost (which isn’t fully true if you want real native look and feel on every platform, especially on Mac OS X), all the rest of VirtualBox is written in a portable way. This is done by using only C/C++ and Assembler when necessary. Everything which needs a different approach, because of the design of the OS (and the API’s which are available there), is implemented in a platform dependent way. In the history of VirtualBox, several modules are created and grown by the time, which makes it really easy to deal with this differences. For stuff like file handling, paths, strings, semaphores or any other basic functionality, you can just use the modules which are available. On the other side it might be necessary, for a new feature we implement, to write it from the ground. In the following post I will show how to create a file shortcut for the three major operation systems available today.

Why do you want to use file shortcuts

On the classical UNIX systems you have hard and soft links. These are implemented by the filesystem and make it possible to link to another file or folder without any trouble. Most of the time soft links are used, but it really depends on the use case. Unfortunately these kind of links are not available on Windows (yes, I know there are also hard links and junctions on NTFS, but they are not common and difficult to handle), these links doesn’t allow any additional attributes. For example one like to add a different icon to the link or provide more information through a comment field. Beside on Mac OS X, shortcuts can also be work as an application launcher, where the link contain the information what application should be started and how. In contrast to filesystem links which are handled by the operation system, these shortcuts are handled by the window system (or shell) running on the host (which doesn’t mean there is no filesystem support for it). On Windows this is the Explorer, on Mac OS X the Finder and on Linux a freedesktop.org conforming file manager.

Creating a Desktop file on Linux

Desktop files on Linux (or any other UNIX system which conforms to freedesktop.org) is easy. It’s a simple text file which implement the Desktop Entry Specification. In version 1.0 there are 18 possible entries, where not all of them are mandatory. In the following example I use Qt to write these files, but it should be no problem to use any other toolkit or plain C.

bool createShortcut(const QString &strSrcFile,
                    const QString &strDstPath,
                    const QString &strName)
{
 QFile link(strDstPath + QDir::separator() + strName + ".desktop");
 if (link.open(QFile::WriteOnly | QFile::Truncate))
 {
  QTextStream out(&link);
  out.setCodec("UTF-8");
  out << "[Desktop Entry]" << endl
      << "Encoding=UTF-8" << endl
      << "Version=1.0" << endl
      << "Type=Link" << endl
      << "Name=" << strName << endl
      << "URL=" << strSrcFile << endl
      << "Icon=icon-name" << endl;
  return true;
 }
 return false;
}

Replace icon-name by a registered icon on the system and you are done.

Creating a Shell link on Windows

Windows provides an interface for IShellLink since Windows XP. The following example shows how to use it:

bool createShortcut(LPCSTR lpszSrcFile,
                    LPCSTR lpszDstPath,
                    LPCSTR lpszName)
{
 IShellLink *pShl = NULL;
 IPersistFile *pPPF = NULL;
 HRESULT rc = CoCreateInstance(CLSID_ShellLink,
                               NULL,
                               CLSCTX_INPROC_SERVER,
                               IID_IShellLink,
                               (void**)(&pShl));
 if (FAILED(rc))
  return false;
 do
 {
  rc = pShl->SetPath(lpszSrcFile);
  if (FAILED(rc))
   break;
  rc = pShl->QueryInterface(IID_IPersistFile, (void**)&pPPF);
  if (FAILED(rc))
   break;
  WORD wsz[MAX_PATH];
  TCHAR path[MAX_PATH] = { 0 };
  lstrcat(path, lpszDstPath);
  lstrcat(path, "\");
  lstrcat(path, lpszName);
  lstrcat(path, ".lnk");
  MultiByteToWideChar(CP_ACP, 0, buf, -1, wsz, MAX_PATH);
  rc = pPPF->Save(wsz, TRUE);
 } while(0);
 if (pPPF)
  pPPF->Release();
 if (pShl)
  pShl->Release();
 return SUCCEEDED(rc);
}

As you may noticed this uses COM. Many API’s on Windows using the COM interface to communicate between processes. If you don’t use COM in your application you have to initialize it first. This is achieved by adding the following call to the front of the function:

 if (FAILED(CoInitialize(NULL))
  return false;

Depending on your application it might be worth to unitialize COM after usage by appending the following to the function:

 CoUninitialize();

The function itself isn’t any magic. It gets a COM interface to the IShellLink interface and then work with it, by setting the source path and adding a target path by using the IPersistFile interface. As I wrote before you could do much more. Providing a path to a specific application or adding your own parameters is no problem. Have a look at the documentation.

Creating an Alias file on Mac OS X

Shortcut files on Mac OS X are a little bit different. At first, they aren’t one. There are the classical filesystem links and Alias files. Alias files are links which targeting a specific file, but they haven’t all the possibilities of shortcuts like on Windows or Linux. As the name suggest they are really only an alias for another file or directory. So specifying an application to start or things like that aren’t possible. Anyway they allow changing the icon and they are more persistent than on Window or Linux cause they are working with several attributes of the target file. Even if you rename or move the target, an Alias file will resolve the target correctly (if it is possible). On the other side, being such special means also being hard to create. In principle there are two possibilities. The first one is, creating a file which is no file at all, but has several resources forks attached. Therefor you need to know exactly how Alias files are built of and make sure with every release of Mac OS X you are following the development. There is a free project which does exactly that: NDAlias. If you are like me and a little bit more lazy, you ask someone who should know how to create Alias files. This is Finder. Although writing the files itself isn’t easy, asking the Finder to do the job is not really easier, cause the information about doing exactly that are really rare. The following code shows how to achieve it:

bool createShortcut(NSString *pstrSrcFile,
                    NSString *pstrDstPath,
                    NSString *pstrName)
{
 /* First of all we need to figure out which process Id the Finder
  * currently has. */
 NSWorkspace *pWS = [NSWorkspace sharedWorkspace];
 NSArray *pApps = [pWS launchedApplications];
 bool fFFound = false;
 ProcessSerialNumber psn;
 for (NSDictionary *pDict in pApps)
 {
  if ([[pDict valueForKey:@"NSApplicationBundleIdentifier"]
         isEqualToString:@"com.apple.finder"])
  {
   psn.highLongOfPSN = [[pDict
                          valueForKey:@"NSApplicationProcessSerialNumberHigh"] intValue];
   psn.lowLongOfPSN  = [[pDict
                          valueForKey:@"NSApplicationProcessSerialNumberLow"] intValue];
   fFFound = true;
   break;
  }
 }
 if (!fFFound)
  return false;
 /* Now the event fun begins. */
 OSErr err = noErr;
 AliasHandle hSrcAlias = 0;
 AliasHandle hDstAlias = 0;
 do
 {
  /* Create a descriptor which contains the target psn. */
  NSAppleEventDescriptor *finderPSNDesc = [NSAppleEventDescriptor
                                            descriptorWithDescriptorType:typeProcessSerialNumber
                                            bytes:&psn
                                            length:sizeof(psn)];
  if (!finderPSNDesc)
   break;
  /* Create the Apple event descriptor which points to the Finder
   * target already. */
  NSAppleEventDescriptor *finderEventDesc = [NSAppleEventDescriptor
                                              appleEventWithEventClass:kAECoreSuite
                                              eventID:kAECreateElement
                                              argetDescriptor:finderPSNDesc
                                              returnID:kAutoGenerateReturnID
                                              transactionID:kAnyTransactionID];
  if (!finderEventDesc)
   break;
  /* Create and add an event type descriptor: Alias */
  NSAppleEventDescriptor *osTypeDesc = [NSAppleEventDescriptor descriptorWithTypeCode:typeAlias];
  if (!osTypeDesc)
   break;
  [finderEventDesc setParamDescriptor:osTypeDesc forKeyword:keyAEObjectClass];
  /* Now create the source Alias, which will be attached to the event. */
  err = FSNewAliasFromPath(nil, [pstrSrcFile fileSystemRepresentation], 0, &hSrcAlias, 0);
  if (err != noErr)
   break;
  char handleState;
  handleState = HGetState((Handle)hSrcAlias);
  HLock((Handle)hSrcAlias);
  NSAppleEventDescriptor *srcAliasDesc = [NSAppleEventDescriptor
                                           descriptorWithDescriptorType:typeAlias
                                           bytes:*hSrcAlias
                                           length:GetAliasSize(hSrcAlias)];
  if (!srcAliasDesc)
   break;
  [finderEventDesc setParamDescriptor:srcAliasDesc
    forKeyword:keyASPrepositionTo];
  HSetState((Handle)hSrcAlias, handleState);
  /* Next create the target Alias and attach it to the event. */
  err = FSNewAliasFromPath(nil, [pstrDstPath fileSystemRepresentation], 0, &hDstAlias, 0);
  if (err != noErr)
   break;
  handleState = HGetState((Handle)hDstAlias);
  HLock((Handle)hDstAlias);
  NSAppleEventDescriptor *dstAliasDesc = [NSAppleEventDescriptor
                                           descriptorWithDescriptorType:t ypeAlias
                                           bytes:*hDstAlias
                                           length:GetAliasSize(hDstAlias)];
  if (!dstAliasDesc)
   break;
  [finderEventDesc setParamDescriptor:dstAliasDesc
    forKeyword:keyAEInsertHere];
  HSetState((Handle)hDstAlias, handleState);
  /* Finally a property descriptor containing the target
   * Alias name. */
  NSAppleEventDescriptor *finderPropDesc = [NSAppleEventDescriptor recordDescriptor];
  if (!finderPropDesc)
   break;
  [finderPropDesc setDescriptor:[NSAppleEventDescriptor descriptorWithString:pstrName]
    forKeyword:keyAEName];
  [finderEventDesc setParamDescriptor:finderPropDesc forKeyword:keyAEPropData];
  /* Now send the event to the Finder. */
  err = AESend([finderEventDesc aeDesc],
               NULL,
               kAENoReply,
               kAENormalPriority,
               kNoTimeOut,
               0,
               nil);
 } while(0);
 /* Cleanup */
 if (hSrcAlias)
  DisposeHandle((Handle)hSrcAlias);
 if (hDstAlias)
  DisposeHandle((Handle)hDstAlias);
 return err == noErr ? true : false;
}

Although the code above looks a little bit scary, it does not much. It fetch the process serial number of the current Finder process, creates an Application event for creating an Alias file and send this event to the Finder.

Conclusion

Beside showing how to create file shortcuts on different platforms, this article also shows which work is necessary to create platform independent code. It’s a simple example. But it also makes clear that one simple solution for platform one, not necessarily mean it’s such simple on platform two.

Making this easy accessible to any developer is the next step. I will leave this exercise to the reader, but have a look at the platform code of the VirtualBox GUI and the corresponding Makefile.

Getting the backtrace from a kernel panic

You may know the following situation. You arrive in the morning in the office, do what you always do and check out the latest changes of the software you are working on. After a little bit of compile time and the first coffee you start the just build application. Bumm, kernel panic. After rebooting and locking through the changes you may have an idea what the reason for this could be. A colleague of you is working on a fancy new feature which needed changes to a kernel module. As you almost know nothing about this code you seek for help and, as it of course not happen on his computer, he is asking for a backtrace of this panic. You have two problems now. First you need to see the panic yourself and second it would be nice to get a copy of the backtrace for sharing this info within a bugtracker. In the following post I will show how both aims could be easily archived.

Let the kernel manage the graphical modes

As most people are working under X11 they don’t see the output of an kernel panic. When a kernel panic happens the kernel prints the reason for the panic and a kernel backtrace to the console window and stops immediately its own execution. It is not written into a log file or somewhere else. In consequence you don’t have the ability to look into the panic text, cause the graphical mode is still on. Historically the mode settings are done by the graphic driver of the X11 system. So the kernel has no idea that or which graphic mode is currently in use. Fortunately the kernel hackers invented a new infrastructure which let the kernel do the mode switch. This subsystem is called Kernel-Mode-Settings (KMS). As the kernel do the mode settings, he can switch back to the console on a panic, regardless which graphical mode is currently configured. Beside this, KMS has other improvements like Fast User Switching or a flicker free switch between text and graphic mode. On the other side is this highly hardware dependent and even if it was introduced with version 2.6.28, not all today available hardware can make use of it. If you are an owner of an Intel graphic card you are in good shape. Radeon and NVidia cards have limited support through the in kernel drivers radeonhd and nouveau. For an Intel i915 card you need to enable the following kernel options:

CONFIG_DRM_I915=y
Location:
-> Device Drivers
-> Graphics support
-> Direct Rendering Manager (XFree86 4.1.0 and higher DRI support) (DRM [=y])
-> Intel 830M, 845G, 852GM, 855GM, 865G ( [=y])

CONFIG_DRM_I915_KMS=y
Location:
-> Device Drivers
-> Graphics support
-> Direct Rendering Manager (XFree86 4.1.0 and higher DRI support) (DRM [=y])
-> Intel 830M, 845G, 852GM, 855GM, 865G ( [=y])
-> i915 driver (DRM_I915 [=y])

The kernel line in your favorite boot loader needs the following additional parameter:

i915.modeset=1

X11 should have this minimal configuration for the device section:

Section "Device"
 Identifier    "i915"
 Driver        "intel"
 Option        "DRI"   "true"
EndSection

Please note that you need of course some recent kernel, X11 version and Intel X11 driver to make this work. After a compile, install and boot of the new kernel, KMS should be in use. You will notice it, cause the boot messages will be printed in a much higher graphical resolution, than the usual text mode provide. The next time a kernel panic occurs, the kernel will switch back to the console before the panic is printed. This allows you to see the info printed and maybe you get a useful hint for the reason of the panic.

Post the panic

If you can’t use KMS or don’t want transcribe the panic text by hand into the bugtracker, it would be nice if the text could be made available on another computer. Kernel hackers usual use the serial port for that. Unfortunately most modern computers doesn’t have such a serial port anymore. Also you need two hosts with a serial port and the setup is complex (you have to know about baud-rates, parity and stuff like this). But there is a simpler solution: netconsole. Netconsole is a kernel module, which sends kernel messages anywhere to the net using UDP. The setup is really simple. In the kernel configuration you need the following setting:

CONFIG_NETCONSOLE=m
Location:
-> Device Drivers
-> Network device support (NETDEVICES [=y])

I prefer to compile it as module, which allows me to turn it on only when I need it. Load it with the following command:

modprobe netconsole netconsole=@/,@192.168.220.10/

The ip has to be replaced by the one of your target computer. You can of course tune it much more, like setting source and target ports or even let netconsole send the text to more than one host. On your client you need a network tool which can read from a socket and print the read text to stdout. Netcat or nc are two tools which are able to do just that. The call for nc looks like the following:

nc -l -u 6666

Now if a kernel panic will happen you will see an output like this:

BUG: unable to handle kernel NULL pointer dereference at (null)
IP: [] rb_erase+0x15c/0x320
PGD 6942f067 PUD a1e4067 PMD 0
Oops: 0000 [#1] SMP
last sysfs file: /sys/devices/virtual/block/md1/dev
CPU 3
Modules linked in: vboxnetadp vboxnetflt vboxdrv netconsole ...

Pid: 18887, comm: VirtualBox Tainted: G        W   2.6.36-gentoo #4 DG33TL/
RIP: 0010:[]  [] rb_erase+0x15c/0x320
RSP: 0018:ffff8800b430db58  EFLAGS: 00010046
RAX: 0000000000000000 RBX: ffff880069557a68 RCX: 0000000000000001
RDX: ffff880069557a68 RSI: ffff880001d8ed58 RDI: 0000000000000000
RBP: ffff8800b430db68 R08: 0000000000000001 R09: 000000008edcb5d6
R10: 0000000000000000 R11: 0000000000000202 R12: ffff880001d8ed58
R13: 0000000000000000 R14: 000000000000ed00 R15: 0000000000000002
FS:  00007fffde457710(0000) GS:ffff880001d80000(0000) knlGS:0000000000000000
CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 0000000000000000 CR3: 00000000064f9000 CR4: 00000000000026e0
DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400
Process VirtualBox (pid: 18887, threadinfo ffff8800b430c000, task ffff880091e227f0)
Stack:
 ffff88000a03ba18 ffff880001d8ed48 ffff8800b430dba8 ffffffff8105bf06
<0> ffff8800b430dba8 ffffffff8105c97c ffff8800b430dbc8 ffff88000a03ba18
<0> 00004ff8a86ba455 ffff880001d8ed48 ffff8800b430dc48 ffffffff8105ce77
Call Trace:
 [] __remove_hrtimer+0x36/0xb0
 [] ? lock_hrtimer_base+0x2c/0x60
 [] __hrtimer_start_range_ns+0x2b7/0x3c0
 [] ? rtR0SemEventMultiLnxWait+0x250/0x3d0 [vboxdrv]
 [] ? RTLogLoggerExV+0x12f/0x180 [vboxdrv]
 [] hrtimer_start+0x13/0x20
 [] rtTimerLnxStartSubTimer+0x60/0x120 [vboxdrv]
 [] rtTimerLnxStartOnSpecificCpu+0x21/0x30 [vboxdrv]
 [] rtmpLinuxWrapper+0x23/0x30 [vboxdrv]
 [] RTMpOnSpecific+0x99/0xa0 [vboxdrv]
 [] ? rtTimerLnxStartOnSpecificCpu+0x0/0x30 [vboxdrv]
 [] RTTimerStart+0x2a6/0x2e0 [vboxdrv]
 [] ? g_abExecMemory+0x33665/0x180000 [vboxdrv]
 [] g_abExecMemory+0xc678/0x180000 [vboxdrv]
 [] g_abExecMemory+0x328d7/0x180000 [vboxdrv]
 [] supdrvIOCtlFast+0x6a/0x70 [vboxdrv]
 [] VBoxDrvLinuxIOCtl+0x47/0x1e0 [vboxdrv]
 [] ? pick_next_task_fair+0xde/0x150
 [] do_vfs_ioctl+0xa1/0x590
 [] ? sys_futex+0x76/0x170
 [] sys_ioctl+0x4a/0x80
 [] system_call_fastpath+0x16/0x1b
Code: 07 a8 01 75 9d eb 81 0f 1f 84 00 00 00 00 00 48 3b 78 10 0f 84 ...
RIP  [] rb_erase+0x15c/0x320
 RSP
CR2: 0000000000000000
---[ end trace 4eaa2a86a8e2da24 ]---

Normally only kernel panics are sent to the console. You can increase the verbosity level by executing dmesg -n 8 as root.

Conclusion

To continue with the story from the beginning: With the shown methods you can hope your colleague get enough information to find the reason for the kernel panic. To be more helpful, the next step would be to try to debug the problem yourself. Even if the KGDB was merged into the kernel in version 2.6.35, it is not really usable for me. The reason is that it seems kernel hackers usually have really old hardware which either has a serial port, a PS/2 keyboard or both. Otherwise I can’t find a reason why USB keyboards don’t work. I asked on the mailing list of KGDB about the status of USB keyboard support and I can only hope support will be integrated soon.

Using suppression files with Valgrind

Valgrind is one of the great tools in the long list of freely available applications for development. Beside several profiling tools it also contains a memory checker. Leaking memory is one of the more common errors a programmer could step into. Basically it means to forget freeing memory (or in a more general sense: any resource) a program has acquired. If you are a perfect developer, this will never happen to you. If you are a good developer it may happen and that’s where Valgrind will save you some trouble. As most of the developers out there are more or less good developers, their programs produce memory leaks, too ;). The right solution for this, is of course to write a bug report. But there are times where this isn’t possible or you are in hurry and don’t want to see all the errors of a third-party library you link against.

In the following post, I will show how to suppress such unwanted error messages to make it much more easier to analyze the output of Valgrind for your own application.

Installing Valgrind

On Mac OS X you can use MacPorts to install Valgrind. You have to use valgrind-devel if you are on Snow Leopard, because Snow Leopard is supported in the current development version only. It’s as simply as typing sudo port install valgrind-devel.

On Gentoo it can become a bit harder. The current stable version is 3.5 (like in MacPorts). If you try this version (at least on an unstable Gentoo like mine) with valgrind ls, you will get the following error:

valgrind:  Fatal error at startup: a function redirection
valgrind:  which is mandatory for this platform-tool combination
valgrind:  cannot be set up.  Details of the redirection are:
valgrind:
valgrind:  A must-be-redirected function
valgrind:  whose name matches the pattern:      strlen
valgrind:  in an object with soname matching:   ld-linux-x86-64.so.2
valgrind:  was not found whilst processing
valgrind:  symbols from the object with soname: ld-linux-x86-64.so.2
valgrind:
valgrind:  Possible fixes: (1, short term): install glibc's debuginfo
valgrind:  package on this machine.  (2, longer term): ask the packagers
valgrind:  for your Linux distribution to please in future ship a non-
valgrind:  stripped ld.so (or whatever the dynamic linker .so is called)
valgrind:  that exports the above-named function using the standard
valgrind:  calling conventions for this platform.
valgrind:
valgrind:  Cannot continue -- exiting now.  Sorry.

The reason is a striped glibc. To work properly, Valgrind needs to overwrite some of the system functions the glibc provide. It does this by getting the symbols by name out of this library. This is of course not possible if all the symbol names are removed. You can prove this by executing nm /lib/ld-linux-x86-64.so.2. Gentoo provides a FEATURE=splitdebug, which adds debug libraries to the installation. Unfortunately setting this feature in /etc/make.conf, means setting it global. Gentoo is known as being configurable as no other distribution out there and of course we can set a feature for one program only. To do so, create a file called glibc in /etc/portage/env/sys-libs/ and add the following content to it.

FEATURES="splitdebug"

After a rebuild of glibc by executing emerge --oneshot glibc, we have a working Valgrind.

As all programs, Valgrind isn’t perfect. Version 3.5 shows many false/positive hits on my system, but fortunately the development goes on. Currently there is no newer version available in the Gentoo tree. Anyway it is not necessary to build one yourself, to get a more recent version. Using layman and the overlay tree of Flameeyes will let you integrate the development version of Valgrind seamlessly into your system. For a general How-to of layman check out this Users’ guide. In short, something like the following should be sufficient:

layman -a flameeyes-overlay
layman -s flameeyes-overlay
echo "=dev-util/valgrind-9999 **" >> /etc/portage/package.keywords
emerge valgrind

Installing the development version of Valgrind is optional of course.

Know your tools

One usage of Valgrind could be look like this:

valgrind --leak-check=full --leak-resolution=high ./VirtualBox

Beside other errors it also shows this error message on my system:

==27174==    at 0x4C26C09: memalign (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==27174==    by 0x4C26CB9: posix_memalign (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==27174==    by 0xBA8967F: ??? (in /usr/lib64/libglib-2.0.so.0.2400.2)
==27174==    by 0xBA89E9D: g_slice_alloc (in /usr/lib64/libglib-2.0.so.0.2400.2)
==27174==    by 0xBA89F86: g_slice_alloc0 (in /usr/lib64/libglib-2.0.so.0.2400.2)
==27174==    by 0xC204847: g_type_create_instance (in /usr/lib64/libgobject-2.0.so.0.2400.2)
==27174==    by 0xC1EB8A5: ??? (in /usr/lib64/libgobject-2.0.so.0.2400.2)
==27174==    by 0xC1ECE5D: g_object_newv (in /usr/lib64/libgobject-2.0.so.0.2400.2)
==27174==    by 0xC1ED494: g_object_new (in /usr/lib64/libgobject-2.0.so.0.2400.2)
==27174==    by 0x72A495F: ??? (in /usr/lib64/qt4/libQtGui.so.4.6.3)
==27174==    by 0x72A0D4F: ??? (in /usr/lib64/qt4/libQtGui.so.4.6.3)
==27174==    by 0x7289264: QGtkStyle::QGtkStyle() (in /usr/lib64/qt4/libQtGui.so.4.6.3)
==27174==    by 0x7215DB6: QStyleFactory::create(QString const&) (in /usr/lib64/qt4/libQtGui.so.4.6.3)
==27174==    by 0x6F5B7FC: QApplication::style() (in /usr/lib64/qt4/libQtGui.so.4.6.3)
==27174==    by 0x6F61DFF: QApplicationPrivate::initialize() (in /usr/lib64/qt4/libQtGui.so.4.6.3)
==27174==    by 0x6F61E88: QApplicationPrivate::construct(_XDisplay*, unsigned long, unsigned long) (in /usr/lib64/qt4/libQtGui.so.4.6.3)
==27174==    by 0x6F61FF3: QApplication::QApplication(_XDisplay*, int&, char**, unsigned long, unsigned long, int) (in /usr/lib64/qt4/libQtGui.so.4.6.3)
==27174==    by 0x44BC38: TrustedMain (main.cpp:371)
==27174==    by 0x44C649: main (main.cpp:651)

If you analyze the backtrace, you see that something in libQtGui is leaking memory. I don’t want to blame someone for it or make a statement if this is right or wrong, I just want to get rid of it, to be able to easily spot errors VirtualBox itself produce. To do so, add --gen-suppressions=all to the Valgrind call. This will produce something similar like this:

{
 Memcheck:Leak
 fun:memalign
 fun:posix_memalign
 obj:/usr/lib64/libglib-2.0.so.0.2400.2
 fun:g_slice_alloc
 fun:g_slice_alloc0
 fun:g_type_create_instance
 obj:/usr/lib64/libgobject-2.0.so.0.2400.2
 fun:g_object_newv
 fun:g_object_new
 obj:/usr/lib64/qt4/libQtGui.so.4.6.3
 obj:/usr/lib64/qt4/libQtGui.so.4.6.3
 fun:_ZN9QGtkStyleC1Ev
 fun:_ZN13QStyleFactory6createERK7QString
 fun:_ZN12QApplication5styleEv
 fun:_ZN19QApplicationPrivate10initializeEv
 fun:_ZN19QApplicationPrivate9constructEP9_XDisplaymm
 fun:_ZN12QApplicationC1EP9_XDisplayRiPPcmmi
 fun:TrustedMain
 fun:main
}

To let Valgrind ignore this error in the future, copy the text into a file vbox.supp and start Valgrind with --suppressions=vbox.supp. Viola, this specific error isn’t shown anymore. The format used there is easy to understand and you can of course tweak this much more. E.g. you could replace some of the fun: entries by “...“. This is a placeholder for one or more functions calls with any name. Beside making suppression rules more general you can of course add as much as you like. Adding a name at the top make it easy to identify the different rules. For all the possibilities have a look at the documentation. Just for the curious, Valgrind is using such a file itself. Have a look at /usr/lib/valgrind/default.supp. You may also have noted that the function names in the normal error message differ from the one in the suppression list. The former is in the demangled form and the later in the saved form. You could force Valgrind to print mangled function names by adding the --demangle=no parameter to the call. This becomes handy if you manually create suppression lists.

Conclusion

By using suppression rules for the own application, unimportant errors could be eliminated in the output of Valgrind. With this is in mind there is no excuse anymore for memory leaks in the self developed applications. Beside memory leaks, Valgrind also finds places where uninitialized variables are in use or where memory is used which isn’t allocated by the application. Also these tests could be filtered out by suppression rules.

Installing Linux on a USB hard disk for the MacBook Pro

One of the features of Mac OS X I love, is the possibility to install Mac OS X on any attached removable media, like a FireWire or USB hard disk. This makes it really easy for me to test VirtualBox on the several versions of Mac OS X we support (formerly Tiger, now Leopard and Snow Leopard). The advantage of this setup is that I don’t waste disk space for operation systems I usually don’t use very often. Currently I have a 150GB hard disk in my MacBook Pro which is really not that much if you deal in the virtualization business. There are several test VM’s of any kind of guest operation systems and of course the ISO’s to install them. The second main OS, I do much of my work, is Linux. For this I have a standard PC with Gentoo on it, which have all that I need. Unfortunately this doesn’t really help when I on travel. As I soon be away for some time, I decided I need, at least for testing, the same flexibility mentioned above for a Linux installation. And here the problems start to arise. Of course Apple didn’t really support installing other OS’s than Mac OS X on Apple hardware. Yes, there is Boot Camp, but this is mainly for Windows, is very inflexible and doesn’t really help if you try to install something on another place than the integrated hard disk. There are projects like rEFIt, which even makes Boot Camp superfluous, but this project has really bad USB boot support. In the following I will explain how it is even possible to install Ubuntu 9.10 on a USB hard disk.

The hardware used, as already said, is a MacBook Pro 3,1 and a Western Digital My Passport Essential 500GB USB hard disk. Other combination may work, but I don’t guarantee this, as always. Also you should be warned that anything I describe here could destroy your existing installation and I’m not responsible for that. Doing some kind of backup might be a good idea. Time Machine is easy to use ;).

Before we start, as usual, the credits for some sites I get my information from. This is mainly the Produnis blog, the Blog of Chris, the Ubuntu wiki and of course the guys around the Grub2 development.

Creating the EFI boot loader

Apple doesn’t use the legacy BIOS to boot their machines, they use the Extensible Firmware Interface (EFI). This new way of booting operation systems is very flexible, as the name suggest, but has several drawbacks, like most of the standard operation system doesn’t speaks it language. Although Linux can be configured to use EFI directly we will emulate a legacy BIOS in the following. For this we need a connector which makes EFI and Linux happy and let them both work smoothly together. This connector is Grub2, which is in development for several years now. It’s the successor of Grub and is the standard in many popular Linux distributions these days. We have to build a version our self, for which an existing Linux installation is really helpful. I used my 64-bit Gentoo installation. First you have to find out if the EFI installation on your target Apple machine is 64 or 32-bit. You can do this by executing:

ioreg -l -p IODeviceTree | grep firmware-abi

This will return EFI64 or EFI32 respectively. In my case I need the 64-bit version, which is a little bit surprising when I consider that my MacBook Pro isn’t able to boot a 64-bit Snow Leopard. Anyway, grab the latest version of Grub2 and unpack it on the Linux machine. Please note that you need a gcc with multilib support if you are targeting an architecture which isn’t the same as the host one. Use the following to configure Grub2 and to build it. Of course you have to adjust the target architecture if it is a different one.

./configure --with-platform=efi --target=x86_64 --disable-werror
make

When this is finished you create the EFI package by executing

./grub-mkimage -d . -o bootx86.efi *.mod

Here I included all modules which are available. If size matter for you, you could of course make a selective choice on the modules included. I didn’t test this myself, so you have to find out yourself which one are important.

Whipping the USB hard disk into shape

Next we have to prepare the USB hard disk for the new installation. In the following I assume your USB hard disk doesn’t contain any valid data and could be reformatted without data lose. Make a backup of your data first if this isn’t the case on your side. Apple uses the GUID Partition Table scheme to organize their partitions on a hard disk. This specification is part of EFI and remove many limitations of the Master boot record (MBR) scheme, which is widely used in the PC world. That is e.g. the disk size limitation of 2TB or the maximum of 4 primary partitions. You reformat your disk, using the Disk Utility application of Mac OS X. Make sure all existing partitions on the disk are unmounted. When necessary, change the partition scheme from MBR to GUID in the Options dialog of the Partitions window. Select the partitions count you want to use. You need at least 3 partitions to make Linux works fine. My partition scheme looks like in the following: As you can see I have 5 partitions configured. The first one is an additional Snow Leopard installation for testing. I also added a Data partition at the end for making the data transfer between the different operation systems as easy as possibly. LINUXBOOT is a small partition which will contain the EFI boot loader (size it 50MB or something like that). Linux Swap, obviously, will become the swap partition of the Linux installation. DISK1S5 is the Linux root partition itself. The Data partition has to be formated as Mac OS Extended. Don’t use the Journaled version of HFS+, cause this makes trouble on the Linux side. The other partitions have to be formatted as MS-DOS (FAT).

After applying the changes we can add the EFI boot loader to the LINUXBOOT partition. The Apple EFI implementation is searching for a file with the efi extensions on all bootable hard disks. Mount LINUXBOOT and create a efi/boot directory on the root path. Copy the bootx86.efi file into the boot/ directory. As bootx86.efi is a Grub2 boot loader we need a valid Grub2 configuration file. The following grub.cfg shows the configuration for a Ubuntu 9.10 i386 installation. For the 64-bit version or any other version of Ubuntu the settings might be slightly different.

menuviewer="text"
timeout=10
default=0
set F1=ctrl-x
menuentry "ubuntu-9.10-desktop-i386"
{
 fakebios
 search --set -f /boot/vmlinuz-2.6.31-14-generic
 linux /boot/vmlinuz-2.6.31-14-generic root=UUID=4e140981-4ab3-41a2-a2fb-26b1287beb87 ro quiet splash noefi video=efifb
 initrd /boot/initrd.img-2.6.31-14-generic
}
menuentry "ubuntu-9.10-desktop-i386 single"
{
 fakebios
 search --set -f /boot/vmlinuz-2.6.31-14-generic
 linux /boot/vmlinuz-2.6.31-14-generic root=UUID=4e140981-4ab3-41a2-a2fb-26b1287beb87 ro noefi video=efifb single
 initrd /boot/initrd.img-2.6.31-14-generic
}
menuentry "ubuntu-9.10-desktop-i386 text"
{
 fakebios
 search --set -f /boot/vmlinuz-2.6.31-14-generic
 linux /boot/vmlinuz-2.6.31-14-generic root=UUID=4e140981-4ab3-41a2-a2fb-26b1287beb87 ro noefi vga=normal
 initrd /boot/initrd.img-2.6.31-14-generic
}
menuentry "Mac OS X"
{
 search --set -f /usr/standalone/i386/boot.efi
 chainloader /usr/standalone/i386/boot.efi
}
menuentry "CD"
{
 appleloader CD
}
menuentry "mbr"
{
 appleloader HD
}
menuentry "reboot"
{
 reboot
}

You have to change the root UUID to the one the Ubuntu installer will assign to your hard disk after installation. Just check the fstab file when the installation has finished. The first entry boots Linux with a splash image enabled. The second one is for the single user mode in the case something went wrong. Please note the video=efifb option, which enables the graphical mode in the boot phase.

Installing Ubuntu

Most of the installation process is straight forward and doesn’t need any special attention. Download the version of your choice from one of the mirrors, burn it on CD and start the installation. You can select the CD as boot medium by pressing Alt when your Mac starts. When the installer ask for the partition scheme, you have to switch to “manual choice”. Select the DISK1S5 (your what it is in your case) as the root / partition and change the filesystem type to ext3. Also remember the path to the system partition, cause you will need it later again. Select the swap partition and change its type to swap. Proceed with the rest of the installation until the last dialog. There select “advanced settings” and change the boot loader target from hd0 to /dev/sdXX, where you replace XX to the path you used previously in the partition tool.

If all went right you should be able to select the LINUXBOOT partition by pressing Alt when your Mac starts. After that Grub2 should shows up, you will be able to boot into your freshly installed Ubuntu.

Conclusion

In this post I showed how to easily add the possibility to boot Linux on your MacBook Pro. With the external USB hard disk solution, no internal valuable space is wasted. Of course the speed isn’t the same as if the OS would be installed on the internal drive, but for testing software on different operation systems this is satisfactory. To increase the speed a little bit more, an external FireWire hard disk could be used.

FRITZ!Box tuning part 2: Access your home network with OpenVPN

AVM has built a VPN server into the FRITZ!Box, why should I use some other software for this job, you may ask. The reason is quite simple: the build in one is a piece of closed source software written by AVM and there is only one official client which could be used to connect to it, the FRITZ!VPN software. This client software is only available for the Windows operation system family and so by no means anything useful to me. I’m pretty sure they are using some official protocol like IPSec, so it might be possible to connect to the FRITZ!Box with other clients as well, but that’s something I didn’t want to try. OpenVPN on the other side is a rock solid open source software which could be used from many popular OS’s these days. Even graphical clients, like TunnelBlick for Mac OS X, are available. So here comes the second article of the FRITZ!Box tuning series, which will explain how to convert your FRITZ!Box into a OpenVPN server, where any number of clients can concurrently be connected. I highly recommend to read the first part of this series, because this post is build on top of the stuff done there. This count especially for the filesystem layout on the usbstick and the way additional software is started. Also in the following it will be helpful to have ssh access to the FRITZ!Box all the time. As already written in the first part, there is no guarantee that the information presented here will work on your side or that I’m responsible for anything happen to your FRITZ!Box. In preparation of the following you need access to a second OpenVPN installation which will be used to create all necessary certificates and keys and which could be used to test the installation afterward. I’m using a Gentoo Linux host where you could install OpenVPN simply by executing emerge openvpn. Make sure you have the examples USE flag set to get all the helper scripts which make the life much more easier.

The agony of choice

OpenVPN could be configured in many different ways. So first of all we have to decide which features of OpenVPN we will use. In its simplest variant OpenVPN could be used with one preshared static key. The advantage of this setup type is the easy configuration. The disadvantage is that only one client could be connected at the time. That is not what we want, so we will configure our setup to be using a Public Key Infrastructure (PKI). This allows more than one client to be connected at the time and it is possible to mark single keys invalid without affecting other client keys. The second question which has to be answered is, if we want use routing or bridging in our setup. Again, routing is simpler to set up and also a little bit faster than bridging. Bridging on the other side allows the tunneling of non IP protocols such as IPX, tunnel all the IP broadcast and make it possible to use the same subnet on the client side as on the server-side. Especially the last point is a nice feature as with routing all hosts on the server-side needs a routing table entry to find the connected clients. But my main reason for preferring bridging over routing is the broadcast feature of the first one. In my home network works a NAS station as a file server. This includes of course simple filesystem sharing with AFP and NFS, but also serves this box audio data with the Digital Audio Access Protocol (DAAP). DAAP is used by Apple in iTunes to share music with other clients in the network and that is what my NAS station does. This protocol, as well AFP, using broadcast messages (ZeroConf) to find some potential clients. So now it should be clear why I want bridging, it allows me to hear music or to simply connect to my file station without any interaction.

Building up a PKI

To create all necessary certificates and keys we are using the easy-rsa scripts from OpenVPN. They are located under /usr/share/openvpn/easy-rsa. First we change the vars file. On the bottom of the file are personal information defined which should be changed to fit to your environment. The entries looking as follow on my side (with a pseudo email address).

export KEY_COUNTRY="DE"
export KEY_PROVINCE="Sachsen"
export KEY_CITY="Dresden"
export KEY_ORG="64k"
export KEY_EMAIL="you@yourdomain.org"

You could also increase the KEY_SIZE from 1024 to 2048 within that file. This should make your keys unbreakable for the next few decades years. Now we are ready to create the master-key and the master certificate of the Certificate Authority (CA) by executing:

source vars
./clean-all
./build-ca

Please be aware that this will delete all previously created keys. Answering all questions with the default values should be sufficient. Next the key for the server has to be created.

./build-key-server server

You can again accept all default values. A challenge password is not necessary, but you have to sign the certificate. Now its time for our first client key and certificate. You could the following repeat again and again, for every new client you want allow to access the OpenVPN server.

./build-key client

Again, don’t forget to sign the key. If you ever have to mark a client certificate invalid you could do it with the following command:

./revoke-full client

This will create or update a Certificate Revocation List (CRL) file which will be later used by the server to verify the certificate integrity of a connecting client. For now we create an empty one with

KEY_CN="" KEY_OU="" KEY_NAME="" $OPENSSL ca -gencrl -out keys/crl.pem -config $KEY_CONFIG

The last step in this key building marathon is to create a Diffie-Hellman key. This key will be used to initiate the secure channel between the two parties. Its created by:

./build-dh

Creating the server and client configuration

We start with the configuration of the server:

# set the path to the tap device
dev tap
dev-node /var/tmp/tap0
mssfix
tun-mtu 1500

# TCP or UDP server?
proto udp
port 1194

# CA and key files
ca /var/media/ftp/FLASH-DISK-01/addons/openvpn/keys/ca.crt
cert /var/media/ftp/FLASH-DISK-01/addons/openvpn/keys/server.crt
key /var/media/ftp/FLASH-DISK-01/addons/openvpn/keys/server.key
crl-verify /keys/crl.pem # this is opened after the chroot

# Diffie hellman parameters
dh /var/media/ftp/FLASH-DISK-01/addons/openvpn/keys/dh1024.pem

# bridge setup
server-bridge 192.168.220.1 255.255.255.0 192.168.220.50 192.168.220.99
mode server
tls-server
push "dhcp-option DNS 192.168.220.1"

ifconfig-pool-persist ipp.txt
client-to-client

# compression?
comp-lzo

# The maximum number of concurrently connected clients we want to
# allow.
max-clients 20

# It's a good idea to reduce the OpenVPN daemon's privileges after
# initialization.
user nobody
group nobody

# Put openvpn in a jail.
chroot /var/media/ftp/FLASH-DISK-01/addons/openvpn

# The persist options will try to avoid accessing certain resources on
# restart that may no longer be accessible because of the privilege
# downgrade.
persist-key
persist-tun

# where to log
log-append /var/media/ftp/FLASH-DISK-01/addons/openvpn/log/openvpn.log

# verbose level for debugging
;verb 4

# make sure the connection is kept alive
keepalive 10 120

For bridged mode it is essential that OpenVPN uses a TAP device and not the TUN device mode (see here for the reason). We use UDP as the transport protocol and enable compression. Also we want that OpenVPN drops its privileges after the initialization and chroot into a jail to make it a little bit more secure. As the subnet for clients 192.168.220.0/24 is used. That is the same as my internal network uses. OpenVPN is allowed to use addresses ranging from 192.168.220.50 to 192.168.220.99. You have to make sure that the FRITZ!Box (or any other DHCP server in your network) doesn’t serve addresses from that range. Of course you could change some of the settings to your needs, but then you have to make sure they match the configuration of the client.

Next we create the client configuration, which looks as follow:

# Specify that we are a client and that we will be pulling certain
# config file directives from the server.
client

# The hostname/IP and port of the server.
remote xtestx.dyndns.org 1194

proto udp
dev tap

pull

mssfix
tun-mtu 1500

# SSL/TLS parms.
tls-client
ca keys/ca.crt
cert keys/client.crt
key keys/client.key

ns-cert-type server

# Keep trying indefinitely to resolve the host name of the OpenVPN
# server. Very useful on machines which are not permanently connected
# to the Internet such as laptops.
resolv-retry infinite

# Don't enable this; It must be disabled for iTunes to find the iTunes
# server and for AFP broadcast in general.
#nobind

# Downgrade privileges after initialization (non-Windows only)
user nobody
group nobody

# Try to preserve some state across restarts.
persist-key
persist-tun

# Enable compression on the VPN link.
comp-lzo

# debug level
;verb 4

mute-replay-warnings
mute 20

You have to change the remote hostname to your needs. You may also add the full path to the certificates and keys depending on your client OpenVPN installation.

Next we pack all files up for the server and the client respectively. The server needs the following one:

server.conf
keys/ca.crt
keys/crl.pem
keys/dh1024.pem
keys/server.crt
keys/server.key

The client needs this one:

client.conf
keys/ca.crt
keys/client.crt
keys/client.key
keys/server.crt

Customizing the FRITZ!Box

Connect to your FRITZ!Box and copy all the server files to the usbstick. I created a directory openvpn under the /var/media/ftp/FLASH-DISK-01/addons path. Next we need the OpenVPN binary for the FRITZ!Box. You can get it here. Unpack the file and copy the openvpn binary to /var/media/ftp/FLASH-DISK-01/addons/bin on the FRITZ!Box. Make sure the file mode has the executable bit set for the user. All files are now on the right places, so we can adjust our start script. Add the following code to the startup.sh file.

# Add a nobody user
echo 'nobody:x:65534:65534:nobody:/:/bin/false' >> /var/tmp/passwd

# Add some groups
echo 'root:x:0:' > /var/tmp/group
echo 'nobody:x:65534:' >> /var/tmp/group

# Create a tap device for openvpn
mknod /var/tmp/tap0 c 10 200

# Start openvpn
${BASE}/openvpn/bin/openvpn --config ${BASE}/openvpn/server.conf --daemon

As you see, we add a new nobody user to the Linux system. Then the device node for the TAP device is created and at the end the OpenVPN server is started. As we using the bridged setup, we have to add the new device to a bridge. Fortunately we haven’t to create one, cause the FRITZ!Box itself is working in bridged mode. You can confirm this by executing:

brctl show lan

To let the system automatically add our TAP device to the lan bridge, we change the internal FRITZ!Box configuration. Edit the /var/flush/ar7.cfg file with nvi and search for the string brinterfaces. There should be one section with the name lan. Simply add the new tap0 interface to the interfaces value. The section should then look like this:

brinterfaces {
     name = "lan";
     dhcp = no;
     ipaddr = 192.168.220.1;
     netmask = 255.255.255.0;
     dstipaddr = 0.0.0.0;
     interfaces = "eth0", "ath0", "tap0", "wdsup1", "wdsdw1",
                  "wdsdw2", "wdsdw3", "wdsdw4";
     dhcpenabled = yes;
     dhcpstart = 192.168.220.100;
     dhcpend = 192.168.220.200;
}

Now, as soon the tap0 interface is created it will be added to the bridge. The last important point in the server setup is to change the internal firewall to allow connections to port 1194 from the outside. Search for forwardrules in the ar7.cfg file and add this new rule:

forwardrules =
               "tcp 0.0.0.0:7777 0.0.0.0:22 0 # SSH-Server",
               "udp 0.0.0.0:1194 0.0.0.0:1194 0 # VPN-Server";

Thats all for the server. A reboot of the FRITZ!Box should start the new OpenVPN server. If you have trouble you should manually start the OpenVPN server and increase the debugging level, as shown in the above configuration with the keyword verb.

Setting up the client

On the client side we need the files we packed above. On Gentoo the OpenVPN configuration is located at /etc/openvpn. Copy the configuration, the certificates and the key file to that place. The OpenVPN start script of Gentoo looks for the configuration file, based on the start script name. So we have to create a link like this:

cd /etc/openvpn
ln -s client.conf openvpn.conf

By the way, this lets you configure more than one OpenVPN connection at the time. Just create a link in /etc/init.d with the new configuration name and link it to the openvpn start script.

To make a first attempt to start the OpenVPN connection type

/etc/init.d/openvpn start

If all goes well you should have a new device tap0 when you execute ifconfig. Also there should be a route set, which point to the tap0 device when the 192.168.220.0/24 subnet is the target. Of course now you should be able to connect to any host in your home network by the IP. I didn’t experiment enough with the pushing of the name server from the OpenVPN server, instead I just added the FRITZ!Box as an additionally name server to my /etc/resolv.conf. So here is room for further experiments.

To let the OpenVPN client start at boot time add it to the default runlevel by executing:

rc-update add openvpn default

Also note that with the above configuration the client always try to reconnect to the server, even if the FRITZ!Box is rebooted, which is a nice feature.

Conclusion

This setup allows you to connect to your home network in a very secure way without loosing any functionality. You are able to browse network shares on the local network or using services like DAAP without any further interaction.  The public key infrastructure make this setup a candidate for small business installations. If an employee leave the company his certificate is simply revoked without affecting the access of other employees.

FRITZ!Box tuning part 1: Enable remote access over ssh

Recently I changed my Internet service provider to Kabel Deutschland which offers some really good bandwidth for a reasonable price. You get 32 MBit in the download direction and 2 Mbit in the upload direction. Additionally to this you can order a FRITZ!Box 7270 which allows you to use VoIP for the phone part. As I always try to maximize the usage of new hardware I started to explore what is possible with this combination. In this post I will explain how you get access to your FRITZ!Box using ssh. Following this post, some articles about how you can turn the FRITZ!Box into a OpenVPN server and how you can use the VoIP access point remotely from any computer having a VoIP software installed, will be done. As I said the hardware used is a FRITZ!Box Fon WLAN 7270 with the firmware 54/74.04.80 from 2009/12/15 which is an official one from AVM. Other firmware versions may work also, but I haven’t tested that. The following topics are all for advanced user. So if you aren’t in touch with Linux or start asking what the hell is vi and how get I out of it, stop reading now. All the information in this and the following posts are based on several websites I found in the Internet. Mainly this are some forum entries at http://www.ip-phone-forum.de, the series about the FRITZ!Box at http://www.tecchannel.de, the posts on http://www.teamarbyte.de, the blog at http://www.realriot.de and this article at http://www.cswpro.de. So thanks to all the people for sharing this kind of information. Before we start some words of caution: It’s easily possible to render the FRITZ!Box unusable. So there is no warranty of any kind if you do some of the things mentioned in this blog. Also I’m not responsible for anything happen to your FRITZ!Box or the Internet connection. After all making a backup of your current configuration might be a good idea.

Preparing the FRITZ!Box

First of all you should register an account at http://www.dyndns.com or any other service for dynamic IP resolution. You can then add this information to the “DynamicDNS” tab of the Internet settings in the FRITZ!Box web frontend. This will allow you to connect to your FRITZ!Box without knowing the IP after a reconnect. Lets use us xtestx.dyndns.org in the following examples. Next you need telnet access to the FRITZ!Box to make the initial configuration for the ssh server. Enabling telnet is as simple as calling #96*7* with a local connected phone. To disable the telnet daemon later you call #96*8*. Now you need a usbstick where all the software will be installed on and which have to be connected to the FRITZ!Box all the time. In principle the size doesn’t matter, around 5 MBytes free space should be enough. Time to connect to the FRITZ!Box for the first time. A simple telnet fritz.box should do it.

Installation and configuration of the ssh server

For the ssh server software dropbear is used. You can get a prepared version for the FRITZ!Box from the spblinux server.

cd /var/tmp
wget http://www.spblinux.de/fbox.new/cfg_dropbear
chmod u+x cfg_dropbear

After downloading, the server has to be installed on the usbstick by executing

./cfg_dropbear usb_install

In my case the software is installed into /var/media/ftp/FLASH-DISK-01/addons. Now we will create a host key which will be reused even after a reboot of the FRITZ!Box.

cd /var/media/ftp/FLASH-DISK-01/addons
dropbear/bin/dropbearkey -t rsa -f dropbear/etc/sshd_rsa_host_key

The FRITZ!Box has initial no root password entry. We have to create one and save the password hash for later usage.

passwd
cat /etc/passwd

You have to note the part between root: and the next colon.

Putting all together

The initial starting point for all modifications on the FRITZ!Box is a file called /var/flash/debug.cfg. Its executed on every boot after all the other services are started. We will use it only to start a script which is located on the usbstick. This makes sure that even after a firmware upgrade our changes are not lost. The content looks like the following:

# execute the start script which is on the usb stick
/var/media/ftp/FLASH-DISK-01/addons/startup.sh

Please note that you have to use the nvi to edit files on the flash device from the FRITZ!Box, cause all files there are marked as non regular. The startup.sh will do all the work necessary for preparing and starting the ssh server. The content is:

#!/bin/sh
# whats the base usb directory
BASE=/var/media/ftp/FLASH-DISK-01/addons

# change to a temporary directory
cd /var/tmp

# install and start dropbear with our host key
${BASE}/cfg_dropbear usb_install
/bin/dropbear -E -r ${BASE}/dropbear/etc/sshd_rsa_host_key

# set passwords
echo 'root:XXXXXX:0:0:root:/var/tmp/:/bin/sh' > /var/tmp/passwd
echo 'ftpuser:any:1000:0:ftp user:/var/media/ftp:/bin/sh' >> /var/tmp/passwd

# add fritz box itself in resolv.conf to resolv dhcp attached machines in your network
echo "nameserver 192.168.220.1" >> /var/tmp/resolv.conf

You have to replace XXXXXX by the password hash you created above. In my case the network is in the 192.168.220.0/24 subnet and the FRITZ!Box has the IP 192.168.220.1. You have to adjust the name server entry to your environment. After a reboot which could be initiated with reboot you should be able to access your FRITZ!Box over ssh.

To allow connections from the outside of your home you need to change the forward rules of the internal FRITZ!Box firewall. Unfortunately you can’t add this rule using the web frontend cause it isn’t allowed to add rules which targeting the FRITZ!Box itself. Most of the settings for the FRITZ!Box configuration are located in a file called /var/flash/ar7.cfg. Use nvi to edit it and search for the string forwardrules. If this string is missing you could temporary create a rule in the web frontend, which will you later remove again. You have to change the line so that it looks like this:

forwardrules =
               "tcp 0.0.0.0:7777 0.0.0.0:22 0 # SSH-Server";

Here the connection port is 7777, but of course could you use any valid port number. You could also add more rules by separating them with a comma. To make the configuration change happen the execution of ar7cfgchanged is sufficient.

Conclusion

With this setup its possible to connect to the FRITZ!Box with the very secure ssh protocol from inside of your home environment and from the outside. This makes it easily possible to change parameters without manually starting the telnet daemon which is in addition very insecure. Don’t forget to remove any temporary rules from the ar7.cfg file and to disable the telnet daemon with the procedure explained above. Stay tuned for the next post about going one step further and making the FRITZ!Box an OpenVPN connection point for your internal network.

Update

There is a new article about installing ssh on the FRITZ!Box. If you are interested have a look at this post.