Jan 31 2010

The abomination known as IPV6_V6ONLY

Let me say it straight away: I strongly disagree with section 5.3 of RFC 3493 that IPV6_V6ONLY should be disabled by default. With this option disabled it's impossible to create a server socket in a truly protocol-independent way.

When RFC 2553 was published in 1999 it described a set of functions which can be used to enumerate and resolve socket addresses in a protocol-independent way. But with the release of RFC 3493, and its introduction of IPV6_V6ONLY, the protocol-independent aspect of these functions has been largely lost. The following example shows why:

One of the functions described in RFC 2553 is getaddrinfo. This function can be used to get a list of socket addresses suitable for bind'ing a server-side socket. On dual-stacked hosts it will return two addresses: one for IPv4 and one for IPv6. But when IPV6_V6ONLY is disabled, you can't have two sockets bound to the two addresses, the second bind will fail with EADDRINUSE. You can either find out which addresses of those returned are IPv6 and use only those, or you can enable IPV6_V6ONLY. But as you see both of these solutions aren't really protocol-independent anymore.

From what I gathered most *BSD distributions have IPV6_V6ONLY enabled by default, only Solaris and Linux don't. At least Debian is starting to break the ice and enabled IPV6_V6ONLY in their latest netbase package (4.40). That has broken a few other packages, but as the bug headline says, those are buggy and thus need to be fixed anyway.


Nov 18 2009

notmuch

http://keithp.com/blogs/notmuch

I've been using sup on and off for the last couple months. At first I had really big troubles getting it to work under opensolaris. Part of it was because it is written in Ruby, which Keith listed as one of the negative points. I don't have a problem with the speed or syntax though, just the portability. Anyway, I'll give notmuch a try to see if it works better.


Oct 30 2009

Build after push

I'm working on a project with a few friends. It's a server software that will run on one of my servers. There will be two versions running in parallel, a live version and a test version. We are using git to manage the source and to make it easier for my friends to update the test server, I've set up a post-receive hook that triggers a build. Issuing make from within the post-receive hook is a bad idea, because it would block the git client that is trying to push the code to wait until the build completes. Therefore I'm using a message bus to pass messages between the post-receive hook and the build script. This way the post-receive script can return quickly and not block the git client. Another advantage is that the build script can run under a different user account than the git server process (I'll probably run it from a screen session so I can see what it is doing). After finishing the build process, the script automatically restarts the test server and sends me an email. What follows here are some of the script involved:

The post-receive script executes the trigger for each updated ref:

#!/bin/sh
while read old new ref; do
   /var/git/hooks/trigger.rb "ProjectName" "$old" "$new" "$ref"
done

The trigger is a tiny ruby script that does nothing more than sending a message to the stomp message bus. I've decided to use a separate queue for each git project. The message body contains the ref that was updated, so that the receiving can decide whether to build the source or not.

#!/usr/bin/env ruby

require 'rubygems'
require 'stomp'

client = Stomp::Client.open "stomp://localhost:61613"
client.send("/git/repos/#{ARGV[0]}", ARGV[3])

The ruby script on the other end of the stomp queues waits for trigger messages which tell us that refs/heads/master was updated. It then executes a shell script that does all the heavy work. All output is captured and sent in an email.

#!/usr/bin/env ruby

require 'rubygems'
require 'stomp'
require 'net/smtp'
require 'session'

client = Stomp::Client.open "stomp://localhost:61613"

client.subscribe("/git/repos/ProjectName") do |msg|
   if msg.body == "refs/heads/master"

      shell, out = Session::Shell.new, StringIO.new
      shell.execute "/path/to/build-script.sh", :stdout => out, :stderr => out

      Net::SMTP.start('localhost') do |smtp|
         smtp.open_message_stream('git-build@domain.tld', ['to@domail.tld']) do |f|
            f.puts 'From: git-build@domain.tld'
            f.puts 'To: to@domail.tld'
            f.puts 'Subject: git build finished'
            f.puts out.string
         end
      end
   end
end

client.join

The build script is a fairly standard make; make install script (with a few project specific commands added before and after the make process), so I won't post it here.


Jul 22 2009

Having fun with gitfs

I imported the four existing gitfs tarballs into a git repository and updated it to work with recent git versions. The code is available in my github repository in the branch original.

However, the code base is quite large and complicated (for what it does) and uses the low-level linux kernel fuse interface. This makes it difficult to port and compile the code on other operating systems (such as MacOSX). I'm currently working on a new version of gitfs that uses libfuse. In only ~330 lines of C code it is able to browse different revisions and display correct file modes. The root directory of the filesystem shows the HEAD revision, but it is also possible to access any revision through the special directory /.refs.

Now to the fun part. I quite like MacOSX application bundles. The idea that an application is fully contained within a directory is ingenious. I can move or copy this directory wherever I want and double-clicking the folder starts the application. Now imagine this folder isn't really a folder but a filesystem backed by a git repository. Imagine updating applications with git pull, and at the same time being able to access older versions (through the /.refs directory)!

I tested this approach (albeit somewhat simplified) with firefox on linux. It's very easy to build firefox from the mozilla mercurial repository and create a distribution tarball. I imported the contents of this tarball into a git repository and tagged the tree. Now I can start $HOME/Minefield.app/.refs/30558-e1ae3719e72a/firefox (30558:e1ae3719e72a is the mercurial version that I used to compile firefox). It takes a while until the application starts because libgit.a is not optimized for long running applications such as gitfs. The caching it does internally is not transparent to the application developer and the git code is also filled with die()'s. To make gitfs perform well I would need to implement the functionality to read and parse the object store myself.

That kind of deployment only works with applications that are relocatable, meaning that they can be run from any directory and don't rely on fixed paths. Fortunately firefox is such application. The same wouldn't work with git itself since it compiles the prefix into the binary and perl files. I haven't tried any other application yet.


Apr 04 2009

SSHFS: the new unix network filesystem

As soon as you start using Unix in a networked environment, you are faced with the difficult question of how to copy files from one system to another. The first choice for any true Unix veteran is to use NFS. I'm sure it's a solid filesystem, but it does have its own weaknesses. For one, you have to punch holes into your firewall, which makes in unsuitable if you are traveling and want to connect from your laptop to a computer at home. And second, you have to keep the user IDs synchronized across all computers, otherwise you'll have all kinds of permission problems when accessing files. Another network filesystem is CIFS. It works alright under Linux, but I try to avoid anything that was designed by Microsoft. And it shares some of the NFS disadvantages.

And this is where SSHFS comes into play. I've been aware of FUSE (Filesystem in Userspace) for quite some time now, but I've never used it until just recently. FUSE basically lets you implement any filesystem as a service that is running in the userspace. When an application tries to open a file, the kernel routes the request back into the userspace, where the service can do whatever is necessary to access the file. And some bright folks have figured out how to write such userspace component based on SSH (actually the SFTP subsystem of SSH). This means that you have instant access to files on any computer where you already have a SSH account. Since it uses SSH it's much easier to route through firewalls, you only have to open a single port.

I've only had positive experience with SSHFS so far. MacOSX has a nice GUI (Macfusion), on Linux I use the sshfs commandline tool. NetBSD, OpenBSD and FreeBSD all seem to have FUSE support, and it's even being ported to Solaris. This makes it available on all major Unix operating systems. I recommend it to anyone who is trying to connect multiple Unix computers together and is trying avoid the complexity of other network filesystems.