<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-4983265076111025145</id><updated>2012-01-28T02:30:00.049Z</updated><category term='languagehacks'/><category term='openid'/><category term='sysadmin'/><category term='sms'/><category term='web'/><category term='rsync'/><category term='latex'/><category term='scifi'/><category term='hash'/><category term='ipv4'/><category term='average'/><category term='rome'/><category term='wtf'/><category term='iphone'/><category term='blogday'/><category term='darcs'/><category term='git'/><category term='horology'/><category term='spam'/><category term='haskell'/><category term='cpanel'/><category term='pam'/><category term='dont-try-this-at-home'/><category term='freebsd'/><category term='x509'/><category term='y2k'/><category term='bind'/><category term='rant'/><category term='bluegene'/><category term='voting'/><category term='socialism'/><category term='retro'/><category term='oh-so-funny'/><category term='java'/><category term='mysql'/><category term='security'/><category term='barwench'/><category term='maths'/><category term='cqx'/><category term='gsm'/><category term='cloud'/><category term='mac-repair-bs'/><category term='ntp'/><category term='ui'/><category term='photo'/><category term='COBOL'/><category term='presenting'/><category term='incompatibility'/><category term='dns'/><category term='html'/><category term='mac'/><category term='ssl'/><category term='dhcp'/><category term='fidonet'/><category term='railway'/><category term='whiskey'/><category term='blogging'/><category term='chess'/><category term='vcs'/><category term='stupid'/><category term='svn'/><category term='ruby'/><category term='hpc'/><category term='version-numbers'/><category term='scuba'/><category term='ec2'/><category term='6to4'/><category term='perl'/><category term='monad'/><category term='mpi'/><category term='ipv6day'/><category term='userinterface'/><category term='graph'/><category term='http'/><category term='osx'/><category term='cookers'/><category term='electricity'/><category term='mrtg'/><category term='decay'/><category term='unnecessary-alarm'/><category term='python'/><category term='shell'/><category term='uptime'/><category term='parallel'/><category term='analytical-engine'/><category term='Erdős'/><category term='blinkenlights'/><category term='hotp'/><category term='markup'/><category term='hardware'/><category term='usb'/><category term='64bits'/><category term='random'/><category term='nmap'/><category term='ln'/><category term='ssh'/><category term='backups'/><category term='draft'/><category term='logfile'/><category term='roman'/><category term='sql'/><category term='attempt-at-explaining'/><category term='words'/><category term='agda'/><category term='unix'/><category term='dnssec'/><category term='tunnel'/><category term='mod_rewrite'/><category term='server'/><category term='https'/><category term='iptables'/><category term='browserid'/><category term='grumble'/><category term='password'/><category term='ipv6'/><category term='utilities'/><title type='text'>benc technical blog</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>97</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-4772593656761320164</id><published>2012-01-28T02:30:00.000Z</published><updated>2012-01-28T02:30:00.055Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='shell'/><category scheme='http://www.blogger.com/atom/ns#' term='iphone'/><category scheme='http://www.blogger.com/atom/ns#' term='html'/><category scheme='http://www.blogger.com/atom/ns#' term='barwench'/><title type='text'></title><content type='html'>&lt;p&gt;Well, I came across a magic line of HTML for making a website look basically readable on an iPhone. Not magic in the sense that I don't understand what it does. But magic in the sense that its a single line that is the first big step to making a site look OK.&lt;/p&gt;&lt;p&gt;The line is (to go in your &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; section:&lt;br/&gt;&lt;code&gt;&amp;lt;meta name="viewport" content="width = device-width" /&amp;gt;&lt;/code&gt;&lt;/p&gt;&lt;p&gt;What it does is make the iPhone web browser render the HTML at a sensible readable font size, with word wrapping at the end of the screen. (the default, otherwise, is to try to fit a regular screen worth of pixels across, then zoom out to make it fit on the small iPhone screen - that means the user has to zoom and pan to do anything).&lt;/p&gt;&lt;p&gt;Now my pages still look like crappy hand written HTML, but at least they're readable on an iPhone now.&lt;/p&gt;&lt;/p&gt;&lt;p&gt;I added this to &lt;a href="http://s0.barwen.ch/projects/browser-login"&gt;the shellinabox installation I have on barwen.ch&lt;/a&gt;, and now its much prettier to use a browser-based shell on an iphone - you get a 30 character terminal thats at sensible font size, rather than a wide wide terminal at unreadable font size.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-4772593656761320164?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/4772593656761320164/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2012/01/well-i-came-across-magic-line-of-html.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/4772593656761320164'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/4772593656761320164'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2012/01/well-i-came-across-magic-line-of-html.html' title=''/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-6855526592923693960</id><published>2012-01-21T10:18:00.000Z</published><updated>2012-01-21T10:18:36.453Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='password'/><category scheme='http://www.blogger.com/atom/ns#' term='hotp'/><category scheme='http://www.blogger.com/atom/ns#' term='security'/><category scheme='http://www.blogger.com/atom/ns#' term='cqx'/><title type='text'>Two hardware OTP keys</title><content type='html'>&lt;p&gt;I got a couple of OTP (one time password) keys to try out. These are hardware dongles that generate a unique code number every time you use them, which you then use in addition to a password when you log in to places (eg your server, some website).&lt;/p&gt;&lt;p&gt;The goal is to make things more secure by not having a password someone can steal.&lt;/p&gt;&lt;p&gt;The mechanics of this used to be hard to describe but enough people use online banking with security tokens now (at least in western europe) that the idea is pretty widely known already. (You can read about &lt;a href="http://en.wikipedia.org/wiki/Two-factor_authentication"&gt;two factor authentication on wikipedia&lt;/a&gt;)&lt;/p&gt;&lt;hr/&gt;&lt;p&gt;&lt;img style="float:right" src="http://static.yubico.com/var/uploads/webstore-banner.jpg" /&gt;First, I tried a &lt;a href="http://yubico.com/yubikey"&gt;yubikey&lt;/a&gt;. Yubikey comes with a silly tagline, "&lt;em&gt;the key to the cloud&lt;/em&gt;", but don't let that put you off. The yubikey plugs into a USB port on your computer and when you press its single button, it types in the next code in sequence as if it was a USB keyboard. I've tried this on a linux box and and an OS X box and it had no problem on either.&lt;/p&gt;&lt;p&gt;Pressing a button seems much less hassle than typing in a code off an LCD screen, but it does come with downsides: you need to have the device in a USB slot when you press the button. On a tower desktop, that's possibly down by the floor. Even worse, maybe you don't have a USB slot at all, in which case the device is useless.&lt;/p&gt;&lt;p&gt;yubikey has a number of different modes, and can store two configurations at once (yes, even though it has just one button).&lt;/p&gt;&lt;p&gt;There's a yubikey proprietary mode which generates a long key string which contains a bunch of stuff (for example, a device ID); an HOTP mode which generates a 6 or 8 character code (with programmable extra decoration); and a static mode which types in a preprogrammed fixed 64 character string. This is all configured by some software that you get for free off the yubikey website. I'm always a bit wary of vendor software to support hardware devices, because it often seems a much lower priority than the hardware device itself. But this worked well enough.&lt;/p&gt;&lt;p&gt;I only tried out the HOTP mode, because I wanted interoperability with other OTP implementations.&lt;/p&gt;&lt;p&gt;The two configurations are accessed by holding down the button either for less than 2s or for more than 2s. I've only used the first configuration, and I haven't had any trouble with accidentally falling through to the second one. But it sounds a bit cracked out and if I was giving it to the kind of user who would hold the button extra long "just to be sure," then maybe there would be trouble. I was hoping there would be an option to switch that second configuration off, but I didn't see one.&lt;/p&gt;&lt;p&gt;My only interaction with the supplier, yubico, was to order the key online, for $30+VAT. This arrived 24h after I order it, by regular mail (from the next village over!).&lt;/p&gt;&lt;hr/&gt;&lt;p&gt;&lt;img src="http://www.gooze.eu/sites/default/files/imagecache/product/products/p1040945_1.png" style="float:right" /&gt;The second device I got was an &lt;a href="http://www.gooze.eu/otp-c200-token-time-based-h3-casing-1-unit"&gt;OTP C200 token&lt;/a&gt; from Gooze. This has a more traditional user interface with a 6 digit LCD display and a button which turns on/off the display. The C200 is a TOTP token: HOTP, except the code changes every 30 seconds instead of when you press the button.&lt;/p&gt;&lt;p&gt;Gooze also makes a C100 which is regular HOTP. I haven't tried one of these, but the design of the C200 case makes me think that the button would get pressed a bunch randomly if you're carrying this with your keys in your pocket. With TOTP, that's not a problem - the code is not related to the button press. But I've encountered loss-of-sync troubles with other hardware tokens before due to this and I don't think the C100 solves that problem.&lt;/p&gt;&lt;p&gt;There is no configuration of the device itself needed - it only does TOTP, and the seed value is preloaded. You get sent that by Gooze. This was sent by GPG encrypted mail so I could cut and paste it into the configuration of my server easily. It means Gooze knows your secret key (although they claim to delete them after sending). I'm not too fussed by that because I'm not aiming for über-high security, but I'm sure some people will.&lt;/p&gt;&lt;p&gt;Worse though was that through some mess up in customer service, it took them over a week to get the codes to me after the devices arrived, and they were pretty silent during that week despite repeated enquiries. I think this is due to the company being pretty small. This is almost enough to make me not order from them again.&lt;/p&gt;&lt;p&gt;Because the C200 has a screen, you have to read the code and type it into your computer by hand. So some properties are inverted from the yubikey: it's a hassle to type in the code; but it doesn't matter if you have a USB port. Because of that, I think this is more appropriate than the yubikey for "I'm going on holiday but want to be able to access my email from public terminals" uses.&lt;/p&gt;&lt;hr /&gt;&lt;p&gt;I wired both of these up to &lt;code&gt;pam_oath&lt;/code&gt; to log into my linux servers; maybe I'll write about that side of things another time. Neither device has beaten the other in being my favourite - the yubikey has substantially higher geek value for plugging into a USB port and greater convenience in some circumstances, but the C200 feels more practical for other use cases such as connecting from unfamilar devices. I've only had a few days to form the above opinions and I expect I'll form more opinions over time.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-6855526592923693960?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/6855526592923693960/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2012/01/two-hardware-otp-keys.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/6855526592923693960'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/6855526592923693960'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2012/01/two-hardware-otp-keys.html' title='Two hardware OTP keys'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-900361192258813781</id><published>2012-01-15T19:28:00.000Z</published><updated>2012-01-15T19:40:41.296Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='uptime'/><category scheme='http://www.blogger.com/atom/ns#' term='sysadmin'/><category scheme='http://www.blogger.com/atom/ns#' term='unix'/><category scheme='http://www.blogger.com/atom/ns#' term='average'/><title type='text'>server availability like uptime</title><content type='html'>&lt;p&gt;I wondered if I could get a measure of server availability as a single number, automatically (for calculating things like how tragically few nines of uptime my own servers have)&lt;/p&gt;&lt;p&gt;So, I wrote a tool called &lt;code&gt;long-uptime&lt;/code&gt; which you use like this:&lt;/p&gt;&lt;p&gt;The first time you run the code, initialise the counter. You can specify your estimate, or let it default to 0:&lt;pre&gt;&lt;br /&gt;$ &lt;em&gt;long-uptime --init&lt;/em&gt;&lt;br /&gt;&lt;/pre&gt;and then every minute in a cronjob run this:&lt;pre&gt;&lt;br /&gt;$ &lt;em&gt;long-uptime&lt;/em&gt;&lt;br /&gt;0.8974271427587808&lt;br /&gt;&lt;/pre&gt;which means that the site has 89.7% uptime.&lt;/p&gt;&lt;p&gt;It computes an &lt;a href="http://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average"&gt;exponentially weighted average&lt;/a&gt; with a decay constant (which is a bit like a half life) of a month. This is how unix load averages (the last three values that come out of the &lt;code&gt;uptime&lt;/code&gt; command) are calculated, though with much shorter decay constants of 1, 5, and 15 minutes.&lt;/p&gt;&lt;p&gt;When the machine is up (that is, you are running &lt;code&gt;long-uptime&lt;/code&gt; in a cron job), then the average moves towards 1. When the machine is down (that is, you are not running &lt;code&gt;long-uptime&lt;/code&gt;), then the average moves towards 0. Or rather, the first time you run &lt;code&gt;long-uptime&lt;/code&gt; after a break, it realises you haven't run it during the downtime and recomputes the average as if it had been accumulating 0 scores.&lt;/p&gt;&lt;p&gt;Download the code:&lt;pre&gt;&lt;br /&gt;$ &lt;em&gt;wget http://www.hawaga.org.uk/tmp/long-uptime-0.1.tar.gz&lt;/em&gt;&lt;br /&gt;$ &lt;em&gt;tar xzvf long-uptime-0.1.tar.gz&lt;/em&gt;&lt;br /&gt;$ &lt;em&gt;cabal install&lt;/em&gt;&lt;br /&gt;$ &lt;em&gt;long-uptime --init&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-900361192258813781?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/900361192258813781/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2012/01/server-availability-like-uptime.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/900361192258813781'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/900361192258813781'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2012/01/server-availability-like-uptime.html' title='server availability like uptime'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-2687306820712915193</id><published>2012-01-09T12:42:00.002Z</published><updated>2012-01-09T12:42:06.068Z</updated><title type='text'>cuba</title><content type='html'>was in cuba for a month. internet is hard there (though possible) so no updates on this blog in that time. sorry.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-2687306820712915193?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/2687306820712915193/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2012/01/cuba.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/2687306820712915193'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/2687306820712915193'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2012/01/cuba.html' title='cuba'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-6912583459636612802</id><published>2011-12-10T11:38:00.000Z</published><updated>2011-12-10T11:38:01.360Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='x509'/><category scheme='http://www.blogger.com/atom/ns#' term='mod_rewrite'/><category scheme='http://www.blogger.com/atom/ns#' term='ssl'/><category scheme='http://www.blogger.com/atom/ns#' term='https'/><category scheme='http://www.blogger.com/atom/ns#' term='sysadmin'/><category scheme='http://www.blogger.com/atom/ns#' term='cpanel'/><category scheme='http://www.blogger.com/atom/ns#' term='security'/><title type='text'>https in cpanel</title><content type='html'>&lt;p&gt;working with someone who has a cpanel server. they want https on it. cpanel doesn't do that by default. google doesn't reveal much in the way of tutorials for this, so here's a note for people to find.&lt;/p&gt;&lt;ol&gt;&lt;li&gt;generate a key pair and certificate using the&lt;em&gt;Generate a SSL Certificate &amp;amp; Signing Request&lt;/em&gt; page. Copy the certificate onto your clipboard.&lt;/li&gt;&lt;li&gt;go to the &lt;em&gt;Install a SSL Certificate and Setup the Domain&lt;/em&gt; page. Paste in the certificate. click &lt;em&gt;fetch&lt;/em&gt; on the key text field and it should populate that field for you. Set the username to &lt;em&gt;nobody&lt;/em&gt; so that all users can use this key pair.&lt;/li&gt;&lt;li&gt;When you save that page, apache will reload and you'll get https service on port 443, with a self-signed certificate (and so with consequent certificate mismatch error messages). But your existing domains won't work on that server - they'll go to the default cpanel parking page - cpanel only configures its virtual hosts on port 80... grr&lt;/li&gt;&lt;li&gt;So next I made an apache mod_rewrite rule in the VirtualHost directive for the port 443 virtual server. That causes all the internal sites appear on port 443.&lt;pre&gt;&lt;br /&gt;    RewriteEngine on&lt;br /&gt;    RewriteRule   ^(.+)          http://%{HTTP_HOST}$1 [P]&lt;br /&gt;&lt;/pre&gt;That's an awkward hack to have to add to cpanel's generated config, but it seems to work (modulo invalid certificate warnings that all users ignore anyway)... &lt;/li&gt;&lt;/ol&gt;&lt;p&gt;There's also a hole in the way that that rewrite rule is implemented: with a custom http client, you can probably make this server act as an arbitrary proxy for you, depending on mod_proxy configuration.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-6912583459636612802?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/6912583459636612802/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/12/https-in-cpanel.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/6912583459636612802'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/6912583459636612802'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/12/https-in-cpanel.html' title='https in cpanel'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-6847625730147575715</id><published>2011-12-03T15:01:00.002Z</published><updated>2011-12-03T15:01:00.205Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='dns'/><category scheme='http://www.blogger.com/atom/ns#' term='barwench'/><category scheme='http://www.blogger.com/atom/ns#' term='dhcp'/><title type='text'>DHmPC</title><content type='html'>I have a server running inside EC2. It gets its network details using dhcp.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;ubuntu@s0:~$ hostname&lt;br /&gt;s0.barwen.ch&lt;br /&gt;ubuntu@s0:~$ hostname --fqdn&lt;br /&gt;hostname: Name or service not known&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;grr.&lt;br /&gt;&lt;br /&gt;This happens pretty much every time the VM reboots. Its something to do with getting a new private IP address each time it reboots.&lt;br /&gt;&lt;br /&gt;Although this manages to work:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;hostname --all-fqdns&lt;br /&gt;s0.barwen.ch polm.pl stacheldraht.it s0.barwen.ch&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The autoconfigured resolv.conf looks like this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;nameserver 172.16.0.23&lt;br /&gt;domain eu-west-1.compute.internal&lt;br /&gt;search eu-west-1.compute.internal&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;and if I comment out or remove both the &lt;code&gt;domain&lt;/code&gt; and &lt;code&gt;search&lt;/code&gt; lines, then everything works...&lt;br /&gt;&lt;br /&gt;Those lines are wrong anyway - this machine is in my barwen.ch domain. It just happens to be hosted in amazon's network...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-6847625730147575715?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/6847625730147575715/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/12/dhmpc.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/6847625730147575715'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/6847625730147575715'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/12/dhmpc.html' title='DHmPC'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-4510153544048082629</id><published>2011-11-29T12:24:00.002Z</published><updated>2011-11-29T12:45:24.054Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='sql'/><category scheme='http://www.blogger.com/atom/ns#' term='mysql'/><category scheme='http://www.blogger.com/atom/ns#' term='rsync'/><category scheme='http://www.blogger.com/atom/ns#' term='backups'/><title type='text'>mysql rsync backup doh!</title><content type='html'>&lt;p&gt;Well, I moved a big live database from one server to another for a customer. I was planning on taking the site offline first and doing an sql dump/move/restore, but someone accidentally deleted the old server before they were meant too (oops) - leaving me with some &lt;code&gt;rsync&lt;/code&gt; backups that I'd made of the whole server over the previous days, to guard against such an eventuality.&lt;/p&gt;&lt;p&gt;Turns out rsyncing a live mysql database is not the right way to do things - I ended up with a database that managed to get &lt;code&gt;mysqld&lt;/code&gt; to actually crash when I tried to dump out the database contents. I managed to fix this by dropping the tables that it was crashing on: it conveniently told me which ones it was crashing on, and it let me drop them, just not dump them. Luckily they were all cache tables so there didn't seem to be harm in dropping them and recreating them empty.&lt;/p&gt;&lt;p&gt;It also turns out (obviously, post facto) that mysql databases don't play nicely with rsync's &lt;code&gt;--link-dest&lt;/code&gt; option, which is supposed to reduce disk space on the target file system when making multiple snapshots. Because it only optimises away duplicate data for identical files, but mysql stores the entire database (to a first approximation) in a single file, hiding its internal structure from rsync, and so the whole db gets duplicated each time.&lt;/p&gt;&lt;p&gt;Taking an SQL dump should solve the first problem. Not entirely sure what the right way to do an incremental backup of the database is. Maybe I shouldn't even try.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-4510153544048082629?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/4510153544048082629/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/11/mysql-rsync-backup-doh.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/4510153544048082629'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/4510153544048082629'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/11/mysql-rsync-backup-doh.html' title='mysql rsync backup doh!'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-685690232789892851</id><published>2011-11-19T08:26:00.001Z</published><updated>2011-11-19T08:37:29.476Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='dns'/><title type='text'>batch checking DNS delegation</title><content type='html'>&lt;p&gt;I'm working with someone who has ten or so domains. All the domains are registered in different places, and with slightly different DNS settings. As part of tidying that up to get everything consistent, I wrote the following bash+dig script to display the delegation of each zone from its parent.&lt;/p&gt;&lt;p&gt;That is importantly not the same as what the name servers for the zone return as NS records. The "authoritative" source of NS (nameserver) records for a zone is that zone itself. Using dig to query the NS records seems to be returning those, unsurprisingly.&lt;/p&gt;&lt;p&gt;However, in order to query a zone, there is a second place where name servers must be configured, separately from in the zone itself. That is in the parent zone. If those are wrong, then you can get awkward to diagnose problems: you can see from dig that the nameservers are right, yet lookups go to the wrong place.&lt;/p&gt;&lt;p&gt;Hence my script.&lt;/p&gt;&lt;p&gt;You can see even on my own domains there's a slight misconfiguration: barwen.ch claims to have s0.barwen.ch as a server, but the .ch registry isn't delegating to it. That won't cause bad DNS lookups but will cause s0.barwen.ch to not be used as a nameserver sometimes. Worse is when the delegation points to an old server that then returns a new servers DNS, giving the illusion that all is well, until you turn off the old server (which is the problem I have on other zones)&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;$ ./list-domains-NS.sh &lt;br /&gt;ZONE hawaga.org.uk&lt;br /&gt;NAMESERVERS ACCORDING TO GOOGLE DNS&lt;br /&gt;dildano.hawaga.org.uk.&lt;br /&gt;paella.hawaga.org.uk.&lt;br /&gt;NAMESERVERS ACCORDING TO org.uk&lt;br /&gt;hawaga.org.uk.  172800 IN NS paella.hawaga.org.uk.&lt;br /&gt;hawaga.org.uk.  172800 IN NS dildano.hawaga.org.uk.&lt;br /&gt;&lt;br /&gt;ZONE barwen.ch&lt;br /&gt;NAMESERVERS ACCORDING TO GOOGLE DNS&lt;br /&gt;paella.hawaga.org.uk.&lt;br /&gt;s0.barwen.ch.&lt;br /&gt;dildano.hawaga.org.uk.&lt;br /&gt;NAMESERVERS ACCORDING TO ch&lt;br /&gt;barwen.ch.  3600 IN NS dildano.hawaga.org.uk.&lt;br /&gt;barwen.ch.  3600 IN NS paella.hawaga.org.uk.&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;So here's the script:&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;#!/bin/bash&lt;br /&gt;cat domainlist.txt | while read d ; do&lt;br /&gt;  echo "ZONE $d"&lt;br /&gt;  echo "NAMESERVERS ACCORDING TO GOOGLE DNS"&lt;br /&gt;  dig @8.8.8.8 -t ns $d +short&lt;br /&gt;&lt;br /&gt;  PARENT=$(echo $d | sed 's/^[^.]*\.//')&lt;br /&gt;  echo "NAMESERVERS ACCORDING TO $PARENT"&lt;br /&gt;&lt;br /&gt;  PARENTNS=$(dig +short -t NS ${PARENT}. | head -n 1)&lt;br /&gt;&lt;br /&gt;  dig @$PARENTNS -t NS +noall +authority +norecurse $d&lt;br /&gt;&lt;br /&gt;  echo&lt;br /&gt;done&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-685690232789892851?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/685690232789892851/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/11/batch-checking-dns-delegation.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/685690232789892851'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/685690232789892851'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/11/batch-checking-dns-delegation.html' title='batch checking DNS delegation'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-8799421344805624771</id><published>2011-11-12T11:06:00.008Z</published><updated>2011-11-12T11:06:00.098Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='ui'/><title type='text'>country selector</title><content type='html'>I've had a rant queued up on this blog but never completed about various forms of internationalisation/localisation.&lt;br /&gt;&lt;br /&gt;One part of the rant is about web forms that ask for your country. A common way to do this is a drop down list of "every country" (deliberately in quotes). That's awful.&lt;br /&gt;&lt;br /&gt;Its especially awful when you are from simultaneously from: Britain; United Kingdom; Great Britain; and England, all "countries" in someone's definition or another - not only do you have to scroll down to your country, you don't even know which of the four countries you are meant to scroll to until you get there and find that country missing.&lt;br /&gt;&lt;br /&gt;I saw (on hacker news) this redesigned country selector: &lt;a href="http://baymard.com/labs/country-selector"&gt;http://baymard.com/labs/country-selector&lt;/a&gt; which presents an autocompleting text box.&lt;br /&gt;&lt;br /&gt;Type "eng" and it brings up "United Kingdom". woo!&lt;br /&gt;&lt;br /&gt;It also converts "deut" into "Germany", but fails to convert ”日” into Japan.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-8799421344805624771?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/8799421344805624771/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/11/country-selector.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/8799421344805624771'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/8799421344805624771'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/11/country-selector.html' title='country selector'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-3556047800738778647</id><published>2011-10-08T10:32:00.000+01:00</published><updated>2011-10-08T10:32:00.421+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='userinterface'/><title type='text'>beep is not useful.</title><content type='html'>sometimes I get a beep on my laptop.my console has ~10 applications on 9 desktops, including being remotely connected to ~5 machines.a beep without any context is not useful.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-3556047800738778647?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/3556047800738778647/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/10/beep-is-not-useful.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/3556047800738778647'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/3556047800738778647'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/10/beep-is-not-useful.html' title='beep is not useful.'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-505284691570748251</id><published>2011-10-01T10:53:00.000+01:00</published><updated>2011-10-01T10:53:00.519+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='logfile'/><category scheme='http://www.blogger.com/atom/ns#' term='ssh'/><category scheme='http://www.blogger.com/atom/ns#' term='security'/><category scheme='http://www.blogger.com/atom/ns#' term='unnecessary-alarm'/><title type='text'>POSSIBLE BREAK-IN ATTEMPT (not really)</title><content type='html'>SSH gives out error messages like this:&lt;pre&gt;&lt;br /&gt;Sep 28 09:50:09 s0 sshd[27967]: reverse mapping checking &lt;br /&gt;                  getaddrinfo for adsl86-34-217-144.romtelecom.net &lt;br /&gt;                  [86.34.217.144] failed - POSSIBLE BREAK-IN ATTEMPT!&lt;br /&gt;&lt;/pre&gt;Why does it label it as &lt;em&gt;POSSIBLE BREAK-IN ATTEMPT!&lt;/em&gt;?How is it more of a possible break-in attempt than a user attempting to connect more than a few times with a wrong password?This has bugged me a bit recently when helping a few people who aren't really used to linux - its shouting at them that something is SERIOUSLY WRONG!!! and when they look through their log files, they've fixated on this (as far as I can) relatively minor misconfiguration of a remote user's network.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-505284691570748251?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/505284691570748251/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/10/possible-break-in-attempt-not-really.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/505284691570748251'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/505284691570748251'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/10/possible-break-in-attempt-not-really.html' title='POSSIBLE BREAK-IN ATTEMPT (not really)'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-7586234914020119035</id><published>2011-09-24T18:57:00.000+01:00</published><updated>2011-09-24T18:57:00.215+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='cookers'/><category scheme='http://www.blogger.com/atom/ns#' term='horology'/><category scheme='http://www.blogger.com/atom/ns#' term='hardware'/><title type='text'>♫ if i could turn back time ♪</title><content type='html'>Cooker has a one-way-only knob for setting time. You wind forwards until you reach now. If you miss now, you wind forwards another 24h (luckily the cooker doesn't have a date too) until you reach now again. On TZ shift (i.e. end/start of summertime / daylight savings), once per year, there's a wind-forward 1h event. fairly easy. you make the clock run faster than real time. but once per year there's a once per year wind-forward 23h event. BUT! Turns out if you can stop the clock, then you can let real time wind forwards faster than clock time. and wait an hour. and then start the clock again. It involves more real time (you have to wait an hour) but a lot less tedious winding.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-7586234914020119035?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/7586234914020119035/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/09/if-i-could-turn-back-time.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/7586234914020119035'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/7586234914020119035'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/09/if-i-could-turn-back-time.html' title='♫ if i could turn back time ♪'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-6034560317323131214</id><published>2011-09-17T13:35:00.000+01:00</published><updated>2011-09-17T13:35:00.812+01:00</updated><title type='text'>wikipedia find-of-the-day: electronic calculator abacus</title><content type='html'>&lt;img height="40%" src="http://upload.wikimedia.org/wikipedia/commons/1/18/Sharp-abacus-japan.jpg" width="40%" /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;linked on the wikipedia page for &lt;a href="http://en.wikipedia.org/wiki/Soroban"&gt;Soroban&lt;/a&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-6034560317323131214?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/6034560317323131214/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/09/wikipedia-find-of-day-electronic.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/6034560317323131214'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/6034560317323131214'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/09/wikipedia-find-of-day-electronic.html' title='wikipedia find-of-the-day: electronic calculator abacus'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-962070808687968462</id><published>2011-09-10T16:59:00.000+01:00</published><updated>2011-09-10T16:59:00.699+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='hpc'/><category scheme='http://www.blogger.com/atom/ns#' term='whiskey'/><category scheme='http://www.blogger.com/atom/ns#' term='mpi'/><category scheme='http://www.blogger.com/atom/ns#' term='bluegene'/><category scheme='http://www.blogger.com/atom/ns#' term='parallel'/><title type='text'>5 centuries ago...</title><content type='html'>I just queued up jobs for a physics simulation that are 5 compute-centuries long:&lt;br /&gt;&lt;br /&gt;6 jobs x 11.5 hours x 16384 nodes x 4 cores/node = 5.16 centuries.&lt;br /&gt;&lt;br /&gt;I'm expecting the runs to be finished in about a week&lt;br /&gt;&lt;br /&gt;If this ran on a single core and I wanted it to be finishing in about a week, I would have had to start them in &lt;a href="http://en.wikipedia.org/wiki/1495"&gt;the year 1495&lt;/a&gt; (the most interesting wikipedia note for that year is &lt;em&gt;Friar John Cor records the first known batch of Scotch whisky&lt;/em&gt;).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-962070808687968462?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/962070808687968462/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/09/5-centuries-ago.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/962070808687968462'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/962070808687968462'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/09/5-centuries-ago.html' title='5 centuries ago...'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-2922072125460076756</id><published>2011-09-03T13:48:00.000+01:00</published><updated>2011-09-03T13:48:00.501+01:00</updated><title type='text'>frustrated-with-slow-simulation haiku</title><content type='html'>&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;br /&gt;microseconds pass&lt;br /&gt;simulation time stretched&lt;br /&gt;long as winters&amp;nbsp;night&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;I can't complain too much though - this was 65536 cores on an IBM BlueGene/P&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-2922072125460076756?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/2922072125460076756/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/09/frustrated-with-slow-simulation-haiku.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/2922072125460076756'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/2922072125460076756'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/09/frustrated-with-slow-simulation-haiku.html' title='frustrated-with-slow-simulation haiku'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-8241389420218908356</id><published>2011-08-31T01:01:00.000+01:00</published><updated>2011-08-31T01:01:00.294+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='blogday'/><category scheme='http://www.blogger.com/atom/ns#' term='blogging'/><title type='text'>today is blog day</title><content type='html'>Today is &lt;a href="http://www.blogday.org/"&gt;blog day&lt;/a&gt; (well, today isn't, it's the 19th of May, but hurrah for autopost)&lt;br /&gt;&lt;br /&gt;I'm supposed to recommend 5 blogs to you.&lt;br /&gt;&lt;br /&gt;I'm also supposed to find *new* blogs. But FTS, I'm too lazy. Plus I know the ones listed here are ones that I know I go back to day after day and find useful.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.oglaf.com"&gt;Oglaf&lt;/a&gt; is some dude's NSFW comic that started out as an attempt at cartoon porn but now is mostly funny. &lt;a href="http://oglaf.com/shield-maiden/"&gt;The shield maiden&lt;/a&gt; is my favourite post.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://news.ycombinator.com/"&gt;Hacker News&lt;/a&gt; from ycombinator - kinda like slashdot in that its a lot of interesting geek links. More links. Less discussion. Feels more intimate, and more focused around the bay area startup crowd.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://lambda-the-ultimate.org/"&gt;Lambda the ultimate&lt;/a&gt; - a programming languages weblog. A cross between programming and maths. If you like Haskell, you'll like this, though all sorts of language stuff (theory, design) is talked about. You can also laugh at the "amusing" paper titles that authors come up with.&lt;br /&gt;&lt;br /&gt;In a bit of shameless self promotion, I'll point you at the &lt;a href="http://s0.barwen.ch/~benc/drupal/tracker"&gt;drupal news feed for my hobby project, barwen.ch&lt;/a&gt; - this is partly blog, partly just a web page change tracker.&lt;br /&gt;&lt;br /&gt;Finally, &lt;a href="http://www.schneier.com/"&gt;Bruce Schneier's security blog&lt;/a&gt;. Aside from his tendency to ramble on about squids, this is pretty interesting. It evolved out of his company newsletter, and talks mostly about computer security but also about other related topics such as social engineering and terrorism. And squid.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-8241389420218908356?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/8241389420218908356/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/08/today-is-blog-day.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/8241389420218908356'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/8241389420218908356'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/08/today-is-blog-day.html' title='today is blog day'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-690115788700068150</id><published>2011-08-27T22:38:00.000+01:00</published><updated>2011-08-27T22:38:00.349+01:00</updated><title type='text'>Exbibyte divergence.</title><content type='html'>Mostly used by debian crackheads, but slowly becoming more widespread (and apparently also defined by the IEC) is the use of kibibyte to mean a kilobyte measured as 1024 bytes, so that KB can be used to mean kilobyte measured as 1000 bytes. There's a 2.4% difference between the two.&lt;br /&gt;&lt;br /&gt;Correspondingly there is Mibibyte, Gibibyte, tebibyte, pebibyte, exbibyte, zebibyte, and yobibyte.&lt;br /&gt;&lt;br /&gt;While the difference is only 2.4% at the kibibyte scale, it gets bigger with larger prefixes: by the time you get to petabytes, the scale of the largest file systems I have access to these days, the difference is over 12% and by yobibytes its 20%&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-690115788700068150?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/690115788700068150/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/08/exbibyte-divergence.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/690115788700068150'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/690115788700068150'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/08/exbibyte-divergence.html' title='Exbibyte divergence.'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-6030285607633152304</id><published>2011-08-20T12:20:00.000+01:00</published><updated>2011-08-20T12:20:00.622+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='grumble'/><category scheme='http://www.blogger.com/atom/ns#' term='sms'/><title type='text'>brazen sms charging</title><content type='html'>Its fairly obvious for anyone who thinks about it for a while that SMS is appalling bad value per-byte compared to IP or voice over the same system.&lt;br /&gt;&lt;br /&gt;But Orange UK really made me laugh/cry with the brazen way in which they juxtaposed two menu options to make it so clear:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;To buy 50 UK SMSes valid for a day for 1 pound, press 1.&lt;br /&gt; To buy 50 Mb of data valid for a day for 1 pound, press 2.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Yes, 1 UK SMS == 1 Mb of internet data.&lt;br /&gt;&lt;br /&gt;W&lt;br /&gt;T&lt;br /&gt;F&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-6030285607633152304?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/6030285607633152304/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/08/brazen-sms-charging.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/6030285607633152304'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/6030285607633152304'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/08/brazen-sms-charging.html' title='brazen sms charging'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-5036573735522950803</id><published>2011-08-13T12:22:00.001+01:00</published><updated>2011-08-13T12:22:00.971+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='y2k'/><title type='text'>y2.1k bug</title><content type='html'>a decade into this century, and I see plenty of people using 2 digit years. we all know where that's leading...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-5036573735522950803?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/5036573735522950803/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/08/y21k-bug.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/5036573735522950803'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/5036573735522950803'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/08/y21k-bug.html' title='y2.1k bug'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-1797485253669393411</id><published>2011-08-06T18:44:00.000+01:00</published><updated>2011-08-06T18:44:00.303+01:00</updated><title type='text'>auctions in course allocation</title><content type='html'>Apparently some universities use an auction mechanism for allocation of places in courses, as detailed for example in page three of &lt;a href="http://www.law.nyu.edu/ecm_dlv/groups/public/@nyu_law_website__academics/documents/web_copytext/ecm_pro_061262.pdf"&gt;this memo from NYU&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Very briefly describing this in quotes:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;em&gt;Students will be allotted bidding points&lt;/em&gt;&lt;/li&gt;&lt;li&gt;&lt;em&gt;After an auction is run, students admitted to a class will be charged a “clearing price” in points equal to the highest bid from a student not admitted to the class at that auction.&lt;/em&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-1797485253669393411?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/1797485253669393411/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/08/auctions-in-course-allocation.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/1797485253669393411'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/1797485253669393411'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/08/auctions-in-course-allocation.html' title='auctions in course allocation'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-7469347342503740264</id><published>2011-07-30T09:03:00.002+01:00</published><updated>2011-07-30T09:03:00.106+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='presenting'/><category scheme='http://www.blogger.com/atom/ns#' term='html'/><title type='text'>HTML Slidy</title><content type='html'>&lt;a&gt;HTML Slidy&lt;/a&gt; is a neat style sheet for making powerpoint-like presentations that are: i) written in HTML, and ii) appear in a browser. I've used it for &lt;a href="http://www.hawaga.org.uk/ben/tech/swiftscript-uu.html#(1)"&gt;one presentation&lt;/a&gt; and quite liked it. My presentation was entirely text. I can imagine it being more awkward for graphics-intensive presentations where the automatic reflowing of content to fit the current window turns into a downside rather than an upside.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-7469347342503740264?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://www.w3.org/Talks/Tools/Slidy2' title='HTML Slidy'/><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/7469347342503740264/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/07/html-slidy.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/7469347342503740264'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/7469347342503740264'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/07/html-slidy.html' title='HTML Slidy'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-3558607401599891118</id><published>2011-07-23T20:52:00.003+01:00</published><updated>2011-07-23T20:52:00.340+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='dnssec'/><title type='text'>dnssec revisited</title><content type='html'>well I was pleased that &lt;a href="http://benctechnicalblog.blogspot.com/search/label/dnssec"&gt;my dnssec notes&lt;/a&gt; remained sufficiently intelligble after a few months to set up a new zone. but the machine I was just using took almost 5h to generate keys for a new zone. oh the entropy.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-3558607401599891118?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/3558607401599891118/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/07/dnssec-revisited.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/3558607401599891118'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/3558607401599891118'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/07/dnssec-revisited.html' title='dnssec revisited'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-1891489725916365265</id><published>2011-07-16T19:10:00.006+01:00</published><updated>2011-07-16T20:36:50.331+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ssh'/><category scheme='http://www.blogger.com/atom/ns#' term='security'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><category scheme='http://www.blogger.com/atom/ns#' term='shell'/><category scheme='http://www.blogger.com/atom/ns#' term='browserid'/><category scheme='http://www.blogger.com/atom/ns#' term='barwench'/><title type='text'>browserid in-browser shell logins</title><content type='html'>I've previously wired up my shell server &lt;a href="http://s0.barwen.ch/"&gt;barwen.ch&lt;/a&gt; to allow browser-based logins using OpenID and shellinabox. I've written about that on this blog before. I saw a few articles about Mozilla's &lt;a href="https://browserid.org/"&gt;BrowserID&lt;/a&gt;. The code snippets there looked like they would integrate well with the code I had already. So my evening project (which ended up only taking about half an hour) was to prototype BrowserID-based shell login.&lt;br /&gt;&lt;br /&gt;It works basically the same as for the OpenID login on barwen.ch:&lt;br /&gt;&lt;br /&gt;To get set up: you need to &lt;a href="http://s0.barwen.ch/signup"&gt;sign up for a barwen.ch account&lt;/a&gt; which will cost you 50 cents on PayPal; you need to &lt;a href="mailto:support@barwen.ch"&gt;send me&lt;/a&gt; the email that you use for BrowserID (instead of / in addition to an SSH key).&lt;br /&gt;&lt;br /&gt;To actually log in, go to the login page &lt;a href="http://s0.barwen.ch/~benc/browserid.html"&gt;http://s0.barwen.ch/~benc/browserid.html&lt;/a&gt; and log in. A terminal will appear in your browser. You do shell stuff.&lt;br /&gt;&lt;br /&gt;This code is pretty crappy so I don't really want to release it until I've had a thought about the security for at least an hour (though you can find fragments of it elsewhere on this blog). I especially think that there might be some attacks possible by using freaky email addresses vs my unsanitised string handling. (I'm looking at you, Bobby Tables).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-1891489725916365265?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/1891489725916365265/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/07/browserid-in-browser-shell-logins.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/1891489725916365265'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/1891489725916365265'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/07/browserid-in-browser-shell-logins.html' title='browserid in-browser shell logins'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-3830924567149046180</id><published>2011-07-16T16:09:00.000+01:00</published><updated>2011-07-16T16:09:00.766+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='http'/><title type='text'>bbc 500</title><content type='html'>During the wedding of the Duke and Duchess of Cambridge in April, the BBC website was for a while overwhelmed and giving http 500 errors. I was amused by the graphic they presented:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://ive.got.syphil.is/bbc500crop.png" /&gt;&lt;br /&gt;&lt;br /&gt;which UK viewers above a certain age &lt;a href="http://www.meldrum.co.uk/mhp/testcard/bbc_test.html"&gt;will find familiar&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-3830924567149046180?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/3830924567149046180/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/07/bbc-500.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/3830924567149046180'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/3830924567149046180'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/07/bbc-500.html' title='bbc 500'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-2485461532586520599</id><published>2011-07-09T09:44:00.000+01:00</published><updated>2011-07-09T09:44:00.920+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='railway'/><category scheme='http://www.blogger.com/atom/ns#' term='blinkenlights'/><title type='text'>blue traffic light</title><content type='html'>when I was a little boy, it always struck me that traffic lights should have a blue light in addition to the red and the green (and the amber), though I could never decide what it should be for.&lt;br /&gt;&lt;br /&gt;anyway I discovered that in Hong Kong, the MTR East Rail line *does* have a blue light on its signals, so my childhood desire is finally satisfied. it even seems a sensible use:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;Passenger trains have been running under an overlaid Automatic Train Protection (ATP) system since 1998. A blue aspect is displayed at signals when a train fitted with working ATP is approaching. A signal showing a blue aspect can be ignored by the driver, who will drive according to the information shown on the cab display.&lt;br /&gt;&lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-2485461532586520599?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://www.railsigns.co.uk/overseas/hongkong1/hongkong1.html' title='blue traffic light'/><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/2485461532586520599/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/07/blue-traffic-light.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/2485461532586520599'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/2485461532586520599'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/07/blue-traffic-light.html' title='blue traffic light'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-3705706241431809240</id><published>2011-07-02T11:22:00.006+01:00</published><updated>2011-07-02T11:22:00.252+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mrtg'/><title type='text'>mrtg user counts</title><content type='html'>I had a graph in mrtg of two variables: number of login sessions (the blue line, the count of users from the unix &lt;code&gt;users&lt;/code&gt; command), and number of unique users logged in (the green solid area - formed by deduping the output of &lt;code&gt;users&lt;/code&gt;).&lt;br /&gt;&lt;br /&gt;I decided that the number of login sessions was a bit useless especially with people using screen. A more interesting graph is perhaps something else based on the number of users doing things rather than the number of things a user is doing. So I chose to count the number of (normal, not system) users who have at least one process running.&lt;br /&gt;&lt;br /&gt;I count it this way: &lt;br /&gt;&lt;br /&gt;&lt;pre&gt;#!/bin/bash&lt;br /&gt;&lt;br /&gt;# first, unique users logged in&lt;br /&gt;echo $(users | sed 's/ /\n/g' | sort | uniq | wc -l)&lt;br /&gt;&lt;br /&gt;# number of users who have 3xxx user numbers and have a process running &lt;br /&gt;# 3xxx is the "normal user" range, though perhaps I could distinguish in other ways&lt;br /&gt;U=$(ps -A --format user= | sort | uniq | while read a ; do id -u $a ; done | grep -e '3...' | wc -l)&lt;br /&gt;&lt;br /&gt;echo $U&lt;br /&gt;echo 0&lt;br /&gt;echo 0&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;and here's the output: (green is logged-in users, blue is users with processes running)&lt;br /&gt;&lt;br /&gt;&lt;a href="http://s0.barwen.ch/~mrtg/s0_usercount.html"&gt;&lt;img src="http://s0.barwen.ch/~mrtg/s0_usercount-day.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;If you click through to the full page, you might see the change happening in the middle of week 17 of 2011, which is the beginning of May.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-3705706241431809240?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://s0.barwen.ch/~mrtg/s0_usercount.html' title='mrtg user counts'/><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/3705706241431809240/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/07/mrtg-user-counts.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/3705706241431809240'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/3705706241431809240'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/07/mrtg-user-counts.html' title='mrtg user counts'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-7076693579867645880</id><published>2011-06-24T09:59:00.000+01:00</published><updated>2011-06-24T09:59:17.331+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ipv4'/><title type='text'>non-cidr netmask: "worked in testing but broke colleagues' brains"</title><content type='html'>CIDR prefix length (for example, the &lt;code&gt;24&lt;/code&gt; in &lt;code&gt;128.9.128.0/24&lt;/code&gt; is a more concise notation for (a commonly used subset of) netmasks.&lt;br /&gt;&lt;br /&gt;A prefix length contains less information - it can only represent netmasks that consist of a sequence of 1 bits, followed by 0 bits to the end. For example, /24 is &lt;code&gt;11111111111111111111111100000000&lt;/code&gt; (24 1s and then 32-24=8 0s)&lt;br /&gt;&lt;br /&gt;This is useful because thats how most people use netmasks.&lt;br /&gt;&lt;br /&gt;But there's a set of netmasks that aren't representable this way - for example &lt;code&gt;11111111000000001111111100000000&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;Did anyone ever use netmasks that weren't prefix-length-representable? &lt;a href="http://mailman.postel.org/pipermail/internet-history/2010-October/001538.html"&gt;Apparently yes&lt;/a&gt;: &lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;Addresses were allocated from these networks sequentially, and the oldest&lt;br /&gt;web sites tended to get the most traffic, so a straightforward setup that&lt;br /&gt;spread the six /18s across the reverse proxies didn't balance the load&lt;br /&gt;particularly well. I toyed with using 0xffff0003 netmasks to split the /16&lt;br /&gt;so that successive addresses could be routed to each of the four London&lt;br /&gt;reverse proxies in turn.&lt;br /&gt;&lt;br /&gt;This worked in testing but I didn't deploy it because it broke my&lt;br /&gt;colleagues' brains and non-contiguous netmasks were an unsupported&lt;br /&gt;feature.&lt;br /&gt;&lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-7076693579867645880?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/7076693579867645880/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/06/non-cidr-netmask-worked-in-testing-but.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/7076693579867645880'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/7076693579867645880'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/06/non-cidr-netmask-worked-in-testing-but.html' title='non-cidr netmask: &quot;worked in testing but broke colleagues&apos; brains&quot;'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-2085931276701928412</id><published>2011-06-18T11:24:00.000+01:00</published><updated>2011-06-18T11:24:20.539+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='password'/><category scheme='http://www.blogger.com/atom/ns#' term='ssh'/><category scheme='http://www.blogger.com/atom/ns#' term='security'/><title type='text'>password policy for ssh key hosts</title><content type='html'>I have a host with a handful of users. When they authenticate, they have to use either ssh public key or openid - there is no on-machine password. But some of the services that are running pretty much need a password: for example, IMAP, SMTP AUTH, web portal. &lt;br /&gt;&lt;br /&gt;I'd like to give these users the ability to acquire a password.&lt;br /&gt;&lt;br /&gt;Two ways are immediately apparent:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Implement a mechanism which presents the user with a new machine-generated password, for example to their registered email address.&lt;/li&gt;&lt;li&gt;Allow the user to run &lt;code&gt;passwd&lt;/code&gt; in a shell, but not require them to enter their existing password first (instead, rely on the fact that they are logged in to be sufficient authentication).&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Dear Reader, can you think of other ways? Do you have any opinions on the wiseness/unwiseness of these approaches?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-2085931276701928412?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/2085931276701928412/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/06/password-policy-for-ssh-key-hosts.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/2085931276701928412'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/2085931276701928412'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/06/password-policy-for-ssh-key-hosts.html' title='password policy for ssh key hosts'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-7705954265205311812</id><published>2011-06-10T15:37:00.002+01:00</published><updated>2011-06-10T16:02:17.966+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='haskell'/><category scheme='http://www.blogger.com/atom/ns#' term='roman'/><title type='text'>roman numeral literals using Template Haskell</title><content type='html'>I've know about &lt;a href="http://www.haskell.org/haskellwiki/Template_Haskell"&gt;Template Haskell&lt;/a&gt; (TH) for ages but never used it. As a diversion from another project, I thought I'd try to implement roman numeral literals for Haskell, using TH -- this seems an easy way to get started without having to dig in too far.&lt;br /&gt;&lt;br /&gt;I already wrote some &lt;a href="http://www.hawaga.org.uk/ben/tech/roman.html"&gt;Haskell roman numeral code&lt;/a&gt; which gives a function &lt;code&gt;numeralsToInteger :: String -&gt; Integer&lt;/code&gt;. I tweaked its source code a bit (it wasn't a module previously, for example) and can write this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;import Roman&lt;br /&gt;&lt;br /&gt;main = do&lt;br /&gt;  putStrLn "roman test 1 (this should print 1998 below)"&lt;br /&gt;  print $ numeralsToInteger "MCMXCVIII"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;What I want to do with TH is shorten the syntax and make the roman-&gt;literal conversion happen at compile time instead of at runtime.&lt;br /&gt;&lt;br /&gt;I'll try to use TH's &lt;a href="http://www.haskell.org/haskellwiki/Quasiquotation"&gt;quasi-quoter syntax&lt;/a&gt;, which should allow me to write:&lt;br /&gt;&lt;pre&gt;print [ro|MCMXCVIII|]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;code&gt;ro&lt;/code&gt; is the name I've chosen for my quasiquoter, and the &lt;code&gt;[...|...|]&lt;/code&gt; bracket syntax comes from template haskell quasiquoting.&lt;br /&gt;&lt;br /&gt;What will happen is that everything inside those &lt;code&gt;[|&lt;/code&gt;brackets&lt;code&gt;|]&lt;/code&gt; will be passed to my code, which can return an arbitrary Haskell expression to be substituted at compile time. Think C-style &lt;code&gt;#define&lt;/code&gt; on steroids.&lt;br /&gt;&lt;br /&gt;In my case, I'll always be returning an integer literal expression. The value of that literal will be determined by parsing the roman numeral.&lt;br /&gt;&lt;br /&gt;I need to define &lt;code&gt;ro :: Language.Haskell.TH.Quote.QuasiQuoter&lt;/code&gt;, and it needs to live in a different module (so that it can be compiled before attempting to parse anything that uses that quasiquoter)&lt;br /&gt;&lt;br /&gt;I want to use my quasiquoter to return an expression. They can be used in more contexts than that (for example, when a type is needed). I'll make the other contexts into an &lt;code&gt;error&lt;/code&gt;, which leaves only the hard one that parses and returns my expression. (after doing a bunch of fiddling Agda, it feels like a relief to be able to say "oh, I'll just make that an error).&lt;br /&gt;&lt;br /&gt;It turns out that the code is mostly plumbing. &lt;code&gt;RomanTH.hs&lt;/code&gt; looks like this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;module RomanTH where&lt;br /&gt;&lt;br /&gt;import Roman&lt;br /&gt;import Language.Haskell.TH&lt;br /&gt;import Language.Haskell.TH.Quote&lt;br /&gt;&lt;br /&gt;ro :: Language.Haskell.TH.Quote.QuasiQuoter&lt;br /&gt;ro = QuasiQuoter parser err err err&lt;br /&gt;&lt;br /&gt;err = error "Roman numeral quasiquoter cannot be used in this context"&lt;br /&gt;&lt;br /&gt;parser :: String -&gt; Q Exp&lt;br /&gt;parser s = litE (IntegerL (numeralsToInteger s))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;which is all types and wiring except the  function&lt;code&gt;parser s = litE (IntegerL (numeralsToInteger s))&lt;/code&gt; which means (right-to-left) convert string to an Integer, embed it in a literal type, and embed that inside an expression.&lt;br /&gt;&lt;br /&gt;The complete test program looks like this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;import RomanTH&lt;br /&gt;&lt;br /&gt;main = do&lt;br /&gt;  putStrLn "roman test 2 (this should print 1998 below)"&lt;br /&gt;  print [ro|MCMXCVIII|]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;and runs like this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;$ ./test2 &lt;br /&gt;roman test 2 (this should print 1998 below)&lt;br /&gt;1998&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Well, that took all of 36 minutes.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-7705954265205311812?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/7705954265205311812/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/06/roman-numeral-literals-using-template.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/7705954265205311812'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/7705954265205311812'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/06/roman-numeral-literals-using-template.html' title='roman numeral literals using Template Haskell'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-2797618699419442553</id><published>2011-06-08T08:29:00.002+01:00</published><updated>2011-06-08T08:29:48.889+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ipv6'/><category scheme='http://www.blogger.com/atom/ns#' term='ipv6day'/><title type='text'>best ipv6 address seen so far</title><content type='html'>&lt;code&gt;2600::&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://[2600::]/"&gt;It even works, serving http for sprint.net&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-2797618699419442553?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/2797618699419442553/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/06/best-ipv6-address-seen-so-far.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/2797618699419442553'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/2797618699419442553'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/06/best-ipv6-address-seen-so-far.html' title='best ipv6 address seen so far'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-1932646183595013324</id><published>2011-06-08T00:01:00.001+01:00</published><updated>2011-06-08T00:01:00.271+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ipv6'/><title type='text'>June 8th is world IPv6 day!</title><content type='html'>June 8th is ISOC's "world ipv6 day". According to the organisers:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;On 8 June, 2011, Google, Facebook, Yahoo!, Akamai and Limelight Networks will be amongst some of the major organisations that will offer their content over IPv6 for a 24-hour “test flight”. The goal of the Test Flight Day is to motivate organizations across the industry – Internet service providers, hardware makers, operating system vendors and web companies – to prepare their services for IPv6 to ensure a successful transition as IPv4 addresses run out.&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;Some of us have been trying to do that for years ;)&lt;br /&gt;&lt;br /&gt;Anyway, some things you can do:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://benctechnicalblog.blogspot.com/search/label/ipv6?max-results=100"&gt;Read all the ipv6 related posts on this blog&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Make sure you are using &lt;a href="http://david.woodhou.se/ipv6-cable-big.jpg"&gt;IPv6 certified cable&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Play &lt;a href="http://www.loopsofzen.co.uk/"&gt;loops of zen&lt;/a&gt;, an ipv6-only browser tile-puzzle&lt;/li&gt;&lt;li&gt;If your ISP does not provide IPv6 and you want easy IPv6 access for a single workstation, I recommend the teredo protocol (its built into windows, and you can &lt;code&gt;apt-get miredo&lt;/code&gt; on debian. For a server or network installation, take a look at Hurricane Electric's &lt;a href="http://tunnelbroker.net/"&gt;tunnel broker&lt;/a&gt; which will give you a /48 prefix routed over your normal internet connection.&lt;/li&gt;&lt;li&gt;If you're on ipv6 already and you're feeling hardcore you can use NAT64 and DNS64 to get rid of all your local ipv4 traffic, instead routing it all through a NAT gateway at (for example) &lt;a href="http://aaisp.net.uk/kb-broadband-ipv6-nat64.html"&gt;Andrews and Arnold&lt;/a&gt;.&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-1932646183595013324?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://isoc.org/wp/worldipv6day/' title='June 8th is world IPv6 day!'/><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/1932646183595013324/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/06/june-8th-is-world-ipv6-day.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/1932646183595013324'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/1932646183595013324'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/06/june-8th-is-world-ipv6-day.html' title='June 8th is world IPv6 day!'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-8301711689657378195</id><published>2011-05-29T17:06:00.004+01:00</published><updated>2011-05-31T07:18:59.209+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ipv6'/><title type='text'>Panickers guide to world ipv6 day</title><content type='html'>8th June 2011 is &lt;a href="http://isoc.org/wp/worldipv6day/"&gt;World IPv6 day&lt;/a&gt;. Maybe you haven't done anything about getting your website ipv6 enabled. Its taken the world 15 years to develop IPv6, so sure it seems *totally* reasonable that you can get it deployed in 10 days.&lt;br /&gt;&lt;br /&gt;What I'm going to tell you about here will get basic IPv6 access to your site. It won't do it in a particularly pretty way, and its probably not the long term way to do it. Also I just hammered this out this afternoon (though its based on years of IPv6 use). (I hope) it should work.&lt;br /&gt;&lt;br /&gt;First some DOs and DONTs:&lt;br /&gt;&lt;br /&gt;* &lt;span style="color:red"&gt;DON'T&lt;/span&gt; deploy IPv6 on your production servers. If you don't know much about IPv6, then blindly sticking it on your real production resources is probably a good way to put even your IPv4 (read: the real IP that everyone actually uses) connectivity at risk (for various reasons that you'll understand when you understand...)&lt;br /&gt;&lt;br /&gt;* &lt;span style="color:green"&gt;DO&lt;/span&gt; deploy a http proxy server on its own machine, and have that proxy your IPv6 traffic. You shouldn't need to modify *anything* on your production machines.&lt;br /&gt;&lt;br /&gt;* &lt;span style="color:red"&gt;DON'T&lt;/span&gt; put the IPv6 address record (AAAA, as opposed to the IPv4 A record) in your normal DNS. If you do, then users who have both IPv4 and IPv6 will usually try to connect over IPv6. You don't know how well this is going to scale, or how good your IPv6 connectivity is going to be (or even how good your users ipv6 connectivity is going to be, if everyone is going to be fucking around that day)&lt;br /&gt;&lt;br /&gt;* &lt;span style="color:green"&gt;DO&lt;/span&gt; put a DNS name (eg www.6.example.com, if your main site is on www.example.com) with the AAAA record. That way, users can choose to try using IPv6, and if its broken can easily get back to your main site. You'll need to publicise this, though, because its not going to get users connecting via IPv6 automatically, and at the same time you should provide some feedback: for example, an email address or a forum.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;So what do you need to do:&lt;br /&gt;&lt;br /&gt;* Get a dedicated server (either a physical hardware server, or a VPS) running a recent version of Linux. (Ubuntu 10.x would be enough)&lt;br /&gt;&lt;br /&gt;* Connect that server to the ipv6 internet. If its on a network with native IPv6, then your host will probably give you connection details. If not, then use Hurricane Electric's free &lt;a href="http://tunnelbroker.net/"&gt;tunnel broker&lt;/a&gt; which will connect you over a regular internet connection.&lt;br /&gt;&lt;br /&gt;* However you connect, you'll end up with an IPv6 address for your machine. It will be a string something like this &lt;code&gt;2001:470:1f09:1288::2&lt;/code&gt; that you can get out of &lt;code&gt;ifconfig&lt;/code&gt;(specifically, if you have a choice, choose the one that begins with a 2, not the one that begins with an f). Put that IPv6 address into an AAAA record in DNS (better hope your DNS hosting provider does AAAA records - the good ones do...) under a new DNS name. Don't put IPv4 addresses in there too. In my example, I'm going to configure:&lt;br /&gt;&lt;code&gt;blog.6.hawaga.org.uk AAAA 2001:470:1f09:1288::2&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;* Put apache httpd on your server, &lt;code&gt;apt-get install apache2&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;* Now you'll need a client machine with IPv6 to try connecting to your new server. If you have a windows PC, you can probably turn on Teredo in the network configuration - it comes built in. On OS X, Linux or BSD, you can install &lt;a href="http://www.remlab.net/miredo/"&gt;miredo&lt;/a&gt; which is a Teredo client. Or you can set up another Hurricane Electric tunnel for your client machine. You can use &lt;a href="http://test-ipv6.com/"&gt;test-ipv6.com&lt;/a&gt; to get a score for how well your new client machine is connected to the ipv6 internet.&lt;br /&gt;&lt;br /&gt;* You should now be able to use a web browser to reach the hostname you configured in DNS back there - you should see apache's welcome/default page.&lt;br /&gt;&lt;br /&gt;* Now, configure apache to forward all requests it receives onwards to your production website over IPv4. In the following example, my production IPv4 website is the one you are reading right now, &lt;code&gt;benctechnicalblog.blogspot.com&lt;/code&gt;. Enable &lt;code&gt;mod_proxy&lt;/code&gt; and &lt;code&gt;mod_proxy_http&lt;/code&gt;, and then set up a virtual host directive like this (or put it in the base of your server config, seeing as this a host dedicated to forwarding ipv6 traffic):&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&amp;lt;virtualHost *:80&gt;&lt;br /&gt;  ServerName blog.6.hawaga.org.uk&lt;br /&gt;  ProxyPass / http://benctechnicalblog.blogspot.com/&lt;br /&gt;  &amp;lt;Proxy http://benctechnicalblog.blogspot.com/&gt;&lt;br /&gt;    Allow from All&lt;br /&gt;  &amp;lt;/Proxy&gt;&lt;br /&gt;&amp;lt;/virtualhost&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Once you've done that, visiting your ipv6 hostname (eg &lt;a href="http://blog.6.hawaga.org.uk"&gt;blog.6.hawaga.org.uk&lt;/a&gt;) should serve you the content of your real production website website.&lt;br /&gt;&lt;br /&gt;Now publish the new hostname in a news item and make it seem like you know what you're doing...&lt;br /&gt;&lt;br /&gt;Some stuff will not work, for sure: if you have anything that does things based on IPv4 address of client, that's all going to be based on the address of the proxy machine, not the real client IPv6 address. Things that might affect are localisation (eg language), and rate/load limiting based on ip address.&lt;br /&gt;&lt;br /&gt;So, please ask questions in the comments and I'll see about answering them...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-8301711689657378195?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://isoc.org/wp/worldipv6day/' title='Panickers guide to world ipv6 day'/><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/8301711689657378195/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/05/panickers-guide-to-world-ipv6-day.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/8301711689657378195'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/8301711689657378195'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/05/panickers-guide-to-world-ipv6-day.html' title='Panickers guide to world ipv6 day'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-4895676019673675500</id><published>2011-05-27T13:06:00.002+01:00</published><updated>2011-05-27T13:06:00.709+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='wtf'/><title type='text'>printing a message to stdout in fortran</title><content type='html'>&lt;code&gt;write (*,'(''I wonder how someone kept a straight face when they invented this syntax for writing a string to the console'')')&lt;br /&gt;&lt;/code&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-4895676019673675500?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/4895676019673675500/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/05/printing-message-to-stdout-in-fortran.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/4895676019673675500'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/4895676019673675500'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/05/printing-message-to-stdout-in-fortran.html' title='printing a message to stdout in fortran'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-6779986949675865398</id><published>2011-05-21T10:48:00.001+01:00</published><updated>2011-05-21T10:48:00.283+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='cloud'/><category scheme='http://www.blogger.com/atom/ns#' term='mrtg'/><category scheme='http://www.blogger.com/atom/ns#' term='ec2'/><title type='text'>ruby cloudwatch -&gt; mrtg interface</title><content type='html'>I use &lt;a href="http://s0.barwen.ch/~mrtg/"&gt;mrtg&lt;/a&gt; to gather historical data on some of my servers. One of those servers lives in Amazon's Elastic Compute Cloud (EC2) and so is also monitored by Amazon CloudWatch.&lt;br /&gt;&lt;br /&gt;Can I get cloudwatch data into mrtg?&lt;br /&gt;&lt;br /&gt;mrtg has a fairly straightforward interface for plugging in arbitrary unix executables to collect data, so my first attempt was to use the main Java-based cloudwatch client to get data. that attempt started up one jvm for each metric collected, which massively overloaded my ec2 microinstance, keeping the load average around 3. pretty lame.&lt;br /&gt;&lt;br /&gt;Amazon also provides a ruby interface. I had never programmed in ruby before, but its often interesting to learn a new language.&lt;br /&gt;&lt;br /&gt;Here's what I ended up with.&lt;br /&gt;&lt;br /&gt;First the config block for mrtg, which calls out to the &lt;code&gt;ruby-mrtg-cloudwatch3&lt;/code&gt; program that I wrote:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;Target[cloudwatch_network]: `/home/mrtg/ruby-mrtg-cloudwatch/ruby-mrtg-cloudwatch3 NetworkIn NetworkOut AWS&lt;br /&gt;/EC2 InstanceId=i-26bcaf51`&lt;br /&gt;Title[cloudwatch_network]: Network traffic according to cloudwatch&lt;br /&gt;options[cloudwatch_network]: growright,absolute,logscaleMaxBytes[cloudwatch_network]: 100000000&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This gives a graph of network traffic according to cloudwatch. I can compare that alongside the network traffic graph for eth0 gathered from the local interface statistics. They should roughly match up, and they do (well hopefully they still do by the time you read this - these are live images):&lt;br /&gt;&lt;br /&gt;According to the on-host network interface:&lt;br /&gt;&lt;img src="http://s0.barwen.ch/~mrtg/s0_eth0-day.png" /&gt;&lt;br /&gt;&lt;br /&gt;According to cloudwatch:&lt;br /&gt;&lt;img src="http://s0.barwen.ch/~mrtg/cloudwatch_network-day.png" /&gt;&lt;br /&gt;&lt;br /&gt;Now the actual ruby code:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;#!/usr/bin/ruby1.8&lt;br /&gt;&lt;br /&gt;require 'rubygems'&lt;br /&gt;require 'AWS'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The two cloudwatch metric names, one that measures output data, one that measures input data, are give on the command line:&lt;br /&gt;&lt;pre&gt;metrico=ARGV[0]&lt;br /&gt;metrici=ARGV[1]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;My code has hardcoded access keys at the moment which is a bit shitty:&lt;br /&gt;&lt;pre&gt;ACCESS_KEY_ID='foo'&lt;br /&gt;SECRET_ACCESS_KEY='bar'&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Using the above credentials, a new cloudwatch object is made, &lt;code&gt;@cw&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;@cw = AWS::Cloudwatch::Base.new(:access_key_id =&gt; ACCESS_KEY_ID, :secret_access_key =&gt; SECRET_ACCESS_KEY, :server =&gt; "eu-west-1.monitoring.amazonaws.com" )&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Each of the two metrics will be probed with the &lt;code&gt;probe&lt;/code&gt; function. This uses a state file based on the metric name to get only readings which have not already been seen by this script. The two metrics use separate state files because cloudwatch doesn't give an atomic read for multiple metrics at once. The state file stores the time of the last seen reading. If there is no state file, we have to invent a time. There is a subtlety here: data does not appear in cloudwatch until around 5 minutes after its time stamp, so using the current time as an initial value results in not seeing any results. Instead, I go back about 15 minutes the first time, which will seems to be far enough back to get something.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;def probe(metric)&lt;br /&gt;&lt;br /&gt;  et = Time.now()&lt;br /&gt;&lt;br /&gt;  statusfn="cloudwatch-"+ARGV[3]+"-"+metric+".status"&lt;br /&gt;  if FileTest.exist?(statusfn) then&lt;br /&gt;&lt;br /&gt;    f = File.new(statusfn, "r")&lt;br /&gt;    tstring = f.gets&lt;br /&gt;    ts = Time.parse(tstring)&lt;br /&gt;    f.close&lt;br /&gt;  else&lt;br /&gt;    ts = et - 900 # needs to be more than 5 mins because otherwise we never get any data.&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;  res = @cw.get_metric_statistics(:measure_name =&gt; metric,  :statistics =&gt; 'Average,Sum', :namespace =&gt; ARGV[2], :period =&gt; 300, :start_time =&gt; ts, :end_time =&gt; et, :dimensions=&gt; ARGV[3])&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now we're going to look at the rows that come back. Usually only one row will come back, if we're running this at about the same rate that cloudwatch is adding readings, but sometheres there will be more, or fewer.&lt;br /&gt;&lt;br /&gt;In the case of network traffic, I want to return the &lt;em&gt;sum&lt;/em&gt; of all readings for this metric. In other cases, such as disk usage, I would want to return the &lt;em&gt;mean&lt;/em&gt;. This distinction is the same as default vs gauge measurements in MRTG.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;samples = 0&lt;br /&gt;  sum = 0&lt;br /&gt;  avgsum = 0&lt;br /&gt;&lt;br /&gt;  datapoints = res["GetMetricStatisticsResult"]["Datapoints"]&lt;br /&gt;&lt;br /&gt;  lt = ts&lt;br /&gt;  if datapoints.nil? then&lt;br /&gt;   # nop&lt;br /&gt;  else&lt;br /&gt;    rows = datapoints["member"]&lt;br /&gt;&lt;br /&gt;    rows.each { |r|&lt;br /&gt;      nlt = Time.parse(r["Timestamp"])&lt;br /&gt;      if(nlt &lt; ts) then&lt;br /&gt;        # nop - time was before requested start&lt;br /&gt;      else&lt;br /&gt;        samples += Float(r["Samples"])&lt;br /&gt;        avgsum += Float(r["Average"])&lt;br /&gt;        sum += Float(r["Sum"])&lt;br /&gt;        nlt += 1&lt;br /&gt;        if(nlt &gt; lt) then&lt;br /&gt;          lt = nlt&lt;br /&gt;        end&lt;br /&gt;      end&lt;br /&gt;    } &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now we can write out the new state file:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;f=File.new(statusfn, "w")&lt;br /&gt;    f.puts(lt)&lt;br /&gt;    f.close&lt;br /&gt;  end&lt;br /&gt;  return sum&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;and finally output the MRTG format information:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;sumo=probe(metrico)&lt;br /&gt;sumi=probe(metrici)&lt;br /&gt;&lt;br /&gt;# output mrtg format&lt;br /&gt;puts sumo&lt;br /&gt;puts sumi&lt;br /&gt;puts 0&lt;br /&gt;puts "cloudwatch: "+metrico+" and "+metrici&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The end.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-6779986949675865398?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/6779986949675865398/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/05/ruby-cloudwatch-mrtg-interface.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/6779986949675865398'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/6779986949675865398'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/05/ruby-cloudwatch-mrtg-interface.html' title='ruby cloudwatch -&gt; mrtg interface'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-3779641602273953030</id><published>2011-05-14T05:13:00.000+01:00</published><updated>2011-05-14T05:13:00.375+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='electricity'/><category scheme='http://www.blogger.com/atom/ns#' term='dont-try-this-at-home'/><title type='text'>UK electric sockets vs chopsticks</title><content type='html'>&lt;img src="http://ive.got.syphil.is/hk/IMG_2413.JPG"/&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.clifford.ac/"&gt;My father&lt;/a&gt; taught me this trick when I was far too young to be taught this trick - how to connect a Europlug to a UK (actually HK in this case) electrical socket.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-3779641602273953030?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/3779641602273953030/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/05/uk-electric-sockets-vs-chopsticks.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/3779641602273953030'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/3779641602273953030'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/05/uk-electric-sockets-vs-chopsticks.html' title='UK electric sockets vs chopsticks'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-5244002341462498040</id><published>2011-05-07T18:00:00.002+01:00</published><updated>2011-05-08T14:26:41.779+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='security'/><category scheme='http://www.blogger.com/atom/ns#' term='pam'/><title type='text'>PAM python module for out-of-band one-time tokens</title><content type='html'>I previous wrote about &lt;a href="http://benctechnicalblog.blogspot.com/2011/04/terminal-login-with-openid.html"&gt;integrating openid with unix shell logins&lt;/a&gt; for my public shell server &lt;a href="http://s0.barwen.ch/"&gt;barwen.ch&lt;/a&gt;. This post talks about the out-of-band PAM token module that I wrote as part of that - where I'm generating the tokens when the user is authenticated by OpenID. But I guess it could also have use when there's some other out of band mechanism (such as sending something by SMS?)&lt;br /&gt;&lt;br /&gt;Subject to some other non-PAM web-based authentication (openid in this case, but it could be anything really), I want to issue a token value to the user in-band with respect to that other authentication (i.e. in a web page shown to the user), which is out-of-band with respect to PAM. That token should then be usable for a short period of time to make a single PAM authorisation to sshd.&lt;br /&gt;&lt;br /&gt;That is, if you can log into the web-based authentication, you should then be able to log into the ssh system.&lt;br /&gt;&lt;br /&gt;So on one side I need a PAM module (which I will write in Python) to check the tokens, and on the other side I need something (a command line tool) to issue the tokens. To complete the loop, I need some database (the filesystem) to store the tokens on the server side.&lt;br /&gt;&lt;br /&gt;So here's the code. The PAM module comes complete with documented security vulnerability which allows anyone to delete certain files on your file system. ho ho.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;First the token creator:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;#!/usr/bin/python&lt;br /&gt;&lt;br /&gt;import base64&lt;br /&gt;import os&lt;br /&gt;import pickle&lt;br /&gt;import sys&lt;br /&gt;import time&lt;br /&gt;&lt;br /&gt;VALIDTIME = 60&lt;br /&gt;&lt;br /&gt;tokenbits = os.urandom(8)&lt;br /&gt;token = base64.b64encode(tokenbits, "+-")&lt;br /&gt;&lt;br /&gt;print token&lt;br /&gt;&lt;br /&gt;fn = token + ".token"&lt;br /&gt;fh = open(fn, 'w')&lt;br /&gt;&lt;br /&gt;obj = (sys.argv[1], time.time() + VALIDTIME)&lt;br /&gt;&lt;br /&gt;pickle.dump(obj, fh)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;and secondly the PAM module, which is based on the PAM module in my previous post:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;import os&lt;br /&gt;import syslog&lt;br /&gt;import pickle&lt;br /&gt;import time&lt;br /&gt;&lt;br /&gt;def pam_sm_authenticate(pamh, flags, argv):&lt;br /&gt;  syslog.syslog("start benc")&lt;br /&gt;  pamh.authtok&lt;br /&gt;  if pamh.authtok == None:&lt;br /&gt;    syslog.syslog("got no password in authtok - trying through conversation")&lt;br /&gt;    passmsg = pamh.Message(pamh.PAM_PROMPT_ECHO_OFF, "Monkeyballs?")&lt;br /&gt;    rsp = pamh.conversation(passmsg)&lt;br /&gt;    syslog.syslog("response is "+rsp.resp)&lt;br /&gt;    pamh.authtok = rsp.resp&lt;br /&gt;  # so we should at this point have the password either through the&lt;br /&gt;  # prompt or from previous module&lt;br /&gt;  syslog.syslog("got password: "+pamh.authtok)&lt;br /&gt;&lt;br /&gt;  # now look for token&lt;br /&gt;  # SECURITY BUG TODO: we're using this to make a path so we need to make sure&lt;br /&gt;  # we're not being directed out into some other directory. We know the&lt;br /&gt;  # range of characters that can be used in a token so we can reject if&lt;br /&gt;  # anything other than those exists.&lt;br /&gt;  # Especially as we delete the token file on use - otherwise could delete&lt;br /&gt;  # arbitrary files on the system...&lt;br /&gt;  tfn = "/root/" + pamh.authtok + ".token"&lt;br /&gt;&lt;br /&gt;  if os.path.exists(tfn):&lt;br /&gt;    fh = open(tfn)&lt;br /&gt;    tokendata = pickle.load(fh)&lt;br /&gt;    tokenuser = tokendata[0]&lt;br /&gt;    tokentime = tokendata[1]&lt;br /&gt;    fh.close()&lt;br /&gt;    os.remove(tfn);&lt;br /&gt;&lt;br /&gt;    # will remove the token even if it was for the wrong user&lt;br /&gt;    # not sure if there's any security different wrt leaving it there if&lt;br /&gt;    # its the wrong user?&lt;br /&gt;&lt;br /&gt;    if tokentime &lt; time.time():&lt;br /&gt;      syslog.syslog("token time expired")&lt;br /&gt;      return pamh.PAM_AUTH_ERR&lt;br /&gt;&lt;br /&gt;    if tokenuser != pamh.user:&lt;br /&gt;      syslog.syslog("token user "+tokenuser+" does not match requested user "+pamh.user)&lt;br /&gt;      return pamh.PAM_AUTH_ERR&lt;br /&gt;    &lt;br /&gt;&lt;br /&gt;    return pamh.PAM_SUCCESS&lt;br /&gt;&lt;br /&gt;  return pamh.PAM_AUTH_ERR&lt;br /&gt;&lt;br /&gt;def pam_sm_setcred(pamh, flags, argv):&lt;br /&gt;  return pamh.PAM_SUCCESS&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-5244002341462498040?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/5244002341462498040/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/05/pam-python-module-for-out-of-band.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/5244002341462498040'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/5244002341462498040'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/05/pam-python-module-for-out-of-band.html' title='PAM python module for out-of-band one-time tokens'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-5136674101123775848</id><published>2011-04-26T15:53:00.006+01:00</published><updated>2011-05-18T12:32:41.699+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='openid'/><category scheme='http://www.blogger.com/atom/ns#' term='ssh'/><category scheme='http://www.blogger.com/atom/ns#' term='pam'/><title type='text'>ssh-like login with openid</title><content type='html'>I rigged together &lt;a href="http://code.google.com/p/shellinabox/"&gt;shellinabox&lt;/a&gt; and &lt;a href="http://findingscience.com/mod_auth_openid/"&gt;mod_auth_openid&lt;/a&gt; with &lt;a href=""http://benctechnicalblog.blogspot.com/2011/05/pam-python-module-for-out-of-band.html"&gt;some custom PAM glue&lt;/a&gt; so people can log into my hobby server &lt;code&gt;s0.barwen.ch&lt;/code&gt; with an in-browser terminal window and openid.&lt;br /&gt;&lt;br /&gt;shellinabox is not ssh (although web-based ssh is a good approximation). Instead it seems to be AJAX-over-https (which is kinda wtf for terminal access, but hey it seems to work).&lt;br /&gt;&lt;br /&gt;The way I've glued it together is: First you visit the login page. That is an openid protected CGI script. The script runs with your openid in &lt;code&gt;$REMOTE_USER&lt;/code&gt;, and does three things: it maps your openid to a local username; it generates (via sudo) an authentication token for you; and it HTTP-meta-redirects you to a hacked version of shellinabox.&lt;br /&gt;&lt;br /&gt;shellinabox gives you a login prompt, asking for username and password. My hacked version stuffs the username and authentication token from the previous step into the keyboard.&lt;br /&gt;&lt;br /&gt;The token ends up at &lt;a href="http://benctechnicalblog.blogspot.com/2011/05/pam-python-module-for-out-of-band.html"&gt;a custom PAM module which verifies that the token is valid&lt;/a&gt; (for that user, and within a small time window after issue) and lets you in.&lt;br /&gt;&lt;br /&gt;Then you get your shell prompt.&lt;br /&gt;&lt;br /&gt;This seems like an interesting addition to barwen.ch's &lt;a href="http://www.barwen.ch/connecting.html"&gt;collection of login methods&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;If you want a play, you can sign up at &lt;a href="http://s0.barwen.ch/"&gt;s0.barwen.ch&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Also, if you break this, let me know rather than deleting &lt;code&gt;/&lt;/code&gt; ...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-5136674101123775848?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/5136674101123775848/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/04/terminal-login-with-openid.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/5136674101123775848'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/5136674101123775848'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/04/terminal-login-with-openid.html' title='ssh-like login with openid'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-4676275878219262669</id><published>2011-04-23T16:36:00.001+01:00</published><updated>2011-05-08T09:43:41.436+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='security'/><category scheme='http://www.blogger.com/atom/ns#' term='pam'/><title type='text'>pam_python</title><content type='html'>I came across pam_python, a PAM module that lets you write PAM modules in Python. I've come across things scripted by python a couple of times in the last few weeks at work so it seems interesting to play in this direction.&lt;br /&gt;&lt;br /&gt;The first module I got sort-of working is this, which lets anyone log in with the password &lt;code&gt;poop53&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;import syslog&lt;br /&gt;&lt;br /&gt;def pam_sm_authenticate(pamh, flags, argv):&lt;br /&gt;  syslog.syslog("start benc")&lt;br /&gt;  at = pamh.authtok&lt;br /&gt;  syslog.syslog("got password: "+at)&lt;br /&gt;  if at == "poop53" : &lt;br /&gt;    return pamh.PAM_SUCCESS&lt;br /&gt;  else:&lt;br /&gt;    return pamh.PAM_AUTH_ERR&lt;br /&gt;&lt;br /&gt;def pam_sm_setcred(pamh, flags, argv):&lt;br /&gt;  return pamh.PAM_SUCCESS&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now this cheats a bit - it assumes that some other module has read in the password from the user - I used pam_unix to do that, configured as below, so that first a check against the unix password happens and then if that fails, check against &lt;code&gt;poop53&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;auth sufficient pam_unix.so&lt;br /&gt;auth sufficient pam_python.so /root/auth.py&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The specific use I am thinking of, I don't want unix passwords to work. So in that case, I need to read in the password myself if it isn't already set.&lt;br /&gt;&lt;br /&gt;Here's how I made that work:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;def pam_sm_authenticate(pamh, flags, argv):&lt;br /&gt;  syslog.syslog("start benc")&lt;br /&gt;  pamh.authtok&lt;br /&gt;  if pamh.authtok == None:&lt;br /&gt;    syslog.syslog("got no password in authtok - trying through conversation")&lt;br /&gt;    passmsg = pamh.Message(pamh.PAM_PROMPT_ECHO_OFF, "Monkeyballs?")&lt;br /&gt;    rsp = pamh.conversation(passmsg)&lt;br /&gt;    syslog.syslog("response is "+rsp.resp)&lt;br /&gt;    pamh.authtok = rsp.resp&lt;br /&gt;  # so we should at this point have the password either through the&lt;br /&gt;  # prompt or from previous module&lt;br /&gt;  syslog.syslog("got password: "+pamh.authtok)&lt;br /&gt;  if pamh.authtok == "poop53" : &lt;br /&gt;    return pamh.PAM_SUCCESS&lt;br /&gt;  else:&lt;br /&gt;    return pamh.PAM_AUTH_ERR&lt;br /&gt;&lt;br /&gt;def pam_sm_setcred(pamh, flags, argv):&lt;br /&gt;  return pamh.PAM_SUCCESS&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To use this with sshd, I need to enable sshd options &lt;code&gt;UsePAM&lt;/code&gt; and &lt;code&gt;ChallengeResponseAuthentication&lt;/code&gt;, and now I get this:&lt;br /&gt;&lt;pre&gt;$ &lt;em&gt;ssh root@192.168.141.128&lt;/em&gt;&lt;br /&gt;Monkeyballs?&lt;em&gt;poop53&lt;/em&gt;&lt;br /&gt;Linux alcf3 2.6.35-28-generic #49-Ubuntu SMP Tue Mar 1 14:40:58 UTC 2011 i686 GNU/Linux&lt;br /&gt;#&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;So I'm happy that I can grab some string from the remote user now, and process it to get an authentication decision.&lt;br /&gt;&lt;br /&gt;Thought its pretty weird to have a regular ssh client giving me a &lt;code&gt;Monkeyballs?&lt;/code&gt; prompt at auth time...&lt;br /&gt;&lt;br /&gt;Modified: 2011-05-08: Later I used pam_python to write an &lt;a href="http://benctechnicalblog.blogspot.com/2011/05/pam-python-module-for-out-of-band.html"&gt;out of band token module&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-4676275878219262669?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/4676275878219262669/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/04/pampython.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/4676275878219262669'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/4676275878219262669'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/04/pampython.html' title='pam_python'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-5444893074155013025</id><published>2011-04-16T15:14:00.000+01:00</published><updated>2011-04-16T15:14:38.095+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='fidonet'/><category scheme='http://www.blogger.com/atom/ns#' term='spam'/><title type='text'>spam and fidonet</title><content type='html'>I got a spam the other day, addressed to an address that isn't mine. That's not unusual. What was unusual was the address they used. It isn't mine, but it was mine when I was a teenager - &lt;code&gt;benc@donor2.demon.co.uk&lt;/code&gt; was my internet address on DoNoR/2, an OS/2 focused BBS near Woking (in those days, young whippersnappers, you cared down to the town level where you were connecting to - DoNoR/2 was in dialing code 01483, the same as Guildford where I grew up). DoNoR/2 was primarily a fidonet system - 2:252/156, and then after the BS of geonetting, 2:440/4, and I was point 2 off that - Ben Clifford at 2:252/156.2&lt;br /&gt;&lt;br /&gt;So although I'm usually annoyed when spam gets through my filters, this one made me think: aaah  natsukashi.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;Date: Thu, 27 Jan 2011 09:44:27 -0800 (PST)                                     &lt;br /&gt;From: Jim Vivona &lt;vivonajj@yahoo.com&gt;                                           &lt;br /&gt;To: benc@donor2.demon.co.uk                                                     &lt;br /&gt;Subject: re                                                                     &lt;br /&gt;                                        &lt;br /&gt;&lt;br /&gt;sup! if you hadn't heard i lost my job at lawncare company about 5              &lt;br /&gt;weeks ago, then i found this news article and made 379 in a few                 &lt;br /&gt;hours!! I guess it was for the best! I learned from - News channel 4            &lt;br /&gt;talk to you later!  &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;or headers in full:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;Return-Path: &lt;vivonajj@yahoo.com&gt;                                               &lt;br /&gt;X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on                      &lt;br /&gt;dildano.hawaga.org.uk                                                       &lt;br /&gt;X-Spam-Level:                                                                   &lt;br /&gt;X-Spam-Status: No, score=-2.1 required=2.5 tests=BAYES_00,DKIM_SIGNED,          &lt;br /&gt;DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FROM,HTML_MESSAGE,RCVD_IN_DNSWL_NONE,     &lt;br /&gt;URIBL_BLACK autolearn=ham version=3.3.1                                     &lt;br /&gt;X-Spam-ASN: AS36646 98.138.0.0/17                                               &lt;br /&gt;X-Spam-tokens-ham: 0.000-57--100h-0s--0d--hadnt,                                &lt;br /&gt;0.000-55--96h-0s--0d--hadn't,                                               &lt;br /&gt;0.000-28--48h-0s--0d--lisa, 0.000-25--44h-0s--0d--Lisa,                     &lt;br /&gt;0.001-19--32h-0s--0d--madison, 0.001-19--32h-0s--0d--Madison,               &lt;br /&gt;0.001-18--31h-0s--0d--Wisconsin, 0.001-18--31h-0s--0d--wisconsin,           &lt;br /&gt;0.001-9--15h-0s--0d--entrepreneurs, 0.002-124--220h-1s--0d--Fwd             &lt;br /&gt;X-Spam-tokens-spam: 0.902-15425--22770h-653297s--0d--H*c:alternative,           &lt;br /&gt;0.861-28--214h-4131s--0d--president,                                        &lt;br /&gt;0.860-6821--59523h-1134787s--0d--H*Ad:D*uk                                  &lt;br /&gt;X-Spam-relays-trusted:                             &lt;br /&gt;&lt;br /&gt;X-Spam-relays-untrusted: [ ip=98.138.85.229                                     &lt;br /&gt;rdns=web120502.mail.ne1.yahoo.com                                           &lt;br /&gt;helo=web120502.mail.ne1.yahoo.com by=dildano.hawaga.org.uk ident=           &lt;br /&gt;envfrom=                                                                    &lt;br /&gt;intl=0 id=p0RHpCGr007860 auth= msa=0 ] [ ip=194.54.47.229 rdns= helo=       &lt;br /&gt;by=web120502.mail.ne1.yahoo.com ident= envfrom= intl=0 id= auth= msa=0 ]    &lt;br /&gt;X-Spam-dkim-identity: @yahoo.com vivonajj@yahoo.com                             &lt;br /&gt;X-Spam-dkim-domain: yahoo.com                                                   &lt;br /&gt;X-Spam-dccb: dcc1.aftenposten.no                                                &lt;br /&gt;X-Spam-dccr: dildano.hawaga.org.uk 1215; Body=1 Fuz1=1 Fuz2=1                   &lt;br /&gt;X-Spam-token-summary: Tokens: new, 1; hammy, 109; neutral, 84; spammy, 3.       &lt;br /&gt;X-Spam-languages: en                                                            &lt;br /&gt;X-Spam-autolearn: ham                                                           &lt;br /&gt;Received: from web120502.mail.ne1.yahoo.com (web120502.mail.ne1.yahoo.com       &lt;br /&gt;[98.138.85.229])                                                            &lt;br /&gt;by dildano.hawaga.org.uk (8.13.8/8.13.8/Debian-3) with SMTP id              &lt;br /&gt;p0RHpCGr007860                                                              &lt;br /&gt;for &lt;benc@hawaga.org.uk&gt;; Thu, 27 Jan 2011 17:51:14 GMT                     &lt;br /&gt;Authentication-Results: dildano.hawaga.org.uk; dkim=pass (1024-bit key)         &lt;br /&gt;header.i=@yahoo.com; dkim-adsp=none                                         &lt;br /&gt;Received: (qmail 94875 invoked by uid 60001); 27 Jan 2011 17:44:27 -0000        &lt;br /&gt;DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yahoo.com; s=s1024;     &lt;br /&gt;t=1296150267; bh=niep53FNFb78lkhNFkO8MX4NIpOCMZRTAUTHm1+GJzQ=;              &lt;br /&gt;h=Message-ID:X-YMail-OSG:Received:X-Mailer:Date:From:Subject:To:MIME-Ver    &lt;br /&gt;sion:Content-Type;                                                          &lt;br /&gt;b=OWa2WgmhLxGRa+pJep7Or929UysoVIk/SCB7BnFfurvysB63Nr6Odfb4b3gm2hFZqK+xOo    &lt;br /&gt;Q6aZmUXn27EQbeA/8av52fU1KV33uhA9Th6rI0uKEIPg5LikGLLXUoaXrLzGdL2qyJ10UTMy    &lt;br /&gt;2TwEkqU6bZQBCpMxY0fWANKPiIK+M=                                              &lt;br /&gt;DomainKey-Signature: a=rsa-sha1; q=dns; c=nofws;                                &lt;br /&gt;s=s1024; d=yahoo.com;                                                         &lt;br /&gt;h=Message-ID:X-YMail-OSG:Received:X-Mailer:Date:From:Subject:To:MIME-Versi    &lt;br /&gt;on:Content-Type;                                                            &lt;br /&gt;b=eIuzjdEYirBi9RTFb1voGAKkH4bQUYyRkN6NT6mDBMj2GJcnf8bKz7R7NrQuWd8j9tjoviMF    &lt;br /&gt;bXv/t3D2DtcoNbvQuDSPMa6ycXDMUkNFpW3dMkyrq6ZBSuw+Ye7TZXH7ect5MJcErjTAyu38    &lt;br /&gt;+Dx4kXmdFIlAhs3Q0CBs4L7t+EI=;                                               &lt;br /&gt;Message-ID: &lt;164580.94308.qm@web120502.mail.ne1.yahoo.com&gt;                      &lt;br /&gt;X-YMail-OSG: vT0rPzMVM1kHrbVNywVWrHu0pysHCRnEnLjmjnEWPDU8KWm                    &lt;br /&gt;9AZTRLoDJBmvlFuAhJxD2.uKxh0LPBsJi.WmUQZBW_OOq9UKFRzUPfKAOemv                 &lt;br /&gt;qni6KAcfSaxd8Y6p2Cf6w5PGXAILIpD_0UuPIQ3LtnYoffxsz6w1ytx9R3cG                   &lt;br /&gt;BiqUC4MNLdG0NlSV2mlQCFGOEBHXYNTzl4ejOnjciku0Z1Y5SGW4aUz_4gmA                   &lt;br /&gt;oWXR.IhJ4dfWwgEVx2H9GvVpuPLizb_vwjmqP1e02TP9qrN0cGC1tFWTbyW9                   &lt;br /&gt;ooJoemIjAwkiYhhY-                                                              &lt;br /&gt;Received: from [194.54.47.229] by web120502.mail.ne1.yahoo.com via HTTP; Thu    &lt;br /&gt;, 27 Jan 2011 09:44:27 PST                                                  &lt;br /&gt;X-Mailer: YahooMailRC/553 YahooMailWebService/0.8.107.285259                    &lt;br /&gt;Date: Thu, 27 Jan 2011 09:44:27 -0800 (PST)                                     &lt;br /&gt;From: Jim Vivona &lt;vivonajj@yahoo.com&gt;                                           &lt;br /&gt;Subject: re                                                                     &lt;br /&gt;To: benc@donor2.demon.co.uk                                                     &lt;br /&gt;MIME-Version: 1.0                                                               &lt;br /&gt;Content-Type: multipart/alternative;                                            &lt;br /&gt;boundary="0-588940240-1296150267=:94308"                                    &lt;br /&gt;X-Greylist: Delayed for 00:06:40 by milter-greylist-3.0                         &lt;br /&gt;(dildano.hawaga.org.uk [81.187.211.37]); Thu,                               &lt;br /&gt;27 Jan 2011 17:51:15 +0000 (GMT)&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-5444893074155013025?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/5444893074155013025/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/04/spam-and-fidonet.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/5444893074155013025'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/5444893074155013025'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/04/spam-and-fidonet.html' title='spam and fidonet'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-6066451425083531760</id><published>2011-04-09T21:21:00.002+01:00</published><updated>2011-11-11T12:45:47.940Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='maths'/><category scheme='http://www.blogger.com/atom/ns#' term='rome'/><category scheme='http://www.blogger.com/atom/ns#' term='roman'/><title type='text'>roman numerals code</title><content type='html'>I made &lt;a href="http://www.hawaga.org.uk/java/benno/applets/RomanNumeralConverter.html"&gt;this roman numeral convert applet&lt;/a&gt; years ago (the RCS tag is &lt;code&gt;$Id: Roman.java,v 1.11 2001/01/07 15:12:00 benc Exp $&lt;/code&gt;) and mostly left it untended since then. It accounts for 25% .. 50% of the hits on hawaga.org.uk and was brought to the front of my consciousness by someone asking if they could use the source in their school project. Now I feel all embarrassed about the clunky UI on that thing.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-6066451425083531760?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://www.hawaga.org.uk/java/benno/applets/RomanNumeralConverter.html' title='roman numerals code'/><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/6066451425083531760/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/04/roman-numerals-code.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/6066451425083531760'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/6066451425083531760'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/04/roman-numerals-code.html' title='roman numerals code'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-2481042469309471099</id><published>2011-04-02T11:30:00.000+01:00</published><updated>2011-04-02T11:30:00.452+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='words'/><title type='text'>holon</title><content type='html'>Word of the day is &lt;a href="http://en.wikipedia.org/wiki/Holon_(philosophy)"&gt;holon&lt;/a&gt; - a thing that is both a whole in itself, and a component of something bigger. This is what a part of a hierarchical system is - both something clearly distinguishable from the rest of the system (in which case it is a distinct whole), but still part of that system.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-2481042469309471099?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/2481042469309471099/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/04/holon.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/2481042469309471099'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/2481042469309471099'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/04/holon.html' title='holon'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-3765092991026299963</id><published>2011-03-26T12:36:00.001Z</published><updated>2011-03-26T12:36:00.170Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='dns'/><category scheme='http://www.blogger.com/atom/ns#' term='dnssec'/><category scheme='http://www.blogger.com/atom/ns#' term='ssh'/><title type='text'>sshfp dns</title><content type='html'>I've set up DNSSEC, so I'm on the path to trusting DNS more. I can put SSH key fingerprints for my hosts into DNS, and SSH clients can check those.&lt;br /&gt;&lt;br /&gt;Even with insecure DNS, this is probably better than what you do now, which is to just to choose 'yes' to the following prompt without actually checking: (seriously, do you ever bother?)&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;The authenticity of host 's0.barwen.ch (192.168.55.55)' can't be established.&lt;br /&gt;RSA key fingerprint is 9e:81:ab:cb:2a:ad:26:2f:10:ed:dd:5c:55:dd:ea:58.&lt;br /&gt;No matching host key fingerprint found in DNS.&lt;br /&gt;Are you sure you want to continue connecting (yes/no)? &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;SSH can check &lt;code&gt;s0&lt;/code&gt;'s DNS record to see if a fingerprint is stored there, and tell you if it matches. So lets set that up.&lt;br /&gt;&lt;br /&gt;I need to add an SSHFP record to the DNS for &lt;code&gt;s0.barwen.ch&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;On host &lt;code&gt;fubar&lt;/code&gt; (without needing to be root):&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;$ ssh-keygen -r s0.barwen.ch&lt;br /&gt;s0.barwen.ch IN SSHFP 1 1 560f08c1687a60e62a65ef427e63698ae1797d6f&lt;br /&gt;s0.barwen.ch IN SSHFP 2 1 4ef38fd457d0afec50ca21eacb771f724e6d7236&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;So those are the records to add to &lt;code&gt;barwen.ch&lt;/code&gt;'s DNS.&lt;br /&gt;(btw, vim on my machine doesn't like SSHFP records and highlights everythign red - eww)&lt;br /&gt;&lt;br /&gt;Now wait for DNS to settle, and when I connect for the first time, I get a different message (my emphasis).&lt;br /&gt;&lt;pre&gt;The authenticity of host 's0.barwen.ch (192.168.55.55)' can't be established.&lt;br /&gt;RSA key fingerprint is 9e:81:ab:cb:2a:ad:26:2f:10:ed:dd:5c:55:dd:ea:58.&lt;br /&gt;&lt;b&gt;Matching host key fingerprint found in DNS.&lt;/b&gt;&lt;br /&gt;Are you sure you want to continue connecting (yes/no)? &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Cool.&lt;br /&gt;&lt;br /&gt;You might need to set the client option &lt;code&gt;VerifyHostKeyDNS ask&lt;/code&gt; in your &lt;code&gt;~/.ssh/config&lt;/code&gt; - if you really trust DNS, you can set it to &lt;code&gt;yes&lt;/code&gt; instead, and it won't even ask you when there's an SSHFP record present.&lt;br /&gt;&lt;br /&gt;You can try this yourself, even without a user account, because host key verification happens before user authentication: &lt;code&gt;ssh -o 'VerifyHostKeyDNS ask' s0.barwen.ch&lt;/code&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-3765092991026299963?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/3765092991026299963/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/03/sshfp-dns.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/3765092991026299963'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/3765092991026299963'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/03/sshfp-dns.html' title='sshfp dns'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-4520569701761729669</id><published>2011-03-19T13:00:00.002Z</published><updated>2011-03-19T13:24:19.026Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='oh-so-funny'/><title type='text'>ASCII</title><content type='html'>&lt;blockquote&gt;An ASCII character walks into a bar.&lt;br /&gt;The bartender says "What's the problem?"&lt;br /&gt;The ASCII character says, "I have a parity error."&lt;br /&gt;The bartender nods, and says, "Yeah, I thought you looked a bit off."&lt;br /&gt;&lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-4520569701761729669?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/4520569701761729669/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/03/ascii.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/4520569701761729669'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/4520569701761729669'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/03/ascii.html' title='ASCII'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-3283406562157544685</id><published>2011-03-17T12:16:00.001Z</published><updated>2011-03-18T07:37:21.204Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='mac-repair-bs'/><title type='text'>time for visa transaction dispute!</title><content type='html'>so I took my laptop back to a-mac utrecht, as its still got its crash-every-three-days problem. They refused to either refund the money or replace the laptop with a new one. Whilst they said they would talk to apple about a replacement, this has gone far enough for a product that was defective as sold, so I'm going to take advantage of the transaction protections I apparently get from the consumer credit act (and Visa), and dispute the transaction.&lt;br /&gt;&lt;br /&gt;I got a lovely comment from the manager of the store when I said that I would expect a kernel crash every one to two years not every three days, that he would expect it to never happen. Which is funny in a "yes we sold you crap but we won't fix it" way.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-3283406562157544685?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/3283406562157544685/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/03/time-for-visa-transaction-dispute.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/3283406562157544685'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/3283406562157544685'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/03/time-for-visa-transaction-dispute.html' title='time for visa transaction dispute!'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-2699113601450401493</id><published>2011-03-11T22:10:00.001Z</published><updated>2011-03-12T09:59:17.960Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='git'/><category scheme='http://www.blogger.com/atom/ns#' term='version-numbers'/><category scheme='http://www.blogger.com/atom/ns#' term='vcs'/><category scheme='http://www.blogger.com/atom/ns#' term='svn'/><title type='text'>numbering commits</title><content type='html'>svn gives each commit a unique number. mercurial does somethign similar. CVS doesn't. git has commit IDs btu they are big and dont' have an intrinsic order (if you dehash two commit IDs you can figure out relative order but its not apparent from the IDs themselves)&lt;br /&gt;&lt;br /&gt;I'm interested in numbering the commits on a git branch that is imported from CVS, so there's a well defined linear order (unlike in git in general).&lt;br /&gt;&lt;br /&gt;I hacked together this script:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;#!/bin/bash&lt;br /&gt;&lt;br /&gt;COMMITCOUNT=$( git rev-list origin | wc -l)&lt;br /&gt;&lt;br /&gt;echo There are $COMMITCOUNT commits on the origin branch&lt;br /&gt;&lt;br /&gt;# this strips whitespace&lt;br /&gt;export COUNT=$(($COMMITCOUNT))&lt;br /&gt;&lt;br /&gt;git rev-list origin | while read commitid ; do&lt;br /&gt;  echo numbering $commitid as $COUNT&lt;br /&gt;  TAG=cvs$COUNT&lt;br /&gt;  git tag $TAG $commitid&lt;br /&gt;  COUNT=$(( $COUNT -1))&lt;br /&gt;done&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;which works like this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;$ ./number-cvs &lt;br /&gt;There are 205 commits on the origin branch&lt;br /&gt;numbering fea9db3bc7b3e36f82a97d3bb194eb60ecb3b57f as 205&lt;br /&gt;numbering aedbfe81cc1dbf3d6f833225aa41826854398a3c as 204&lt;br /&gt;numbering f040d23565211acb637d3325d240d675fe1e61a6 as 203&lt;br /&gt;numbering eef4a46ce30de2836402a373e8eae49fc1b75935 as 202&lt;br /&gt;numbering 2bb5b931be9beb0d90a2796b85585149465f8fc3 as 201&lt;br /&gt;numbering a4372569c60bcb6d92a63b258389b5f1a210dd40 as 200&lt;br /&gt;fatal: tag 'cvs200' already exists&lt;br /&gt;numbering bbd6af71ef17fcb05a9cf86a372837dcf470e30b as 199&lt;br /&gt;fatal: tag 'cvs199' already exists&lt;br /&gt;numbering 6e1dc4d7d59b3515a3f46c17517c1bb85172c7bc as 198&lt;br /&gt;fatal: tag 'cvs198' already exists&lt;br /&gt;numbering 87c59d4b1bf4c7b1bcb64af078a66290d74f6ebf as 197&lt;br /&gt;fatal: tag 'cvs197' already exists&lt;br /&gt;[...]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This being a hack, I don't attempt to handle previously tagged revisions and instead let &lt;code&gt;git tag&lt;/code&gt; give a fatal error that isn't really fatal...&lt;br /&gt;&lt;br /&gt;Now I end up with commit tags that look like &lt;code&gt;cvs200&lt;/code&gt;, &lt;code&gt;cvs201&lt;/code&gt;, ...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-2699113601450401493?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/2699113601450401493/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/03/numbering-commits.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/2699113601450401493'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/2699113601450401493'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/03/numbering-commits.html' title='numbering commits'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-7200943908421746787</id><published>2011-03-11T11:20:00.000Z</published><updated>2011-03-11T11:20:19.322Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='osx'/><category scheme='http://www.blogger.com/atom/ns#' term='mac'/><category scheme='http://www.blogger.com/atom/ns#' term='mac-repair-bs'/><title type='text'>last repair attempt which pretty much says they did nothing actually seems to have done nothing</title><content type='html'>Well, it took about 46h from my Mac being returned to me for it to crash again. I won't be in Utrecht until next Thursday so I can't take it back to a-mac to grumble some more until then.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-7200943908421746787?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/7200943908421746787/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/03/last-repair-attempt-which-pretty-much.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/7200943908421746787'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/7200943908421746787'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/03/last-repair-attempt-which-pretty-much.html' title='last repair attempt which pretty much says they did nothing actually seems to have done nothing'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-8449832333481218278</id><published>2011-03-08T08:24:00.003Z</published><updated>2011-03-08T11:28:51.334Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='osx'/><category scheme='http://www.blogger.com/atom/ns#' term='mac'/><category scheme='http://www.blogger.com/atom/ns#' term='mac-repair-bs'/><title type='text'>result of 2nd repair</title><content type='html'>after the &lt;a href="http://benctechnicalblog.blogspot.com/2011/03/disappointing-new-mac.html"&gt;previous failed attempt to make my new mac work&lt;/a&gt;, the 2nd repair attempt of my new mac is reported as:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;Uitvoerig getest . weer waren er kleine problemen op de schijf&lt;br /&gt;ditmaal konden ze wel hersteld worden&lt;br /&gt;Ik zie aan de log files dat de kernel panic puur om software gaan en&lt;br /&gt;niet hardware.&lt;br /&gt;deze problemen zijn hersteld , en de Macbook werkt weer prima&lt;br /&gt;voor een goede test zal ik de mac nog door testen &lt;br /&gt; Na diverse testen werkt de Macbook prima geen verdere problemen&lt;br /&gt;&lt;br /&gt;07-03-2011 Aan de log files van de machine te zien heeft het probleem&lt;br /&gt;te maken met het feit dat er een Backup is terug gezet , waarvan de&lt;br /&gt;fouten waarschijnlijk mee over zijn gekopieerd&lt;br /&gt;Na herstel van de Harddisk * software ( werkt hij prima&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;which in a one-liner is "we found some corrupted files and repaired them. this is a software problem. it works fine now."&lt;br /&gt;&lt;br /&gt;To be honest, that sounds like complete BS given the symptoms and the actions taken for previous repair (meaning its had at least two separate OS installs on two different hard-drives)... plus yes "it works fine" most of the time - just it crashes every 3 or 4 days. As the mac has been in their possession for repair for only 3 days, and I'm sure they haven't been using it as much as I do during that time, I don't think they can accurately state that it works now. The next week should tell, though.&lt;br /&gt;&lt;br /&gt;In the meantime, Apple sent me a questionnaire about the previous repair attempt which gave me the opportunity for further grumbling (can you tell I'm English?).&lt;br /&gt;&lt;br /&gt;-- later --&lt;br /&gt;&lt;br /&gt;got laptop back - total result of repair: some screen dumps of them running the disk repair utility, and they wiped some fingerprints off the screen. lets see if that fixed the problem. &amp;lt;cough;&amp;gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-8449832333481218278?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/8449832333481218278/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/03/result-of-2nd-repair.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/8449832333481218278'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/8449832333481218278'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/03/result-of-2nd-repair.html' title='result of 2nd repair'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-2170144859456055805</id><published>2011-03-05T13:16:00.000Z</published><updated>2011-03-05T13:16:15.188Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='ssh'/><category scheme='http://www.blogger.com/atom/ns#' term='tunnel'/><title type='text'>ssh over CONNECT over port 80</title><content type='html'>I run an ssh server on port 443 (the https port) of one of my machines. That's good when I want to get ssh from a network which bans "everything but web-browsing" - once I have ssh I can tunnel pretty much what I want.&lt;br /&gt;&lt;br /&gt;But the other day I ran across a network (at an airport) which allowed only port 80 - the regular http port. Even port 443 was banned (so not only could I not connect to my ssh server, but no facebook, gmail, etc...)&lt;br /&gt;&lt;br /&gt;I already have stuff on port 80 - my webserver - so what could I do? I know ssh can be tunnelled through the http CONNECT method. Could I set that up so that my web server would serve web traffic as usual but allow me to CONNECT to the ssh server?&lt;br /&gt;&lt;br /&gt;Turns out yes.&lt;br /&gt;&lt;br /&gt;In the http server config - enable mod_proxy and mod_proxy_connect. Then this config in proxy.conf (or the main server config, I guess):&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&amp;lt;IfModule mod_proxy.c&gt;&lt;br /&gt;         ProxyRequests On&lt;br /&gt;        &amp;lt;Proxy *&gt;&lt;br /&gt;                AddDefaultCharset off&lt;br /&gt;                Order deny,allow&lt;br /&gt;                Deny from all&lt;br /&gt;        &amp;lt;/Proxy&gt;&lt;br /&gt;        &amp;lt;Proxy localhost&gt;&lt;br /&gt;          Allow from all&lt;br /&gt;        &amp;lt;/Proxy&gt;&lt;br /&gt;        AllowCONNECT 22&lt;br /&gt;        ProxyVia On&lt;br /&gt;&amp;lt;/IfModule&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;(If you're doing this yourself - be careful about what you allow - if you are too liberal you turn your server into an open spamming and hacking engine for the world to exploit. And it will be found. Automatically, and within days or maybe hours.)&lt;br /&gt;&lt;br /&gt;On the client side, use netcat as a proxy command in SSH: (you can also put the proxy command in ~/.ssh/ssh_config)&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;ssh -o 'ProxyCommand nc -X connect -x myhost.example.com:80 localhost 22' myhost.example.com&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Tada.&lt;br /&gt;&lt;br /&gt;Note that the ssh server now sees you connecting from localhost, and the http server is the one who logs the real IP address. A caveat here is if you have IP-based security policies that make localhost special, then anyone connecting through this proxy is also special.&lt;br /&gt;&lt;br /&gt;Lots of the config for this came from &lt;a href="http://mariobrandt.de/archives/technik/ssh-tunnel-bypassing-transparent-proxy-using-apache-170/"&gt;http://mariobrandt.de/archives/technik/ssh-tunnel-bypassing-transparent-proxy-using-apache-170/&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;So that gets me a CONNECT proxy.&lt;br /&gt;&lt;br /&gt;But does it work at the airport? No.&lt;br /&gt;&lt;br /&gt;Turns out the airport is also running everything through a transparent proxy: when I connect to s0.barwen.ch port 80, I'm actually connected to the airport's transparent proxy. So an http CONNECT command to localhost:22 both points to the wrong place and is administratively prohibits.&lt;br /&gt;&lt;br /&gt;It turns out I can http CONNECT to s0.barwen.ch port 80, though, and there issue another CONNECT command to get to localhost:22:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;$ &lt;em&gt;nc -4 -X connect -x s0.barwen.ch:80 s0.barwen.ch 80&lt;/em&gt; &lt;br /&gt;CONNECT localhost:22&lt;br /&gt;HTTP/1.0 200 Connection Established&lt;br /&gt;Proxy-agent: Apache/2.2.14 (Ubuntu)&lt;br /&gt;&lt;br /&gt;SSH-2.0-OpenSSH_5.3p1 Debian-3ubuntu5&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;So I can get to the server this way.&lt;br /&gt;&lt;br /&gt;How can I get the ssh commandline to talk to the remote server, with the extra proxy step added?&lt;br /&gt;&lt;br /&gt;Like this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;$ &lt;em&gt;cat t.sh&lt;/em&gt; &lt;br /&gt;( echo "CONNECT localhost:22" ; cat ) | nc -X connect -x s0.barwen.ch:80 s0.barwen.ch 80 | ( read ; read ; read; cat)&lt;br /&gt;&lt;br /&gt;$ &lt;em&gt;ssh -o 'ProxyCommand ./t.sh' s0.barwen.ch&lt;/em&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now once that's done, I can run a SOCKS proxy over the same ssh connection, and route my normal web browsing through that - giving me access to https sites, and anything else that can use SOCKS.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-2170144859456055805?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/2170144859456055805/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/03/ssh-over-connect-over-port-80.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/2170144859456055805'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/2170144859456055805'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/03/ssh-over-connect-over-port-80.html' title='ssh over CONNECT over port 80'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-4300806028373629400</id><published>2011-03-04T10:28:00.004Z</published><updated>2011-03-08T08:27:39.794Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='grumble'/><category scheme='http://www.blogger.com/atom/ns#' term='osx'/><category scheme='http://www.blogger.com/atom/ns#' term='mac'/><category scheme='http://www.blogger.com/atom/ns#' term='mac-repair-bs'/><title type='text'>disappointing new mac</title><content type='html'>Well my last mac died, as they do every now and then. So I activated my usual replacement behaviour: go to nearest mac store (in this case, &lt;a href="http://www.a-mac.nl/amac/"&gt;Amac&lt;/a&gt; in Utrecht) and buy a new macbook.&lt;br /&gt;&lt;br /&gt;Alas this new macbook started &lt;a href="http://ericstoller.com/blog/2007/06/22/macs-crash-too/"&gt;BSOD&lt;/a&gt;ing every few days, usually after a resume. So I took it back to the shop. They took it off for repair and diagnosed it with a faulty hard drive (?) and returned it after a week with a new HD.&lt;br /&gt;&lt;br /&gt;Macbook still BSODs ever few days.&lt;br /&gt;&lt;br /&gt;So back to the shop again. I thought maybe this time they'd give me a real replacement. No they want to take it off for repair again. I wonder if some dude has to sit in a room repeatedly suspending it and unsuspending it for a week to try to reproduce? &lt;a href="http://benctechnicalblog.blogspot.com/2011/03/result-of-2nd-repair.html"&gt;Presumably it'll come back in a week with "cannot reproduce"&lt;/a&gt; and then it will continue crashing for me and then I will have to go argue with them lots?&lt;br /&gt;&lt;br /&gt;This is a disappointingly slow and lame process which is not what I expected from either a Mac or from a Premium Reseller. grumble.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-4300806028373629400?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/4300806028373629400/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/03/disappointing-new-mac.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/4300806028373629400'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/4300806028373629400'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/03/disappointing-new-mac.html' title='disappointing new mac'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-3525317920457884217</id><published>2011-02-27T14:16:00.002Z</published><updated>2011-02-27T15:10:58.436Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='dns'/><category scheme='http://www.blogger.com/atom/ns#' term='dnssec'/><title type='text'>dnssec: automated re-signing of hawaga.org.uk</title><content type='html'>In &lt;a href="http://benctechnicalblog.blogspot.com/2011/01/dnssec-signing-hawagaorguk.html"&gt;a previous post on DNSSEC-signing my zone &lt;code&gt;hawaga.org.uk&lt;/code&gt;&lt;/a&gt;, I mentioned that signatures will expire after 30 days, and so I (or rather one of my computers) will need to re-sign the zone at least every month.&lt;br /&gt;&lt;br /&gt;Basically I need to run the &lt;code&gt;dnssec-signzone&lt;/code&gt; command again, but there is some dancing around that needs to happen.&lt;br /&gt;&lt;br /&gt;The most awkward was that I need to increment the zone serial number in the &lt;code&gt;SOA&lt;/code&gt; record of my zone. Previously I've maintained this by hand, keeping it in format &lt;code&gt;YYYYMMDDNN&lt;/code&gt; (year, month, day, sequence-number-on-that-day). That format is quite appealing because even if I forget what number I got up to, I can wait a day and know that I have a number in sequence.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;dnssec-signzone&lt;/code&gt; offers a couple of options for doing things to serial numbers, but neither was what I wanted: one will increment the input SOA by one, but I want to maintain a pristine source zone file; another will set the SOA to the number of seconds since the unix epoch. This changes the format away from what I want.&lt;br /&gt;&lt;br /&gt;So I wrote a quick utility, &lt;code&gt;soatick&lt;/code&gt;, to generate zone serial numbers based on the current time and a state file, so that each invocation will generate a new serial number matching the format that I want:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;$ &lt;em&gt;./soatick&lt;/em&gt;&lt;br /&gt;2011010901&lt;br /&gt;$ &lt;em&gt;./soatick&lt;/em&gt;&lt;br /&gt;2011010902&lt;br /&gt;$ &lt;em&gt;./soatick&lt;/em&gt;&lt;br /&gt;2011010903&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now I'll use the &lt;code&gt;m4&lt;/code&gt; macro processor to put this in place before signing the zone:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;export NEWSERIAL=$(/home/benc/src/soatick/soatick )&lt;br /&gt;m4 -D___SERIAL___=$NEWSERIAL &lt; db.hawaga.template &gt; db.hawaga.generated&lt;br /&gt;/usr/sbin/dnssec-signzone -S -t -a -l dlv.isc.org -f db.hawaga.signed -o hawaga.org.uk db.hawaga.generated&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I put the above in a script called from cron, and set it to run every week.&lt;br /&gt;&lt;br /&gt;Now a weakness here is that I have to keep my signing key unpassworded and on a system connected to the internet. The zone-signing and key-signing key separation should help here, by allowing me to keep a more important key offline and a less important key online, but I haven't investigated it in any greater depth - perhaps I should...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-3525317920457884217?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/3525317920457884217/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/02/dnssec-automated-re-signing-of.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/3525317920457884217'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/3525317920457884217'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/02/dnssec-automated-re-signing-of.html' title='dnssec: automated re-signing of hawaga.org.uk'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-9168895095002191861</id><published>2011-02-19T07:35:00.000Z</published><updated>2011-02-19T07:35:00.249Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='nmap'/><title type='text'>public nmap server</title><content type='html'>Well, I put up a &lt;a href="http://s0.barwen.ch/~nmapme/"&gt;public nmap server&lt;/a&gt; on barwen.ch that will nmap whatever address you are connecting from in your browser. I really wonder what the bad uses and the good uses (if any) this can be put to are. It was at least funny to watch yahoo and google hammer on it 'till I put a robots.txt in place.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-9168895095002191861?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://s0.barwen.ch/~nmapme/' title='public nmap server'/><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/9168895095002191861/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/02/public-nmap-server.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/9168895095002191861'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/9168895095002191861'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/02/public-nmap-server.html' title='public nmap server'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-615533793494453752</id><published>2011-02-13T09:34:00.002Z</published><updated>2011-02-13T09:34:00.746Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='random'/><category scheme='http://www.blogger.com/atom/ns#' term='password'/><title type='text'>choosing a password</title><content type='html'>many sites want a password. for a lot of middle-to-low security accounts, I keep a(&lt;a href="http://www.gnupg.org/"&gt;n encrypted&lt;/a&gt;) database of passwords on my computer, rather than making them memorable or using the same one on all. So I cut and paste each password and don't care about it being easily typable. To generate the passwords, I use a command-line like this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;$ cat /dev/random | strings -n 16&lt;br /&gt;:*jx4%8er:&gt;kRKh:&lt;br /&gt;a#ka;lPB6rB9SX";lk&lt;br /&gt;6B!'X@Q{@QQ LZB?&lt;br /&gt;hZ if=A2u3;-S]v?P&lt;br /&gt;Ix6RwEwqVqEg~0fFi&lt;br /&gt;[hkE*0T~GZX^5=h&lt;4&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-615533793494453752?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/615533793494453752/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/02/choosing-password.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/615533793494453752'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/615533793494453752'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/02/choosing-password.html' title='choosing a password'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-2162395094415326702</id><published>2011-02-06T09:59:00.001Z</published><updated>2011-02-27T16:37:09.308Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='mrtg'/><category scheme='http://www.blogger.com/atom/ns#' term='ipv6'/><category scheme='http://www.blogger.com/atom/ns#' term='iptables'/><title type='text'>Using mrtg and iptables to record IPv4 vs IPv6 traffic</title><content type='html'>I wanted to plot IPv6 vs IPv4 traffic on my hosts - I have most services enabled for IPv6 but I know they don't get used much. I had MRTG already.&lt;br /&gt;&lt;br /&gt;iptables on Linux counts bytes that pass through it, even if there are no iptables rules:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;$ &lt;em&gt;iptables -L -v -x&lt;/em&gt;&lt;br /&gt;Chain INPUT (policy ACCEPT 6079694 packets, 2474020715 bytes)&lt;br /&gt;[...]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;That counts ipv4 packets. The ipv6 equivalent is ip6tables.&lt;br /&gt;&lt;br /&gt;So without needing to add any iptables rules at all, I can feed this output to mrtg with a script as follows, which outputs IPv4 traffic (for all three categories) as the first (input) variable, and IPv6 as the second(output) variable.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;#!/bin/bash&lt;br /&gt;A=0&lt;br /&gt;IP4=$(/sbin/iptables -L -x -v | grep -e ^Chain | sed 's/.* \([0-9]*\) bytes)$/\1/' | ( while read n ; do A=$(( $A + $n )) ; done ; echo $A))&lt;br /&gt;&lt;br /&gt;A=0&lt;br /&gt;IP6=$(/sbin/ip6tables -L -x -v | grep -e ^Chain | sed 's/.* \([0-9]*\) bytes)$/\1/' | ( while read n ; do A=$(( $A + $n )) ; done ; echo $A))&lt;br /&gt;&lt;br /&gt;echo $IP4&lt;br /&gt;echo $IP6&lt;br /&gt;echo 0&lt;br /&gt;echo unknown&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;On one host, there really is hardly any ipv6 traffic (2 bytes/sec!) so I turned on the log scale  plot option in MRTG to show the ipv6 a bit more (though to be honest its still pretty invisible).&lt;br /&gt;&lt;br /&gt;Here's the config I used in MRTG to call the above script:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;Target[ip46]: `/home/benc/bin/iptables-to-mrtg`&lt;br /&gt;Target[ip46]: `curl http://dildano.hawaga.org.uk/mrtg-iptables.txt`&lt;br /&gt;options[ip46]: growright,logscale&lt;br /&gt;MaxBytes[ip46]: 1000000000000&lt;br /&gt;Title[ip46]: IPv4 vs IPv6&lt;br /&gt;YLegend[ip46]: bytes/sec&lt;br /&gt;LegendI[ip46]: IPv4&lt;br /&gt;LegendO[ip46]: IPv6&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;and here's an example graph (live, click for historical data):&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.hawaga.org.uk/mrtg/paella_ip46.html"&gt;&lt;img src="http://www.hawaga.org.uk/mrtg/paella_ip46-week.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Caveats:&lt;br /&gt;&lt;br /&gt;I'm summing the all three iptables chains: input, output, and forwarded, for all interfaces. So some traffic here can be counted unexpectedly: A forwarded packet traverses all three chains (I think) so this is not a good way to count traffic if your linux box is a router; The lo interface will also be counted, so traffic to localhost (127.0.0.1 or ::1) will be counted in this graph. This might be useful to remove.&lt;br /&gt;&lt;br /&gt;When there's a tunnel endpoint on the machine, then traffic to that tunnel will be counted twice: one as it passes the tunnel interface, and once as the encapsulated form passes the physical ethernet interface.&lt;br /&gt;&lt;br /&gt;These are not insurmountable, I think: by setting specific iptables rules that address these concerns and counting traffic from those instead of the main chain counters.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-2162395094415326702?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/2162395094415326702/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/02/using-mrtg-and-iptables-to-record-ipv4.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/2162395094415326702'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/2162395094415326702'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/02/using-mrtg-and-iptables-to-record-ipv4.html' title='Using mrtg and iptables to record IPv4 vs IPv6 traffic'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-1552680223086770566</id><published>2011-01-29T09:41:00.000Z</published><updated>2011-01-29T09:41:37.317Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='dns'/><category scheme='http://www.blogger.com/atom/ns#' term='dnssec'/><title type='text'>dnssec: signing hawaga.org.uk</title><content type='html'>In this post I will sign my main DNS zone &lt;code&gt;hawaga.org.uk&lt;/code&gt; using DNSSEC so that other resolvers can verify the contents when they make a DNS lookup.&lt;br /&gt;&lt;br /&gt;The parent domain &lt;code&gt;org.uk&lt;/code&gt; does not do DNSSEC at the moment so I am going to use &lt;a href="http://www.isc.org/solutions/dlv"&gt;DLV (DNSSEC Lookaside Validation)&lt;/a&gt;. DLV will let me publish my zone public keys in DLVs registry instead of in the parent &lt;code&gt;org.uk&lt;/code&gt; zone, and my zones will be validated by anyone who has configured DLV in their resolver, rather than by all DNSSEC users (pretty much the first is a strict subset of the second). This is something of a hack - hopefully in a few months &lt;code&gt;org.uk&lt;/code&gt; will support DNSSEC and I'll be able to directly publish my keys there instead.&lt;br /&gt;&lt;br /&gt;Most of what follows works for both normal DNSSEC and DNSSEC-with-DLV.&lt;br /&gt;&lt;br /&gt;I need to generate two public/private keypairs: a zone signing key, and a key signing key. The key signing key will be used to sign the zone signing key, and the zone signing key will be used to sign the records inside the zone. Its not entirely clear to me which private keys need to be online at which step in the process, but for my immediate purposes I don't mind too much - I'm keeping everything online the whole time for now.&lt;br /&gt;&lt;br /&gt;bind comes with a utility called &lt;code&gt;dnssec-keygen&lt;/code&gt; to generate the keys, and it is used much like any other command-line key generator such as &lt;code&gt;gpg&lt;/code&gt; or&lt;code&gt;openssl&lt;/code&gt;:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;$ &lt;em&gt;/usr/sbin/dnssec-keygen -a RSASHA1 -b 768 -n ZONE hawaga.org.uk&lt;/em&gt;&lt;br /&gt;Generating key pair...................++++++++ ..++++++++&lt;br /&gt;Khawaga.org.uk.+005+48075&lt;br /&gt;$ &lt;em&gt;/usr/sbin/dnssec-keygen  -f KSK -a RSASHA1 -b 2048 -n ZONE hawaga.org.uk&lt;/em&gt;&lt;br /&gt;...&lt;br /&gt;$ &lt;em&gt;ls Khawaga*&lt;/em&gt;&lt;br /&gt;Khawaga.org.uk.+005+48075.key      Khawaga.org.uk.+005+48196.key&lt;br /&gt;Khawaga.org.uk.+005+48075.private  Khawaga.org.uk.+005+48196.private&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now, I'm going to sign the zone:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;$ &lt;em&gt;/usr/sbin/dnssec-signzone -S -l dlv.isc.org -o hawaga.org.uk db.hawaga&lt;/em&gt; &lt;br /&gt;Fetching KSK 48196/RSASHA1 from key repository.&lt;br /&gt;Verifying the zone using the following algorithms: RSASHA1.&lt;br /&gt;Zone signing complete:&lt;br /&gt;Algorithm: RSASHA1: KSKs: 1 active, 0 stand-by, 0 revoked&lt;br /&gt;                    ZSKs: 1 active, 0 stand-by, 0 revoked&lt;br /&gt;db.hawaga.signed&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;and I get these output files:&lt;br /&gt;&lt;pre&gt;-rw-r--r-- 1 benc benc 35505 2011-01-04 21:47 db.hawaga.signed&lt;br /&gt;-rw-r--r-- 1 benc benc   195 2011-01-04 21:47 dlvset-hawaga.org.uk.&lt;br /&gt;-rw-r--r-- 1 benc benc   171 2011-01-04 21:47 dsset-hawaga.org.uk.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The signed zone lives in &lt;code&gt;db.hawaga.signed&lt;/code&gt;. It looks like my input zone but with some new records added. For every input records, there is a new &lt;code&gt;RRSIG&lt;/code&gt; record; there are &lt;code&gt;DNSKEY&lt;/code&gt; records which contain the public keys generated above; and there are &lt;code&gt;NSEC&lt;/code&gt; records which are used to securely deny the existence of entries.&lt;br /&gt;&lt;br /&gt;In my bind directory, I replace my original &lt;code&gt;db.hawaga&lt;/code&gt; zone file with the new signed version and restart bind.&lt;br /&gt;&lt;br /&gt;Now bind will serving the appropriate DNSSEC records. I can see them by querying using the &lt;code&gt;+dnssec&lt;/code&gt; flag to dig:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;$ &lt;em&gt;dig +dnssec www.hawaga.org.uk&lt;/em&gt;&lt;br /&gt;[...]&lt;br /&gt;;; ANSWER SECTION:&lt;br /&gt;www.hawaga.org.uk.      3600    IN      CNAME   paella.hawaga.org.uk.&lt;br /&gt;www.hawaga.org.uk.      3600    IN      RRSIG   CNAME 5 4 3600 20110208095742 20110109095742 48075 hawaga.org.uk. lNdWf71In/J8F7evBxxi1yTw7Fx4WRkcHK9vOilEyLmDq31uwYlTtf+d bAZ4WHSeF9aHGRlvy1c6bAWEgWs64K8RNr/7pkZW+y0kmP8qL+Eu0nOH DvvQQ32eShnoEYGc&lt;br /&gt;paella.hawaga.org.uk.   3600    IN      A       174.143.245.7&lt;br /&gt;paella.hawaga.org.uk.   3600    IN      RRSIG   A 5 4 3600 20110208095742 20110109095742 48075 hawaga.org.uk. EeUBihcnpDJad9JJnwLcR0nm9ef1E58fxdwio/SK1iSorkFZLZjxvk7r GYkCR0aCwAw1F2/lJ3kb6pTk+10H00lyowQc8crtBPIDkwiqDE0pTtEE JPCm51XuAc3bz/lX&lt;br /&gt;[...]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;So now my zone is signed.&lt;br /&gt;&lt;br /&gt;But! (and this is a big but) there is no trust path for any resolver to validate that - there is no reason for any resolver to believe that it really was *me* that generated the keys rather than some random attacker.&lt;br /&gt;&lt;br /&gt;In X.509, certificate authorities tell you how to trust someone by signing certificates. In plain DNSSEC, the parent zone acts as a CA by providing a signed DS (delegation signer) record in addition to an NS record. In DLV, ISC's DLV service fills this role by providing the signed DS record.&lt;br /&gt;&lt;br /&gt;I'm going to use ISC's DLV zone (which appears to be the only serious one). There's a web interface to sign up, at &lt;a href="https://dlv.isc.org/"&gt;https://dlv.isc.org/&lt;/a&gt;. When you sign up there, the interface authenticates your ownership of the zone by giving you a cookie TXT record to put in your zone. Once that authentication has happened, you can upload a &lt;code&gt;DNSKEY&lt;/code&gt;, &lt;code&gt;DS&lt;/code&gt; or &lt;code&gt;DLV&lt;/code&gt; record.&lt;br /&gt;&lt;br /&gt;Then ISC will publish a DLV record set for you (presumably it constructs one for you if you give a &lt;code&gt;DNSKEY&lt;/code&gt; or &lt;code&gt;DS&lt;/code&gt; record?). For example, mine looks like this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;hawaga.org.uk.dlv.isc.org. 877  IN      DLV     48196 5 2 FD144A3B36AC7E939DD7A04D5C6F967A07D01FB0029CA8B440F6CC2D 6F2415B8&lt;br /&gt;hawaga.org.uk.dlv.isc.org. 877  IN      DLV     48196 5 1 0DE399B36303676F1243A923B6FC3893AE248F90&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;It takes a few hours for that DLV record to appear, but when it does, that is enough for a DLV-enabled resolver to be able to authenticate.&lt;br /&gt;&lt;br /&gt;&lt;a href="https://www.dns-oarc.net/"&gt;DNS-OARC&lt;/a&gt; has some open DNSSEC-enabled resolvers that can be used for testing this. I can check my DNSSEC using one of them:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;$ &lt;em&gt;dig +dnssec @2001:4f8:3:2bc:1::64:20 -t a www.hawaga.org.uk&lt;/em&gt;&lt;br /&gt;[...]&lt;br /&gt;;; flags: qr rd ra ad; QUERY: 1, ANSWER: 4, AUTHORITY: 3, ADDITIONAL: 5&lt;br /&gt;[...]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;and look out for the &lt;code&gt;ad&lt;/code&gt; bit to appear in the flags field. It does, which means that resolver trusts my results.&lt;br /&gt;&lt;br /&gt;OK. That's the setup pretty much done. Two last notes:&lt;br /&gt;&lt;br /&gt;Firstly, when a bind resolver is authoritative for a zone, then it won't check signatures and give an &lt;code&gt;ad&lt;/code&gt; flag on results for that zone. That left me puzzling for a while, wondering why it wasn't checking my results, and is why I have to use one of DNS-OARC's resolvers above.&lt;br /&gt;&lt;br /&gt;Secondly, this zone is only signed for 30 days. After that period (specifically, 30 days after I ran the dnssec-signzone command), the signatures will become invalid. This is quite a change from my previous non-DNSSEC zone update habits - I changed my zone a few times per year and only when I needed a change, but now I'm going to need to update it at least every month, and inaction will cause it to break. In a subsequent post, I'm going to write about some of the automation I did to do this.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-1552680223086770566?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/1552680223086770566/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/01/dnssec-signing-hawagaorguk.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/1552680223086770566'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/1552680223086770566'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/01/dnssec-signing-hawagaorguk.html' title='dnssec: signing hawaga.org.uk'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-5887818358570718858</id><published>2011-01-22T18:16:00.000Z</published><updated>2011-01-22T18:16:33.801Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='dns'/><category scheme='http://www.blogger.com/atom/ns#' term='dnssec'/><category scheme='http://www.blogger.com/atom/ns#' term='bind'/><category scheme='http://www.blogger.com/atom/ns#' term='security'/><title type='text'>dnssec: Configuring my resolver</title><content type='html'>DNSSEC, a mechanism for securing DNS, has around for a long time but only in the last year or so has it seen serious deployment. The root zone was signed about 6 months ago, which provides a security root from which all other DNSSEC can flow. Last time I looked at DNSSEC that was far in the untimetabled future, so I didn't put much effort then to get it actually working.&lt;br /&gt;&lt;br /&gt;In this post, I'm going to write about configuring bind to check DNSSEC when I make DNS queries. In later posts, I'll write about the other side of things: securing my own zones with DNS.&lt;br /&gt;&lt;br /&gt;I mentioned the root zone being signed above. That's one way of checking DNSSEC signatures (and in the long term, the main way). Another way is DLV (DNSSEC Lookaside Validation) which acts as a certificate authority, providing a way for a DNS zone to be signed without having to have a path all the way from the root. A third way is by listing the keys for known domains, which allows everything under those domains to be validated without needing a signature from a higher level in DNS. (This third way is how IANA's &lt;a href="https://itar.iana.org/"&gt;Interim Trust Anchor Repository&lt;/a&gt; worked, before being superseded by the signing of the root zone).&lt;br /&gt;&lt;br /&gt;I want to configure both validation from the root, and DLV.&lt;br /&gt;&lt;br /&gt;I very roughly followed along with &lt;a href="https://dnssec.surfnet.nl/?p=402"&gt;this page&lt;/a&gt;, though different versions of bind, and differences in what I want to do, lead to differences.&lt;br /&gt;&lt;br /&gt;First I need the root key-signing (public) key. This is the single well-known value that must be securely obtained. So I use insecure DNS to obtain it:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;$ &lt;em&gt;dig . dnskey | grep "257 " &gt; root.dnskey&lt;/em&gt;&lt;br /&gt;$ &lt;em&gt;cat root.dnskey&lt;/em&gt;&lt;br /&gt;.                       172363  IN      DNSKEY  257 3 8&lt;br /&gt;AwEAAagAIKlVZrpC6Ia7gEzahOR+9W29euxhJhVVLOyQbSEW0O8gcCjF&lt;br /&gt;FVQUTf6v58fLjwBd0YI0EzrAcQqBGCzh/RStIoO8g0NfnfL2MTJRkxoX&lt;br /&gt;bfDaUeVPQuYEhg37NZWAJQ9VnMVDxP/VHL496M/QZxkjf5/Efucp2gaD&lt;br /&gt;X6RS6CXpoY68LsvPVjR0ZSwzz1apAzvN9dlzEheX7ICJBBtuA6G3LQpz&lt;br /&gt;W5hOA2hzCTMjJPJ8LbqF6dsV6DoBQzgul0sGIcGOYl7OyQdXfZ57relS&lt;br /&gt;Qageu+ipAdTTJ25AsRTAoub8ONGcLmqrAmRLKBP1dfwhYB4N7knNnulq&lt;br /&gt;QxA+Uk1ihz0=&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now at this point I should carefully check that this is right against various out-of-band sources. I didn't bother.&lt;br /&gt;&lt;br /&gt;I also want a trusted key for DLV. &lt;a href="https://www.isc.org/solutions/dlv#dlv_key"&gt;That is available here.&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Next I need to tell bind about these keys. I'm going to use bind's managed-key mechanism for this.&lt;br /&gt;I put the key material into a file called &lt;code&gt;benc-managed-keys&lt;/code&gt;, like this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;managed-keys {&lt;br /&gt;   dlv.isc.org. initial-key 257 3 5 "BEAAAAPHMu/5onzrEE7z1egmhg/WPO0+juoZrW3euWEn4MxDCE1+lLy2 brhQv5rN32RKtMzX6Mj70jdzeND4XknW58dnJNPCxn8+jAGl2FZLK8t+ 1uq4W+nnA3qO2+DL+k6BD4mewMLbIYFwe0PG73Te9fZ2kJb56dhgMde5 ymX4BI/oQ+cAK50/xvJv00Frf8kw6ucMTwFlgPe+jnGxPPEmHAte/URk Y62ZfkLoBAADLHQ9IrS2tryAe7mbBZVcOwIeU/Rw/mRx/vwwMCTgNboM QKtUdvNXDrYJDSHZws3xiRXF1Rf+al9UmZfSav/4NWLKjHzpT59k/VSt TDN0YUuWrBNh";&lt;br /&gt;   . initial-key 257 3 8 "AwEAAagAIKlVZrpC6Ia7gEzahOR+9W29euxhJhVVLOyQbSEW0O8gcCjF FVQUTf6v58fLjwBd0YI0EzrAcQqBGCzh/RStIoO8g0NfnfL2MTJRkxoX bfDaUeVPQuYEhg37NZWAJQ9VnMVDxP/VHL496M/QZxkjf5/Efucp2gaD X6RS6CXpoY68LsvPVjR0ZSwzz1apAzvN9dlzEheX7ICJBBtuA6G3LQpz W5hOA2hzCTMjJPJ8LbqF6dsV6DoBQzgul0sGIcGOYl7OyQdXfZ57relS Qageu+ipAdTTJ25AsRTAoub8ONGcLmqrAmRLKBP1dfwhYB4N7knNnulq QxA+Uk1ihz0=";&lt;br /&gt;&lt;br /&gt;};&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;em&gt;Note that the format is different from the key files obtained - the &lt;code&gt;IN DNSKEY&lt;/code&gt; keywords go away and &lt;code&gt;initial-key&lt;/code&gt; goes in instead, and the key material is now quoted with &lt;code&gt;"&lt;/code&gt; and with a &lt;code&gt;;&lt;/code&gt; at the end of the line.&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;Then I told bind to import this into its configuration:&lt;br /&gt;&lt;pre&gt;$ grep benc-managed-keys *&lt;br /&gt;named.conf:include "/etc/bind/benc-managed-keys";&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I need to tell bind to start using dnssec by adding these to named.conf.options:&lt;br /&gt;&lt;pre&gt;dnssec-enable yes;&lt;br /&gt;dnssec-validation yes;&lt;br /&gt;dnssec-lookaside auto;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now restart bind and hope that it all works.&lt;br /&gt;&lt;br /&gt;How can I test my setup? I can use &lt;code&gt;dig&lt;/code&gt; with the &lt;code&gt;+dnssec&lt;/code&gt; parameter. This adds a flag to the query saying that DNSSEC is desired. For example:&lt;br /&gt;&lt;pre&gt;$ &lt;em&gt;dig @192.168.1.254 +dnssec hawaga.org.uk&lt;/em&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;code&gt;dig&lt;/code&gt; will give a flag line in its output, like this:&lt;br /&gt;&lt;pre&gt;;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 4, ADDITIONAL: 1&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;If DNSSEC was able to check the results (which means that the result was signed, and that the resolver that you just queried was able to validate the results, then there will be another flag in there: &lt;code&gt;ad&lt;/code&gt;. If not, then something didn't work correctly.&lt;br /&gt;&lt;br /&gt;You can get more DNSSEC logging by adding this to named.conf:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;logging {&lt;br /&gt;           channel dnssec_log {             // a DNSSEC log channel&lt;br /&gt;                     file "/var/log/bind/dnssec.log" size 200m;&lt;br /&gt;                   print-time yes;        // timestamp the entries&lt;br /&gt;                     print-category yes;    // add category name to entries&lt;br /&gt;                   print-severity yes;    // add severity level to entries&lt;br /&gt;                     severity debug 3;      // print debug message &lt;= 3&lt;br /&gt;   &lt;br /&gt;             };&lt;br /&gt;   &lt;br /&gt;       category dnssec  { dnssec_log;  };&lt;br /&gt;     };&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;So that's DNSSEC configured for resolving. Next, I want to sign my own zones so that others can verify them with DNSSEC on their own resolvers - I'll write about that in another post.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-5887818358570718858?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/5887818358570718858/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/01/dnssec-configuring-my-resolver.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/5887818358570718858'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/5887818358570718858'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/01/dnssec-configuring-my-resolver.html' title='dnssec: Configuring my resolver'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-7308542890171719514</id><published>2011-01-14T13:26:00.013Z</published><updated>2011-05-03T11:24:51.083+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='6to4'/><category scheme='http://www.blogger.com/atom/ns#' term='tunnel'/><category scheme='http://www.blogger.com/atom/ns#' term='ipv6'/><category scheme='http://www.blogger.com/atom/ns#' term='ec2'/><title type='text'>IPv6 in Amazon EC2</title><content type='html'>Amazon declares that IPv6 is unsupported on EC2 (the Elastic Compute Cloud), but I wanted it anyway. Here's two ways I got it: using &lt;a href="http://en.wikipedia.org/wiki/6to4"&gt;6to4&lt;/a&gt; and using a tunnel from &lt;a href="http://www.tunnelbroker.net/"&gt;Hurricane Electric&lt;/a&gt;. In short, the winner was Hurricane.&lt;br /&gt;&lt;br /&gt;I did everything below on one of the example Linux instances supplied by Amazon, with an elastic IP address attached.&lt;br /&gt;&lt;br /&gt;The first approach I tried was using 6to4 - a protocol which automatically gives anyone with a static IPv4 address their own range of IPv6 addresses. In &lt;a href="http://benctechnicalblog.blogspot.com/2011/01/6-to-4-in-5.html"&gt;another blog post&lt;/a&gt;, I described how to get 6to4 running on Linux in 5 command lines. I ran those commands on my EC2 instance and end up with my own IPv6 address.&lt;br /&gt;&lt;br /&gt;There were, however, some reachability problems. Its not clear to me how much of that comes from EC2 (for example, I tried to disable their firewall as much as possible, but its not clear how disabled it is), and how much it comes from the flakiness of 6to4. (see, for example, that &lt;a href="http://www.google.com/intl/en/ipv6/faq.html#tunnelbroker"&gt;Google doesn't support ipv6 over tunnels&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;I have two IPv6 hosts already, one on 6to4 (P), one with native connectivity (D).&lt;br /&gt;&lt;br /&gt;I can ping from my EC2 host to my own host P. Pinging from P to EC2 gives no response *until* I start a ping from EC2 to P, in which case I start getting responses. That makes me think very much that Amazon is running a stateful firewall for protocol 41 that allows traffic back in once something has been sent out, for any particular IPv4 address.&lt;br /&gt;&lt;br /&gt;I cannot ping at all between D and EC2, in either direction.&lt;br /&gt;&lt;br /&gt;From &lt;a href="http://www.subnetonline.com/pages/ipv6-network-tools/online-ipv6-ping.php"&gt;this online ping tool&lt;/a&gt; I can ping my EC2 instance OK.&lt;br /&gt;&lt;br /&gt;The second method I tried is &lt;a href="http://tunnelbroker.net/"&gt;a manually configured tunnel via Hurricane Electric&lt;/a&gt;. HE have been around a long time and have a good reputation (and who I used before years ago). The configuration is a set of fairly straightforward web forms. I allocated a /64 (even that is excessive - I only need a single IPv6 address for this host)&lt;br /&gt;&lt;br /&gt;The web form also gives example configuration instructions for a variety of platforms - The &lt;code&gt;Linux-net-tools&lt;/code&gt; instructions are the ones I want. I pasted the 4 given commands literally into a root prompt on my EC2 machine, and that was all I needed for my machine to be connected (at least until reboot).&lt;br /&gt;&lt;br /&gt;I see similar "stateful firewall" behaviour as I mentioned above, but they difference is that that is only be between me and the HE tunnel endpoint - as long as *anything* goes over the tunnel, then connectivity with the entire IPv6 world stays up. And when that tunnel is up, the connectivity from machines D and P seem *much* better. As in: I can ping both ways without any mysterious losses. So it looks like I'll need a ping to the tunnel endpoint (or anywhere, really) every minute or so. That's no big deal - I have &lt;a href="http://s0.barwen.ch/~mrtg/hawaga_ping6.html"&gt;MRTG&lt;/a&gt; set up to measure some ipv6 latencies anyway, and that suffices.&lt;br /&gt;&lt;br /&gt;So HE is a little bit (a few web forms) more effort to set up. But the connectivity is much much better. I recommend HE over 6to4 for this.&lt;br /&gt;&lt;br /&gt;Other links: &lt;a href="http://atopuzovic.dyndns.org/blog/?p=10"&gt;&lt;i&gt;aco&lt;/i&gt; wrote about getting IPv6 on EC2 using sixxs&lt;/a&gt;, and if you're interested in getting a shell account on this machine to try for yourself: &lt;a href="http://www.barwen.ch/"&gt;www.barwen.ch&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Modified: 2011-04-19 Rephrasing a bit based on ongoing experience, and some more hyperlinks&lt;br /&gt;&lt;br /&gt;&lt;a href="http://flattr.com/thing/181269/IPv6-in-Amazon-EC2" target="_blank"&gt;&lt;br /&gt;&lt;img src="http://api.flattr.com/button/flattr-badge-large.png" alt="Flattr this" title="Flattr this" border="0" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-7308542890171719514?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/7308542890171719514/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/01/ipv6-in-amazon-ec2.html#comment-form' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/7308542890171719514'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/7308542890171719514'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/01/ipv6-in-amazon-ec2.html' title='IPv6 in Amazon EC2'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-6399565302132125281</id><published>2011-01-14T13:25:00.000Z</published><updated>2011-01-14T13:25:55.261Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='6to4'/><category scheme='http://www.blogger.com/atom/ns#' term='ipv6'/><title type='text'>6 to 4 in 5</title><content type='html'>I have native IPv6 on my LAN for one server. I have another server which lives on LAN with only native IPv4. Can I get IPv6 on that? Yes!&lt;br /&gt;&lt;br /&gt;My present configuration uses &lt;a href="http://en.wikipedia.org/wiki/6to4"&gt;6to4&lt;/a&gt;. This is a very straightforward mechanism to configure on linux, without needing any setup outside of my own machine. On the downside, it gives very little control of routing which can lead to strange connectivity problems that are hard to debug.&lt;br /&gt;&lt;br /&gt;I followed the &lt;br /&gt;&lt;a href="http://tldp.org/HOWTO/Linux+IPv6-HOWTO/configuring-ipv6to4-tunnels.html"&gt;Linux IPv6 HOWTO&lt;/a&gt; howto pretty faithfully.&lt;br /&gt;&lt;br /&gt;I needed these 5 steps:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;$ ipv4="174.143.245.7"; printf "2002:%02x%02x:%02x%02x::1" `echo $ipv4 | tr "." " "`&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This tells me that my prefix is: &lt;code&gt;2002:ae8f:f507::/48&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;I get a 16-bit (4 hex-digit) subnet number to pick my own networks (I'll pick 2 in this case, fairly arbitrarily), and then the remaining 64 bits go towards identifying the host on the network (which I'll pick 0123:4567:89ab:cdef. Another good choice would be ::1)&lt;br /&gt;&lt;br /&gt;So I end up choosing the IP address: 2002:ae8f:f507:2:0123:4567:89ab:cdef&lt;br /&gt;&lt;br /&gt;Next:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;modprobe sit  (it was compiled but not loaded)&lt;br /&gt;ifconfig sit0 add 2002:ae8f:f507:2:0123:4567:89ab:cdef/16 up&lt;br /&gt;/sbin/route -A inet6 add 2000::/3 gw ::192.88.99.1 dev sit0&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;and now I can ping my own machine:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;benc@paella:~$ ping6 dildano.hawaga.org.uk&lt;br /&gt;PING dildano.hawaga.org.uk(dildano.hawaga.org.uk) 56 data bytes&lt;br /&gt;64 bytes from dildano.hawaga.org.uk: icmp_seq=1 ttl=62 time=136 ms&lt;br /&gt;64 bytes from dildano.hawaga.org.uk: icmp_seq=2 ttl=62 time=132 ms&lt;br /&gt;64 bytes from dildano.hawaga.org.uk: icmp_seq=3 ttl=62 time=132 ms&lt;br /&gt;&lt;br /&gt;--- dildano.hawaga.org.uk ping statistics ---&lt;br /&gt;3 packets transmitted, 3 received, 0% packet loss, time 2000ms&lt;br /&gt;rtt min/avg/max/mdev = 132.002/133.335/136.002/1.931 ms&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;easy peasy. shame it's quite unreliable.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-6399565302132125281?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/6399565302132125281/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/01/6-to-4-in-5.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/6399565302132125281'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/6399565302132125281'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/01/6-to-4-in-5.html' title='6 to 4 in 5'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-5748128958825376624</id><published>2011-01-13T13:07:00.000Z</published><updated>2011-01-13T13:07:58.149Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='analytical-engine'/><title type='text'>building an analytic engine: plan 28</title><content type='html'>A while ago, I pledged some money to &lt;a href="http://plan28.org/"&gt;plan 28&lt;/a&gt; through pledgebank.&lt;br /&gt;&lt;br /&gt;In short:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;Plan 28 is asking 10,000 people to pledge money towards building the Analytical Engine&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;It sounds like the project is going to go ahead even though they haven't reached the 10000 people they wanted. But maybe you want to make a pledge anyway?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-5748128958825376624?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://plan28.org/' title='building an analytic engine: plan 28'/><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/5748128958825376624/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/01/building-analytic-engine-plan-28.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/5748128958825376624'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/5748128958825376624'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/01/building-analytic-engine-plan-28.html' title='building an analytic engine: plan 28'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-7496678333477212354</id><published>2011-01-09T11:30:00.001Z</published><updated>2011-01-12T11:27:01.941Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='photo'/><title type='text'>3d cat</title><content type='html'>here is a picture of me with a stuffed cat at the oxford natural history museum. I happened to have two frames with a slight difference in position, so I combined them in an animated gif in a 3d display technique that I've seen before. It has the advantage that you don't need special viewing apparatus, but the disadvantage that you get seasick watching it.&lt;br /&gt;&lt;br /&gt;&lt;img src="http://www.hawaga.org.uk/ben/cat.gif" /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-7496678333477212354?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/7496678333477212354/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/01/3d-cat.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/7496678333477212354'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/7496678333477212354'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/01/3d-cat.html' title='3d cat'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-7582093455966326191</id><published>2011-01-02T10:06:00.005Z</published><updated>2011-01-17T11:47:30.293Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='server'/><category scheme='http://www.blogger.com/atom/ns#' term='ntp'/><category scheme='http://www.blogger.com/atom/ns#' term='horology'/><title type='text'>pool.ntp.org</title><content type='html'>I've used pool.ntp.org to give me reasonable NTP servers before. On Christmas day I investigated how I could add my server. It turns out there is a self-service user interface to do so, with a scoring system that dynamically decides if your server is good enough to be published.&lt;br /&gt;&lt;br /&gt;So I added by NTP server &lt;code&gt;ntp.hawaga.org.uk&lt;/code&gt; (that also does a bunch of other stuff), waited a while for the score to rise, and then sat back and watched the port 123 packets flow.&lt;br /&gt;&lt;br /&gt;There is lots of fun stuff about NTP breaking in ways which result in server floods - summarised in Wikipedia's &lt;a href="http://en.wikipedia.org/wiki/NTP_server_misuse_and_abuse"&gt;NTP_server_misuse_and_abuse&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;One accusation that I've seen is that 50% of traffic is well behaved clients, and that the other 50% is a small number of misbehaving hosts which poll and poll and poll and poll. I've seen that behaviour in some of my &lt;code&gt;tcpdump&lt;/code&gt; runs, though that is not too bad today:&lt;br /&gt;&lt;br /&gt;Over a 15 minute period, I got 282 packets, less that one per second, from 53 different IP addresses, with a very unbalanced distribution: 24 hosts sending one packet, 50 sending less than one per minute (15 packets total), 3 sending more than one per minute, and one host sent 65 packets.&lt;br /&gt;&lt;br /&gt;Update (Jan 6th 2011): You can watch the score history for my server &lt;a href="http://www.pool.ntp.org/user/bxc"&gt;in my pool.ntp.org profile&lt;/a&gt;, although at time of writing its kinda dull, being a flat top score for the past few days.&lt;br /&gt;&lt;br /&gt;Update (Jan 7th 2011): I grabbed packets for about the last 24, and see 2736 distinct IP addresses, 32778 packets total (8 packets avg), noisiest host was 85.248.56.73 with 4628 packets. Anyway that's the most number of users I've ever provided service to, I think!&lt;br /&gt;&lt;br /&gt;Update (Jan 17th 2011): I now have MRTG monitoring this server's estimated offset from correct time (similar but different to the profile graph above):&lt;br /&gt;&lt;a href="http://www.hawaga.org.uk/mrtg/ntp.html"&gt;&lt;img src="http://www.hawaga.org.uk/mrtg/ntp-day.png" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-7582093455966326191?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/7582093455966326191/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/01/poolntporg.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/7582093455966326191'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/7582093455966326191'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2011/01/poolntporg.html' title='pool.ntp.org'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-1192991472741586359</id><published>2010-12-26T13:45:00.001Z</published><updated>2010-12-26T13:45:00.119Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='socialism'/><category scheme='http://www.blogger.com/atom/ns#' term='scifi'/><title type='text'>cybersyn</title><content type='html'>I found this in wikipedia:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;a href="http://en.wikipedia.org/wiki/Project_Cybersyn"&gt;Project Cybersyn&lt;/a&gt; was a Chilean attempt at real-time computer-controlled planned economy in the years 1970–1973 (during the government of president Salvador Allende). It was essentially a network of telex machines that linked factories with a single computer centre in Santiago, which controlled them using principles of cybernetics.&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;I think that's quite an interesting cross-over between socialism and computer control.&lt;br /&gt;&lt;br /&gt;The wikipedia page has a cool pic of the "control room" which looks totally Star Trek (although other reading suggests that was a prototype control room and not actually used in real life).&lt;br /&gt;&lt;img src="http://upload.wikimedia.org/wikipedia/en/thumb/7/70/Cybersyn_control_room.jpg/250px-Cybersyn_control_room.jpg" /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-1192991472741586359?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://en.wikipedia.org/wiki/Project_Cybersyn' title='cybersyn'/><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/1192991472741586359/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/12/cybersyn.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/1192991472741586359'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/1192991472741586359'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/12/cybersyn.html' title='cybersyn'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-1993399501582746785</id><published>2010-12-19T13:57:00.000Z</published><updated>2010-12-19T13:57:55.431Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='latex'/><title type='text'>latex double-cite</title><content type='html'>Turns out LaTeX you can write markup like &lt;code&gt;&lt;br /&gt;\cite{a,b}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;to get multiple citations in the same place like this: &lt;code&gt;[1,2]&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Which is kinda obvious.&lt;br /&gt;&lt;br /&gt;But I'd never thought of it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-1993399501582746785?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/1993399501582746785/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/12/latex-double-cite.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/1993399501582746785'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/1993399501582746785'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/12/latex-double-cite.html' title='latex double-cite'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-3425846331874606544</id><published>2010-09-26T14:52:00.000+01:00</published><updated>2010-09-26T14:52:53.372+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='gsm'/><title type='text'>foreign sim cards work better</title><content type='html'>international mobile phone roaming has a strange property: in any particular location, a foreign roaming sim card gets better coverage rather than a local sim card: generally all local networks are available for a foreign sim card to connect to, but only one network is available for the local network.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-3425846331874606544?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/3425846331874606544/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/09/foreign-sim-cards-work-better.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/3425846331874606544'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/3425846331874606544'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/09/foreign-sim-cards-work-better.html' title='foreign sim cards work better'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-4562409634439748957</id><published>2010-09-12T17:36:00.000+01:00</published><updated>2010-09-12T17:36:00.171+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='scuba'/><title type='text'>extreme diving for work</title><content type='html'>Some quotes from a paper on the &lt;a href="http://www.scuba-doc.com/rgbmim.pdf"&gt;Reduced Gradient Bubble Model&lt;/a&gt; for scuba diving decompression calculation which I've been looking at recently. Most of what I've looked at so far has been recreational no-decompression diving, so the dives described below seem quite extreme. (for comparison, the deepest recreational dives are to about 40 metres = 130 feet, and for a maximum of 8 minutes)&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;Pearling ﬂeets, operating in the deep tidal waters off northern Australia, employed Okinawan divers who regularly journeyed to depths of 300 f sw for as long as one hour, two times a day, six days per week, and ten months out of the year.&lt;br /&gt;[...]&lt;br /&gt;With higher incidence of surface decompression sickness, as might be expected, the Australians  devised a simple, but very effective, in-water recompression procedure. The stricken diver is taken back down to 30 fsw on oxygen for roughly 30 minutes in mild cases, or 60 minutes in severe cases. Increased pressures help to constrict bubbles, while breathing pure oxygen maximizes inert gas washout (elimination).&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;blockquote&gt;Similar schedules and procedures have evolved in Hawaii, among diving ﬁshermen [...] Harvesting the oceans for food and proﬁt, Hawaiian divers make beween 8 and 12 dives a day to depths beyond 350fsw.&lt;br /&gt;[...]&lt;br /&gt;Consistent with bubble and nucleation theory, these divers make their &lt;br /&gt;deep dive ﬁrst, followed by shallower excursions.&lt;br /&gt;[...]&lt;br /&gt;In a broad sense, the ﬁnal shallow dives have been tagged as prolonged safety stops.&lt;br /&gt;&lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-4562409634439748957?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://www.scuba-doc.com/rgbmim.pdf' title='extreme diving for work'/><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/4562409634439748957/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/09/extreme-diving-for-work.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/4562409634439748957'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/4562409634439748957'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/09/extreme-diving-for-work.html' title='extreme diving for work'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-5611346379492791046</id><published>2010-09-05T14:12:00.000+01:00</published><updated>2010-09-05T14:12:00.417+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='photo'/><category scheme='http://www.blogger.com/atom/ns#' term='hardware'/><title type='text'>new photo frame</title><content type='html'>On sale, €33 at Media Markt, I got a digital photo frame.&lt;br /&gt;&lt;br /&gt;It has a slot for a USB stick, and for various memory cards. It looks like an upgraded version of one I bought for my parents a year ago, where the USB interface worked well but the card reader seemed unreliable. So I put one of the many free conference USB sticks in my collection in.&lt;br /&gt;&lt;br /&gt;People seem to have an instinct to unplug these things continuously. Why? You don't put curtains over the print photos on your wall and only open the curtains for a few minutes at a time to look at the photos behind.&lt;br /&gt;&lt;br /&gt;The frame is 16:9 aspect ratio. My camera makes files of ratio 4:3 (or 3:4 in landscape). (apparently print photos are usually 3:2. my camera has a helper mode that overlays grey bars on screen to show you 3:2 ratio. But it doesn't have a similar mode for 16:9)&lt;br /&gt;&lt;br /&gt;On my laptop, I have gimp. But I found that Preview is good enough for rough cropping of photos to size. That's the technical side of cropping easily dealt with. On the artistic side, though, its hard to get in the habit of evaluating photos for such cropping - photos that I think of as "too much space at the top / bottom" are ones that are perfect for this frame, and photos that fill the frame nicely are too full for cropping.&lt;br /&gt;&lt;br /&gt;As the frame is so low resolution (~ VGA) its possibly to crop smaller details from a frame, so that you end up with an entirely new picture rather than something that looks like the original photo but a bit cropped.&lt;br /&gt;&lt;br /&gt;I've had a desire but not enough motivation to actually look at my photos from a cropping perspective. Perhaps this will provide the motivation.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-5611346379492791046?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/5611346379492791046/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/09/new-photo-frame.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/5611346379492791046'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/5611346379492791046'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/09/new-photo-frame.html' title='new photo frame'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-2639018066624701079</id><published>2010-08-29T01:31:00.002+01:00</published><updated>2011-05-18T14:04:46.708+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='maths'/><title type='text'>nominal, ordinal, interval, ratio</title><content type='html'>&lt;a href="http://mdm.gwu.edu/Forman/DBO.pdf"&gt;Decision by Objectives&lt;/a&gt; (DBO) classes applications of numbers into a hierarchy of four different types, each building on the properties of the previous one.&lt;br /&gt;&lt;br /&gt;The concepts are not unfamiliar at all to a mathematician, but they are approaching from a management direction rather than from pure mathematics, and I think that is an interesting different perspective.&lt;br /&gt;&lt;br /&gt;The first application of numbers is &lt;em&gt;nominal&lt;/em&gt;. In this application, numbers are used to name things. DBO gives examples from horse racing: The number on a jockey or a horse is a nominal use. It identifies a particular jockey or horse; but it is meaningless to (for example) add two jockey numbers together, or divide one by another. In Maths, that means it is possible to permute the numbers in any way without losing any information. The numbers, ℕ, are used purely as a set of distinct objects without structure or relation. We could equally well use any other sufficiently large set of symbols (such as letters).&lt;br /&gt;&lt;br /&gt;&lt;p&gt;The next application of numbers is &lt;em&gt;ordinal&lt;/em&gt;. In this application, numbers are used to order things. In the horse racing example, the finishing order of the horses is an ordinal application. Some horse finishes &lt;em&gt;first&lt;/em&gt;, another finishes &lt;em&gt;second&lt;/em&gt;, yet another finishes &lt;em&gt;third&lt;/em&gt;. In Maths, there is a linear order between the numbers, and we have a relation &lt; or ≤ that makes sense. We can compare numbers in this application, with a notion of bigger or faster or stronger). But it still doesn't make sense to (for example) add or subtract those numbers, or to make statements like "horse X finished 3 places ahead of horse y".&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;The third application of numbers is &lt;em&gt;interval&lt;/em&gt;. It makes sense to subtract interval numbers from each other. The authors are a little vague in DSO about whether they think it makes sense to add interval numbers. I think in some applications it does (how much chocolate did you eat? how much chocolate did I eat? we can compute: how much chocolate did we eat together?) but in some it does not (what is the time now? what time did the meeting start? we can compute: how long has the meeting been going on (subtraction), but adding the time now and the meeting start time doesn't make sense). When it does make sense to add, the numbers form a group with operation +. When it only makes sense to take the difference, then the numbers form something like a &lt;a href="http://math.ucr.edu/home/baez/torsors.html"&gt;torsor&lt;/a&gt; - a group without identity.&lt;br /&gt;&lt;br /&gt;The final application is &lt;em&gt;ratio&lt;/em&gt;. These is use of numbers where ratios make sense - for example, a horse might finish in &lt;em&gt;half the time&lt;/em&gt; of another, meaning that the ratio of the time of the first horse to the second horse is 1/2. It makes sense to multiply and divide these numbers; and ratios can also be used to scale interval numbers. Mathematically, we have a (meaningful use of a) field, ℝ or ℚ.&lt;br /&gt;&lt;br /&gt;The point of them making these distinctions in DSO was to point out that just because you have some information in your decision making represented as numbers, it doesn't always make sense to do things like taking the mean, or summing, or whatever.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-2639018066624701079?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://mdm.gwu.edu/Forman/DBO.pdf' title='nominal, ordinal, interval, ratio'/><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/2639018066624701079/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/08/nominal-ordinal-interval-ratio.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/2639018066624701079'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/2639018066624701079'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/08/nominal-ordinal-interval-ratio.html' title='nominal, ordinal, interval, ratio'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-6648818167273685996</id><published>2010-08-22T13:58:00.000+01:00</published><updated>2010-08-22T13:58:00.818+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='unix'/><category scheme='http://www.blogger.com/atom/ns#' term='rsync'/><title type='text'>rsync --fake-super</title><content type='html'>Every now and then I discover new options on old utilities.&lt;br /&gt;&lt;br /&gt;One I am very happy to have discovered in &lt;a href="http://www.samba.org/rsync/"&gt;rsync&lt;/a&gt; is the &lt;code&gt;--fake-super&lt;/code&gt; option.&lt;br /&gt;&lt;br /&gt;Scenario:&lt;br /&gt;&lt;br /&gt;I have machine A. I want to back up (some portion of) the file system onto machine B. I want to include permissions and ownership (for example, because I am backing up &lt;code&gt;/home&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;I can run &lt;code&gt;rsync&lt;/code&gt; on machine A as root from a &lt;code&gt;cron&lt;/code&gt; job. OK. But then (traditionally) it needs root access to machine B in order to set permissions and ownersip of the files it creates. I can't have it connect to machine B as some normal user because of that. Additionally, the user-id and group-id name/number spaces on both machines need to match up somewhat so that users on machine B don't get access to files they shouldn't have access to.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;--fake-super&lt;/code&gt; changes that. When &lt;code&gt;rsync&lt;/code&gt; tries to change the permission or ownership of a file and finds that it cannot do that, it instead stores that information in extended attributes on that file. So now access to machine B can be through some normal user account without special privileges. &lt;br /&gt;&lt;br /&gt;A downside is that if some user has an account on both sides, they don't get full privilege access to the backups of their own files.&lt;br /&gt;&lt;br /&gt;Another use I found for this is on my laptop under OS X, where one of my external hard-drives is now mounted with an option to prevent user and group IDs being changed, or makes them ignored somehow (presumably for a better experience when using the hard-drive on multiple machines).&lt;br /&gt;Incremental rsync backups were experience an inability to change group ownership on files, which mean that instead of being hard-linked (using &lt;code&gt;--link-dest&lt;/code&gt;) they were being copied afresh each time. This was fixed by &lt;code&gt;--fake-super&lt;/code&gt; too - instead of changing ownership on the external HD filesystem, they're added to the extended attributes.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-6648818167273685996?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/6648818167273685996/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/08/rsync-fake-super.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/6648818167273685996'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/6648818167273685996'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/08/rsync-fake-super.html' title='rsync --fake-super'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-9190846597317647544</id><published>2010-08-18T12:21:00.000+01:00</published><updated>2010-08-18T12:21:08.371+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='agda'/><title type='text'>fractions and proof-irrelevance IV - forgetting proofs</title><content type='html'>Continuing from &lt;a href="http://benctechnicalblog.blogspot.com/2010/08/fractions-and-proof-irrelevance-i-sane.html"&gt;previous posts on implementing fractions in Agda&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;We've tried to force our representations of fractions to have a canonical form, but requiring the inclusion of a proof of that canonical form breaks canonicity.&lt;br /&gt;&lt;br /&gt;So we need a different approach.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;Data.Rational&lt;/code&gt; does it something like this, which I found hard to understand at first. Trying to understand &lt;code&gt;isCoprime&lt;/code&gt; here was the motivation for this sequence of posts:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;record ℚ : Set where&lt;br /&gt;  field&lt;br /&gt;    numerator     : ℤ&lt;br /&gt;    denominator-1 : ℕ&lt;br /&gt;    isCoprime     : True (coprime? ( ∣ numerator ∣ ) (suc denominator-1))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;code&gt;isComprime&lt;/code&gt; is different. Rather than directly expressing a proposition (&lt;code&gt;Coprime numerator (suc denominator-1)&lt;/code&gt;) as the type of &lt;code&gt;isCoprime&lt;/code&gt;, we instead use a slightly different proposition (&lt;code&gt;coprime?&lt;/code&gt;) and wrap it in &lt;code&gt;True&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;coprime?&lt;/code&gt; has type &lt;code&gt;Decidable Coprime&lt;/code&gt;. It is a &lt;em&gt;decidable&lt;/em&gt; version of the coprime proposition. A normal proposition type has values that are proofs of that proposition. If I give you a proof, then you know the proposition is true. However, if I don't give you a proof, you don't know that the proposition is false. Perhaps its true but I haven't given you a proof. A &lt;em&gt;decidable proposition&lt;/em&gt; type has values that are either proofs that the proposition is true, or proofs that the proposition is false, wrapped in &lt;code&gt;yes&lt;/code&gt; or &lt;code&gt;no&lt;/code&gt; respectively. &lt;code&gt;coprime?&lt;/code&gt; is a function that &lt;em&gt;decide&lt;/em&gt; whether two numbers are coprime or not, and return a corresponding proof.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;True&lt;/code&gt; is a function that maps a decidable proposition type into another type: either ⊤ (when the proposition is true) or ⊥ (when the proposition is false).&lt;br /&gt;&lt;br /&gt;⊤ (&lt;em&gt;top&lt;/em&gt;) is the unit type, that has only one member value. ⊥ (&lt;em&gt;bottom&lt;/em&gt;) is the empty type with no member values at all.&lt;br /&gt;&lt;br /&gt;Both of those seem a bit useless to a traditional programmer: if I have a variable of the unit type ⊤, it can only have one value. So then I'll always know what that value is - its pretty much a constant. So why have a variable? And if I have a variable of the empty type ⊥ then I can't ever invent a value. If that variable is an input parameter, I can never even call my program, because I can't give input parameters. But it turns out this weird behaviour is exactly right for &lt;code&gt;Data.Rational&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;Say I'm going to construct a half. I'm going to pass &lt;code&gt;numerator = 1&lt;/code&gt; and &lt;code&gt;denominator = 2&lt;/code&gt;. We also need to pass an &lt;code&gt;isCoprime&lt;/code&gt; parameter.&lt;br /&gt;&lt;br /&gt;To start with, what type of parameter is that?&lt;br /&gt;&lt;br /&gt;That &lt;em&gt;depends&lt;/em&gt; on the value of &lt;em&gt;numerator&lt;/em&gt; and &lt;em&gt;denominator&lt;/em&gt; (that's what dependent types are). Once we know the numerator and denominator, we can evaluate the expression &lt;code&gt;True (coprime? ( ∣ numerator ∣ ) (suc denominator-1))&lt;/code&gt; to find out the type of &lt;code&gt;isCoprime&lt;/code&gt;.  1 and 2 are coprime, so &lt;code&gt;coprime?&lt;/code&gt; evaluates to &lt;code&gt;yes _&lt;/code&gt;, and so &lt;code&gt;True (yes _)&lt;/code&gt; evaluates to ⊤.&lt;br /&gt;&lt;br /&gt;So we need to give a value for coprime that is of type ⊤. ⊤ only has one value, so thats the value that we'll put in &lt;code&gt;isCoprime&lt;/code&gt;. The resulting structure has no proof of coprimality in it. If two fractions have the same numerator and denominator, then they'll always have the same value of &lt;em&gt;isCoprime&lt;/em&gt;. So we've got equality of equivalent fractions!&lt;br /&gt;&lt;br /&gt;But now what happens if we try to construct a non-canonical fraction, for example 3/6 ?&lt;br /&gt;Once we have specified a numerator of 3 and a denominator of 6, we can figure out the type of &lt;code&gt;isCoprime&lt;/code&gt;. 3 and 6 are not coprime, and &lt;code&gt;True (no _)&lt;/code&gt; will return ⊥.&lt;br /&gt;So we need to provide a value of type ⊥. There are no values in ⊥. So there is no way we can call the constructor in this case, because we can't come up with a value for each parameter.&lt;br /&gt;&lt;br /&gt;So now the goal is achieved - a data structure for representing rationals, where equal rationals are definitionally equal.&lt;br /&gt;&lt;br /&gt;My goal in picking apart &lt;code&gt;Data.Rational&lt;/code&gt; like this was two-fold: i) I wanted to use rationals, and wanted to understand what was going on; ii) I want to implement my own canonical forms (for finitely-generated abelian groups) and understanding how its done for rationals seems like a step in the right direction.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-9190846597317647544?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/9190846597317647544/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/08/fractions-and-proof-irrelevance-iv.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/9190846597317647544'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/9190846597317647544'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/08/fractions-and-proof-irrelevance-iv.html' title='fractions and proof-irrelevance IV - forgetting proofs'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-4947290393294709084</id><published>2010-08-18T12:19:00.001+01:00</published><updated>2010-08-18T12:21:34.799+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='agda'/><title type='text'>fractions and proof-irrelevance III - proofs are too isomorphic to values</title><content type='html'>Continuing from &lt;a href="http://benctechnicalblog.blogspot.com/2010/08/fractions-and-proof-irrelevance-i-sane.html"&gt;my earlier post about fractions and proof-irrelevance&lt;/a&gt;...&lt;br /&gt;&lt;br /&gt;In the previous post, we added a parameter to the rational constructor to require the numerator and denominator of rationals to be coprime, in order that we can only construct canonical rationals, so that we get equality that behaves "right" - when two fractions are the same, they will be equal in the eyes of Agda.&lt;br /&gt;&lt;br /&gt;But the way we did it doesn't work quite right:&lt;br /&gt;&lt;br /&gt;The Curry-Howard correspondence connects propositions and proofs on one side, and datatypes/sets and values/elements of sets on the other side:&lt;br /&gt;&lt;br /&gt;&lt;table border="1"&gt;&lt;tr&gt;&lt;th&gt;Side A&lt;/th&gt;   &lt;th colspan="2"&gt;Side B&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th&gt;&lt;/th&gt;         &lt;th&gt;Programmer view&lt;/th&gt; &lt;th&gt;Maths view&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Proposition&lt;/td&gt; &lt;td&gt;Data type&lt;/td&gt;  &lt;td&gt;Set&lt;/td&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Proof&lt;/td&gt;       &lt;td&gt;Value&lt;/td&gt;      &lt;td&gt;Element&lt;/td&gt; &lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;When we added the coprimality test to the rational constructor, we added a parameter of type &lt;code&gt;Coprime numerator denominator&lt;/code&gt;. That corresponds to the proposition that the numerator and denominator are coprime. Its a parameter so it will have a value when it is used to construct some rational, and that value will be a proof that the numerator and denominator are co-prime.&lt;br /&gt;&lt;br /&gt;So now think about constructing a half. We'll put in numerator 1, and denominator 2, and some proof, &lt;code&gt;pr1&lt;/code&gt;, that &lt;code&gt;{1,2}&lt;/code&gt; are co-prime.&lt;br /&gt;&lt;br /&gt;Now somebody else builds a half. They'll put in numerator 1, and denominator 2, and some proof, &lt;code&gt;pr2&lt;/code&gt;, that &lt;code&gt;{1,2}&lt;/code&gt; are co-prime.&lt;br /&gt;&lt;br /&gt;Now I'm going to compare my half to that other person's half to see if they are equal. They're equal if all the constructor parameters are equal. The numerator, 1, matches. The denominator, 2, matches. But we have a third parameter now, the coprimality proof. Do they match? Does &lt;code&gt;pr1 = pr2&lt;/code&gt; ? In general, no, that isn't the case. There could be many different proofs of the same proposition.&lt;br /&gt;&lt;br /&gt;So we've ended up with a canonical form for the numerator and denominator, but we've just pushed the problem into a different field of the data structure, and we still don't have a canonical data structure.&lt;br /&gt;&lt;br /&gt;Somehow we want to insist that the coprime proposition is true, but without different proofs of that causing different representations of fractions. That is &lt;em&gt;proof irrelevance&lt;/em&gt;.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://benctechnicalblog.blogspot.com/2010/08/fractions-and-proof-irrelevance-iv.html"&gt;Read on for part IV, the final part&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-4947290393294709084?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/4947290393294709084/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/08/fractions-and-proof-irrelevance-iii.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/4947290393294709084'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/4947290393294709084'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/08/fractions-and-proof-irrelevance-iii.html' title='fractions and proof-irrelevance III - proofs are too isomorphic to values'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-840677067132057508</id><published>2010-08-18T12:18:00.001+01:00</published><updated>2010-08-18T12:20:29.853+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='agda'/><title type='text'>fractions and proof-irrelevance II - equality of fractions</title><content type='html'>This continues &lt;a href="http://benctechnicalblog.blogspot.com/2010/08/fractions-and-proof-irrelevance-i-sane.html"&gt;from the first in a series of posts&lt;/a&gt; reviewing the implementation of &lt;code&gt;Data.Rational&lt;/code&gt; in Agda.&lt;br /&gt;&lt;br /&gt;We've got a datatype for representing fractions, ensuring that the denominator is strictly positive. So now any element of the mathematical set ℚ can be represented by an element in our datatype ℚ; and vice-versa any element of our datatype ℚ represents an element of the mathematical set ℚ.&lt;br /&gt;&lt;br /&gt;Sometimes, we want to be able to compare fractions to see if they are the same or not. It's fairly straightforward to define an equivalence relation between two fractions, using integer multiplication, something like this:&lt;br /&gt;&lt;pre&gt;p ≃ q  =  P.numerator ℤ* Q.denominator ≡ Q.numerator ℤ* P.denominator&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;But sometimes we want to use equivalence where we don't get to specify the equivalence relation. One example of that is type-checking: say we want to use a fraction (for some purpose) as part of a type specification. In that case, we need equivalent fractions to be &lt;em&gt;definitionally equal&lt;/em&gt;: they must have been constructed with the same constructor, and the parameters to the constructor must be the same.&lt;br /&gt;&lt;br /&gt;The numerator, denominator representation described in the previous post breaks down here: we can represent a half as  &lt;code&gt;numerator = 1, denominator = 2&lt;/code&gt;; but equally well as &lt;code&gt;numerator = 3, denominator = 6&lt;/code&gt;. Yet, &lt;code&gt;1 ≠ 3&lt;/code&gt; and &lt;code&gt;2 ≠ 6&lt;/code&gt; so the two representations are not definitionally equal.&lt;br /&gt;&lt;br /&gt;One way of solving this is by forcing the representations to be in some canonical form. For fractions, one such form is when the numerator and denominator are co-prime so that there are no shared factors. This way, &lt;code&gt;1/2&lt;/code&gt; is the canonical representation of a half. &lt;code&gt;3/6 = (3*1) / (3*2)&lt;/code&gt; is not, because 3 is a factor of both the numerator and denominator.&lt;br /&gt;&lt;br /&gt;So maybe we could demand that when you call the constructor for a fraction, you have to prove that the numerator and denominator are co-prime, by defining something like this:&lt;br /&gt;&lt;pre&gt;record ℚ : Set where&lt;br /&gt;  field&lt;br /&gt;    numerator     : ℤ&lt;br /&gt;    denominator-1 : ℕ&lt;br /&gt;    isCoprime     : Coprime ( | numerator | ) (suc denominator-1)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;That way, whenever we construct an element of ℚ, we'll know that the numerator and denominator are co-prime, because we give a proof of the proposition "Coprime numerator denominator" (see that we would add one onto &lt;code&gt;denominator-1&lt;/code&gt; to get the denominator).&lt;br /&gt;&lt;br /&gt;&lt;a href="http://benctechnicalblog.blogspot.com/2010/08/fractions-and-proof-irrelevance-iii.html"&gt;Read on for part III...&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-840677067132057508?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/840677067132057508/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/08/fractions-and-proof-irrelevance-ii.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/840677067132057508'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/840677067132057508'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/08/fractions-and-proof-irrelevance-ii.html' title='fractions and proof-irrelevance II - equality of fractions'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-6690237867845660877</id><published>2010-08-18T12:17:00.001+01:00</published><updated>2010-08-18T12:18:38.007+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='agda'/><title type='text'>fractions and proof-irrelevance I - sane denominators</title><content type='html'>This begins a series of posts reviewing the implementation of rationals in Agda.&lt;br /&gt;&lt;br /&gt;I'll talk about broken ways of building the rationals ℚ in Agda, how its desirable to be able to compare rationals for equality, how having proofs in the language can get in the way of that, and how to cope with that.&lt;br /&gt;&lt;br /&gt;This is all based on the code I found in the Data.Rational standard library module, which I have been trying to understand as I want to use both the module itself, and its techniques, in a scientific units-of-measurement library that I am writing. &lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Rationals&lt;/h2&gt;A rational (or a fraction) can be described as an integer (the numerator) divided by a strictly positive integer (the denominator).&lt;br /&gt;&lt;br /&gt;It's not hard to build an Agda data type to represent a fraction as a pair of two integers:&lt;br /&gt;&lt;pre&gt;data ℚ : Set where&lt;br /&gt;    field&lt;br /&gt;      numerator : ℤ&lt;br /&gt;      denominator : ℤ&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This implemenation doesn't require the denominator to be a positive integer, though. So we can represent "invalid" fractions. Most importantly, we can represent a fraction with denominator 0 this way, which is nonsensical.&lt;br /&gt;&lt;br /&gt;So lets store &lt;em&gt;the denominator minus 1&lt;/em&gt; as a natural number (0, 1, 2, ...) like this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;record ℚ : Set where&lt;br /&gt;  field&lt;br /&gt;    numerator     : ℤ&lt;br /&gt;    denominator-1 : ℕ&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;em&gt;The denominator minus 1&lt;/em&gt; can be 0 or higher, which means the denominator can be 1 or higher - a positive integer as desired.&lt;br /&gt;&lt;br /&gt;On the downside, now every time we want to compute with the denominator, we have to compute it first by adding one to &lt;em&gt;the denominator minus 1&lt;/em&gt;.&lt;br /&gt;&lt;br /&gt;This is a pattern I found strange (and still find unintuitive) in Agda. Rather than storing something in a format that you want (the denominator), you instead store it in a format that makes correctness work.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://benctechnicalblog.blogspot.com/2010/08/fractions-and-proof-irrelevance-ii.html"&gt;Read on for part II...&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-6690237867845660877?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/6690237867845660877/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/08/fractions-and-proof-irrelevance-i-sane.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/6690237867845660877'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/6690237867845660877'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/08/fractions-and-proof-irrelevance-i-sane.html' title='fractions and proof-irrelevance I - sane denominators'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-4364793863210092169</id><published>2010-08-15T11:27:00.000+01:00</published><updated>2010-08-15T11:27:00.223+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='presenting'/><title type='text'>acetate slides</title><content type='html'>&lt;a href="http://cs.ioc.ee/efftt/mcbride-slides.pdf"&gt;This pdf&lt;/a&gt; shows an interesting style of PDF slide generation that seems to be "acetates + digital camera". I guess they were presented on acetates to begin with and scanned in? Not so weird as I first though, which was that they were drawn on whiteboard and photographed... but still novel compared to powerpoint.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-4364793863210092169?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://cs.ioc.ee/efftt/mcbride-slides.pdf' title='acetate slides'/><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/4364793863210092169/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/08/acetate-slides.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/4364793863210092169'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/4364793863210092169'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/08/acetate-slides.html' title='acetate slides'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-549610965353507739</id><published>2010-08-08T12:09:00.001+01:00</published><updated>2010-08-09T13:15:52.102+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Erdős'/><title type='text'>Erdős number</title><content type='html'>There's this concept of an &lt;a href="http://en.wikipedia.org/wiki/Erdős_number"&gt;Erdős number&lt;/a&gt; for an academic author: take the graph of academic authors where two authors are joined together if they have written a paper together; then your Erdős number is the number of hops from you to the currently deceased mathematician Paul Erdős.&lt;br /&gt;&lt;br /&gt;I first read about him a decade ago in &lt;a href="http://en.wikipedia.org/wiki/The_Man_Who_Loved_Only_Numbers"&gt;The Man Who Loved Only Numbers&lt;/a&gt; but I've never got round to actually computing my number 'till now.&lt;br /&gt;&lt;br /&gt;Borja Sotomayor shares a co-author with me (Ian Foster), so my number is less than or equal to his. &lt;a href="http://www.borjanet.com/archives/2007/05/07/numero-erdos-6"&gt;He is number 6&lt;/a&gt;, so my number is ≤6, through this chain:&lt;br /&gt;&lt;br /&gt;Ben Clifford → &lt;a href="http://www.mcs.anl.gov/about/people_detail.php?id=285"&gt;Ian T. Foster&lt;/a&gt; → Patrick H. Worley → Robert S. Schreiber → John Russell Gilbert → Roger C. Entringer → &lt;a href="http://en.wikipedia.org/wiki/Paul_Erdős"&gt;Paul Erdös&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;(p.s. its hard to type the ő in Erdős. Its not ö, although it looks like it)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-549610965353507739?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/549610965353507739/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/08/theres-this-concept-of-erdos-number-for.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/549610965353507739'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/549610965353507739'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/08/theres-this-concept-of-erdos-number-for.html' title='Erdős number'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-6811475308095991407</id><published>2010-07-29T13:53:00.007+01:00</published><updated>2010-08-02T17:22:17.168+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='electricity'/><title type='text'>weight-based electrical measurement and other fun stuff</title><content type='html'>From a paper &lt;em&gt;&lt;a href="http://www.cl.cam.ac.uk/~rja14/Papers/meters-weis.pdf"&gt;On the security economics of electricity metering&lt;/a&gt;&lt;/em&gt; by Ross Anderson and Shailendra Fuloria (via &lt;a href="http://www.schneier.com/"&gt;Bruce Schneier's blog&lt;/a&gt;), a few of quotes that amused me.&lt;br /&gt;&lt;blockquote&gt;Edison invented a meter consisting of a jar holding two zinc plates: each month the electrodes were weighed and the customer billed according to the change in their weight. &lt;br /&gt;&lt;/blockquote&gt;and&lt;br /&gt;&lt;blockquote&gt;For example, customers in Soweto noticed that their meters would set  themselves to maximum credit in a brown-out (a voltage reduction to 160-180V) because they had not been tested adequately for African conditions; this only came to light when customers started throwing chains over the 11kV feeders in order to credit their meters. &lt;br /&gt;&lt;/blockquote&gt;and (about california):&lt;br /&gt;&lt;blockquote&gt;generators had enough market power to boost peak prices by shutting down some of their plants for “maintenance reasons”. They had learned this trick in 1998 when the replacement reserve price of electricity peaked at $9,999.99/MW in July as against the average of $10/MW for the ﬁrst three months of the year&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;A bit later they talk about contract languages, which reminds me very much of recent talk of expressing stock market contracts in Python:&lt;br /&gt;&lt;blockquote&gt;This analysis suggests a technical research project: to design a command language for meters that enables an energy company to specify complex tariﬀs in a piece of signed downloaded code  that would implement a particular contract between the supplier and the customer. The sort of  tariﬀ we have in mind is: “Your basic rate is 5p from midnight to 0600, 15p for the peak from  1730 to 2100 and 10p the rest of the time; however the 15p basic peak rate applies only provided  you restrict demand to two kilowatts else it becomes 50p. Furthermore we may at any time request you to limit consumption to 1kW and if you don’t then any half-hour segment during which you draw more than 1kW at any time will be charged at 50p”. The code would have to export to the energy company enough data for it to bill the customer correctly, and enough to the distibution company for it to operate the system eﬃciently. This problem is nontrivial but we believe it is solvable.&lt;br /&gt;&lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-6811475308095991407?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://www.cl.cam.ac.uk/~rja14/Papers/meters-weis.pdf' title='weight-based electrical measurement and other fun stuff'/><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/6811475308095991407/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/07/weight-based-electrical-measurement.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/6811475308095991407'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/6811475308095991407'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/07/weight-based-electrical-measurement.html' title='weight-based electrical measurement and other fun stuff'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-8855595265271949166</id><published>2010-07-23T17:38:00.001+01:00</published><updated>2010-07-23T17:38:00.609+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='agda'/><title type='text'>branching on the left vs branching on the right (agda bubble sort part 3)</title><content type='html'>I gave a definition of &lt;code&gt;onepass&lt;/code&gt;, the inner loop of my Agda bubblesort implementation, in &lt;a href="http://benctechnicalblog.blogspot.com/2010/07/bubble-sort-1-bubblesort-in-agda.html"&gt;an earlier post&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;That wasn't the first implementation that I tried.&lt;br /&gt;&lt;br /&gt;Instead, first I wrote it like this, in a way that uses &lt;code&gt;with&lt;/code&gt; expressions. The difference is that the version I wrote uses an &lt;code&gt;if&lt;/code&gt; construct that is manually defined. Agda seems to prefer doing case analysis / branching using &lt;code&gt;with&lt;/code&gt;, which makes it seem as if you're getting a new parameter to pattern match on - in the below example, the results of &lt;code&gt;y ≤? y&lt;/code&gt; which is either &lt;code&gt;yes _&lt;/code&gt; or &lt;code&gt;no _&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;onepass : {n : ℕ}→ Vec ℕ n → Vec ℕ n&lt;br /&gt;  onepass {zero} [] = []&lt;br /&gt;  onepass {suc zero} (y :: []) = y :: []&lt;br /&gt;  onepass {suc (suc n)} (y :: (y' :: y0)) with y ≤? y'&lt;br /&gt;  ... | yes _ = y :: (onepass {suc n} (y' :: y0))&lt;br /&gt;  ... | no _ = y' :: (onepass {suc n} (y :: y0))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;For comparison, here's the definition in my first post:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;onepass {suc (suc n)} (y :: (y' :: y0)) = if y ≤? y' then        &lt;br /&gt;                                        y :: (onepass {suc n} (y' :: y0))     &lt;br /&gt;                                   else y' :: (onepass {suc n} (y :: y0))  &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The two implementations do the same thing at runtime - they swap the first two elements of the list and recurse. But their compile-time behaviour is different.&lt;br /&gt;&lt;br /&gt;The &lt;code&gt;with&lt;/code&gt;-based version doesn't pass the termination checker. The &lt;code&gt;if&lt;/code&gt;-based one does.&lt;br /&gt;&lt;br /&gt;The &lt;code&gt;with&lt;/code&gt;-based version with with the proofs that I gave in &lt;a href=""&gt;part 2&lt;/a&gt;. The &lt;code&gt;if&lt;/code&gt; based version doesn't.&lt;br /&gt;&lt;br /&gt;So I pretty much am forced to choose: I can use the &lt;code&gt;with&lt;/code&gt;-based definition, and turn off the termination checker in the Agda compiler. Or not. Either way seems pretty lame. In order to proof all this stuff, I have to turn off the termination checker which is a key part of checking the proofs that I'm writing.&lt;br /&gt;&lt;br /&gt;I don't really understand what is going on inside Agda to make the first version not work. But that's what I actually use in practice (with termination checking turned off) - perhaps I can investigate a bit more later. But for now, I want to continue working on a proof (albeit not termination checked) that bubblesort actually sorts...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-8855595265271949166?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://code.google.com/p/agda/issues/detail?id=59&amp;can=1&amp;q=termination' title='branching on the left vs branching on the right (agda bubble sort part 3)'/><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/8855595265271949166/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/07/branching-on-left-vs-branching-on-right.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/8855595265271949166'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/8855595265271949166'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/07/branching-on-left-vs-branching-on-right.html' title='branching on the left vs branching on the right (agda bubble sort part 3)'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-1583381620893405071</id><published>2010-07-22T14:04:00.000+01:00</published><updated>2010-07-22T14:04:49.639+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='agda'/><title type='text'>permutations (bubble sort in agda, pt 2)</title><content type='html'>(following from &lt;a href="http://benctechnicalblog.blogspot.com/2010/07/bubble-sort-1-bubblesort-in-agda.html"&gt;part 1&lt;/a&gt;)&lt;br /&gt;&lt;br /&gt;I implemented bubblesort in Agda, and it looks bug-free. But I want to &lt;em&gt;prove&lt;/em&gt; that it is correct.  First I want to prove that the output  is a permutation of the input. I'm not going to try proving anything about the ordering yet, but I want to know that at least I'm getting back &lt;em&gt;the same list, rearranged&lt;/em&gt;.&lt;br /&gt;&lt;br /&gt;This is where Agda starts getting really hard for me, and becomes more like stuff I did in my maths degree courses than stuff I've done with hobby computer hacking. Except you can't write "clearly, .... is true" in your Agda programs like you could in your maths homework, so you don't get to bluff your way through proofs quite so easily.&lt;br /&gt;&lt;br /&gt;The previous post showed how you could augment the datatypes of the functions in your program to carry more information about how those functions behave. That's one way of using dependent types. In this post, I'm going to use a different style to prove that the output of bubblesort is a permutation.&lt;br /&gt;&lt;br /&gt;The &lt;a href="http://en.wikipedia.org/wiki/Curry–Howard_correspondence"&gt;Curry-Howard isomorphism&lt;/a&gt; which (for the purposes of this post) says (for the purposes of this post) that you can regard propositions as types, and proofs of those propositions as values in those types.&lt;br /&gt;&lt;br /&gt;So you define a type to represent whatever proposition it is you want to prove, and then you write a function that creates a value of that type - that value is then a proof of the proposition. When you use values normally, you care what that value is. When you use proofs though, you don't care so much what the proof is. You just care that the proof exists.&lt;br /&gt;&lt;br /&gt;First I need to make a definition of what I mean by permutation. There are a few different ways I could have done this. I ended up settling on this one:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;data SwapBasedPermutation {A : Set} : {n : ℕ} -&gt; (Vec A n) -&gt; (Vec A n) -&gt; Set where&lt;br /&gt;&lt;br /&gt;    reflexivity : {n : ℕ} -&gt; (i : Vec A n) -&gt; SwapBasedPermutation i i&lt;br /&gt;&lt;br /&gt;    transitivity : {n : ℕ} -&gt; {a : Vec A n} -&gt; {b : Vec A n} -&gt; {c : Vec A n} -&gt; (SwapBasedPermutation a b) -&gt; (SwapBasedPermutation b c) -&gt; SwapBasedPermutation a c&lt;br /&gt;&lt;br /&gt;    prepend : {pn : ℕ} -&gt; {l r : Vec A pn } -&gt; (e : A) -&gt; (SwapBasedPermutation l r) -&gt; SwapBasedPermutation (e :: l) (e :: r)&lt;br /&gt;&lt;br /&gt;    swapheads : {pn : ℕ} -&gt; {first second : A} -&gt; {rest : Vec A pn} -&gt; SwapBasedPermutation ( first :: second :: rest ) ( second :: first :: rest)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;My permutations are swap-based. Given a list, you can swap the first two elements (using the &lt;code&gt;swapheads&lt;/code&gt; constructor, and you get a proof that the first list (but only the first two elements, not some arbitrary pair of elements) is a permutation of the second; and given two lists that are permutations of each other, you can add the same element onto the front of both, and you get a proof that those two new lists will be permutations of each other (using the &lt;code&gt;prepend&lt;/code&gt; constructor).&lt;br /&gt;&lt;br /&gt;I also declare that permutations are reflexive and transitivitive by definition (although I think I could build those up from more fundamental parts, but I'm more interested in looking at the higher level behaviour of bubblesort).&lt;br /&gt;&lt;br /&gt;Those are proofs-by-definition and so they are trivially true (at least as far as the type-checker is concerned).&lt;br /&gt;&lt;br /&gt;Now given this &lt;code&gt;SwapBasedPermutation&lt;/code&gt; type, I can write a theorem that the top level &lt;code&gt;sortlist&lt;/code&gt; function (defined in my &lt;a href="http://benctechnicalblog.blogspot.com/2010/07/bubble-sort-1-bubblesort-in-agda.html"&gt;previous post&lt;/a&gt;) outputs a permutation of its input.&lt;br /&gt;&lt;br /&gt;The theorem is a type signature for a function, and body of the function needs to generate a proof of that theorem:&lt;br /&gt;&lt;br /&gt;The type signature for my theorem looks like this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;sortlist-permutes : {n : ℕ} -&gt; (l : Vec ℕ n) -&gt; SwapBasedPermutation l (sortlist l)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;It says: for all ℕumbers &lt;code&gt;n&lt;/code&gt;: for all &lt;code&gt;Vec&lt;/code&gt;tors &lt;code&gt;l&lt;/code&gt; of length &lt;code&gt;n&lt;/code&gt;:   &lt;code&gt;l&lt;/code&gt; is a permutation of &lt;code&gt;sortlist l&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;If we can write a definition for this function that typechecks correctly, then this theorem holds. We never actually need to invoke the function at runtime - it is enough to know that we &lt;code&gt;could&lt;/code&gt;. (That's another thing thats a bit weird in Agda- you're writing functions that will never be called at runtime but they're still useful. I guess its like writing javadocs...)&lt;br /&gt;&lt;br /&gt;Here its important that Agda functions are total - we need to know that our proof generating function really will return a value and not loop forever or error out. Otherwise, our theorem could typecheck but not really be proved.&lt;br /&gt;&lt;br /&gt;Now that we've stated the theorem (as a type), we need to build the body (the proof).&lt;br /&gt;&lt;br /&gt;To get there, we can define lemmas, in the same way that you can define helper functions to help implement your main function.&lt;br /&gt;&lt;br /&gt;First, invoking &lt;code&gt;onepass&lt;/code&gt; on a list permutes that list. &lt;code&gt;onepass&lt;/code&gt; is the code in my implementation that actually re-orders the list, so the proof of this lemma makes use of the &lt;code&gt;swaphead&lt;/code&gt; definition. It also recursively calls itself on a smaller list - that means we're using proof by induction. This definition roughly follows the shape of the definition of &lt;code&gt;onepass&lt;/code&gt; itself.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;lemma-onepass-permutes : {n : ℕ} -&gt; (l : Vec ℕ n) -&gt; SwapBasedPermutation l (onepass l)&lt;br /&gt;  lemma-onepass-permutes [] = reflexivity []&lt;br /&gt;  lemma-onepass-permutes (e :: []) = reflexivity (e :: [])&lt;br /&gt;  lemma-onepass-permutes (y :: y' :: rest) with  y ≤? y'&lt;br /&gt;  ... | yes _ = prepend y (lemma-onepass-permutes (y' :: rest))&lt;br /&gt;  ... | no _ = let                      &lt;br /&gt;       p1 = lemma-onepass-permutes (y :: rest)                              &lt;br /&gt;       p2 = prepend y' p1&lt;br /&gt;       p3 = swapheads -- magically the list that we're swapping the             &lt;br /&gt;                      -- heads for is inferred (from use in p4)                 &lt;br /&gt;       p4 = transitivity p3 p2&lt;br /&gt;    in p4&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Another lemma, this time about the behaviour of &lt;code&gt;innersort&lt;/code&gt;:&lt;br /&gt;&lt;pre&gt;lemma-innersort-permutes : {vn : ℕ } -&gt; (sn : ℕ) -&gt; (l : Vec ℕ vn) -&gt; SwapBasedPermutation l (innersort sn l)&lt;br /&gt;  lemma-innersort-permutes zero l = reflexivity l&lt;br /&gt;  lemma-innersort-permutes (suc sm) l = let&lt;br /&gt;             a = lemma-onepass-permutes l&lt;br /&gt;                  -- this shows l is a permutation of onepass l                                  &lt;br /&gt;             b = lemma-innersort-permutes sm (onepass l)&lt;br /&gt;                  -- this shows that (onepass l) is a permutation of (innersort sm (onepass l))               &lt;br /&gt;          in transitivity a b&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;and finally we can give our definition for our theorem, &lt;code&gt;sortlist-permutes&lt;/code&gt;, using these lemmas:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;sortlist-permutes {n} l = lemma-innersort-permutes  n l&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And now, because &lt;code&gt;sortlist-permutes&lt;/code&gt; typechecks correctly at compile time, I know that I can generate a proof that &lt;code&gt;sortlist&lt;/code&gt; really does permute a list, no matter what list I give it.&lt;br /&gt;&lt;br /&gt;Tada!&lt;br /&gt;&lt;br /&gt;Except this doesn't actually typecheck in Agda due to a problem with my definition of &lt;code&gt;onepass&lt;/code&gt;... see the next post.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-1583381620893405071?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/1583381620893405071/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/07/permutations-bubble-sort-in-agda-pt-2.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/1583381620893405071'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/1583381620893405071'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/07/permutations-bubble-sort-in-agda-pt-2.html' title='permutations (bubble sort in agda, pt 2)'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-6743029271372887932</id><published>2010-07-21T20:20:00.003+01:00</published><updated>2010-07-23T09:09:35.926+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='agda'/><title type='text'>bubblesort in agda (part 1)</title><content type='html'>I've been playing with &lt;a href="http://wiki.portal.chalmers.se/agda/pmwiki.php"&gt;Agda&lt;/a&gt;, a dependently typed programming language (where types of expressions can depend on earlier values).&lt;br /&gt;&lt;br /&gt;Dependent types let you make quite rich compile-time assertions about the behaviour of your program. These assertions are written and checked using the type system.&lt;br /&gt;&lt;br /&gt;One of the examples for Agda seems to be quicksort. So, I thought I'd experiment with bubblesort - similar but different.&lt;br /&gt;&lt;br /&gt;First I want an implementation of bubblesort in Agda. Then I want to use dependent types to express something interesting that I cannot express in another language: that bubblesort really sorts.&lt;br /&gt;&lt;br /&gt;Agda looks syntactically quite like Haskell, so I first made a Haskell implementation. This doesn't look like a procedural normal bubblesort implementation, as I wanted to keep a functional style.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;bubblesort t = applyLoop t&lt;br /&gt;&lt;br /&gt;applyLoop t = if isSorted t then t else applyLoop (onePass t)&lt;br /&gt;&lt;br /&gt;onePass [e] = [e]&lt;br /&gt;onePass t =&lt;br /&gt;  let a:b:rest = t&lt;br /&gt;      (as,bs) = if a &amp;lt; b then (a,b) else (b,a)&lt;br /&gt;      newlist = as:bs:rest  -- list with head two elements in order&lt;br /&gt;      bsrest = onePass (bs:rest)&lt;br /&gt;      endlist = as:bsrest&lt;br /&gt;    in endlist&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Next I wanted to convert the above Haskell code into Agda. Syntactically, this translates quite easily.&lt;br /&gt;&lt;br /&gt;My only major stumbling block comes with termination.&lt;br /&gt;&lt;br /&gt;Functions in Agda must be &lt;em&gt;total&lt;/em&gt;.&lt;br /&gt;&lt;br /&gt;That means they must always terminate with a value. They can't fail with an error, and they can't run forever (which means that Agda is not a turing-complete language, I think!)&lt;br /&gt;&lt;br /&gt;Agda contains a termination checker that gives a compile error if it can't prove that your functions terminate.&lt;br /&gt;&lt;br /&gt;Bubblesort does always terminate. But the Agda compiler can't see that from the above definition. The proof looks something like: every iteration of &lt;code&gt;applyLoop&lt;/code&gt; makes the list more sorted, and once you've called it enough times (as many as there are elements in the list) then the list will be sorted.&lt;br /&gt;&lt;br /&gt;So I changed my implementation to run the outer loop a fixed number of times, rather than until the list is sorted. That makes the termination provable. But it makes it less obvious that the final result is actually sorted: now we end after a certain number of runs, rather than when the &lt;code&gt;isSorted&lt;/code&gt; test is true...&lt;br /&gt;&lt;br /&gt;Here's the Agda code:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;onepass : {n : ℕ}→ Vec ℕ n → Vec ℕ n&lt;br /&gt;  onepass {zero} [] = []&lt;br /&gt;  onepass {suc zero} (y :: []) = y :: []&lt;br /&gt;  onepass {suc (suc n)} (y :: (y' :: y0)) = if y ≤? y' then                   &lt;br /&gt;                                        y :: (onepass {suc n} (y' :: y0))     &lt;br /&gt;                                   else y' :: (onepass {suc n} (y :: y0)) &lt;br /&gt;&lt;br /&gt;  innersort : {n : ℕ} -&gt; ℕ -&gt; Vec ℕ n -&gt; Vec ℕ n&lt;br /&gt;  innersort zero list = list&lt;br /&gt;  innersort (suc i) sorted = innersort i (onepass sorted)&lt;br /&gt;&lt;br /&gt;  sortlist : {n : ℕ} -&gt; Vec ℕ n -&gt; Vec ℕ n&lt;br /&gt;  sortlist {n} l = innersort n l&lt;br /&gt;&lt;/pre&gt;(I actually started with a different definition of &lt;code&gt;onepass&lt;/code&gt; - I'll come back to that in a different post).&lt;br /&gt;&lt;br /&gt;Now, that's enough (given wrappers for main, etc) to successfully sort a list:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;$ ./run&lt;br /&gt;2 :: 3 :: 5 :: 8 :: 11 :: 12 :: 14 :: []&lt;br /&gt;&lt;/pre&gt;So what's different from doing this in Haskell?&lt;br /&gt;&lt;br /&gt;Well, on the downside, it is not as obvious that this sort actually returns a sorted list, as mentioned above.&lt;br /&gt;&lt;br /&gt;On the upside: we know that this program won't loop forever (although it might loop for a very long time) - because of the termination checker.&lt;br /&gt;&lt;br /&gt;And the types of the functions, which I haven't mentioned yet, tell us something that relates the inputs and the outputs. &lt;code&gt;sortlist&lt;/code&gt; has a type that tells us that its output will be the same length as its input. This is not a full definition of sortedness but its more than is offered by Haskell lists.&lt;br /&gt;&lt;br /&gt;The type signature looks like this:&lt;br /&gt;&lt;pre&gt;sortlist : {n : ℕ} -&gt; Vec ℕ n -&gt; Vec ℕ n&lt;/pre&gt;which says something like: &lt;code&gt;sortlist&lt;/code&gt; is defined for every natural number &lt;code&gt;n&lt;/code&gt;, and takes as input a &lt;code&gt;Vec&lt;/code&gt;tor (of ℕumbers) of length &lt;code&gt;n&lt;/code&gt; and returns as output a &lt;code&gt;Vec&lt;/code&gt;tor  (of ℕumbers) of that same length &lt;code&gt;n&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;The type checker checks that this is true at compile time, so the inner functions must have the length similarly encoded in their types.&lt;br /&gt;&lt;br /&gt;At compile time, when we call n, we don't know what the n will be for every invocation (this is not &lt;a href="http://en.wikibooks.org/wiki/Pascal_Programming/Arrays"&gt;Pascal statically-sized arrays&lt;/a&gt;) - but we do know that the output will be the same size as the input.&lt;br /&gt;&lt;br /&gt;Compare that to type of the Haskell implementation:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;bubblesort :: [Int] -&gt; [Int]&lt;br /&gt;&lt;/pre&gt;which says that bubblesort takes a list of numbers and returns a list of numbers. Typewise, a perfectly valid implementation of quicksort here could return the empty list &lt;code&gt;[]&lt;/code&gt; for every input. This can't happen here.&lt;br /&gt;&lt;br /&gt;So in &lt;a href="http://benctechnicalblog.blogspot.com/2010/07/permutations-bubble-sort-in-agda-pt-2.html"&gt;subsequent posts&lt;/a&gt;, I want to try to make compile-time assertions richer. I want to prove that bubblesort really sorts, by which I mean (I think):the output is a permutation of the input; and the output is in order.&lt;br /&gt;&lt;br /&gt;(note 1) actually the Haskell type is: &lt;code&gt; bubblesort :: (Ord t) =&gt; [t] -&gt; [t]&lt;/code&gt; meaning Lists of anything that can be ordered, rather than lists of integers. That same abstraction is mostly achievable in Agda, but not quite - it doesn't have typeclasses in the same way that Haskell does.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://benctechnicalblog.blogspot.com/2010/07/permutations-bubble-sort-in-agda-pt-2.html"&gt;Read part 2...&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-6743029271372887932?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/6743029271372887932/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/07/bubble-sort-1-bubblesort-in-agda.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/6743029271372887932'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/6743029271372887932'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/07/bubble-sort-1-bubblesort-in-agda.html' title='bubblesort in agda (part 1)'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-2647433890982485090</id><published>2010-07-15T10:23:00.000+01:00</published><updated>2010-07-15T10:23:18.681+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='rant'/><category scheme='http://www.blogger.com/atom/ns#' term='unix'/><category scheme='http://www.blogger.com/atom/ns#' term='wtf'/><title type='text'>autoconf and portable programs: the joke</title><content type='html'>This is not my rant, but I like it:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;I have a simple request for you: each time you run into software that does&lt;br /&gt;this idiotic kind of test, please interact with the idiots upstream for&lt;br /&gt;whom all the world is linux, and try to get them to replace their "joke"&lt;br /&gt;of an autoconf macro with actual genuine tests that actually CHECK FOR THE&lt;br /&gt;FUCKING FEATURE.&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;a href="http://marc.info/?l=openbsd-ports&amp;m=126805847322995&amp;w=2"&gt;http://marc.info/?l=openbsd-ports&amp;m=126805847322995&amp;w=2&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-2647433890982485090?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://marc.info/?l=openbsd-ports&amp;m=126805847322995&amp;w=2' title='autoconf and portable programs: the joke'/><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/2647433890982485090/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/07/autoconf-and-portable-programs-joke.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/2647433890982485090'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/2647433890982485090'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/07/autoconf-and-portable-programs-joke.html' title='autoconf and portable programs: the joke'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-4399146742447022746</id><published>2010-07-03T11:24:00.000+01:00</published><updated>2010-07-03T11:24:26.579+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='photo'/><category scheme='http://www.blogger.com/atom/ns#' term='chess'/><category scheme='http://www.blogger.com/atom/ns#' term='voting'/><title type='text'>chess rankings for photos</title><content type='html'>I have a lot of photos - around 50000. I want to have some kind of scoring system for them.&lt;br /&gt;&lt;br /&gt;The obvious one is something like a 5-star scale: when presented with a photo, you can award it from 0 to 5 stars, with 5 being best and 0 being worst.&lt;br /&gt;&lt;br /&gt;But thats not very granular, and it is not clear to me that clearly defined standards for the 6 different scores will emerge.&lt;br /&gt;&lt;br /&gt;I preferred something that says "this is better than that, so this should have a higher score than that".&lt;br /&gt;&lt;br /&gt;But I don't want to have to manually make a strict linear order of all photos (despite the fact that a numerical score would do so), and I want the system to tolerate inconsistencies (eg. A&gt;B, B&gt;C, C&gt;A) somehow.&lt;br /&gt;&lt;br /&gt;Eventually I read about chess ranking, where each player is assigned a numerical score indicating their "goodness" and the scores are adjusted by pairwise comparisons between players - chess matches.&lt;br /&gt;&lt;br /&gt;I adapted this for scoring my photographs. I started with the &lt;a href="http://en.wikipedia.org/wiki/Glicko_rating_system"&gt;glicko system&lt;/a&gt; and modified it some.&lt;br /&gt;&lt;br /&gt;The way this works is:&lt;br /&gt;&lt;br /&gt;Photos compete against each other, as chess players compete against each other. The equivalent of a chess match is a presentation of two photos alongside each other in a web browser, with the user clicking on the photo they prefer. So, users do not assign an absolute score to a photo. Nor do they pick how much better one photo is than the other. They pick have a simple choice: "a&gt;b" or "b&gt;a".&lt;br /&gt;&lt;br /&gt;Each photo is assumed to have a single numeric score, such that the difference in the score reflects the probability that one photo will win over the other photo. (this is affine: 900 vs 1000 is the same probability as 4000 vs 4100)&lt;br /&gt;&lt;br /&gt;It is assumed that the score cannot be known exactly, but is approximated by a normal distribution (so there is a mean, and a standard deviation).&lt;br /&gt;&lt;br /&gt;Adding a comparison between two photos gives information about the distributions for both photos causing the mean and standard deviation to be changed to more accurately reflect the score, as described in the glicko paper.&lt;br /&gt;&lt;br /&gt;For my 50000 photos from the past 5 years, I have about 20% voted on at least once.&lt;br /&gt;&lt;br /&gt;For a recent trip to rome, where I took about 1000 photos, it took a few hours to include each photo in at least one vote, where each comparison was an unvoted photo vs a random photo (which may or may not have been previously voted). This does not give a huge amount of information per photo.&lt;br /&gt;Once that was done, I spent some hours making other votes: sometimes random vs random, sometimes random vs the photo with the closest mean. This caused rankings to become somewhat refined (sometimes causing surprisingly large changes in mean score)&lt;br /&gt;&lt;br /&gt;So here are the top 3 photos from that trip:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://ive.got.syphil.us/rthu/rome6371.jpeg" /&gt;&lt;img src="http://ive.got.syphil.us/rthu/rome5825b.jpeg" /&gt;&lt;img src="http://ive.got.syphil.us/rthu/rome5825.jpeg" /&gt;&lt;br /&gt;&lt;br /&gt;and here are the bottom 3:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://ive.got.syphil.us/rthu/rome0899.jpeg" /&gt;&lt;img src="http://ive.got.syphil.us/rthu/rome0940.jpeg" /&gt;&lt;img src="http://ive.got.syphil.us/rthu/rome0947.jpeg" /&gt;&lt;br /&gt;&lt;br /&gt;It seems to work reasonably well, though I think I need many more votes to get more accuracy. But that will come over time: as new photos are added, they'll get their scores by being compared against old photos, which will give more information about the old photos too.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-4399146742447022746?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/4399146742447022746/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/07/chess-rankings-for-photos.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/4399146742447022746'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/4399146742447022746'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/07/chess-rankings-for-photos.html' title='chess rankings for photos'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-6981482291807729343</id><published>2010-06-19T01:48:00.000+01:00</published><updated>2010-06-19T01:48:00.644+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='rant'/><category scheme='http://www.blogger.com/atom/ns#' term='haskell'/><category scheme='http://www.blogger.com/atom/ns#' term='random'/><title type='text'>normally distributed random numbers in haskell</title><content type='html'>somehow I expected to be able to get a normally distributed random number in haskell really easily by typing some term like "haskell random normal" into google. that came up with a bunch of stuff but nothing so simple as:  &lt;pre&gt;n &amp;lt;- getNormal mean dev&lt;/pre&gt;.&lt;br /&gt;&lt;br /&gt;The most promising seemed to be the cabal package &lt;code&gt;&lt;a href="http://hackage.haskell.org/package/random-fu"&gt;random-fu&lt;/a&gt;&lt;/code&gt; - although it looks like it has a lot of stuff in it and really all I want is the above single monadic action.&lt;br /&gt;&lt;br /&gt;Cue the usual BS about installing packages (not haskell specific, just damned packaging in general) - today I need to edit my local hackage repository to remove some malformed packages, and upgrade GHC. frr. 6 hours just getting &lt;code&gt;cabal install random-fu&lt;/code&gt; to work.&lt;br /&gt;&lt;br /&gt;But once that was all done, the next morning, I got what I wanted - a short &lt;code&gt;IO Double&lt;/code&gt; action: &lt;code&gt;sampleFrom DevURandom (normal mean dev)&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Hurrah.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-6981482291807729343?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/6981482291807729343/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/06/normally-distributed-random-numbers-in.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/6981482291807729343'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/6981482291807729343'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/06/normally-distributed-random-numbers-in.html' title='normally distributed random numbers in haskell'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-340455274821373814</id><published>2010-06-12T09:37:00.001+01:00</published><updated>2010-06-12T09:37:00.435+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='utilities'/><category scheme='http://www.blogger.com/atom/ns#' term='shell'/><title type='text'>rlwrap</title><content type='html'>I got frustrated when some application I was using *still* didn't support delete properly and discovered that the solution in that community (the OCaML world) seems to be (at least for commandline use) to use 'rlwrap' to add readline support to arbitrary applications.&lt;br /&gt;&lt;br /&gt;eg:&lt;br /&gt;&lt;pre&gt;$ rlwrap cat&lt;br /&gt;Now you can use readline features (eg delete, cursor movement) to edit text&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Neat.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://utopia.knoware.nl/~hlub/rlwrap/"&gt;http://utopia.knoware.nl/~hlub/rlwrap/&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-340455274821373814?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://utopia.knoware.nl/~hlub/rlwrap/' title='rlwrap'/><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/340455274821373814/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/06/rlwrap.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/340455274821373814'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/340455274821373814'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/06/rlwrap.html' title='rlwrap'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-8105018356902353920</id><published>2010-06-05T00:01:00.000+01:00</published><updated>2010-06-05T00:01:00.206+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='hardware'/><category scheme='http://www.blogger.com/atom/ns#' term='usb'/><title type='text'>pink panther usb hub</title><content type='html'>I got this &lt;a href="http://www.sweex.com/en/assortiment/connectivity/usb-firewire-hubs/US800"&gt;pink panther USB hub&lt;/a&gt; from mediamarkt - it was the same price as a USB extension cable (which is what I was looking for) - €7.99&lt;br /&gt;&lt;br /&gt;Now I have three extra USB ports too, which is useful for attaching (for example) bamboo encrusted memory sticks, or my wide SD card reader.&lt;br /&gt;&lt;br /&gt;One problem: when there are two of them chained with my ext hd on the end, the ext hd makes terrible clicking sound and doesn't start up properly. maybe thats a power problem?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-8105018356902353920?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://www.sweex.com/en/assortiment/connectivity/usb-firewire-hubs/US800' title='pink panther usb hub'/><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/8105018356902353920/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/06/pink-panther-usb-hub.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/8105018356902353920'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/8105018356902353920'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/06/pink-panther-usb-hub.html' title='pink panther usb hub'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-1866654584845083368</id><published>2010-05-28T15:46:00.000+01:00</published><updated>2010-05-28T15:46:00.196+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='64bits'/><category scheme='http://www.blogger.com/atom/ns#' term='wtf'/><title type='text'>64 bit linux BS</title><content type='html'>Several years ago I shied away from using 64-bit linux because many programs seem to have obscure bugs related to that. Its frustrating that I am still to this day encountering such bugs.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-1866654584845083368?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/1866654584845083368/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/05/64-bit-linux-bs.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/1866654584845083368'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/1866654584845083368'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/05/64-bit-linux-bs.html' title='64 bit linux BS'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-7137216502182846003</id><published>2010-05-21T00:01:00.000+01:00</published><updated>2010-05-21T00:01:00.382+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='freebsd'/><category scheme='http://www.blogger.com/atom/ns#' term='incompatibility'/><category scheme='http://www.blogger.com/atom/ns#' term='unix'/><category scheme='http://www.blogger.com/atom/ns#' term='wtf'/><category scheme='http://www.blogger.com/atom/ns#' term='shell'/><title type='text'>For reasons which might not be intuitively obvious, the     broken behavior is required</title><content type='html'>I previously regarded &lt;code&gt;/usr/bin/env&lt;/code&gt; as being portable onto any sane unix platform.&lt;br /&gt;&lt;br /&gt;But now I've had to work with FreeBSD and my beliefs are dashed.&lt;br /&gt;&lt;br /&gt;FreeBSD /usr/bin/env implements what posix says, rather than what everyone else does. That leads to trouble when you want to pass a parameter in a shebang, like this: &lt;code&gt;#!/usr/bin/env perl -w&lt;/code&gt; which works almost everywhere but not in FreeBSD :(&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-7137216502182846003?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://groups.google.com/group/comp.lang.ruby/msg/bb9af4d00af4739b' title='For reasons which might not be intuitively obvious, the     broken behavior is required'/><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/7137216502182846003/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/05/for-reasons-which-might-not-be.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/7137216502182846003'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/7137216502182846003'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/05/for-reasons-which-might-not-be.html' title='For reasons which might not be intuitively obvious, the     broken behavior is required'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-1734665936067684785</id><published>2010-05-15T00:01:00.000+01:00</published><updated>2010-05-15T00:01:00.635+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='attempt-at-explaining'/><category scheme='http://www.blogger.com/atom/ns#' term='haskell'/><category scheme='http://www.blogger.com/atom/ns#' term='draft'/><category scheme='http://www.blogger.com/atom/ns#' term='monad'/><title type='text'>monads: overloading semicolon, &amp;&amp; and ||</title><content type='html'>Monads are sometimes described as 'overloading semicolon', aimed at eg. the C or Java programmer. This is some text about that idea.&lt;br /&gt;&lt;br /&gt;But references there don't seem to refer to &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; and &lt;code&gt;||&lt;/code&gt; - two other control operators that are regularly used (especially in C) and that I think are interesting to compare to ;&lt;br /&gt;&lt;br /&gt;&amp;amp;&amp;amp; and || are defined primarily as 'boolean or' and 'boolean and'. But its very common to write expressions like this:&lt;br /&gt;&lt;pre&gt;succeeded = (p != NULL &amp;amp;&amp;amp; *p==5)&lt;/pre&gt;Evaluation of this uses more than the 'boolean and' functionality of &amp;amp;&amp;amp;. It also uses the property that &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; only evaluates its second parameter if its first parameter returned true - if the first parameter returned false, then the second parameter is never evaluated. That's a little bit of laziness sneaking into an otherwise strict language. In the example above, it stops the evaluation of &lt;code&gt;*p&lt;/code&gt; when &lt;code&gt;p&lt;/code&gt; is a null pointer.&lt;br /&gt;&lt;br /&gt;So contrast &lt;code&gt;;&lt;/code&gt; and &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;If you're a C programmer, they're totally different things - &lt;code&gt;;&lt;/code&gt; sequences statements, and &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; is an operator. Try to forget that difference for now.&lt;br /&gt;&lt;br /&gt;You can write:&lt;br /&gt;&lt;pre&gt;f() ; g()&lt;br /&gt;&lt;/pre&gt;which executes &lt;code&gt;f&lt;/code&gt;, discards its result and then executes &lt;code&gt;g&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;Or you can write:&lt;br /&gt;&lt;pre&gt;f() &amp;amp;&amp;amp; g()&lt;br /&gt;&lt;/pre&gt;which executes &lt;code&gt;f&lt;/code&gt;, then if &lt;code&gt;f&lt;/code&gt; does not fail, it executes g - if either &lt;code&gt;f&lt;/code&gt; or &lt;code&gt;g&lt;/code&gt; fail then the expression as a whole fails, otherwise the expression succeeds. What do I mean by fail? I mean that &lt;code&gt;f&lt;/code&gt; and &lt;code&gt;g&lt;/code&gt; must return booleans indicated whether they succeeded or failed at doing whatever it was they were supposed to do.&lt;br /&gt;&lt;br /&gt;So there are different ways to sequence operations (&lt;code&gt;;&lt;/code&gt; and &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt;, and the operations (&lt;code&gt;f&lt;/code&gt; and &lt;code&gt;g&lt;/code&gt;) need to have different interfaces depending on the particular way of sequencing: when using &lt;code&gt;;&lt;/code&gt; there are no particular constraints. When using &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; we must indicate success or failure in every operation.&lt;br /&gt;&lt;br /&gt;Both of these look like the Haskell operator &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt;: In Haskell, you can write:&lt;br /&gt;&lt;pre&gt;f &amp;gt;&amp;gt; g&lt;br /&gt;&lt;/pre&gt;meaning "first do f, and then do g". But the novelty is that &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt; has different meanings in different contexts - and those contexts are called &lt;em&gt;monads&lt;/em&gt;.&lt;br /&gt;&lt;br /&gt;For example, the &lt;code&gt;IO&lt;/code&gt; monad behaves quite like &lt;code&gt;;&lt;/code&gt;&lt;br /&gt;&lt;pre&gt;&lt;b&gt;print "hello" &amp;gt;&amp;gt; print "World"&lt;/b&gt;&lt;br /&gt;hello&lt;br /&gt;World&lt;br /&gt;&lt;/pre&gt;which looks like:&lt;br /&gt;&lt;pre&gt;&lt;b&gt;printf("hello\n") ; printf("world\n");&lt;/b&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;and the &lt;code&gt;Maybe&lt;/code&gt; monad behaves a bit like (but also a bit different from) &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt;&lt;br /&gt;&lt;pre&gt;main() {&lt;br /&gt; test() &amp;amp;&amp;amp; printf("world\n");&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;int test() {&lt;br /&gt;  return (2==3); // fail...&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;prints nothing.&lt;br /&gt;&lt;br /&gt;In Haskell, the &lt;code&gt;Maybe&lt;/code&gt; monad looks more like this:&lt;br /&gt;&lt;pre&gt;&lt;b&gt;Nothing &amp;gt;&amp;gt; Just 5&lt;/b&gt;&lt;br /&gt;Nothing&lt;br /&gt;&lt;b&gt;Just 1 &amp;gt;&amp;gt; Just 5&lt;/b&gt;&lt;br /&gt;Just 5&lt;br /&gt;&lt;b&gt;Just 1 &amp;gt;&amp;gt; Nothing&lt;/b&gt;&lt;br /&gt;Nothing&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;In the Haskell example, &lt;code&gt;Nothing&lt;/code&gt; means failure, and &lt;code&gt;Just &lt;em&gt;something&lt;/em&gt;&lt;/code&gt; means success (more on that in a bit).&lt;br /&gt;&lt;br /&gt;In the first &lt;code&gt;Maybe&lt;/code&gt; expression, &lt;code&gt;Nothing &amp;gt;&amp;gt; Just 5&lt;/code&gt;, our first statement fails, so we don't run the second.&lt;br /&gt;&lt;br /&gt;In the second example, &lt;code&gt;Just 1 &amp;gt;&amp;gt; Just 5&lt;/code&gt;, the first statement succeeds (it returns &lt;code&gt;Just &lt;em&gt;something&lt;/em&gt;&lt;/code&gt; - so then we run the second statement which also succeeds (because it returns &lt;code&gt;Just &lt;em&gt;something&lt;/em&gt;&lt;/code&gt;) and so the statement as a whole succeeds.&lt;br /&gt;&lt;br /&gt;And in the third example, the first statement succeeds, but second one fails, so the whole expression fails.&lt;br /&gt;&lt;br /&gt;To add confusion, there are two distinct syntaxes in Haskell for writing monadic expressions. They do the same thing, and there is no real advantage to one over the other so far, but in a bit, the differences will be exposed. The above has introduced one form, using &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt;. The other form looks like this, using the &lt;code&gt;do&lt;/code&gt; keyword&lt;br /&gt;&lt;pre&gt;do&lt;br /&gt;  putStrLn "hello"&lt;br /&gt;  putStrLn "world"&lt;br /&gt;&lt;/pre&gt;and&lt;br /&gt;&lt;pre&gt;do&lt;br /&gt;  Just 1&lt;br /&gt;  Just 5&lt;br /&gt;&lt;/pre&gt;Those are the same as earlier example, but in a different syntax. When you write something using &lt;code&gt;do&lt;/code&gt; notation it is automatically translated into &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt; notation by the compiler. For now, that is just joining the lines together and putting in &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt;, which is not much difference. But there will be bigger differences later.&lt;br /&gt;&lt;br /&gt;The Maybe example looks quite contrived - and it is - because so far I haven't explained the other important part of monads, which is value binding. That's where the analogy with &lt;code&gt;;&lt;/code&gt; and &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; breaks down and you start getting into more functional programming territory.&lt;br /&gt;&lt;br /&gt;Note that all the above examples are missing some things that are used all the time (in C, Java and Haskell): return values, and variable assignment. (in one of the examples, I used &lt;code&gt;p&lt;/code&gt; but only to read, not to write). This is an area where Haskell can be quite different from languages like C and Java.&lt;br /&gt;&lt;br /&gt;Now, remember how I said that depending on what you're using to sequence your statements, those statements must match a particular interface. For example, if you're using &lt;code&gt;&amp;&amp;&lt;/code&gt;, then those statements must return a boolean success. Using &lt;code&gt;&amp;&amp;&lt;/code&gt; hides away that return value - you write &lt;code&gt;f() &amp;&amp; g()&lt;/code&gt; without actually mentioning or testing the return value. Instead you let &lt;code&gt;&amp;&amp;&lt;/code&gt; deal with it; and in the &lt;code&gt;Maybe&lt;/code&gt; monad, you let &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt; deal with it, and if using &lt;code&gt;do&lt;/code&gt; notation you just put statements in sequence on separate lines, and magically they'll stop running when one of them fails!&lt;br /&gt;&lt;br /&gt;What happens when I want variables and return values though?&lt;br /&gt;&lt;br /&gt;In the &lt;code&gt;;&lt;/code&gt; style of writing C, you can write:&lt;br /&gt;&lt;pre&gt;x = f(); g(x)&lt;br /&gt;&lt;/pre&gt;Now we expect &lt;code&gt;f&lt;/code&gt; to return a value, and later on in a subsequent statement, we want to be able to access that value. In this C example, we do that by assigning the return value of &lt;code&gt;f()&lt;/code&gt; into a variable &lt;code&gt;x&lt;/code&gt; and then when we want to pass a parameter to &lt;code&gt;g&lt;/code&gt;, we can write &lt;code&gt;x&lt;/code&gt; again to get that value back. People do that every day. Nothing fancy - just normal programming like you learned when you were 8 years old.&lt;br /&gt;&lt;br /&gt;So now how does that work in C using &lt;code&gt;&amp;&amp;&lt;/code&gt; to sequence statements? We can't write:&lt;br /&gt;&lt;pre&gt;( x = f() ) &amp;&amp; g(x)&lt;br /&gt;&lt;/pre&gt;because we're already using the return value of &lt;code&gt;f()&lt;/code&gt; to indicate success or&lt;br /&gt;failure - we can't return some arbitrary value too. (and remember we're using &lt;code&gt;&amp;&amp;&lt;/code&gt; here to sequence our statements - you're not allowed to suddenly say "ok lets put a &lt;code&gt;;&lt;/code&gt; and then an if statement). Uh oh.&lt;br /&gt;&lt;br /&gt;Well, actually sometimes in C, we do manage to return a value and indicate success/failure at the same time. For example, &lt;code&gt;fopen&lt;/code&gt; returns a pointer to a &lt;code&gt;FILE&lt;/code&gt; or returns a null pointer to indicate failure. So &lt;code&gt;fopen&lt;/code&gt; is reporting two things: did it succeed? (null or not-null), and if it did succeed then we can extract some more information - the pointer to the &lt;code&gt;FILE&lt;/code&gt; struct.&lt;br /&gt;&lt;br /&gt;The &lt;code&gt;Maybe&lt;/code&gt; monad in Haskell does something similar. Remember a statement in the Maybe monad above returns &lt;code&gt;Nothing&lt;/code&gt; if the operation failed, or &lt;code&gt;Just &lt;em&gt;something&lt;/em&gt;&lt;/code&gt; if the operation succeeded. I coughed politely and ignored the &lt;em&gt;something&lt;/em&gt; at the time. But now see that the &lt;em&gt;something&lt;/em&gt; what the operation returns on success.&lt;br /&gt;&lt;br /&gt;This is more general than the fopen NULL pointer case. The fopen style only works when your data-type has a "magic value" to indicate failure - when using pointers, &lt;code&gt;NULL&lt;/code&gt;, or perhaps &lt;code&gt;countDinosaurs()&lt;/code&gt; could return &lt;code&gt;-1&lt;/code&gt; if it failed. But this isn't always possible (for example, if I am using some &lt;code&gt;xor()&lt;/code&gt; function then any output is possible and there is nowhere for me to indicate failure)... and it is awkward for every procedure to use its own convention.&lt;br /&gt;&lt;br /&gt;So now consider our &lt;code&gt;Maybe&lt;/code&gt; monad example from before (slightly rewritten):&lt;br /&gt;&lt;pre&gt;f = Just 1&lt;br /&gt;g = Nothing&lt;br /&gt;f &gt;&gt; g&lt;br /&gt;&lt;/pre&gt;In this, &lt;code&gt;f&lt;/code&gt; succeeds and returns &lt;code&gt;1&lt;/code&gt;, and &lt;code&gt;g&lt;/code&gt; fails and so does not return a value.&lt;br /&gt;&lt;br /&gt;Well, what can we do with that return value? Just like in other languages, we can use it in later computations.&lt;br /&gt;&lt;br /&gt;Now, there are two ways of writing this: the &lt;code&gt;do&lt;/code&gt; style and the &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt; style. The &lt;code&gt;do&lt;/code&gt; style is most like C and Java, and the &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt; style looks totally different.&lt;br /&gt;&lt;br /&gt;I'll try to explain both. Try to understand both - the &lt;code&gt;do&lt;/code&gt; style should be pretty easy to understand because it looks (aside from which symbols are used) quite like Java. But the &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt; style is more "functional" and might steer your thinking into writing functional code functionally, rather than "writing C in Haskell" as the &lt;code&gt;do&lt;/code&gt; notation leads to (or at least I find it tempts me to).&lt;br /&gt;&lt;br /&gt;Lets look at the &lt;code&gt;IO&lt;/code&gt; monad. This lets us have functions which do IO (something which is forbidden in plain Haskell code (for better or worse)).&lt;br /&gt;&lt;br /&gt;I want to have a program which will read a line from stdin, and output it on stdout. In C, that might be (assuming we have a nice readline function):&lt;br /&gt;&lt;pre&gt;x=readline();&lt;br /&gt;puts(x);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;So in Haskell: As building blocks, we have &lt;code&gt;print&lt;/code&gt;, which is a command you can run in the &lt;code&gt;IO&lt;/code&gt; monad that will output the string that you pass to it (this was used right up near the top of this article); and we have &lt;code&gt;getLine&lt;/code&gt; which is a command you can run in the &lt;code&gt;IO&lt;/code&gt; monad that reads in a line from stdin and returns it.&lt;br /&gt;&lt;br /&gt;So we want to run getLine, and then we want to run putStrLn, passing in the value that we got from getLine.&lt;br /&gt;&lt;br /&gt;First lets write it in &lt;code&gt;do&lt;/code&gt; notation:&lt;br /&gt;&lt;pre&gt;do&lt;br /&gt;  x &lt;- getLine&lt;br /&gt;  putStrLn x&lt;br /&gt;&lt;/pre&gt;This style runs &lt;code&gt;getLine&lt;/code&gt; and binds its return value to &lt;code&gt;x&lt;/code&gt;. That means whenever you refer to &lt;code&gt;x&lt;/code&gt; later on in the &lt;code&gt;do&lt;/code&gt; block, you mean whatever value was returned by &lt;code&gt;getLine&lt;/code&gt;. This style is very similar to C or Java - it looks like we do some computation, assign its value to a variable, and then later on use that variable.Now here's the more functional way:&lt;pre&gt;getLine &gt;&gt;= print&lt;br /&gt;&lt;/pre&gt;This uses an operator &lt;code&gt;&amp;gt;&amp;gt;=&lt;/code&gt;. This behaves a bit like &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt; in that it puts two operations in sequence. But it also wires up the return value of the first statement to be the parameter of the second statement.Now remember that &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt; is different for each monad. It represents that monad's particular way of sequencing operations. &lt;code&gt;&amp;gt;&amp;gt;=&lt;/code&gt; is likewise different for each monad. In the &lt;code&gt;IO&lt;/code&gt; monad, it sequences operations and passes their values around just like above.But what about in the &lt;code&gt;Maybe&lt;/code&gt; monad?Here's an example. First we have an operation in the maybe monad that takes an integer and succesfully returns that integer incremented. We use &lt;code&gt;Just &lt;em&gt;something&lt;/em&gt;&lt;/code&gt; to indicate success, and that something is &lt;code&gt;x+1&lt;/code&gt; to increment our integer.&lt;pre&gt;increment x = Just (x+1)&lt;br /&gt;&lt;/pre&gt;Lets use this:In &lt;code&gt;do&lt;/code&gt; notation:&lt;pre&gt;do&lt;br /&gt;  v &lt;- Just 5&lt;br /&gt;  increment v&lt;br /&gt;&lt;/pre&gt;which gives us:&lt;pre&gt;Just 6&lt;br /&gt;&lt;/pre&gt;or the same in &lt;code&gt;&gt;&gt;=&lt;/code&gt; notation:&lt;pre&gt;Just 5 &gt;&gt;= increment&lt;br /&gt;&lt;/pre&gt;In the do notation, you can write as many lines as you like, and similarly in the &lt;code&gt;&gt;&gt;&lt;/code&gt; notation you can write as many statements as you like:&lt;pre&gt;&lt;b&gt;Just 5 &gt;&gt;= increment &gt;&gt;= increment &gt;&gt;= increment&lt;/b&gt;&lt;br /&gt;Just 8&lt;br /&gt;&lt;/pre&gt;But the point of the &lt;code&gt;Maybe&lt;/code&gt; monad is to handle failures nicely. So what happens if we have a failure? What do these two fragments do:&lt;pre&gt;Nothing &gt;&gt;= increment&lt;br /&gt;&lt;/pre&gt;or equivalently:&lt;pre&gt;do&lt;br /&gt;  x &lt;- Nothing&lt;br /&gt;  increment x&lt;br /&gt;&lt;/pre&gt;Now &lt;code&gt;&gt;&gt;=&lt;/code&gt; will behave like &lt;code&gt;&gt;&gt;&lt;/code&gt; which in the &lt;code&gt;Maybe&lt;/code&gt; monad is quite like how &lt;code&gt;&amp;&amp;&lt;/code&gt; behaves in C. Ignore the value passing bit of &lt;code&gt;&gt;&gt;=&lt;/code&gt; and concentrate just on the &lt;code&gt;&gt;&gt;&lt;/code&gt; bit of it: in the &lt;code&gt;Maybe&lt;/code&gt; monad, &lt;code&gt;&gt;&gt;&lt;/code&gt; behaves like &lt;code&gt;&amp;&amp;&lt;/code&gt; - if the left-hand side fails, then we don't evaluate the right hand side. And thats what happens here - the left hand side fails (by returning Nothing) and so we never even to try evaluate &lt;code&gt;increment&lt;/code&gt;.So now look at the &lt;code&gt;do&lt;/code&gt; notation version. First we try to bind x to some computation, but that computation fails, so we don't evaluate the rest of the line.Now look at this Java code fragment:&lt;pre&gt;x = v();&lt;br /&gt; y = increment(x);&lt;br /&gt;&lt;/pre&gt;That sets x to the some value caused by running a function &lt;code&gt;v&lt;/code&gt;, and then runs increment on that value. First we run &lt;code&gt;v&lt;/code&gt; and then we always run &lt;code&gt;increment&lt;/code&gt;. Right?Yes, if &lt;code&gt;v&lt;/code&gt; looks something like:&lt;pre&gt;static int v() { return 5; }&lt;br /&gt;&lt;/pre&gt;But what about this:&lt;pre&gt;static int v() { throw new RuntimeException(); }&lt;br /&gt;&lt;/pre&gt;Now we evaluate &lt;code&gt;v&lt;/code&gt; but it fails, and so we don't evaluate &lt;code&gt;increment&lt;/code&gt;. Instead, the program as a whole fails.So now the &lt;code&gt;Maybe&lt;/code&gt; monad looks like &lt;code&gt;&amp;&amp;&lt;/code&gt; but also looks like exception handling - &lt;code&gt;&amp;&amp;&lt;/code&gt; and exception handling are almost the same thing! (which is quite surprising sounding, but they're often used for very similar purposes).What's the difference then?Well, an Exception propagates upwards and makes the whole of your program fail, unless you explicitly &lt;code&gt;catch&lt;/code&gt; that exception, and you use &lt;code&gt;;&lt;/code&gt; to separate statements.Failures with &lt;code&gt;&amp;&amp;&lt;/code&gt; don't propagate out of the expression using the &lt;code&gt;&amp;&amp;&lt;/code&gt; - instead the expression returns a boolean false to whatever program code is surrounding it, andcontinues running. Essentially, you are forced to catch the failure, because you get told explicitly "I succeeded" or "I failed" (by the expression as a whole evaluating to true or false).So, I've shown two notations for monads - &lt;code&gt;do&lt;/code&gt; notation and &lt;code&gt;&gt;&gt;&lt;/code&gt; notation - and a few examples of code written in both styles. Turns out you've already probably written code in both styles if you've programmed in C and programmed in Java, because you will have used &lt;code&gt;&amp;&amp;&lt;/code&gt; for error handling in some places, and Java exceptions in some other places. So there's nothing new here.Which style is better? Well, it depends...If you are passing a value from one operation to the next, with nothing else going on, then the &lt;code&gt;&gt;&gt;&lt;/code&gt; style is very concise:  &lt;code&gt;getLine &gt;&gt; print&lt;/code&gt; - we don't waste space assigning the line to a variable only to use it immediately and then forget about it.But when there are lots of actions returning lots of values, it can be easier to use the variables of &lt;code&gt;do&lt;/code&gt; notation to keep track of them all:&lt;pre&gt;do&lt;br /&gt;  print "what is your name?"&lt;br /&gt;  name &lt;- getLine&lt;br /&gt;  print "what is your age?"&lt;br /&gt;  age &lt;- getLine&lt;br /&gt;  print ( "Hello "++name++". You are "++age++" years old and you are cool!" )&lt;br /&gt;&lt;/pre&gt;Using &lt;code&gt;&gt;&gt;&lt;/code&gt; notation is awkward here - somehow you need to combine 5 actions into a linear sequence of actions to perform:  &lt;code&gt;print &gt;&gt; getline &gt;&gt; print &gt;&gt; getline &gt;&gt; print&lt;/code&gt;  but you need to wire the values from getline around in non-linear fashion.Its possible (indeed, when you write in &lt;code&gt;do&lt;/code&gt; notation it is translated into &lt;code&gt;&gt;&gt;&lt;/code&gt; form by the compiler, as I said above) but it looks ugly - its easier often to use &lt;code&gt;do&lt;/code&gt; notation and let the compiler sort it out for you.The &lt;code&gt;Maybe&lt;/code&gt; monad adds failure handling to sequenced operations. It turns out that you already probably used that functionality without explicitly realising that was happening. What other interesting stuff can we build by making a monad with its own &lt;code&gt;&gt;&gt;&lt;/code&gt; behaviour?One that gives behaviour that you don't find natively in C or Java is backtracking and filtering computation using the list (&lt;code&gt;[]&lt;/code&gt;) monad.In the &lt;code&gt;IO&lt;/code&gt; monad, a monadic action always returns a value. In the &lt;code&gt;Maybe&lt;/code&gt; monad, a monadic action either returns a value or fails. In the &lt;code&gt;[]&lt;/code&gt; monad, a monadic action can return multiple values (or one or zero values).We can use the &lt;code&gt;[]&lt;/code&gt; monad to try out all combinations of some sets of values. For example:&lt;pre&gt;do&lt;br /&gt;  x &lt;- [1,2,3,4]&lt;br /&gt;  y &lt;- [100,1000, 1000000]&lt;br /&gt;  [x * y]&lt;br /&gt;&lt;/pre&gt;gives the result:&lt;pre&gt;[100,1000,1000000,200,2000,2000000,300,3000,3000000,400,4000,4000000]&lt;br /&gt;&lt;/pre&gt;This looks like a normal do block, but instead of &lt;code&gt;Just&lt;/code&gt; and &lt;code&gt;Nothing&lt;/code&gt; from the &lt;code&gt;Maybe&lt;/code&gt; monad, we now specify values using &lt;code&gt;[]&lt;/code&gt;. And we can put many values inside the brackets.In this code, &lt;code&gt;x&lt;/code&gt; will iterate over each the value of &lt;code&gt;[1,2,3,4]&lt;/code&gt;, and inside that, &lt;code&gt;y&lt;/code&gt; will iterate over each value of &lt;code&gt;[100,1000,1000000]&lt;/code&gt;, and then we'll evaluate the value of [x*y], and then all the results for every iteration will be collected together into the output list.You can write list filters like this too:&lt;pre&gt;do&lt;br /&gt;  x &lt;- [1,2,3,4,5]&lt;br /&gt;  y &lt;- if isEven x then x else []&lt;br /&gt;  [y]&lt;br /&gt;&lt;/pre&gt;(assuming we've defined: &lt;code&gt;isEven x = x `rem` 2 == 0&lt;/code&gt;)This gives &lt;code&gt;[2,4]&lt;/code&gt; as output.And we can write searches that combine both of the above iteration and filtering. For example, say we want to find all of the pairs of digits that add to 5 (for example, 3+2 = 5, so (3,2) is a solution, but 8+1=9 so (8,1) is not a solution)We can write it like this:&lt;pre&gt;do&lt;br /&gt;  l &lt;- [0,1,2,3,4,5,6,7,8,9]&lt;br /&gt;  r &lt;- [0,1,2,3,4,5,6,7,8,9]&lt;br /&gt;  if l+r==5 then [(l,r)] else []  &lt;br /&gt;&lt;/pre&gt;which gives us this answer:&lt;code&gt;[(0,5), (1,4), (2,3), (3,2), (4,1), (5,0)]&lt;/code&gt;There's a more specific syntax for the list monad in Haskell, called &lt;em&gt;list comprehensions&lt;/em&gt;. The above could be written as:&lt;pre&gt;[(l,r) | l &lt;- [0,1,2,3,4,5,6,7,8,9], r&lt;-[0,1,2,3,4,5,6,7,8,9], l+r == 5]&lt;/pre&gt;To keep tying in with other langauges, Python has list comprehensions too with a very similar syntax:&lt;pre&gt;&gt;&gt;&gt; [(x,y) for x in [0,1,2,3,4,5,6,7,8,9] for y in [0,1,2,3,4,5,6,7,8,9] if x+y==5]&lt;br /&gt;[(0, 5), (1, 4), (2, 3), (3, 2), (4, 1), (5, 0)]&lt;br /&gt;&lt;/pre&gt;This article is about overloading &lt;code&gt;;&lt;/code&gt; which the above seems to be drifting away from. So what does our overloaded semicolon operator (&lt;code&gt;&gt;&gt;&lt;/code&gt; and &lt;code&gt;&gt;&gt;=&lt;/code&gt;) look like in the list monad?Lets look at &lt;code&gt;f &gt;&gt;= g&lt;/code&gt;. In general, this runs &lt;code&gt;f&lt;/code&gt; then runs &lt;code&gt;g&lt;/code&gt; feeding in the result of f - onto that behaviour we add our monad-specific behaviour. Lets look at how this behaves in a simple &lt;code&gt;[]&lt;/code&gt; monad expression to increment the numbers in a list.First define increment:&lt;pre&gt;increment x = [x+1]&lt;br /&gt;&lt;/pre&gt;which is used like this:&lt;pre&gt;&lt;b&gt;increment 10&lt;/b&gt;&lt;br /&gt;11&lt;br /&gt;&lt;/pre&gt;Now use increment in a monadic expression:&lt;pre&gt;&lt;b&gt;[1,2,3] &gt;&gt;= increment&lt;/b&gt;&lt;br /&gt;[2,3,4]&lt;br /&gt;&lt;/pre&gt;What did &lt;code&gt;&gt;&gt;=&lt;/code&gt; do here?It seems to have run increment three times, to increment three different numbers. And indeed it did. In the &lt;code&gt;[]&lt;/code&gt; monad, our sequencing operator will run its right hand side operation multiple times, once for each of the values returned by the left hand side, and then it will collect the results together into a list.How does that tie into the &lt;code&gt;do&lt;/code&gt; notation? It means that when I say &lt;code&gt;x&lt;-[4,5,6]&lt;/code&gt;, then the whole rest of the program will be run three times - once with x=4, once with x=5 and once with x=6, and then the final result of the program will be joined together in a list.Almost done with rambling for now, but I'll make one final comment:I used increment twice in the above text, once with the &lt;code&gt;Maybe&lt;/code&gt; monad:&lt;pre&gt;increment x = Just (x+1)&lt;br /&gt;&lt;/pre&gt;and once with the &lt;code&gt;[]&lt;/code&gt; list monad:&lt;pre&gt;increment x = [x+1]&lt;br /&gt;&lt;/pre&gt;These are almost the same - they both mean "return a value" in the language of the particular monad they live in.But just as &lt;code&gt;&gt;&gt;&lt;/code&gt; and &lt;code&gt;do&lt;/code&gt; syntax is independent of the particular monad being used, there is an independent way to "return a value", using &lt;code&gt;return&lt;/code&gt;:&lt;pre&gt;increment x = return (x+1)&lt;br /&gt;&lt;/pre&gt;When you use increment in the &lt;code&gt;Maybe&lt;/code&gt; monad, then it will mean &lt;code&gt;Just x+1&lt;/code&gt;, and when you use it in &lt;code&gt;[]&lt;/code&gt; then it will mean &lt;code&gt;[x+1]&lt;/code&gt;, but even better, someone else can use that definition of &lt;code&gt;increment&lt;/code&gt; in a monad that I have never even heard of - because &lt;code&gt;return&lt;/code&gt; will do the 'right thing' whatever that happens to be.&lt;br /&gt;&lt;br /&gt;ok definitely enough rambling for now.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-1734665936067684785?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/1734665936067684785/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/05/monads-overloading-semicolon-and.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/1734665936067684785'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/1734665936067684785'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/05/monads-overloading-semicolon-and.html' title='monads: overloading semicolon, &amp;&amp; and ||'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-4034739347839776913</id><published>2010-05-07T15:43:00.000+01:00</published><updated>2010-05-07T15:43:15.686+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='haskell'/><category scheme='http://www.blogger.com/atom/ns#' term='languagehacks'/><category scheme='http://www.blogger.com/atom/ns#' term='shell'/><title type='text'>making haskell EDSLs shebangable</title><content type='html'>There's a mindset that says to solve your problem, first write a language to solve your problem in, and then solve your problem in that language. And there's a mindset that says that your language should be an embedded language inside Haskell. In many ways that is appealing.&lt;br /&gt;&lt;br /&gt;I wanted to write something that would delete old rsync snapshots. I took the domain-specific language approach, where the language specifies the retention policy for snapshots, like this:&lt;br /&gt;&lt;pre&gt;policy = recent &amp;lt;||&gt; weekly &amp;lt;||&gt; monthly&lt;br /&gt;&lt;/pre&gt;meanings the final policy is to keep 'recent', 'weekly' and 'monthly' backups, with other statements defining what those three other policies mean. I won't write more about the language here - there's more on the webpage for the tool, &lt;a href="http://www.hawaga.org.uk/ben/tech/snaprotate/"&gt;snaprotate&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;One particular piece of this I don't feel terribly comfortable with, and thats how to invoke/run the policy files.&lt;br /&gt;&lt;br /&gt;I can imagine two different interfaces:&lt;br /&gt;&lt;pre&gt;$ snaprotate -f policyfile&lt;br /&gt;&lt;/pre&gt;or &lt;pre&gt;$ policyfile&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I took the second approach, making policies first order unix executables - commands that can be run like any other command.&lt;br /&gt;&lt;br /&gt;In unix tradition the way you indicate the language of a script is with a shebang line (#!/something) at the start of the script.&lt;br /&gt;&lt;br /&gt;So then I want my scripts to look like something like this:&lt;br /&gt;&lt;pre&gt;#!/usr/bin/snaprotate&lt;br /&gt;&lt;br /&gt;policy = recent &lt;||&gt; weekly &lt;||&gt; monthly&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This is almost a valid Haskell program, but: i) I need to import the SnapRotate module, and ii) I don't want to specify a &lt;code&gt;main&lt;/code&gt; routine (which looks something like:&lt;br /&gt;&lt;pre&gt;main = runPolicy policy&lt;br /&gt;&lt;/pre&gt;).&lt;br /&gt;&lt;br /&gt;So the &lt;code&gt;snaprotate&lt;/code&gt; commandline looks almost like &lt;code&gt;runhaskell&lt;/code&gt; but does some source file rearranging to address the above two points, and to remove the #! shebang line.&lt;br /&gt;&lt;pre&gt;#!/bin/bash&lt;br /&gt;FN=$(mktemp /tmp/snaprotateXXXXX).hs&lt;br /&gt;FNHS=$FN.hs&lt;br /&gt;LIBDIR=$(dirname $0)&lt;br /&gt;&lt;br /&gt;cat &gt; $FNHS &lt;&lt; 32804384892038493284093&lt;br /&gt;import SnapRotate&lt;br /&gt;main = runLevels policy&lt;br /&gt;&lt;br /&gt;32804384892038493284093&lt;br /&gt;&lt;br /&gt;cat $1 | grep --invert-match '^#!' &gt;&gt; $FNHS&lt;br /&gt;shift&lt;br /&gt;runhaskell -i$LIBDIR $FNHS $@&lt;br /&gt;&lt;/pre&gt;I wonder if this is the best way to implement such an embedding?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-4034739347839776913?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://www.hawaga.org.uk/ben/tech/snaprotate/' title='making haskell EDSLs shebangable'/><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/4034739347839776913/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/05/making-haskell-edsls-shebangable.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/4034739347839776913'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/4034739347839776913'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/05/making-haskell-edsls-shebangable.html' title='making haskell EDSLs shebangable'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-574227070273158487</id><published>2010-05-01T12:01:00.001+01:00</published><updated>2011-05-10T12:21:09.844+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='COBOL'/><category scheme='http://www.blogger.com/atom/ns#' term='languagehacks'/><category scheme='http://www.blogger.com/atom/ns#' term='wtf'/><title type='text'>ALTER</title><content type='html'>I discovered the &lt;code&gt;ALTER&lt;/code&gt; command in COBOL, in &lt;a href="http://home.swbell.net/mck9/cobol/style/alter.html"&gt;this note on eliminating that command&lt;/a&gt;, allowing self-modifying code. A couple of quotes:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;ALTER changes the destination of a GO TO statement elsewhere in the program. It complicates the task of eliminating GO TO, especially if you don't notice that the ALTER is present.&lt;br /&gt;&lt;/blockquote&gt;and even worse:&lt;br /&gt;&lt;blockquote&gt;In the presence of segmentation, ALTER can behave in unexpected ways. If an independent segment contains an altered GO TO, and the segment is overlaid and then reloaded, the GO TO is reset to its original destination.&lt;br /&gt;If you ever encounter this form of pathology, proceed with extreme caution. You have found either a bizarre and subtle bug or a vicious marauding style of programming. Quite possibly both.&lt;br /&gt;&lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-574227070273158487?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://home.swbell.net/mck9/cobol/style/alter.html' title='ALTER'/><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/574227070273158487/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/05/alter.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/574227070273158487'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/574227070273158487'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/05/alter.html' title='ALTER'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-6940524689784605821</id><published>2010-04-24T00:01:00.003+01:00</published><updated>2010-04-24T00:01:00.846+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='rsync'/><category scheme='http://www.blogger.com/atom/ns#' term='graph'/><category scheme='http://www.blogger.com/atom/ns#' term='ln'/><title type='text'>google chart venn diagram of disk usage</title><content type='html'>&lt;img src="http://chart.apis.google.com/chart?cht=v&amp;chs=200x200&amp;chd=t:0.138,0.106,0,0.1019&amp;chtt=backup+overlap&amp;chdl=May+2008+(138G)|April+2010+(106G)&amp;chdlp=b"/&gt;&lt;br /&gt;&lt;br /&gt;This compares disk usage of a backup I made of /home two years ago with one made this month. There's a lot of overlap - more than I expected.&lt;br /&gt;&lt;br /&gt;Method: creating the second backup, I used rsync with &lt;code&gt;--link-dest&lt;/code&gt; specified; for measuring, I used du -hsc old new; du -hsc new old. The size of the overlap is the size of new given by the second du minus the size of new given by the first du.&lt;br /&gt;&lt;br /&gt;I'm trying to figure out ways of showing overlapping disk usage for more than two or three rsync trees created in this way (for example, to visualise monthly, weekly and daily backups over a long time period)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-6940524689784605821?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/6940524689784605821/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/04/google-chart-venn-diagram-of-disk-usage.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/6940524689784605821'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/6940524689784605821'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/04/google-chart-venn-diagram-of-disk-usage.html' title='google chart venn diagram of disk usage'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-1749890416928737942</id><published>2010-04-17T00:01:00.000+01:00</published><updated>2010-04-17T00:01:01.424+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='darcs'/><category scheme='http://www.blogger.com/atom/ns#' term='git'/><category scheme='http://www.blogger.com/atom/ns#' term='version-numbers'/><category scheme='http://www.blogger.com/atom/ns#' term='vcs'/><category scheme='http://www.blogger.com/atom/ns#' term='svn'/><title type='text'>versions from version control systems</title><content type='html'>Sometimes its nice for built software to contain version information from the version control system - this means less to users and more to developers.&lt;br /&gt;&lt;br /&gt;This is useful when people, for whatever reason, incorrectly report their version: "please test the release candidate for version 1.0 // I found bug B // That's strange because I thought bug B only got introduced yesterday in the dev code // oh yes, well I checked out the dev code and tested that, not actually version 1.0"; or "I have a new bug C // Are you building from a pristine source checkout? // Yes // Why does your log file contain a warning message that the source was modified? // oh, well I hacked at component Q // yes, that's why you're seeing bug C."&lt;br /&gt;&lt;br /&gt;Different version control systems have different version representations. SVN has a sequential revision number; git has directory tree hashes; darcs doesn't seem to have any explicit version representation.&lt;br /&gt;&lt;br /&gt;So here is code I've used at different times for svn, git and darcs:&lt;br /&gt;&lt;br /&gt;For darcs, here's code I found in &lt;a href="http://bugs.darcs.net/issue1142"&gt;darcs issue tracker 1142&lt;/a&gt;:&lt;br /&gt;&lt;pre&gt;echo "$(darcs show tags | head -1) (+ $(darcs changes --count --from-tag .) patches)"&lt;br /&gt;&lt;/pre&gt;which gives the most recent tag in the repository and how many additional patches have been added:&lt;br /&gt;&lt;pre&gt;v1.1 (+ 39 patches)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;For svn, we used something like this in &lt;a href="http://www.ci.uchicago.edu/swift/"&gt;Swift&lt;/a&gt; &lt;br /&gt;&lt;pre&gt;R=$(svn info | grep '^Revision' | sed "s/Revision: /$1-r/")&lt;br /&gt;M=$(svn status | grep --invert-match '^\?' &gt; /dev/null &amp;&amp; echo "($1 modified locally)")&lt;br /&gt;echo $R $M&lt;br /&gt;&lt;/pre&gt;which we use to form version strings like this:&lt;br /&gt;&lt;pre&gt;swift-r1833 (swift modified locally)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Using git-svn, so that I was interested in the SVN version information rather than git version information, swap out R and M above for these:&lt;br /&gt;&lt;pre&gt;    R=$(git svn info | grep '^Revision' | sed "s/Revision: /$1-r/")&lt;br /&gt;    if git status -a &gt;/dev/null ; then&lt;br /&gt;      M="($1 modified locally)"&lt;br /&gt;    fi &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Both of the above two return the version of the last change in a particular checkout. When there are many modules, its sometimes desirable to give version information for those modules independently. The &lt;code&gt;svnversion&lt;/code&gt; command that gives that:&lt;br /&gt;&lt;pre&gt;svnversion -c somecomponent&lt;br /&gt;&lt;/pre&gt;that will produce different output forms that vary depending on the state of the checkout but at simplest gives something like:&lt;br /&gt;&lt;pre&gt;4168&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To get similar per-directory handling for git, you can do something like this anywhere except in the root of a repository:&lt;br /&gt;&lt;pre&gt;SUBDIRPREFIX=$(git rev-parse --show-prefix | sed 's%/$%%')&lt;br /&gt;cd ./$(git rev-parse --show-cdup)&lt;br /&gt;git ls-tree HEAD $SUBDIRPREFIX | (read a b c d ; echo $c)&lt;br /&gt;&lt;/pre&gt;which will emit a tree hash.&lt;br /&gt;&lt;br /&gt;So there are a bunch of different techniques for different version control systems. It would be nice if they were all a bit more consistent - really cool would be a single command that knew all version control systems and could output in a consistent format.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-1749890416928737942?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/1749890416928737942/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/04/versions-from-version-control-systems.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/1749890416928737942'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/1749890416928737942'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/04/versions-from-version-control-systems.html' title='versions from version control systems'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-5987453279491315613</id><published>2010-04-10T00:01:00.001+01:00</published><updated>2010-04-10T00:01:00.202+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='hash'/><title type='text'>size as a hash function</title><content type='html'>Sometimes its been desirable to de-dupe a large collection of data files.&lt;br /&gt;&lt;br /&gt;I wrote a tool called &lt;a href="http://www.hawaga.org.uk/ben/tech/lsdupes/"&gt;lsdupes&lt;/a&gt; to do that.&lt;br /&gt;&lt;br /&gt;Originally, it was going to run md5sum to hash every file, and then list files with matching hashes to be deleted.&lt;br /&gt;&lt;br /&gt;To optimise it a bit, I put in a size check: a first round happens looking at the size of each file, and only when there are multiple files for a given size will md5sums be computed. I though this would the md5sum load a bit.&lt;br /&gt;&lt;br /&gt;It turns out that in my photo collection of 44590 photos and videos, the size is a perfect hash - there are no photos with the same size that have different content.&lt;br /&gt;&lt;br /&gt;So while md5sum does get run on the dupes, it doesn't find anything different from what the size-based hash does; and the size-based hash runs a lot faster: to walk the tree and get sizes takes around 15 seconds. To then take md5sums of the roughly 5000 files that have non-unique size takes around 7 minutes.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-5987453279491315613?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/5987453279491315613/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/04/size-as-hash-function.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/5987453279491315613'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/5987453279491315613'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/04/size-as-hash-function.html' title='size as a hash function'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-2776002304745316557</id><published>2010-04-03T00:01:00.006+01:00</published><updated>2010-04-17T10:24:28.159+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='retro'/><category scheme='http://www.blogger.com/atom/ns#' term='markup'/><category scheme='http://www.blogger.com/atom/ns#' term='decay'/><category scheme='http://www.blogger.com/atom/ns#' term='html'/><title type='text'>HTML forms in the early 90s</title><content type='html'>Before HTML forms used the &lt;code&gt;&amp;lt;form&gt;&lt;/code&gt; tag, there was a simpler mechanism called &lt;code&gt;&amp;lt;ISINDEX&gt;&lt;/code&gt;. I was reminded about this because someone asked why CGI examples showed urls without &lt;code&gt;name=value&lt;/code&gt; pairs in the &lt;code&gt;?query&lt;/code&gt; section, like this: &lt;code&gt;http://ive.got.syphil.is/disease-registry.html?pox&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;I then went to see if &amp;lt;ISINDEX&gt; still worked in modern browsers. I discovered that in&lt;br /&gt;Safari, it renders a box, but there seems to be no way to submit the query (traditionally, pressing enter would submit it). Lynx and w3m still support it.&lt;br /&gt;&lt;br /&gt;From others, I hear:&lt;br /&gt;&lt;blockquote&gt;apparently the default browser on the g1 doesn't even recognize isindex as some sort of input field, although it renders it as such.&lt;br /&gt;sort of weird; won't let you type in it but it renders an input box&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;and Chrome is also reported to work correctly.&lt;br /&gt;&lt;br /&gt;Anyway, this post has an isindex tag right here:&lt;br /&gt;&lt;br /&gt;&lt;isindex&gt;&lt;br /&gt;&lt;br /&gt;&amp;lt--- there, so you can see how it works in your browser...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-2776002304745316557?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/2776002304745316557/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/04/html-forms-in-early-90s.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/2776002304745316557'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/2776002304745316557'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/04/html-forms-in-early-90s.html' title='HTML forms in the early 90s'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-4382156414618488750</id><published>2010-03-27T00:01:00.001Z</published><updated>2010-03-27T00:01:00.928Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='stupid'/><category scheme='http://www.blogger.com/atom/ns#' term='languagehacks'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><title type='text'>stack trace in perl</title><content type='html'>As a user, I hate java stack traces (because they expose the guts of the program, when I don't care about those guts). But as a programmer, I have come to realise how much I like them, when faced with errors like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Can't call method "emitCode" on an undefined value at /Users/benc/src/stupid/stupid-crypto/src/Stupid/C.pm line 27.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;where the error is actually deep inside the call on line 27.&lt;br /&gt;&lt;br /&gt;So googling around I found this line to put at the start of a program:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;$SIG{ __DIE__ } = sub { Carp::confess( @_ ) };&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;which replaces those short die messages with a stack dump produced by Carp:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Can't call method "emitCode" on an undefined value at /Users/benc/src/stupid/stupid-crypto/src/Stupid/C.pm line 27.&lt;br /&gt; at /Users/benc/src/stupid/stupid-crypto/src/Stupid/C.pm line 7&lt;br /&gt; Stupid::C::__ANON__('Can\'t call method "emitCode" on an undefined value at /Users...') called at /Users/benc/src/stupid/stupid-crypto/src/Stupid/C.pm line 27&lt;br /&gt; Stupid::LanguageWrapper::emitCode('Stupid::LanguageWrapper=HASH(0x8ff9b4)') called at ../src/stupid.pl line 44&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;which I hope I'll find more useful...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-4382156414618488750?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://www.perlmonks.org/?node_id=640319' title='stack trace in perl'/><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/4382156414618488750/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/03/stack-trace-in-perl.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/4382156414618488750'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/4382156414618488750'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/03/stack-trace-in-perl.html' title='stack trace in perl'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-5647368194602445564</id><published>2010-03-20T12:00:00.002Z</published><updated>2010-03-30T01:15:02.348+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='hash'/><category scheme='http://www.blogger.com/atom/ns#' term='stupid'/><category scheme='http://www.blogger.com/atom/ns#' term='shell'/><title type='text'>so many ways to hash</title><content type='html'>I was making commandline tools for stupid to drive the example sha256 code, resulting in multiple tools that deliberately did the same but using different language backends. Then I realised I have a shitload of (where shitload==4) md5sum commandline tools already:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;$ echo abc | md5&lt;br /&gt;0bee89b07a248e27c83fc3d5951213c1&lt;br /&gt;&lt;br /&gt;$ echo abc | gmd5sum&lt;br /&gt;0bee89b07a248e27c83fc3d5951213c1  -&lt;br /&gt;&lt;br /&gt;$ echo abc | openssl dgst &lt;br /&gt;0bee89b07a248e27c83fc3d5951213c1&lt;br /&gt;&lt;br /&gt;$ echo abc | gpg2 --print-md md5&lt;br /&gt;0B EE 89 B0 7A 24 8E 27  C8 3F C3 D5 95 12 13 C1&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-5647368194602445564?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/5647368194602445564/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/03/so-many-ways-to-hash.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/5647368194602445564'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/5647368194602445564'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/03/so-many-ways-to-hash.html' title='so many ways to hash'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-2300772574164745942</id><published>2010-03-13T14:50:00.000Z</published><updated>2010-03-13T14:50:00.197Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='maths'/><category scheme='http://www.blogger.com/atom/ns#' term='shell'/><title type='text'>seq</title><content type='html'>You(*) whisper to X, "the same package [GNU coreutils] which provides &lt;code&gt;seq&lt;/code&gt;"&lt;br /&gt;You(*) whisper to X, "the command line utility"&lt;br /&gt;You(*) whisper to X, "also provides &lt;code&gt;factor&lt;/code&gt;"&lt;br /&gt;You(*) whisper to X, "which outputs prime factors"&lt;br /&gt;You(*) whisper to X, "makes me want to start making fucked up shell scripts"&lt;br /&gt;You(*) whisper to X, "that use goedel numbering"&lt;br /&gt;You(*) whisper to X, "for some purpose."&lt;br /&gt;You(*) whisper to X, "i have to wonder how a commandline utility for factoring primes is 'core' in any way, though"&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-2300772574164745942?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/2300772574164745942/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/03/seq.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/2300772574164745942'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/2300772574164745942'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/03/seq.html' title='seq'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-1542375249207883367</id><published>2010-03-06T01:00:00.000Z</published><updated>2010-03-06T01:00:00.694Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='languagehacks'/><title type='text'>ASCII art C++ constants</title><content type='html'>I came across &lt;a href="http://www.xs4all.nl/~weegen/eelis/analogliterals.xhtml"&gt;Multi-Dimensional Analog Literals in C++&lt;/a&gt;. This is an implementation in standard C++ templates that lets you write constants out using ASCII art (in 1, 2 or 3 dimensions):&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  unsigned int c = ( o-----o&lt;br /&gt;                     |     !&lt;br /&gt;                     !     !&lt;br /&gt;                     !     !&lt;br /&gt;                     o-----o ).area;&lt;br /&gt;&lt;br /&gt;  assert( c == (I-----I) * (I-------I) );&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I think the same would be straightforward to implement using Template Haskell's quasiquoting mechanism, which allows you to embed parsers for fairly arbitrary sublanguages inside Haskell source. I wonder what other languages you could also implement something like this in.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-1542375249207883367?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://www.xs4all.nl/~weegen/eelis/analogliterals.xhtml' title='ASCII art C++ constants'/><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/1542375249207883367/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/03/ascii-art-c-constants.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/1542375249207883367'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/1542375249207883367'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/03/ascii-art-c-constants.html' title='ASCII art C++ constants'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-2985681457251324254</id><published>2010-02-27T21:13:00.002Z</published><updated>2010-02-27T21:21:07.758Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='presenting'/><category scheme='http://www.blogger.com/atom/ns#' term='hardware'/><title type='text'>slide controller</title><content type='html'>I just brought my third one of these:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://ecx.images-amazon.com/images/I/41SYTJC80NL._SL500_AA280_.jpg"/&gt;&lt;br /&gt;&lt;br /&gt;(my first got lost at an event a few years ago, and I lost the USB dongle for my 2nd a few months ago)&lt;br /&gt;&lt;br /&gt;If you want something to change your PowerPoint (or powerpoint-like) slides, this is the tool to get. Its worked with every computer I've tried it on, and every piece of presentation software (PDF viewers and HTML slidy in addition to PowerPoint and Open Office).&lt;br /&gt;&lt;br /&gt;No software to install either - the dongle appears as a USB keyboard - the four keys send PgUp and PgDown to change slides; the laser beam button sends an F5 (which is start-presentation in PowerPoint) and the bottom button sends 'b' (which is blank screen in PowerPoint).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-2985681457251324254?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://www.amazon.co.uk/gp/product/B000FPGP4U' title='slide controller'/><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/2985681457251324254/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/02/slide-controller.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/2985681457251324254'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/2985681457251324254'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/02/slide-controller.html' title='slide controller'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4983265076111025145.post-3095985673251846487</id><published>2010-02-27T10:59:00.007Z</published><updated>2010-03-21T19:17:56.355Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='stupid'/><category scheme='http://www.blogger.com/atom/ns#' term='wtf'/><title type='text'>binary numeric promotion in java numeric types</title><content type='html'>I've been working on a Java backend for &lt;a href="http://code.google.com/p/stupid-crypto/"&gt;stupid&lt;/a&gt;, a crypto algorithm language proposed by Ben Laurie.&lt;br /&gt;&lt;br /&gt;This has led me to look at Java numeric types a little more closely (and with a decade more cynicism than last time).&lt;br /&gt;&lt;br /&gt;I'd forgotten entirely that there were &lt;code&gt;short&lt;/code&gt; and &lt;code&gt;long&lt;/code&gt; numeric types - for pretty much any use I've made of numbers in Java ever, &lt;code&gt;int&lt;/code&gt; has been fine.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;short&lt;/code&gt; behaves a little surprisingly:&lt;br /&gt;&lt;br /&gt;If &lt;code&gt;a&lt;/code&gt; and &lt;code&gt;b&lt;/code&gt; are both &lt;code&gt;long&lt;/code&gt;, then &lt;code&gt;a &amp; b&lt;/code&gt; is a &lt;code&gt;long&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;If &lt;code&gt;a&lt;/code&gt; and &lt;code&gt;b&lt;/code&gt; are both &lt;code&gt;int&lt;/code&gt;, then &lt;code&gt;a &amp; b&lt;/code&gt; is an &lt;code&gt;int&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;If &lt;code&gt;a&lt;/code&gt; and &lt;code&gt;b&lt;/code&gt; are both &lt;code&gt;short&lt;/code&gt;, then &lt;code&gt;a &amp; b&lt;/code&gt; is an ... &lt;code&gt;int&lt;/code&gt;.  wtf. why?&lt;br /&gt;&lt;br /&gt;Likewise &lt;code&gt;byte &amp; byte&lt;/code&gt; -&gt; &lt;code&gt;int&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;This is specified in &lt;a href="http://java.sun.com/docs/books/jls/third_edition/html/conversions.html#170983"&gt;5.6.2 Binary Numeric Promotion&lt;/a&gt; of the Java language specification.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;(later:)&lt;br /&gt;Its pointed out that C also does implicit casting of smaller types to &lt;code&gt;int&lt;/code&gt;; but gcc is a little gentler with its warnings. &lt;code&gt;a+b&lt;/code&gt; gives a warning (not an error) similar to the Java code above, but &lt;code&gt;a&amp;b&lt;/code&gt; does not (presumably because it knows that this can't make the number be any bigger than the types you started out with... something that gets lost in the Java type checking)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4983265076111025145-3095985673251846487?l=benctechnicalblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benctechnicalblog.blogspot.com/feeds/3095985673251846487/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/02/binary-numeric-promotion-in-java.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/3095985673251846487'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4983265076111025145/posts/default/3095985673251846487'/><link rel='alternate' type='text/html' href='http://benctechnicalblog.blogspot.com/2010/02/binary-numeric-promotion-in-java.html' title='binary numeric promotion in java numeric types'/><author><name>benc</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>
