16 February, 2018

Build a Crap Web Form in Haskell in 28 days.

I've been writing an informal series of posts about a small scout camp registration system that I've been building:

Build a Crap Web Form in Haskell in 28 days

29 January, 2018

Yellow Pages for Modern Times

Early on in my career it was pretty common to use clusters comprised of a pile of heterogeneous unix systems: some Sun boxes, some Linux machines, maybe IRIX and AIX in there too.

The thing that made them into a single cluster was that your user account existed on them all, and you had the same home directory on them all: make files on one machine, and they're visible on the other machines; but you still had access to the machine specific features of each host.

The technology of the time often used Network File System (NFS) and Network Information Service (NIS) (formerly known as Yellow Pages, with that name living on in the yp prefix of commands like yppasswd).

Fast-forward a decade or two and things look different: virtual machines are thing now, and more recently, containers. It's now very common to custom build a virtual machine or a container, both with something approximating an entire OS, specifically for running one application, or for running just a single piece of one application.

So maybe you'd connect these pieces - virtual machines or containers - with some kind of socket connection: a web front end exposing HTTP and talking to a PostgreSQL database in another container with no shared files between them.

I did a bunch of stuff this way, and it was great: you can install separate package stacks in isolation from each other. Want this weird version of a library or compiler? Or to run some curl | sudo script without messing up the rest of your system? Or stick with an old distribution of your OS just for one tool? Easy.

But it was a pain getting getting files between different places. Got my text editor and version control set up in one place, but need to compile in another? There are all sorts of different ways to get files between those places: for example, commit regularly to version control; or rsync.

Docker running on one host has options for mounting pieces of the host file system inside containers; but I lacked a good idea of what to mount where.

It was all so simple before: you had ~ everywhere, and nothing else.

So I started using the unix cluster model, described at the top of this post, to guide how I set up a lot of my containers and virtual machines.

The actual technology (NFS, docker volume mounts, YP, LDAP, HESIOD, ...) isn't massively relevant: I've used different mechanisms in different places.

What really matters is: all the (regular human) users get their home directory, mounted at the same place (under /home).

Pretty much with most ways of sharing files, that means the unix user id for that user should be the same everywhere too.

I've implemented this basic model in a few different ways: for a couple of VMs inside the same physical server, a traditional NFS and OpenLDAP setup (NFS for file sharing, LDAP for distributing account details) which is a more modern replacement for NFS/NIS; on my laptop and some of my physical servers, I've got a wrapper around docker called cue which creates exactly one user (the invoking user) inside the container, and mounts their home directory appropriately; I have some ad-hoc docker server containers (eg inbound SMTP) where the whole of /home is volume-mounted, and then OpenLDAP is used to share accounts.

There are plenty of downsides: for example, your whole home directory is accessible in more places than it needs to be and so is more vulnerable; you can't access files outside your home directory, so ~ is now a specially magic directory; posix filesystems work badly in distributed systems. For lots of what I want, these downsides are outweighed by the upsides.

One twist that doesn't happen so much with a cluster of physical machines: a server such as a mail server is now a container which has a mail queue which I want to persist across rebuilds. Rebuilding would be unusual in the physcial machine model because you don't usually rebuild physical servers often. So where should that persistent data go? Inside a specific /home directory? in a /container-data directory that is mounted too, like an alternate version of /home? What user-id should own the queue? Different builds of a container might assign different user-ids to the mail server.

21 January, 2018

A string of DNS protocol bugs.

I went to turn on DNSSEC for cqx.ltd.uk today - the server that signed it broken right before my Christmas busy period so I disabled DNSSEC on that zone until I got round to fixing it.

I've encountered three different apparent protocol implementation bugs in the space of a few hours:

  • Andrews and Arnold's web based control panel accepts DS records as generated by BIND's dnssec-keygen tool but then throws a complicated looking error when talking to Nominet, the UK domain registry, to put those records where they need to be. As far as I can tell, this is because the BIND output has whitespace in the middle of a hex string, something RFC 4034 s5.3 seems to think is acceptable. Why is installing crypto keys always so hard?
  • For a while, Hetzner's recursive resolvers were unable to verify (and therefore refused to answer) results for my zone. I have a suspicion (but I don't have much to go on other than a hunch) that this was something to do with DS records and the actual zone having some kind of mismatch - although Google Public DNS at, and Verisign's DNSSEC checker both worked ok.
  • I discovered an implementation quirk in the Haskell dns library, which I use inside a debugging tool I'm slowly building. This is to do with the mechanism which DNS uses to compress replies: where a domain name would be repeated in a response, it can be replaced by a pointer to another occurence of that name in the reply. It looks like in this case that the dns library will only accept those pointers if they point to regions of the reply that have specifically already been parsed by the domain name parsing code, rather than pointers to arbitrary bytes in the reply. This is frustratingly familiar to another bug I encountered (at Campus London) where their (not-so) transparent firewall was reordering DNS replies; giving a bug that only manifested when I was sitting in their cafe. (github issue #103)

18 January, 2018


One of my customers uses cPanel to administer their internet facing servers.

It has an interesting virtual filesystem setup for sandboxing user accounts: cPanel creates, per user, a new root filesystem for that user, and then chroot into that before running user code (shells, php, ...).

To create that file system, cPanel uses bind mounts to make the root-jail file system look very much like the real root file system.

bind mounts are a thing that appeared after the 1997-era of me spending a lot of time learning Linux. (if it was invented post 2000, lol I've never heard of it)

In the intervening years, isolation techniques like this have been becoming more mainstream - for example, my main use of docker (via my tool cue) has been to prepare and use different root file systems.

Anyway, back to cPanel. I was trying to figure out how this virtual filesystem was constructed. Bind mounts don't appear in the output of mount or df or /proc/mounts with all the information I wanted: the mount just shows are being from the same device that its target is, without saying where that target is.

For example, I can see that /home/virtfs/x/usr/sbin goes to somewhere in the filesystem on s_os-lv_root but not where. (I can guess it's /usr/sbin in this case).

/dev/mapper/vg_os-lv_root   50G   21G   30G  41% /home/virtfs/x/usr/sbin
/dev/mapper/vg_os-lv_root   50G   21G   30G  41% /home/virtfs/x/var/spool
/dev/mapper/vg_os-lv_root   50G   21G   30G  41% /home/virtfs/x/etc/apache2
/dev/mapper/vg_os-lv_root   50G   21G   30G  41% /home/virtfs/y/usr/sbin

Anyway, surely this can't be the way things are??

So along comes findmnt, which gives me this info:

│ │ ├─/home/virtfs/x/usr/sbin     /dev/mapper/vg_os-lv_root[/usr/sbin]
      xfs         ro,relatime,seclabel,attr2,inode64,sunit=512,swidth=512,usrquota

... which tells me that yes that really is mounted from /usr/sbin.

Anyway, a nice new command to discover, around since only util-linux v2.18 in mid 2010.

29 September, 2017

Infrared Raspberry Pi

When I ordered my Pi Zero W with a no-IR-filter camera, I also ordered a single infrared LED. This was enough to prove that I could illuminate things with IR, but not really good enough for more than a small dot of light.

So, then I ordered 50 x £0.04 infrared LEDs from RS (which arrived overnight!) and wired them up yesterday onto a single breadboard powered by a regular USB 5v power supply. You can see the layout in the first picture.

Here's a scene: the first with natural light showing the LEDs on a board, and the next two with infrared illumination. The two infrared scenes appeared almost pitch black to a human eye.

And then I set it up overnight, with a timelapse video of myself sleeping:

10 September, 2017

Unix exit codes as an indicator of tooling (im)maturity.

If your compiler for your new language, or your test running, or whatever, doesn't return a unix exit code when it exits with an error - that's something that annoys me - and it's an indicator that no one is using your tool for serious - for example in an automated build system.

I've hit this a couple of times at least in the last year. grr.

01 September, 2017

Pattern matching in Idris `do` notation has surprising reliance on type checking the action.

Idris is syntactically quite Haskell-like, and especially it has do notation for sequencing "actions".

Like (traditional) Haskell, do blocks are desugared to a sequence of >>= (bind) operators. But, unlike (traditional) Haskell, that >>= is not always the monadic >>= : m a -> (a -> m b) -> m b. (This can also happen in Haskell using rebindable syntax)

In Idris, you can use different "better-than-monad" types to statically (at compile time) reason about a computation beyond using the monad laws. For example, an effect system might track which effects are in scope.

Total parsing

In the case of Text.Parser (in the Idris contrib/ package) the type signature of actions (Grammar _ _ _ indicates whether a parser consumes any characters so that the compiler can tell if a parser might loop forever. (see http://www.cse.chalmers.se/~nad/publications/danielsson-parser-combinators.html)

I was trying to write a new JSON parser for idris-todaybot using Text.Parser. Previously JSON was parsed using lightyear, but Text.Parser has more gratuitous dependent types so was an obvious way to proceed.

A problem.

I ran into a surprising compile error which initially made no sense to me at all.

This code compiles:

objectValuePair : Grammar Char True (List Char, ())
-- definition elided

jsonObject : Grammar Char True (List Char, ())
jsonObject = do
  llll <- objectValuePair
  pure llll

where llll is a tuple; but the following version of jsonObject, which desconstructs that tuple and reassembles it, does not compile:

jsonObject : Grammar Char True (List Char, ())
jsonObject = do
  (k,v) <- objectValuePair
  pure (k,v)

It gives this error:

When checking right hand side of Main.case block
in jsonObject at bug-bind.idr:55:14 with expected type
        Grammar Char c2 (List Char, ())

Type mismatch between
        Grammar Char False (List Char, ()) (Type of pure (k, v))
        Grammar Char c2 (List Char, ()) (Expected type)

        Type mismatch between

Another attempt to deconstruct llll also fails:

  jsonObject : Grammar Char True (List Char, ())
  jsonObject = do
    llll <- objectValuePair
    let (k,v) = llll
    pure (k,v)

but the following deconstruction by function application rather than pattern matching succeeds:

jsonObject : Grammar Char True (List Char, ())
  jsonObject = do
    llll <- objectValuePair
    let k = fst llll
    let v = snd llll
    pure (k,v)

That type error

Let's dig into that type error:

Type mismatch between
        Grammar Char False (List Char, ()) (Type of pure (k, v))
        Grammar Char c2 (List Char, ()) (Expected type)

Grammer _ _ _ is the type of parser actions, where the first parameter Char is the type of symbols we're consuming, the final parameter (List Char, ()) is the type that the parser will return on success, and the middle parameter (False or c2) represents whether the parser will definitely consume input (True) or might succeed without consuming anything (False - for example, a parser which removes whitespace, or pure which never even looks at the input stream).

This "consumes" parameter contains the main novelty in Text.Parser beyond monadic parser combinators: Text.Parser combinators manipulate and use this value at compile time to help check that parsers really will consume things: for example, a parser that definitely consumes followed by a parser that might not, results in a parser that definitely consumes; while sequencing two parsers that might not consume results in a parser that might not consume. (See: the source)

So what on earth has this parameter, manipulated by >>=, got to do with pattern matching pure values after they've already been returned by an action?


It turns out we can forget that our troublesome tuple is being returned from an action; let (a,b) = (1,2) breaks in the same way when run inside a Text.Parser do block.

Let's (very roughly) desugar some of the examples above, and then look at the types involved:

jsonObject : Grammar Char True (List Char, ())
jsonObject = do
  llll <- objectValuePair
  pure llll

-- becomes:
jsonObject = objectValuePair >>= (\v => pure v)

jsonObject = do
  (k,v) <- objectValuePair
-- becomes: 
    objectValuePair >>= (\(k,v) => pure (k,v))
-- becomes: 
    objectValuePair >>= (\llll => case llll of
      (k,v) => pure (k,v)

So in the second fragment, there's an extra case expression in there to deconstruct llll using pattern matching.

Apparently that gets in the way of type inference/checking:

  • On the one hand, that pure has type: Grammar Char False (List Char, ()) - false because it may (actually, will always) succeed without consuming input.
  • On the other hand, >>= doesn't care whether the right hand side consumes or not - it will take either, as shown by the compile time variable c2 appearing in the error message.
Idris doesn't manage to unify c2 = False.

With further pinning of types using the, an uglier form of pattern matching does work:

export jsonObject : Grammar Char True (List Char, ())
  jsonObject = 
    llll <- objectValuePair
    the (Grammar Char False (List Char, ())) $ do
        let (k,v) = llll
        pure (k, v)



Thanks to Melvar on #idris for explaining this.

highlight.js in blogger

Syntax highlighting is pretty. highlight.js can do it in a browser. I just added it to this blog.

  1. In Blogger, click "Theme" then "Edit HTML"
  2. Find the <head> section of the theme.
  3. Insert the following at the end, before the closing <head> tag:
    <link href='//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/default.min.css' rel='stylesheet'/>
    <script src='//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/highlight.min.js'/>
    <script src='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/languages/haskell.min.js'/>
  4. Add as many copies of the third line as you want, modifying haskell to the additional languages that you want to be able to highglight.
  5. When writing code, wrap it in <pre><code class="haskell"> YOUR CODE </code><pre>

31 August, 2017

Raspberry Pi Zero W + cam timelapse

I got a Raspberry Pi Zero W and a camera module.

I wanted a timelapse video.

This is how I did it:

Capture a sequence of image frames

This will capture a sequence of images (forever) approximately every minute, with the filename being a unix timestamp.

while true; do raspi-still -o $(date +%s).jpeg ; sleep 60s; done

Leave this to run for as long as you want to collect frames, and you'll end up with a bunch of numerically named files, like this:

$ ls

Combine the sequence of image frames into a jpeg

After you've got all the frames you want, use ffmpeg to join the frames together. I do this on a different Linux box (my laptop) but you should be able to do it on the Pi too.

First make a command file listing all the jpeg files:

ls *jpeg | while read a ; do echo file $a; done > e.cmd

Next, feed that command file into ffmpeg to generate a video:

ffmpeg -f concat -i e.cmd -b:v 1500000 -r 24 -c:v libx264 e8.mp4

As a result, the output video should be in e8.mpg which is in a form suitable for playing with vlc or uploading to YouTube.

20 August, 2017

An income tax explorer, using R and Shiny

I got carried away with my R code for plotting income tax rates, and now it is an interactive webapp https://benc.shinyapps.io/r-income-tax/ using Shiny, a web framework for turning your R code into a website. Thanks Tom Nielsen for convincing me (in this talk about making a Haskell port of Shiny) to try it out.