Alan Grow's Bloghttps://alangrow.com/blog/Alanalangrow+blog@gmail.comhttps://alangrow.com/blog/music-to-program-toMusic To Program To2021-02-27T20:36:24Alanalangrow+blog@gmail.com<style type="text/css">
@media only screen and (max-width: 899px) {
body {
width: 95%;
}
}
@media only screen and (min-width: 900px) {
body {
max-width: 600px;
}
}
.post .content h2,
.post .content h3 {
margin-top: 0;
margin-bottom: 0;
}
.post .content h2 {
line-height: 2.5;
}
.post .content h3 {
line-height: 1.5;
}
.post .content p {
padding: 1em 0;
}
.post .content p + p {
padding-top: 0;
}
.post .content h2 + p {
padding: 1em 0 2em 0;
}
.post ul {
margin-top: 1em;
margin-bottom: 1em;
}
.post .content h3 + p {
padding: 0;
}
.post .content h3 + p a {
display: block;
padding-top: 1em;
width: 100%;
height: 1px;
}
.post .content h3 + p a:target {
margin-top: -5em;
padding-top: 6em;
}
.spotify {
width: 100%;
height: 360px;
margin-bottom: 4em;
}
.spotify.ep {
height: 240px;
margin-bottom: 0;
}
</style>
<p>Programming is deep work. Tuning out distractions is key, and music is one of the most effective tools at your disposal.</p>
<p>But not all music helps you program. Music with lyrics can interfere with your ability to read and write code. Music with too many surprises can add rather than remove distraction. After some experimentation, many programmers arrive at the same conclusion: repetitive electronic music helps them program.</p>
<p>After a couple decades of programming, including a decade of remote work with the talented musician-programmers at <a href="https://blend.io">blend.io</a> and <a href="https://roli.com">ROLI</a>, here's some of the music I turn to when I need to Get Shit Done.</p>
<p>All music has a Spotify embed and a quick review. Know the mood you're after? Start with this index of mental states. YMMV. Enjoy! ✌️</p>
<h2>By Desired Mental State</h2>
<h3>Focus, Intensity, Urgency 🎯</h3>
<ul>
<li><a href="#deep-dark-minimal">Deep Dark Minimal</a></li>
<li><a href="#basic-channel-sampler">Basic Channel - Sampler</a></li>
<li><a href="#maurizio-m-series">Maurizio: M-Series</a></li>
<li><a href="#jonas-kopp-desire-ep">Jonas Kopp: Desire EP</a></li>
<li><a href="#etapp-kyle-klockworks-10">Etapp Kyle: Klockworks 10</a></li>
<li><a href="#luke-hess-facette">Luke Hess: Facette</a></li>
<li><a href="#the-field-sound-of-light-nordic-light-hotel">The Field: Sound of Light - Nordic Hotel</a></li>
<li><a href="#eod-questionmarks">EOD: Questionmarks</a></li>
<li><a href="#trickfinger-sampler">Trickfinger - Sampler</a></li>
<li><a href="#tin-man-dripping-acid">Tin Man: Dripping Acid</a></li>
<li><a href="#anthony-naples-fog-fm">Anthony Naples: Fog FM</a></li>
<li><a href="#john-tejada-parabolas">John Tejada: Parabolas</a></li>
</ul>
<h3>Calmness, Contemplation, Perfection 🧘</h3>
<ul>
<li><a href="#microlith-dance-with-me">Microlith: Dance With Me</a></li>
<li><a href="#martin-schulte-slow-beauty">Martin Schulte: Slow Beauty</a></li>
<li><a href="#steve-reich-music-for-18-musicians">Steve Reich: Music for 18 Musicians</a></li>
<li><a href="#terry-riley-in-c">Terry Riley: In C</a></li>
<li><a href="#substance-session-elements">Substance: Session Elements</a></li>
<li><a href="#eod-utrecht">EOD: Utrecht</a></li>
<li><a href="#automatic-tasty-fieldworks-ep">Automatic Tasty: Fieldwork EP</a></li>
<li><a href="#khotin-baikal-acid">Khotin: Baikal Acid</a></li>
</ul>
<h3>Creative, Energetic, Mischevious 👿</h3>
<ul>
<li><a href="#modern-acid">Modern Acid</a></li>
<li><a href="#beatwife-cornbrail-acid-2">Beatwife: Cornbrail Acid 2</a></li>
<li><a href="#dmx-krew-broken-sd140-part-ii">DMX Krew: Broken SD140 Part II</a></li>
<li><a href="#ceephax-sampler">Ceephax - Sampler</a></li>
<li><a href="#superski-mondo-moderno">Superski: Mondo Moderno</a></li>
</ul>
<h3>Wistful, Reflection 🍂</h3>
<ul>
<li><a href="#mikron-severance">Mikron: Severance</a></li>
<li><a href="#boards-of-canada-music-has-the-right-to-children">Boards of Canada: Music Has The Right To Children</a></li>
<li><a href="#seb-wildblood-emoticon">Seb Wildblood: The One with the Emoticon</a></li>
<li><a href="#rx-101-dopamine">RX-101: Dopamine</a></li>
<li><a href="#cn-the-expedition-beyond">CN: The Expedition Beyond</a></li>
<li><a href="#four-tet-new-energy">Four Tet: New Energy</a></li>
</ul>
<h2>Playlists</h2>
<p>If you're not sure where to start, pick one of these 2+ hour playlists and dig in. These artists have deeper catalogs you can branch out into.</p>
<h3>Deep Dark Minimal</h3>
<p><a name="deep-dark-minimal"></a></p>
<p>Repetitive, trance-inducing electronic music for intense focus and deep work. No vocals or lame chord progressions. Mostly German.</p>
<div class="spotify">
<iframe src="https://open.spotify.com/embed/playlist/64It3ioYct9CzrPeuIq3xh" width="100%" height="100%" frameborder="0" allowtransparency="true" allow="encrypted-media"></iframe>
</div>
<h3>Modern Acid</h3>
<p><a name="modern-acid"></a></p>
<p>The tasty sounds of the <a href="https://en.wikipedia.org/wiki/Roland_TB-303">303</a> / <a href="https://en.wikipedia.org/wiki/Roland_TR-808">808</a> / 909 used in new ways. All tracks post 2000. Higher energy, faster tempos, and busier arrangements. 🧠</p>
<div class="spotify">
<iframe src="https://open.spotify.com/embed/playlist/42Opdl26Go26eWo7oiEhmK" width="100%" height="100%" frameborder="0" allowtransparency="true" allow="encrypted-media"></iframe>
</div>
<h2>Albums</h2>
<p>Some full-length albums that won't disappoint. Each is good for about an hour of listening.</p>
<h3>Microlith: Dance With Me (2016)</h3>
<p><a name="microlith-dance-with-me"></a></p>
<p>An album of sublime electro from Maltese producer Rhys Celeste. Everything Rhys made until his <a href="https://ra.co/news/38277">tragic death at age 24</a> is worth a listen. See also the <a href="https://open.spotify.com/playlist/4CvNSwKn7f9ngVLlP2Jr0O">Float House microgenre</a>.</p>
<div class="spotify">
<iframe src="https://open.spotify.com/embed/album/6yzYSkVba6an7sxMfWcFOg" width="100%" height="100%" frameborder="0" allowtransparency="true" allow="encrypted-media"></iframe>
</div>
<h3>Beatwife: Cornbrail Acid 2 (2014)</h3>
<p><a name="beatwife-cornbrail-acid-2"></a></p>
<p>A Scottish acid madman with an artist name you can't mention in polite company. Fast, frenetic music with a quirky sense of humor. See also the <a href="https://daily.bandcamp.com/lists/braindance-feature">Braindance microgenre</a>.</p>
<div class="spotify">
<iframe src="https://open.spotify.com/embed/album/3qku0YYzoZPtU7G5zpY9lL" width="100%" height="100%" frameborder="0" allowtransparency="true" allow="encrypted-media"></iframe>
</div>
<h3>Tin Man: Dripping Acid (2017)</h3>
<p><a name="tin-man-dripping-acid"></a></p>
<p>How much acid is too much acid? This monster neo acid album may provide the answer. Haunting, hypnotic tunes with slow builds.</p>
<div class="spotify">
<iframe src="https://open.spotify.com/embed/album/1rVsIxC6nSp6I3VC0QQVyy" width="100%" height="100%" frameborder="0" allowtransparency="true" allow="encrypted-media"></iframe>
</div>
<h3>Mikron: Severance (2019)</h3>
<p><a name="mikron-severance"></a></p>
<p>Peaceful, aquatic, ambient techno landscapes from an Irish duo. Track 4, "Ghost Node", highsteps out of a thick fog.</p>
<div class="spotify">
<iframe src="https://open.spotify.com/embed/album/7d33r7YgvCP120orCDLVJT" width="100%" height="100%" frameborder="0" allowtransparency="true" allow="encrypted-media"></iframe>
</div>
<h3>Anthony Naples: Fog FM (2019)</h3>
<p><a name="anthony-naples-fog-fm"></a></p>
<p>Another case of driving beats shrouded in fog, this time from an NYC-based producer.</p>
<div class="spotify">
<iframe src="https://open.spotify.com/embed/album/0XBtXweoCbA6MkNZ5F2NUW" width="100%" height="100%" frameborder="0" allowtransparency="true" allow="encrypted-media"></iframe>
</div>
<h3>Boards of Canada: Music Has The Right To Children (1998)</h3>
<p><a name="boards-of-canada-music-has-the-right-to-children"></a></p>
<p>By law I am required to include this album, and I'm happy to comply. A landmark in electronic music from the Scottish duo. This album hasn't aged a day.</p>
<div class="spotify">
<iframe src="https://open.spotify.com/embed/album/1vWnB0hYmluskQuzxwo25a" width="100%" height="100%" frameborder="0" allowtransparency="true" allow="encrypted-media"></iframe>
</div>
<h3>Maurizio: M-Series (1997)</h3>
<p><a name="maurizio-m-series"></a></p>
<p>Minimal dub techno from the master of the genre, <a href="#basic-channel-sampler">Basic Channel</a> co-founder Moritz von Oswald. If you're new to dub techno you may be forgiven for thinking "nothing ever happens." That's kind of the point, but it's also not <em>quite</em> true: there's lots of subtle variation if you start looking for it, yet never enough to distract if you aren't.</p>
<div class="spotify">
<iframe src="https://open.spotify.com/embed/album/3dQcknWb1G439u0whEtJCQ" width="100%" height="100%" frameborder="0" allowtransparency="true" allow="encrypted-media"></iframe>
</div>
<h3>Substance: Session Elements (1998)</h3>
<p><a name="substance-session-elements"></a></p>
<p>Lush but restrained minimal German techno variations.</p>
<div class="spotify">
<iframe src="https://open.spotify.com/embed/album/0gZqMjLTDZrSAGIfsDrvvF" width="100%" height="100%" frameborder="0" allowtransparency="true" allow="encrypted-media"></iframe>
</div>
<h3>RX-101: Dopamine (2019)</h3>
<p><a name="rx-101-dopamine"></a></p>
<p>Bask in the warm analog glow cast by these 13 tracks from Dutch producer Erik Jong.</p>
<div class="spotify">
<iframe src="https://open.spotify.com/embed/album/32dttKpIJvL2ndTAUSyQui" width="100%" height="100%" frameborder="0" allowtransparency="true" allow="encrypted-media"></iframe>
</div>
<h3>John Tejada: Parabolas (2011)</h3>
<p><a name="john-tejada-parabolas"></a></p>
<p>Dark, intelligent tech house from an Austrian-Californian producer. Tejada teaches at CalArts and consistently puts out great tech house albums, among them <a href="https://open.spotify.com/album/5Rr2NMSWTD6F7rsKAbhTXb?si=OeaOL5eaTmqrwshsFqgMjQ">Signs Under Test (2015)</a>, <a href="https://open.spotify.com/album/4tuihdFNCR2nz6WeIiQsLC?si=fZNo8_yER0aGd6MpT87LQg">Live Rytm Trax (2018)</a>, and <a href="https://open.spotify.com/album/0jwjHNhMdj3V6fOvx36ozD?si=IT0PBz_EQQOBoSzNsWHs8A">Year Of The Living Dead (2021)</a>.</p>
<div class="spotify">
<iframe src="https://open.spotify.com/embed/album/1ysY4ZKWker8yinW7hg5Jx" width="100%" height="100%" frameborder="0" allowtransparency="true" allow="encrypted-media"></iframe>
</div>
<h3>Superski: Mondo Moderno (2023)</h3>
<p><a name="superski-mondo-moderno"></a></p>
<p>I'll be honest: trance is not my jam. Yet somehow these trancey, cinematic, Italo-disco-influenced techno tracks from Litrowski & Voiski won me over. Look, you can wear your sunglasses at night. They can be fine Italian sunglasses. You can even be the protagonist in a Fellini film. But if you raise them and wink like Ferris Bueller, we're not going to take you entirely seriously -- and I think that's the idea.</p>
<div class="spotify">
<iframe src="https://open.spotify.com/embed/album/5NCPvM8NsBT12SwVXmUWdB" width="100%" height="100%" frameborder="0" allowtransparency="true" allow="encrypted-media"></iframe>
</div>
<h3>CN: The Expedition Beyond (2011)</h3>
<p><a name="cn-the-expedition-beyond"></a></p>
<p>The year is 3984, and this is the soundtrack to our mysterious space explorations. CN is one of several projects from the outrageously talented and prolific Norwegian producer <a href="https://eodtracks.bandcamp.com/">Stian Gjevik</a>. There's <a href="https://open.spotify.com/album/0YPxzyy8doEk5wh5XT0AkW?si=gUmFatscRnKPufh5M1SfpA">a second album</a> that picks up where this one left off.</p>
<div class="spotify">
<iframe src="https://open.spotify.com/embed/album/65zlbg1882ABbikYSMkmZ9" width="100%" height="100%" frameborder="0" allowtransparency="true" allow="encrypted-media"></iframe>
</div>
<h3>Martin Schulte: Slow Beauty (2012)</h3>
<p><a name="martin-schulte-slow-beauty"></a></p>
<p>Ambient that gets its inspiration from nature. While most music is busy painting portraits, these tracks are content to paint landscapes. If you like this stuff, Schulte has a whole series of albums exploring different seasons and places.</p>
<div class="spotify">
<iframe src="https://open.spotify.com/embed/album/42jWm6Kiir3tsV5EFGe3M2" width="100%" height="100%" frameborder="0" allowtransparency="true" allow="encrypted-media"></iframe>
</div>
<h3>Four Tet: New Energy (2017)</h3>
<p><a name="four-tet-new-energy"></a></p>
<p>Natural inspiration in this one too, which comes to you from a cabin in upstate New York.</p>
<div class="spotify">
<iframe src="https://open.spotify.com/embed/album/74r6JJ97ipO0CREXP9PMqZ" width="100%" height="100%" frameborder="0" allowtransparency="true" allow="encrypted-media"></iframe>
</div>
<h3>Steve Reich: Music for 18 Musicians (1976)</h3>
<p><a name="steve-reich-music-for-18-musicians"></a></p>
<p>A minimalist classical masterpiece from 1976 that anticipated electronic music as we know it: layering, envelopes, precise rhythms, repetitiveness, gradual rather than sudden harmonic changes...it's all in there. I find it incredible that 18 skilled humans can approximate dense electronic music like this. "18 Musicians" is structurally interesting too, as the interior sections are organized around a cycle of eleven chords articulated in the opening and closing "Pulses" movements.</p>
<div class="spotify">
<iframe src="https://open.spotify.com/embed/album/1w9O7mS9WEp5xlZUpYbDt9" width="100%" height="100%" frameborder="0" allowtransparency="true" allow="encrypted-media"></iframe>
</div>
<h3>Terry Riley: In C (1964)</h3>
<p><a name="terry-riley-in-c"></a></p>
<p>The granddaddy of all minimalist classical masterpieces. For about an hour we never leave the key of C. Unlike Spinal Tap, Riley pulls it off. A fascinating and elevating listen.</p>
<div class="spotify">
<iframe src="https://open.spotify.com/embed/album/3dz5dt5vdJEKhTvTI0ZR9J" width="100%" height="100%" frameborder="0" allowtransparency="true" allow="encrypted-media"></iframe>
</div>
<h2>EPs</h2>
<p>These are half-albums that nonetheless stand out as excellent music to work to. They vary in length, but are ½ an hour on average.</p>
<h3>EOD: Utrecht (2010)</h3>
<p><a name="eod-utrecht"></a></p>
<p>Lush synth landscapes collide with hard-edge acid techno, leaving you stranded in the best of both worlds. <a href="https://eodtracks.bandcamp.com/">EOD</a> is Norwegian producer Stian Gjevik's main shingle. His melodic gift and arranging skills are on full display here.</p>
<div class="spotify ep">
<iframe src="https://open.spotify.com/embed/album/7IPZCf0sK3jXZoXSRoKIeJ" width="100%" height="100%" frameborder="0" allowtransparency="true" allow="encrypted-media"></iframe>
</div>
<h3>EOD: Questionmarks (2012)</h3>
<p><a name="eod-questionmarks"></a></p>
<p>On this EP Gjevik strips away the lushness and lets the hard-edge techno rip. Sweet, intricately arranged melodies take a back seat to an urgency and raw speed that's borderline frightening. Fear not: Gjevik is a professional driver on a closed course.</p>
<div class="spotify ep">
<iframe src="https://open.spotify.com/embed/album/0uVF1xoHTzpsWeGIq00ILF" width="100%" height="100%" frameborder="0" allowtransparency="true" allow="encrypted-media"></iframe>
</div>
<h3>Automatic Tasty: Fieldwork EP (2012)</h3>
<p><a name="automatic-tasty-fieldworks-ep"></a></p>
<p>Morning, afternoon, evening, night: you must admit this is a nice four-part cyclic structure for an EP. Although the instrumentation uses the innocent but dated sounds of early techno, Dillon also weaves in real field recordings from different times of day. The result is charming and feel-good.</p>
<div class="spotify ep">
<iframe src="https://open.spotify.com/embed/album/0zgcTeyhSIBGCiJI17mEgp" width="100%" height="100%" frameborder="0" allowtransparency="true" allow="encrypted-media"></iframe>
</div>
<h3>The Field: Sound of Light - Nordic Light Hotel (2007)</h3>
<p><a name="the-field-sound-of-light-nordic-light-hotel"></a></p>
<p>Another four-part day cycle EP from Sweden. True to form, these tracks are driving, repetitive, and awash in sound -- the kind of thing that makes you hitch up the sled dogs and log a couple hundred miles of frozen tundra.</p>
<div class="spotify ep">
<iframe src="https://open.spotify.com/embed/album/2asOJ9R5vdPsqKX3hvNCEE" width="100%" height="100%" frameborder="0" allowtransparency="true" allow="encrypted-media"></iframe>
</div>
<h3>Seb Wildblood: The One with the Emoticon (2017)</h3>
<p><a name="seb-wildblood-emoticon"></a></p>
<p>Before emoji conquered the world, we typed things like <code>:~^</code>, which is the actual name of this album, and possibly a self-portrait? Lush, organic deep house from the UK.</p>
<div class="spotify ep">
<iframe src="https://open.spotify.com/embed/album/0xNHXAqiVHPSSRO9ZxiewT" width="100%" height="100%" frameborder="0" allowtransparency="true" allow="encrypted-media"></iframe>
</div>
<h3>DMX Krew: Broken SD140 Part II (2013)</h3>
<p><a name="dmx-krew-broken-sd140-part-ii"></a></p>
<p>What is an SD140, and are we sure it's safe to use a broken one? Harsh electro rhythm sounds topped with sweet melodies. "Apple Grid" is a standout track.</p>
<div class="spotify ep">
<iframe src="https://open.spotify.com/embed/album/1eDpqYQe0AgJKLEVd0TsMz" width="100%" height="100%" frameborder="0" allowtransparency="true" allow="encrypted-media"></iframe>
</div>
<h3>Khotin: Baikal Acid (2016)</h3>
<p><a name="khotin-baikal-acid"></a></p>
<p>Dancy, imaginative acid house from up north. Khotin saves the best for last: side B has not one, but two lovely, warm tunes.</p>
<div class="spotify ep">
<iframe src="https://open.spotify.com/embed/album/6c9E0fOhQBFImF2tqS0Ocl" width="100%" height="100%" frameborder="0" allowtransparency="true" allow="encrypted-media"></iframe>
</div>
<h3>Jonas Kopp: Desire EP (2013)</h3>
<p><a name="jonas-kopp-desire-ep"></a></p>
<p>German minimal techno by way of Argentina. It's dark, but the opener is funkier than your typical Tresor track, and the closer feels like some kind of ceremonial ascension.</p>
<div class="spotify ep">
<iframe src="https://open.spotify.com/embed/album/4MS9uxTHx0zLQbgdiGABFc" width="100%" height="100%" frameborder="0" allowtransparency="true" allow="encrypted-media"></iframe>
</div>
<h3>Etapp Kyle: Klockworks 10 (2015)</h3>
<p><a name="etapp-kyle-klockworks-10"></a></p>
<p>Dark, driving, haunted minimal techno of the German variety. All of the albums on <a href="https://ra.co/dj/benklock/biography">Ben Klok's</a> Klockworks series are worth a listen, but Klockworks 10 and 16 from this Ukranian producer are standouts.</p>
<div class="spotify ep">
<iframe src="https://open.spotify.com/embed/album/0KpFLppuoYEtWSsmLq42lz" width="100%" height="100%" frameborder="0" allowtransparency="true" allow="encrypted-media"></iframe>
</div>
<h3>Luke Hess: Facette (2017)</h3>
<p><a name="luke-hess-facette"></a></p>
<p>Modern Detroit minimal techno. A propulsion system made from deep, dark textures and thumping beats.</p>
<div class="spotify ep">
<iframe src="https://open.spotify.com/embed/album/6U1eW8QyHMjxnAZpzPUy90" width="100%" height="100%" frameborder="0" allowtransparency="true" allow="encrypted-media"></iframe>
</div>
<h2>Artist Samplers</h2>
<p>Some artists don't fit well into the album box. And some artists make albums of such breadth that they no longer fit into the "music to work to" box. Here's a few sampler playlists from artists not featured above, but no less deserving.</p>
<h3>Basic Channel - Sampler</h3>
<p><a name="basic-channel-sampler"></a></p>
<p>As <a href="https://en.wikipedia.org/wiki/Basic_Channel">Basic Channel</a>, the duo of Moritz von Oswald and Mark Ernestus pioneered minimal dub techno in the early 90s. Except for BCD and BCD-2, their output consists of a series of cryptically labeled singles. Here's a curated selection.</p>
<div class="spotify">
<iframe src="https://open.spotify.com/embed/playlist/6jLp6IXv49MaE4MYHn077N" width="100%" height="100%" frameborder="0" allowtransparency="true" allow="encrypted-media"></iframe>
</div>
<h3>Trickfinger - Sampler</h3>
<p><a name="trickfinger-sampler"></a></p>
<p>Did you know John Frusciante -- yes, <a href="https://en.wikipedia.org/wiki/John_Frusciante">that John Frusciante</a> -- has a side gig making acid techno? Insane. There are a couple tracks here where it's hard to believe he didn't pick the melody out first on a guitar.</p>
<div class="spotify">
<iframe src="https://open.spotify.com/embed/playlist/4wApGodUxCIQ0a5My3wsuM" width="100%" height="100%" frameborder="0" allowtransparency="true" allow="encrypted-media"></iframe>
</div>
<h3>Ceephax - Sampler</h3>
<p><a name="ceephax-sampler"></a></p>
<p>No list would be complete without Andy Jenkinson, AKA Ceephax. Personally I like his stuff more than <a href="https://en.wikipedia.org/wiki/Squarepusher">his brother's</a>. It's funny, nostalgic, slightly unhinged, and brimming with bonafide musical genius.</p>
<div class="spotify">
<iframe src="https://open.spotify.com/embed/playlist/4spSHMH3oRUB9bYvFio93p" width="100%" height="100%" frameborder="0" allowtransparency="true" allow="encrypted-media"></iframe>
</div>https://alangrow.com/blog/slippery-device-namesSlippery Device Names and Portable AMIs2020-12-10T21:12:32Alanalangrow+blog@gmail.com<p>Pain, thy name is hotpluggable device name assignment.</p>
<p>In the course of migrating some EC2 servers from C3 to C5, I learned why this feature in newer linux kernels is controversial.</p>
<p>To be clear, most people couldn't care less whether their primary network interface is called <code>eth0</code> or <code>enx0150b6e42dfe</code>, or whether a drive appears as <code>/dev/xvda</code> or <code>/dev/nvme9n5</code>, as long as they can continue to do their Computer Stuff. For ops folks trying to make a portable system image, though, this can be a real problem.</p>
<p>My goal was to create an AMI that can be booted on a variety of EC2 instance types. Here's how I got there.</p>
<h3>Network Interfaces</h3>
<p>Hotpluggable network interface names make sense for multi-homed systems, and systems that might change network configuration later.</p>
<p>They also make sense for consumer devices that don't need to be portably imaged. When was the last time you pulled the hard drive out of your laptop, put it in a different brand of laptop, and had everything just work? Would you even expect this to work? No, this is crazy talk.</p>
<p>However, those of us who operate and upgrade servers have different (higher?) expectations.</p>
<p>The essential problem is described in gory detail on debian's <a href="https://wiki.debian.org/NetworkInterfaceNames">NetworkInterfaceNames wiki page</a>. "We're not in the 90s anymore, network devices come and go, deal with it. That said, here are a dozen different ways to avoid this new nonsense..."</p>
<p>Adding <code>net.ifnames=0</code> boot parameters to <code>/etc/default/grub</code> worked for me:</p><div class="highlight"><pre><span></span><span class="nv">GRUB_CMDLINE_LINUX_DEFAULT</span><span class="o">=</span><span class="s2">"... net.ifnames=0"</span>
<span class="nv">GRUB_CMDLINE_LINUX</span><span class="o">=</span><span class="s2">"net.ifnames=0"</span>
</pre></div>
<p>Follow this up with a perfunctory <code>update-grub</code>.</p>
<p>Importantly for portability, this means any config files under <code>/etc/</code> can refer to <code>eth0</code> directly, and that will continue to work even if you make an AMI and boot it on another instance type — so long as it has just one network interface.</p>
<h3>NVMe Disks</h3>
<p>Next we have the disk problem. C5 instances use NVMe throughout, even for EBS storage. <a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/nvme-ebs-volumes.html">The AWS docs warn us</a> that:</p>
<blockquote>
<p>The block device driver can assign NVMe device names in a different order than you specified for the volumes in the block device mapping.</p>
</blockquote>
<p>And oh boy, do they ever like to assign in different order.</p>
<p>If you only have one disk, you might never have this problem. I use multiple disks because:</p>
<ol>
<li>Different subsystems have different access patterns and performance requirements. Using separate disks for, say, <code>/var/lib/postgresql/</code> and <code>/var/log/</code> lets you provision and tune them separately.</li>
<li>The disk boundary is a convenient blast radius. Have you ever had a server fill up with log files and grind to a halt? A separate <code>/var/log/</code> disk will contain the problem and let other subsystems continue to run normally.</li>
</ol>
<p>So I've got 5 different NVMe disks attaching to an EC2 instance in random order. Once in a while it works, but usually home directories have become the database, logfiles are now volatile runtime state, and so on. A real Mister Potato Head mess.</p>
<p><a href="https://russell.ballestrini.net/aws-nvme-to-block-mapping/">Russell Ballestrini ran into this same issue</a> and found a script, <a href="https://russell.ballestrini.net/uploads/2019/ebsnvme-id"><code>ebsnvme-id</code></a>, that ships with Amazon Linux. This script interrogates an EBS NVMe device (eg <code>/dev/nvme1n1</code>) and outputs the original name specified in the block mapping (eg <code>/dev/xvdb</code>).</p>
<p>But we're not quite there yet. Armed with <code>ebsnvme-id</code>, you can create symlinks like <code>/dev/nvme1n1 -> /dev/xvdb</code>, but how and when you should you do this?</p>
<p>The <code>/dev</code> directory gets populated anew via <code>udev</code> during boot. So there's a right time to do this, and there are many wrong times to do this. My first attempt via <code>/etc/rc.local</code> failed horribly — it ran too late.</p>
<p>Eventually I came around to the idea of using <code>udev</code>, and I learned from this <a href="http://www.reactivated.net/writing_udev_rules.html">nice udev primer</a> that udev rules can be flexible in the extreme. You can pattern match on device names. You can also run an external program that figures out how to rename a device. This culminated in the following magical one liner:</p><div class="highlight"><pre><span></span><span class="nv">KERNEL</span><span class="o">==</span><span class="s2">"nvme[0-9]*n1"</span>, <span class="nv">PROGRAM</span><span class="o">=</span><span class="s2">"ebsnvme-namer %k"</span>, <span class="nv">SYMLINK</span><span class="o">+=</span><span class="s2">"%c"</span>
</pre></div>
<p>Save this to <code>70-persistent-ebsnvme.rules</code> under <code>/etc/udev/rules.d/</code>. You'll notice it doesn't hardcode any device names, so it's safe to include in a portable machine image. It creates <code>/dev</code> symlinks that look like:</p><div class="highlight"><pre><span></span>lrwxrwxrwx 1 root root 7 Dec 9 14:34 xvda1 -> nvme0n1
lrwxrwxrwx 1 root root 7 Dec 9 14:34 xvdd -> nvme1n1
lrwxrwxrwx 1 root root 7 Dec 9 14:34 xvde -> nvme3n1
lrwxrwxrwx 1 root root 7 Dec 9 14:34 xvdf -> nvme4n1
lrwxrwxrwx 1 root root 7 Dec 9 14:34 xvdg -> nvme2n1
</pre></div>
<p>These match the old disk device names on my C3 instances. All my scripts and config files that reference specific <code>xvd*</code> names? In the end, not a single one needed changing for the C3 -> C5 upgrade!</p>
<p>Finally, here's the <code>ebsnvme-namer</code> script:</p><div class="highlight"><pre><span></span><span class="ch">#!/bin/sh -e</span>
<span class="nv">ebsdev</span><span class="o">=</span><span class="sb">`</span>ebsnvme-id --block-dev /dev/<span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span><span class="sb">`</span>
<span class="nb">echo</span> xvd<span class="si">${</span><span class="nv">ebsdev</span><span class="p">##sd</span><span class="si">}</span>
</pre></div>
https://alangrow.com/blog/weechat-matrixMatrix Chat in the Terminal with weechat-matrix2020-08-30T19:27:00Alanalangrow+blog@gmail.com<div class="image">
<img src="../images/blog/weechat-matrix.png" width="100%"/>
</div>
<p>I've been using the <a href="https://apps.apple.com/app/vector/id1083446067">Element iOS App</a> to chat with a few security-conscious friends. It works fine, but at some point you outgrow chatting with your thumbs, and long for the full ten-fingered chat experience. (A 5x improvement!!)</p>
<p>Fortunately the <a href="https://en.wikipedia.org/wiki/Matrix_(protocol)">Matrix protocol</a> is an open standard with <a href="https://matrix.org/clients/">plenty of clients</a>. I like terminal programs, and <a href="https://github.com/wee-slack/wee-slack">this Slack plugin for weechat</a> has been a pleasant surprise of late. It turns out there's a <a href="https://github.com/poljar/weechat-matrix">Matrix plugin for weechat</a> too. A few months back I tried and failed to set up <code>weechat-matrix</code>, but today things went a little better.</p>
<p>So here's what worked for me. But first...</p>
<h3>Are You Sure You Want to Do This</h3>
<p>The Matrix ecosystem is new, peopled with technical users, and hardcore about security. This doesn't make for a pleasant experience for most human beings. If you don't like messing around with tech for its own sake, and you just want 1:1 secure chats, might I recommend <a href="https://signal.org/">Signal</a>?</p>
<p>Still here? Onward...</p>
<h3>Forget Python2</h3>
<p>Although weechat still supports Python2, and <code>weechat-matrix</code> claims to support it, its <a href="https://pypi.org/project/matrix-nio/"><code>matrix-nio</code> dependency doesn't</a>. Don't waste your time. Start with Python3.</p>
<h3>Isolate the Install</h3>
<p>To avoid hosing my existing weechat setup or my base system, I started from a clean <a href="https://wiki.debian.org/Debootstrap">Debian 10 <code>debootstrap</code></a>. If you're hipper than me maybe you prefer docker. Either way, it pays to isolate!</p>
<h3>Use the Weechat Development Packages</h3>
<p>Once your environment is up and running, you'll want to grab the <a href="https://weechat.org/download/debian/active/files/"><code>weechat-devel</code></a> packages for maximum Python3 compat.</p>
<h3>Follow the README</h3>
<p>Follow the <a href="https://github.com/poljar/weechat-matrix#installation"><code>weechat-matrix</code> README</a>. If all goes well you'll be connected, logged in, and automatically joined to your channels. But there's a problem: your new "device" isn't verified.</p>
<h3>Verify Devices</h3>
<p>Immediately after <code>weechat-matrix</code> successfully connected, Element popped up a modal that it was verifying the new device. This was the interactive verification flow, and it didn't work for me. Close out of it.</p>
<p>What worked instead was going to Element -> Settings -> Security, finding my new "Weechat Matrix" session, and manually verifying it. To make sure things match on the other end, switch to any Matrix channel in weechat, type <code>/olm info</code>, then switch back to the first weechat buffer. You should see your new weechat device's identity keys. If they match, you can verify them in Element.</p>
<p>You can also verify devices from weechat's perspective via <code>/olm verify <username></code>. There's even some pattern syntax that lets you verify multiple devices at once — but be careful with this.</p>
<p>At this point, you should be able to chat safely with other devices you've done the verification dance with, but there's still a problem: you can't read channel history. You'll see usernames and timestamps in weechat, but each message will start with <code><Unable to decrypt></code>.</p>
<h3>Decrypt Old Messages</h3>
<p>To decrypt channel history, you'll need to export keys from Element and import them into <code>weechat-matrix</code>. Element makes this pretty easy: go to Settings -> Security -> "Export keys manually." Create a passphrase for the key file, and email it to yourself.</p>
<p>Back in weechat, use <code>/olm import ~/path/to/riot-keys.txt <passphrase></code>. This may take a bit, and <code>weechat</code> will likely hit 100% cpu during the process.</p>
<p>On success, you still can't read channel history...that is, until you restart weechat!</p>
<h3>Feedback</h3>
<p>Did it work? Did I miss something? <a href="https://twitter.com/alangrow">Let me know</a>.</p>
<h3>More Info</h3>
<p>Here's <a href="https://hispagatos.org/post/weechat-matrix/">another useful guide to weechat + matrix</a>.</p>
<p>I'd be remiss if I didn't mention that <code>weechat-matrix</code> is in maintenance mode, and a <a href="https://github.com/poljar/weechat-matrix-rs">new Rust port is underway</a>. At the time of this writing it isn't very far along.</p>https://alangrow.com/blog/on-remote-workOn Remote Work: An Interview2020-03-26T00:00:00Alanalangrow+blog@gmail.com<p>This interview originally appeared on <a href="https://remoteworkers.community/interviews/3-alan-grow">remote.community in March 2020</a>.</p>
<h3>Hello! who are you and where do you work?</h3>
<p>Hello! I'm Alan Grow, co-founder at <a href="https://endcrawl.com">Endcrawl</a>. We're a SaaS that makes credits for film & TV. We've been used on thousands of productions including Oscar Winners "Moonlight" and "Nomadland."</p>
<h3>How did you get started working remotely?</h3>
<p>I first tried remote work back in 2006. It was a failure. Part of that failure was lack of consulting experience, but part of it was also my lack of experience with remote work. After two decades of classrooms and offices, I didn't know how to create productive environments and habits for myself.</p>
<p>The next time I tried full-time remote work was in 2014. I interviewed over Skype and was hired the next day, by someone I'd never met in real life. Six months would pass before we finally met face to face. But that was fine, because this time, things were actually working great! I'd finally figured out what keeps me focused and motivated within a remote team.</p>
<p>What I realize now is that remote work starts and ends with a team of one: yourself. If you can’t keep yourself focused and motivated, someone will have to do that for you remotely, and that’s a tall order. You have to figure those out for yourself the hard way. But the good news is, once you have them, they’re a superpower.</p>
<h3>Describe your typical work day or week.</h3>
<p>I begin and end my work days at my home office, but in the middle I almost always leave the house to work from a coffee shop. This is a crucial thing that I learned the hard way: even as my home office improves, I still need to get out of the house to clear my head and avoid cabin fever.</p>
<p>Coffee shops and coworking spaces are less predictable in a couple ways that affect work. Sometimes there are network issues, and sometimes there's <strike>an annoying conversation</strike> noise. Both make it harder to have meetings. So I adapt by taking meetings at home and using these spaces instead for deep work.</p>
<p>That brings some challenges of its own. How do you focus deeply in a noisy public space, where people you know or strangers may interrupt you? For me: <a href="./music-to-program-to">repetitive electronic music</a>! A decent pair of headphones with focus-inducing music works wonders, both for your own mental state, and to signal to others that you're uninterruptible.</p>
<p>Just like a change of venue in the afternoon seems to keep me focused throughout the day, changing activities during nights and weekends helps me reset and avoid burnout. Whether it's playing music, lifting weights, or exploring the wilderness here in Utah, getting into <a href="https://media3.giphy.com/media/oWWfwpLj5l0XK/source.gif">a very different mental state</a> keeps my "work mind" fresh.</p>
<p>When I was younger I wanted to program 24/7, and every other activity seemed like a waste of time. Now I know that the opposite is true: the right mix of non-programming activities makes me a much more productive programmer.</p>
<h3>What tools do you use when working remotely?</h3>
<p>I spend a fair bit of time in Github and Slack. Video conferencing is usually via Google Meet. Beyond that, I try to stay minimal and inhabit a simpler world of text.</p>
<p>I keep long-running tmux sessions both locally and on remote machines. For email I use mutt + offlineimap, and try to only poll for new emails at the beginning and end of the day.</p>
<p>I like spending time in my editor (vim) and at a shell prompt. Learning unix and the Unix toolset has been a 1000x investment over the course of my career.</p>
<h3>Describe how working remotely has affected your life.</h3>
<p>Not gonna lie — remote work was a difficult adjustment at first. I'm a social person, and most of my socialization when I lived in NYC in the 2000s was (surprise) via the workplace. I didn't know any different.</p>
<p>But learning how to make and maintain friendships turned out to be an orthogonal concern. It doesn't <em>have</em> to happen through the workplace. That sort of bundling is just an easy default. Friendship can be <a href="https://hbr.org/2014/06/how-to-succeed-in-business-by-bundling-and-unbundling">unbundled</a> from work.</p>
<p>When I left NYC, I had to make friends all over again, as well as maintain more remote friendships. Both of those turned out to be good exercises.</p>
<p>Remote work has let me live away from a major city, but still work with people in major cities on exciting things. I don't have to deal with the stress of traffic, rush hour, crowds, or the concrete jungle. In 15 minutes I can be enjoying a quiet hike in the wilderness. It's the best of both worlds.</p>
<p>Remote work has also let me work with people around the world. I was fortunate to work for London-based music hardware & software maker <a href="https://roli.com/">ROLI</a>, with teams from all over the US and Europe. That kind of geographic diversity comes with all other wonderful kinds of diversity, and it's a difficult thing to replicate in non-remote companies.</p>
<p>Finally, remote work has made me much more results-oriented. Physical offices inevitably become a stage for productivity theater. Try as they might, people are pulled towards the things that look busy, and away from the things that actually produce value. Remote work keeps you honest – you're constantly proving yourself, and you're measured by your output. Those are good things!</p>
<h3>What advice would you give to people working remotely?</h3>
<ol>
<li><strong>Adopt a schedule and commit to it.</strong> Once it’s “burned in” you can start to experiment, but try to build rhythm first.</li>
<li><strong>Measure your daily output.</strong> This could be git commits, tasks checked off, new revenue booked, etc. It doesn’t have to be how others measure you. But it should loosely correlate with that, and it should be something trivially easy for you to measure.</li>
<li><strong>Course correct often.</strong> When something impacts your daily output, act on it. Does your output drop noticeably on less than 7 hours of sleep? Get more sleep!</li>
<li><strong>Focus is sacred.</strong> Your focus is your temple — don't let anything or anyone defile it. Especially yourself. Find ways to subdue the part of your brain that craves distraction.</li>
<li><strong>Embrace the written word.</strong> Always be reading, always be writing, and always be improving your reading and writing.</li>
<li><strong>Review, and seek review.</strong> Critical — but constructive — written dialog with your co-workers will level you both up.</li>
</ol>
<h3>Would you like to add anything else?</h3>
<p>If you're bootstrapping a remote-first company, consider applying to <a href="https://earnestcapital.com/">Earnest Capital</a>. They're an incredible resource to companies like ours, and their community of founders and mentors is very much aligned with the future of work — which is remote!</p>https://alangrow.com/blog/shell-quirk-assign-from-heredocShell Quirk: Assignment From a Heredoc2017-06-10T20:30:00Alanalangrow+blog@gmail.com<p>I have a <strike>fetish for</strike> fascination with POSIX shell corner cases. It all started a decade ago with a segfault: a certain <code>while read</code> loop ran fine on every Unix except AIX. We were stumped, and I was hooked.</p>
<p>Here's a new find. What will the following POSIX shell program print?</p><div class="highlight"><pre><span></span><span class="ch">#!/bin/sh</span>
<span class="nv">paths</span><span class="o">=</span><span class="sb">`</span>tr <span class="s1">'\n'</span> <span class="s1">':'</span> <span class="p">|</span> sed -e <span class="s1">'s/:$//'</span><span class="sb">`</span><span class="s"><<EOPATHS</span>
<span class="s">/foo</span>
<span class="s">/bar</span>
<span class="s">/baz</span>
<span class="s">EOPATHS</span>
<span class="nb">echo</span> <span class="s2">"</span><span class="nv">$paths</span><span class="s2">"</span>
</pre></div>
<p>If you said <code>/foo:/bar:/baz</code>, you're right...that is, if you're on Linux and <code>/bin/sh</code> is provided by <a href="https://en.wikipedia.org/wiki/Almquist_shell#dash:_Ubuntu.2C_Debian_and_POSIX_compliance_of_Linux_distributions">dash</a>.</p>
<p>If you're on MacOS <a href="#1">[1]</a> or FreeBSD instead, this same script will wait for input and print nothing. This is probably the behavior on all BSD derivatives, and it's likely the correct behavior too, since the BSDs are usually right about these things.</p>
<p>Correct or not, the <code>dash</code> behavior is a bit more useful. It also points to a fundamental difference in the way <a href="http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_07_04">here-documents</a> work: <code>dash</code> interprets the heredoc <em>before</em> anything else on the line. When the assignment is interpreted next, stdin already has the contents of the heredoc. I'm not even sure what the other POSIX shells do. Is the heredoc interpreted after the assignment? Where does it even go?</p>
<p>Fortunately there's an easy portable alternative: wrap the whole thing in backquotes.</p><div class="highlight"><pre><span></span><span class="ch">#!/bin/sh</span>
<span class="nv">paths</span><span class="o">=</span><span class="sb">`</span>tr <span class="s1">'\n'</span> <span class="s1">':'</span> <span class="p">|</span> sed -e <span class="s1">'s/:$//'</span><span class="s"><<EOPATHS</span>
<span class="s">/foo</span>
<span class="s">/bar</span>
<span class="s">/baz</span>
<span class="s">EOPATHS</span><span class="sb">`</span>
<span class="nb">echo</span> <span class="s2">"</span><span class="nv">$paths</span><span class="s2">"</span>
</pre></div>
<p><a name="1">[1]</a> Note that on recent MacOS versions, <code>/bin/sh</code> is actually <code>bash</code> in POSIX mode. Don't believe me? Run <code>/bin/sh --help</code> and <code>/bin/sh -c 'echo $POSIXLY_CORRECT'</code>.</p>
<style>
.highlight .s { color: #dd7700; }
</style>https://alangrow.com/blog/blog-refresh-now-with-lessBlog Refresh: Now With Less2017-05-01T02:13:42Alanalangrow+blog@gmail.com<p>To readers who enjoyed the 3-column layout, the Edgar Allen Poe quote, and the engraving of the fragile rowboat disappearing into the mighty maelstrom: I'm sorry. It's all gone. To me, minimalism is less an aesthetic than it is the search for time invariants, and well...here we are some years later.</p>
<p>It's actually a bit more practical than all that. After porting this blog from <a href="https://jekyllrb.com/">jekyll</a> to <a href="https://github.com/acg/tinysite">tinysite</a>, I discovered that the very problem I set out to solve -- fast incremental site rebuilds -- was still a problem. No comment on why this seems to be a common failure mode for shiny two-point-oh-y things.</p>
<p>The culprit? That index of posts in the right column. The simple act of fixing a typo on a single page would cause <code>posts.json</code> to rebuild, and then every post would be rebuilt in a cascade, since the right column of every post depended on <code>posts.json</code>. Other static site generators probably learned to avoid this years ago. I finally came around to it this weekend.</p>
<p>In the interim, editing posts has been pretty unpleasant. Doubly so because I had no one to blame but myself. Now incremental site rebuilds are quick and can be accelerated with <code>make -j</code> as before.</p>
<p>With that out of the way, I decided to take advantage of the "let's optimize the shit out of everything" mental state I was in and see what could be done to speed up the publishing side of things. I really like the Heroku / Github Pages approach of "just git push and we'll do the rest," and have spent the last few years building systems to make everything at <a href="https://endcrawl.com">Endcrawl</a> work like that. Maybe those years would have been better spent learning docker or kube. Maybe the people who regard <a href="https://news.ycombinator.com/item?id=5927843">deploying-via-git as an antipattern</a> are right. But I can't shake the idea that we're overengineering the hell out of this problem right now. As <a href="https://news.ycombinator.com/item?id=14216655">one HN commenter</a> put it:</p>
<blockquote>
<p>In the long term I predict that base OS everywhere will improve support for deployment, workload scheduling, resource allocation, endpoint discovery, and dependency management. These will match and eventually surpass the additional capabilities that containers offer, and <strong>then we can all go back to putting files on a server and restarting a process</strong>, which is all that 99% of us actually need.</p>
</blockquote>
<p>There's a bit more to the story than the part I emphasized, but that's one for another day. Suffice to say there's tooling now that fully realizes the <a href="./dream-deploys-atomic-zero-downtime-deployments/">"dream deploys"</a> idea, this site uses it, and who knows, maybe it'll get opensourced one day.</p>
<p>I also took a stab at the horribly clunky <code>{% highlight lang %}</code> template syntax this blog used for code highlighting. When I started there was no good standard for this kind of thing, but now it seems <a href="https://help.github.com/articles/creating-and-highlighting-code-blocks/">fenced code blocks</a> have won. Good for them, they're awesome. Switching <code>tinysite</code> to fenced code turned out to be trivial <a href="https://github.com/acg/tinysite/commit/d6ea6fe0bf58ef6a28776a7f4f0b622f8c47c747">(diff)</a>, mainly because the original approach was a small regex hack rather than a more evolved approach. That Yagni guy they're always invoking knows what's up!</p>
<p>Oh yeah. The Disqus comments section is gone. <a href="http://donw.io/post/github-comments/">Good riddance</a>. It's been broken for years, ever since I migrated from <code>acg.github.io</code> to this domain. I probably made a mistake somewhere in the Disqus migration tool but never could figure it out. If you feel a burning desire to rebutt or high-five something, <a href="https://twitter.com/alangrow">hit me up on twitter</a> and I may link to it. Better yet, <a href="https://github.com/acg/alangrow.com/issues/new">open a github issue against this blog</a>.</p>https://alangrow.com/blog/dream-deploys-atomic-zero-downtime-deploymentsDream Deploys: Atomic, Zero-Downtime Deployments2015-06-05T21:11:00Alanalangrow+blog@gmail.com<p>(Update: here's a <a href="https://github.com/endcrawl/deployer">real implementation</a>.)</p>
<p>Are you afraid to deploy? Do deployments always mean either downtime, leaving your site in an inconsistent state for a while, or both? It doesn't have to be this way!</p>
<p>Let's conquer our fear. Let's deploy whenever we damn well feel like it.</p>
<div class="image">
<img src="../images/blog/donnie-darko-not-afraid-anymore.jpg" width="100%"/>
</div>
<h3>You Don't Need Much</h3>
<p>This is a tiny demo to convince you that Dream Deploys are not only possible, they're easy.</p>
<p>To live the dream, you don't need much:</p>
<ul>
<li>You don't need a fancy load balancer.</li>
<li>You don't need magic "clustering" infrastructure.</li>
<li>You don't need a specific language or framework.</li>
<li>You don't need a queue system.</li>
<li>You don't need a message bus or fancy IPC.</li>
<li><em>You don't even need multiple instances of your server running.</em></li>
</ul>
<p>All you need is a couple old-school Unix tricks.</p>
<h2>A Quick Demo</h2>
<p>Don't take my word for it. Grab the code <a href="https://github.com/acg/dream-deploys">here</a> with:</p><div class="highlight"><pre><span></span>git clone git@github.com:acg/dream-deploys.git
<span class="nb">cd</span> dream-deploys
</pre></div>
<p>In a terminal, run this and visit the link:</p><div class="highlight"><pre><span></span>./serve
</pre></div>
<p>In a second terminal, deploy whenever you want:</p><div class="highlight"><pre><span></span>./deploy
</pre></div>
<p>Refresh the page to see it change.</p>
<p>Edit code, static files, or both under <code>./root.unused</code>. Then leave <code>./root.unused</code> and run <code>./deploy</code> to see your changes appear atomically and with zero downtime.</p>
<h2>Questions & Answers</h2>
<h3>What do you mean by a "zero downtime" deployment?</h3>
<p>At no point is the site unavailable. Requests will continue to be served before, during, and after the deployment. In other words, this is about <strong>availability</strong>.</p>
<h3>What do you mean by an "atomic" deployment?</h3>
<p>For a given connection, either you will talk to the new code working against the new files, or you will talk to the old code working against the old files. You will never see a mix of old and new. In other words, this is about <strong>consistency</strong>.</p>
<h3>How does the zero downtime part work?</h3>
<p>This brings us to Unix trick #1. If you keep the same listen socket open throughout the deployment, clients won't get <code>ECONNREFUSED</code> under normal circumstances. The kernel places them in a listen backlog until our server gets around to calling <code>accept(2)</code>.</p>
<p>This means, however, that our server process can't be the thing to call <code>listen(2)</code> if we want to stop and start it, or we'll incur visible downtime. Something else – some long running process – must call <code>listen(2)</code> and keep the listen socket open across deployments.</p>
<p>The trick in a nutshell, then, is this:</p>
<ul>
<li>
<p>A <a href="https://github.com/acg/dream-deploys/blob/master/tcplisten">tiny, dedicated program</a> calls <code>listen(2)</code> and then passes the listen socket to child processes as descriptor 0 (stdin). This process replaces itself by executing a subordinate program.</p>
</li>
<li>
<p>The subordinate program is <a href="https://github.com/acg/dream-deploys/blob/master/loop-forever">just a loop</a> that repeatedly executes our server program. Because this loop program never exits, the listen socket on descriptor 0 stays open.</p>
</li>
<li>
<p>Our server program, instead of calling <code>bind(2)</code> and <code>listen(2)</code> like everyone <strong>loves to do</strong>, humbly calls <code>accept(2)</code> on stdin in a loop and handles one client connection at a time.</p>
</li>
<li>
<p>When it's time to restart the server process, we tell the server to exit after handling the current connection, if any. That way deployment doesn't disrupt any pending requests. We tell the server process to gracefully exit by sending it a <code>SIGHUP</code> signal.</p>
</li>
</ul>
<p><strong>Note</strong>: a shocking, saddening number of web frameworks force you to call <code>listen(2)</code> in your Big Ball Of App Code That Needs To Be Restarted. The <a href="https://github.com/strongloop-forks/connect/blob/7edb875a9f305e38f4d960fa46ac674038241892/lib/proto.js#L231">connect</a> HTTP server framework used by <a href="https://github.com/strongloop/express">express</a>, the most popular web app framework for <a href="https://nodejs.org/">Node.js</a>, is one of them.</p>
<p>"I'll just use the new <a href="https://lwn.net/Articles/542629/"><code>SO_REUSEPORT</code> socket option in Linux</a>!" you say.</p>
<p>Fine, but take care that at least one server process is always running at any given time. This means some handoff coordination between the old and new server processes. Alternately, you could run an unrelated process on the port that just listens.</p>
<p>At any rate, an <code>accept(2)</code>-based server is simpler. It also has some nice added benefits unrelated to deployments:</p>
<ul>
<li>
<p>An <code>accept(2)</code>-based server is network-agnostic. For instance, you can run it behind a Unix domain socket without modifying a single line of code.</p>
</li>
<li>
<p>An <code>accept(2)</code>-based server is a more secure factoring of concerns. If your server listens directly on a privileged port (80 or 443), you'll need root privileges or a fancy capabilities setup. After binding, a listen server should also drop root privileges (horrifyingly, some don't). The <code>accept(2)</code> factoring means a tiny, well-audited program can bind to the privileged port, drop privileges to a minimally empowered user account, and run a known program. This is a huge security win.</p>
</li>
</ul>
<h3>How does the atomic part work?</h3>
<p>A connection will either be served by the old server process or the new server process. The question is whether the old process might possibly see new files, or the new process might see old files. If we update files in-place then one of these inconsistencies can happen. This forces us to keep two complete copies of the files, an old copy and a new copy.</p>
<p>While we're updating the new files, no server process should use them. If the old server process is restarted during this phase, intentionally or accidentally, it should continue to work off the old files. When the new copy is finally ready, we want to "throw the switch": deactivate the old files and simultaneously activate the new files for future server processes. The trick is to make throwing the switch an atomic operation.</p>
<div class="image">
<img src="../images/blog/mad-scientist-with-switch.jpg" width="100%"/>
</div>
<p>There are a number of <a href="http://rcrowley.org/2010/01/06/things-unix-can-do-atomically.html">things Unix can do atomically</a>. Among them: use <code>rename(2)</code> to replace a symlink with another symlink. If the "switch" is a simply a symlink pointing at one directory or the other, then deployments are atomic. This is Unix trick #2.</p>
<h3>What about serving inconsistent assets? Browsers open multiple connections.</h3>
<p>This is a problem, but there's also a straightforward solution.</p>
<p>Let's clarify the problem first: during a deployment, a client may request a page from the old server, then open more connections that request assets from the new server. (Remember, consistency is only guaranteed within the same connection.) So you can get old page content mixed with new css, js, images, etc.</p>
<p>The solution in prevailing practice is to build a new tagged set of static assets for every deployment, then have the page refer to all assets via this tag. You can do this by modifying the <a href="https://github.com/acg/dream-deploys/blob/master/deploy"><code>./deploy</code> script</a> to do this, like so:</p>
<ul>
<li>Update the new files.</li>
<li>Generate a unique tag <code>$TAG</code>. Epoch timestamps are usually good enough.</li>
<li>Record <code>$TAG</code> in a file inside the new file directory.</li>
<li>Copy all the static assets into a new directory <code>assets.$TAG</code> outside of both file copies.</li>
<li>Continue with the deployment.</li>
</ul>
<p>When the server starts up, it should read <code>$TAG</code> from the file, and make sure all asset URLs it generates contain <code>$TAG</code>.</p>
<p>That's pretty much it. Eventually you'll want to delete them, but if you keep the old <code>assets.$TAG</code> directories around for a while, even sessions that haven't reloaded the page will continue to get consistent results across deployments.</p>
<p>The long term solution to this problem is <a href="https://http2.github.io/faq/#why-is-http2-multiplexed">HTTP/2 multiplexing</a>, which makes multiple browser connections unnecessary.</p>
<h3>What about serving inconsistent ajax requests?</h3>
<p>Let's clarify this problem: during a deployment, a client may request a page from the old server, then open more connections that make ajax requests of the new server using old client code.</p>
<p>There's a less technical solution to this one: simply make your API backwards compatible. This is a good idea regardless.</p>
<h3>What about concurrency? Your example only serves one connection at a time.</h3>
<p>You can run as many <code>accept(2)</code>-calling server processes as you want on the same listen socket. The kernel will efficiently multiplex connections to them.</p>
<p>In production, I use a small program I wrote called <code>forkpool</code> that keeps N concurrent child processes running. It doesn't do anything beyond this, which means it doesn't have any bugs at this point and never needs restarting. Remember, children are a precious resource, but without a parent to keep that listen socket open they're <em>orphans</em>.</p>
<h3>What about deployment collisions?</h3>
<p>Yes, you really should prevent concurrent deployments via a lock. That's not demonstrated here, but it's extremely easy and reliable to do with <a href="http://cr.yp.to/daemontools/setlock.html">the setlock(8) program from daemontools</a>.</p>
<h3>What about deploying database schema changes?</h3>
<p>This topic has been covered <a href="https://blog.rainforestqa.com/2014-06-27-zero-downtime-database-migrations/">well elsewhere</a>.</p>https://alangrow.com/blog/turn-vim-into-excel-tips-for-tabular-data-editingTurn Vim Into Excel: Tips for Editing Tabular Data2013-03-29T00:00:00Alanalangrow+blog@gmail.com<div class="center image">
<a href="../images/blog/vim-as-spreadsheet.png"><img src="../images/blog/vim-as-spreadsheet-thumbnail.png" /></a><br/>
<small>Vim editing <a href="http://www.census.gov/econ/cbp/download/">some 2010 US census data</a></small>
</div>
<p><a href="https://www.vim.org/">Vim</a> can edit just about anything, including tabular data. This post has a few tips for making stock Vim more spreadsheet-like.</p>
<p>We'll assume you're editing files in tab-separated value format (TSV). CSV is a <a href="http://en.wikipedia.org/wiki/Comma-separated_values#Lack_of_a_standard">notoriously thorny</a> file format with plenty of edge cases and surprises, so if you have CSV files, it's simpler to sidestep all that and roundtrip CSV to TSV for editing.</p>
<h2>A Note on the TSV Format</h2>
<p>To do TSV right, you should escape newline and tab characters in data. Here are two scripts, <a href="https://gist.github.com/acg/5312217">csv2tsv</a> and <a href="https://gist.github.com/acg/5312238">tsv2csv</a>, that will handle escaping during CSV <-> TSV conversions.</p>
<p>Converting CSV to TSV, with C-style escaping:</p>
<pre><code>csv2tsv -e < file.csv > file.tsv
</code></pre>
<p>Converting TSV back to CSV, with C-style un-escaping:</p>
<pre><code>tsv2csv -e < file.tsv > file.csv
</code></pre>
<h2>Setting up Tabular Editing in Vim</h2>
<p>Open the file:</p>
<pre><code>:e file.tsv
</code></pre>
<p>Excel numbers the rows, why can't we?</p>
<pre><code>:set number
</code></pre>
<p>Adjust your tab settings so you're editing with hard tabs:</p>
<pre><code>:setlocal noexpandtab
</code></pre>
<p>Now, widen the columns enough so they're aligned:</p>
<pre><code>:setlocal shiftwidth=20
:setlocal softtabstop=20
:setlocal tabstop=20
</code></pre>
<p>Fiddle with that number 20 as needed. As far as I can tell, Vim doesn't support variable tab stops. It would be real nifty if I was wrong about this. It would be even niftier if column width detection / tabstop setting could be automated.</p>
<h2>Tall Spreadsheets: Always-Visible Column Names Above</h2>
<p>Typically, the first line of the tsv file is a header containing the column names. We want those column names to always be visible, no matter how far down in the file we scroll. The way we'll do this is by splitting the current window in two. The top window will only be 1 line high and will show the headers. The bottom window will be for data editing.</p>
<pre><code>:sp
:0
1 CTRL-W _
CTRL-W j
</code></pre>
<p>At this point you should have two windows, one above the other showing the first row of column headers. If you don't have very many columns, then you're done.</p>
<h2>Wide Spreadsheets: Horizontal Scrolling</h2>
<p>If you do have lots of columns, or very wide columns, you're probably noticing how confusing it looks when lines wrap. Your columns don't line up so well anymore. So turn off wrapping for both windows:</p>
<pre><code>:set nowrap
CTRL-W k
:set nowrap
CTRL-W j
</code></pre>
<p>One problem remains: when you scroll right to edit columns in the data pane, the header pane doesn't scroll to the right with it. Once again, your columns aren't aligned.</p>
<p>Fortunately Vim has a solution: you can "bind" horizontal scrolling of the two windows. This forces them to scroll left and right in tandem.</p>
<pre><code>:set scrollopt=hor
:set scrollbind
CTRL-W k
:set scrollbind
CTRL-W j
</code></pre>
<p>Wide spreadsheets also make it harder to eyeball other cells in the current row. You can enable a row highlight with:</p>
<pre><code>:set cursorline
</code></pre>
<h2>But What About Formulas and Calculations?!</h2>
<p>It's true, Excel does way more than just edit tabular data. Vim is "just" an editor.</p>
<p>If you're up for some programming, this approach might work for you:</p>
<ol>
<li>Start with your data tsv.</li>
<li>Mirror it with a second "formula tsv" that contains interpreted cells.</li>
<li>Write a program that will apply (2) to (1), "rendering" a tsv with calculated data.</li>
<li>View (3) in a read-only buffer. Separately edit the data and formula tsvs.</li>
</ol>
<p>If you're not up for that, I <a href="https://twitter.com/hillelogram/status/1455949281165250561">hear good things</a> about <a href="https://www.visidata.org">VisiData</a>.</p>https://alangrow.com/blog/printf-length-delimited-stringHow to printf a length-delimited string2012-11-15T00:00:00Alanalangrow+blog@gmail.com<p>Sometimes you're dealing with a string that isn't null-delimited but rather length-delimited, and you wind up doing somersaults just to print it out:</p><div class="highlight"><pre><span></span><span class="kt">void</span> <span class="nf">logit</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">string</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">length</span><span class="p">)</span> <span class="p">{</span>
<span class="kt">char</span> <span class="n">buf</span><span class="p">[</span><span class="mi">255</span><span class="p">];</span>
<span class="n">strncpy</span><span class="p">(</span><span class="n">buf</span><span class="p">,</span> <span class="n">string</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">buf</span><span class="p">));</span>
<span class="n">buf</span><span class="p">[</span><span class="k">sizeof</span><span class="p">(</span><span class="n">buf</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="sc">'\0'</span><span class="p">;</span>
<span class="n">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="s">"debug: %s</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">buf</span><span class="p">);</span>
<span class="p">}</span>
</pre></div>
<p>The extra copying isn't necessary, and you don't have to live with the potential length-truncation either. Did you know <code>printf(3)</code> can format length-delimited strings directly? Buried in the man page is this little gem:</p>
<pre><code>The precision
An optional precision, in the form of a period ('.') followed by an optional decimal digit string. Instead of a decimal digit string one may write "*" or "*m$" (for some decimal integer m) to specify that the precision is given in the next argument, or in the m-th argument, respectively, which must be of type int. This gives ... the maximum number of characters to be printed from a string for s and S conversions.
</code></pre>
<p>With that in mind, we can just write:</p><div class="highlight"><pre><span></span><span class="kt">void</span> <span class="nf">logit</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">string</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">length</span><span class="p">)</span> <span class="p">{</span>
<span class="n">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="s">"debug: %.*s</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="p">(</span><span class="kt">int</span><span class="p">)</span><span class="n">length</span><span class="p">,</span> <span class="n">string</span><span class="p">);</span>
<span class="p">}</span>
</pre></div>
https://alangrow.com/blog/recovering-a-dying-ipod-diskRecovering a Dying iPod Disk2012-04-03T00:00:00Alanalangrow+blog@gmail.com<p>An <a href="http://en.wikipedia.org/wiki/IPod_Classic#Sixth_generation">80GB iPod Classic</a> filled with 4 years of music started to die on us. The symptom: the menu screen suddenly showed "No Music," but disk usage was still nearly 100%. I figured this meant the internal 1.8" hard disk had started to go south and had taken some critical sectors with it.</p>
<p>That turned out to be the case. But here's how we recovered nearly 10,000 files from the iPod anyway...</p>
<h3>The Winning Ticket</h3>
<p>Before things got any worse, I decided to grab an image of the entire disk:</p><div class="highlight"><pre><span></span>sudo dd <span class="k">if</span><span class="o">=</span>/dev/sdc <span class="nv">bs</span><span class="o">=</span>1M <span class="nv">conv</span><span class="o">=</span>noerror,sync <span class="p">|</span> pv > ipod.img
</pre></div>
<p>The "conv=noerror" directive tells dd to keep on going if there are disk read errors instead of erroring out. (There were about a dozen. Sectors had probably been going bad for some time, and finally a critical one bit the dust.)</p>
<p>The "conv=sync" directive tells dd to write out an appropriately sized block of zeroes whenever there's an error reading a block. This is necessary, or file offsets will be wrong from the point of the error onward.</p>
<p>The pv command just shows some nice info about how much data is flowing through and how long it's taken. It's not essential here.</p>
<p>As described <a href="#deadends_and_other_things_we_tried">below</a>, I tried to fsck.vfat the first partition of the disk image, but this reported that an unusually high number of free cluster chains would be reclaimed. This indicated that FAT32 metadata had been damaged and that walking the complete filesystem directory structure wouldn't be possible anymore.</p>
<p>The new approach was to say, to hell with directory structure, let's just linearly scan the disk image for files and extract them. This needles-in-the-haystack approach isn't for everybody: you will lose filenames, permissions, directory locality etc. But most mp3s have self-identifying id3 tag metadata so we didn't care too much.</p>
<p>There are a couple programs that can find file needles in a disk image haystack. The one that worked was <a href="http://www.cgsecurity.org/wiki/PhotoRec">PhotoRec</a>, which can actually find much more than just photo files. For an opensource unix program it has a rather strange set of options and user interface. Anyway, I ran it with:</p><div class="highlight"><pre><span></span>photorec /log /debug /d rescue ipod.img
</pre></div>
<p>All in all photorec recovered over 8,000 mp3s and some other files to boot.</p>
<pre><code>Pass 1 - Reading sector 135045680/155907592, 9944 files found
Elapsed time 1h14m22s - Estimated time for achievement 0h11m29
mp3: 8339 recovered
mov: 1264 recovered
txt: 129 recovered
apple: 96 recovered
tx?: 63 recovered
jpg: 21 recovered
aif: 13 recovered
riff: 12 recovered
mpg: 3 recovered
gpg: 1 recovered
others: 3 recovered
</code></pre>
<p>Afterwards, the files were scattered randomly in flat directories named rescue.1, rescue.2, rescue.3 etc:</p><div class="highlight"><pre><span></span>ls rescue.1 <span class="p">|</span> grep mp3 <span class="p">|</span> head
</pre></div>
<pre><code>f0234384.mp3
f0241008.mp3
f0247536.mp3
f0254352.mp3
f0257680.mp3
f0263664.mp3
f0271120.mp3
f0277872.mp3
f0284784.mp3
f0292176.mp3
</code></pre>
<p>If desired, they can be renamed into Artist + Album + Track + Title directories via a program like <a href="http://search.cpan.org/~acg/supertag-0.2.1/supertag">supertag</a> (disclaimer: I'm the author). But I'm not sure iTunes even cares about filenames.</p>
<p>Addendum: as time has gone on, we've noticed that a fair percentage of the songs were truncated by photorec, something like 1 in 5. One of these rainy weekends I'm going to see if I can patch photorec's mp3 recognition.</p>
<h3>Dead-Ends and Other Things We Tried</h3>
<p>The filesystem was W95 FAT32 but couldn't be mounted due to the bad sectors. Doing an fsck on the block device was also not possible because of read errors. The errors manifested themselves like this in dmesg:</p>
<pre><code>[64658.941382] sd 6:0:0:0: [sdc] Unhandled sense code
[64658.941395] sd 6:0:0:0: [sdc] Result: hostbyte=DID_OK driverbyte=DRIVER_SENSE
[64658.941407] sd 6:0:0:0: [sdc] Sense Key : Medium Error [current]
[64658.941422] Info fld=0x0
[64658.941428] sd 6:0:0:0: [sdc] Add. Sense: Unrecovered read error
[64658.941442] sd 6:0:0:0: [sdc] CDB: Read(10): 28 00 00 00 00 40 00 00 01 00
[64658.941470] end_request: I/O error, dev sdc, sector 512
[64658.941484] Buffer I/O error on device sdc, logical block 64
</code></pre>
<p>After capturing the disk image, it was possible to run fsck.vfat directly on the partition file; it doesn't actually require a block device, which is cool.</p>
<p>To run fsck on the disk image file, we needed to extract the lone FAT32 partition into a file by itself. The trick here was figuring out where the partition started. Doing an fdisk on the actual block device for the iPod (/dev/sdc) to figure out the disk geometry helped. Using that geometry, this command let us figure out the sector offset of the first partition:</p><div class="highlight"><pre><span></span>fdisk -u -C <span class="m">14991</span> -b <span class="m">4096</span> -l ipod.img
</pre></div>
<pre><code>Device Boot Start End Blocks Id System
ipod.img1 63 19488469 77953628 b W95 FAT32
</code></pre>
<p>A trick to extract the partition image:</p><div class="highlight"><pre><span></span><span class="o">{</span> dd <span class="nv">bs</span><span class="o">=</span><span class="m">4096</span> <span class="nv">skip</span><span class="o">=</span><span class="m">63</span> <span class="nv">count</span><span class="o">=</span><span class="m">0</span> <span class="p">;</span> pv <span class="p">;</span> <span class="o">}</span> < ipod.img > ipod.img.part1
</pre></div>
<p>This took a while. Disks are slow.</p>
<p>Then I ran fsck.vfat on the partition image:</p><div class="highlight"><pre><span></span>fsck.vfat -v -n ipod.img.part1
</pre></div>
<pre><code>...
Checking for unused clusters.
Reclaimed 3561014 unused clusters (58343653376 bytes).
...
</code></pre>
<p>As you can see, it thought most of the disk consisted of free clusters -- this is bad. If I had tried to repair the disk via fsck, only a small fraction of the files would have been recovered.</p>
<p>You can see which file paths were traversed with the -l switch:</p><div class="highlight"><pre><span></span>fsck.vfat -v -n -l ipod.img.part1
</pre></div>
<p>In our case this helped me verify that only a small number of files were actually going to be recovered by the fsck.</p>
<p>Once I gave up on fsck and embarked on needle-in-haystack file extraction, I tried <a href="http://www.itu.dk/~jobr/magicrescue/">magicrescue</a>. It found mp3s but kept saying "invalid mp3 file" and extracted almost none of them. It was also really slow -- it shells out to perl scripts and mpg123 to test mp3 validity. Yuck.</p>https://alangrow.com/blog/how-many-consonant-pairsHow Many Consonant Pairs Do We Actually Use?2012-02-26T00:00:00Alanalangrow+blog@gmail.com<p>Of all possible pairs of consonants you could start a word with, how many are actually valid in the English language?</p>
<p>The question came up at a party during a disappointing Ouija board session where the spirits conjured gibberish like "QHPEV." Someone wondered aloud how difficult it was to pick a valid pairs of consonants at random. Instinctively, we felt that most of them were invalid.</p>
<p>This is a nice little problem for the unix text processing toolset. I used the <a href="http://www.isc.ro/lists/twl06.zip">2006 Scrabble Tournament Word List</a> because /usr/share/dict/words contains many proper names and non-words. To get the count:</p><div class="highlight"><pre><span></span>tr <span class="s1">'[A-Z]'</span> <span class="s1">'[a-z]'</span> < TWL06.txt <span class="p">|</span>
sed -nEe <span class="s1">'s/^([a-z]{2}).*$/\1/p'</span> <span class="p">|</span>
grep -v <span class="s1">'[aeiouy]'</span> <span class="p">|</span>
sort -u <span class="p">|</span>
wc -l
<span class="m">82</span>
</pre></div>
<p>There are 20 consonants in the language after removing "aeiouy", so that makes 400 possible pairs of consonants.</p>
<p>So only 20.5% of all consonant pairs are valid beginnings for an English word.</p>
<p>To see the 82 valid pairs:</p><div class="highlight"><pre><span></span>tr <span class="s1">'[A-Z]'</span> <span class="s1">'[a-z]'</span> < TWL06.txt <span class="p">|</span>
sed -nEe <span class="s1">'s/^([a-z]{2}).*$/\1/p'</span> <span class="p">|</span>
grep -v <span class="s1">'[aeiouy]'</span> <span class="p">|</span>
sort -u <span class="p">|</span>
tr <span class="s1">'\n'</span> <span class="s1">' '</span>
</pre></div>
<pre><code>bd bh bl br bw ch cl cn cr ct cw cz
dh dj dr dw fj fl fr gh gj gl gn gr
gw hm hr hw jn kb kh kl kn kr kv kw
ll lw mb mh mm mn mr ng nt pf ph pl
pn pr ps pt qw rh sc sf sg sh sj sk
sl sm sn sp sq sr st sv sw tc th tm
tr ts tw tz vr wh wr zl zw zz
</code></pre>
<p>To see an example word for each valid pair (remember, this is the Scrabble dictionary, so there's some pretty weird stuff in there):</p><div class="highlight"><pre><span></span>tr <span class="s1">'[A-Z]'</span> <span class="s1">'[a-z]'</span> < TWL06.txt <span class="p">|</span>
tr -d <span class="s1">'\r'</span> <span class="p">|</span>
sed -nEe <span class="s1">'s/^([a-z]{2})(.*)$/\1\2 \1/p'</span> <span class="p">|</span>
grep <span class="s1">' [^aeiouy][^aeiouy]'</span> <span class="p">|</span>
sort <span class="p">|</span>
uniq -f1 <span class="p">|</span>
awk <span class="s1">'{ print $2, $1 }'</span>
</pre></div>
<pre><code>bd bdellium
bh bhakta
bl blabbed
br brabble
bw bwana
ch chabazite
cl clabber
cn cnida
cr craal
ct ctenidia
cw cwm
cz czar
dh dhak
dj djebel
dr drabbed
dw dwarf
fj fjeld
fl flabbergasted
fr frabjous
gh gharial
gj gjetost
gl glabellae
gn gnar
gr graal
gw gweduc
hm hm
hr hryvna
hw hwan
jn jnana
kb kbar
kh khaddar
kl klatches
kn knacked
kr kraaled
kv kvases
kw kwacha
ll llama
lw lwei
mb mbaqanga
mh mho
mm mm
mn mnemonically
mr mridangam
ng ngultrum
nt nth
pf pfennige
ph phaeton
pl placabilities
pn pneuma
pr praam
ps psalmbook
pt ptarmigan
qw qwerty
rh rhabdocoele
sc scabbarded
sf sferics
sg sgraffiti
sh shabbatot
sj sjamboked
sk skag
sl slabbed
sm smacked
sn snacked
sp spaceband
sq squabbier
sr sraddha
st stabbed
sv svarajes
sw swabbed
tc tchotchkes
th thacked
tm tmeses
tr trabeated
ts tsaddikim
tw twaddled
tz tzaddikim
vr vroomed
wh whacked
wr wracked
zl zlote
zw zwiebacks
zz zzz
</code></pre>
<p>Aside: finding good and freely available (ie opensource or creative commons) word lists is surprisingly annoying.</p>https://alangrow.com/blog/mutt-tip-attach-multiple-filesMutt Tip: Attach Multiple Files2011-11-25T00:00:00Alanalangrow+blog@gmail.com<p>You can attach multiple files in <a href="http://www.mutt.org/">mutt</a>'s file browser, if they're in the same directory: just use 't' to tag them, then ';'-Enter. (Quickly, one after the other.) You can also view files from the file browser before attaching them, just hit Space. Ten years of mutt and I'm still discovering this stuff...</p>https://alangrow.com/blog/python-split-is-inconsistentInconsistent split Behavior in Python2011-11-05T00:00:00Alanalangrow+blog@gmail.com<p>Here's a futile but cathartic <a href="http://bugs.python.org/issue13346">bug report</a> I filed against Python recently.</p>
<p>In Python, string.split and re.split both take an optional argument that limits the number of splits that are done. This is unlike Perl's split builtin, which limits the number of <em>pieces</em>. But it makes sense I guess, and consistency between the two languages is not something I'd necessarily expect.</p>
<p>However, consistency <em>within</em> a language...a reasonable expectation, no?</p>
<p>The inconsistency lies in how the string.split and re.split handle the edge cases of "do an unlimited number of splits" and "don't do any splits." The two agree that "unlimited splits" is the default. They don't agree on how to interpret the value of an explicit maxsplit parameter.</p>
<table class="matrix">
<thead>
<td class="col-header row-header"></td>
<td class="col-header">maxsplit=0</td>
<td class="col-header">maxsplit=-1</td>
</thead>
<tr>
<td class="row-header">string.split</td>
<td>no splits</td>
<td>unlimited splits</td>
</tr>
<tr>
<td class="row-header">re.split</td>
<td>unlimited splits</td>
<td>no splits</td>
</tr>
</table>
<p>I think string.split is doing the sensible thing here.</p>
<p>Of course, the "bug" has zero chance of being fixed at this point. I pretty much just filed it to create a search result for others similarly bitten, annoyed, or both.</p>https://alangrow.com/blog/postgresql-tip-bulk-copying-data-between-tablesPostgreSQL Tip: Bulk Copying Data Between Tables2011-06-17T00:00:00Alanalangrow+blog@gmail.com<p>Suppose you have two different PostgreSQL databases, db1 and db2. You want to populate db2.table2 with data from db1.table1. How?</p>
<p>Try this:</p><div class="highlight"><pre><span></span>psql -c <span class="s1">'COPY table1 TO STDOUT'</span> db1 <span class="p">|</span> <span class="se">\</span>
psql -c <span class="s1">'COPY table2 FROM STDIN'</span> db2
</pre></div>
<p>Is there a more efficient way to do this if the two databases are hosted by the same server instance? Probably.</p>
<p>Then again, if the databases are on different servers, this works:</p><div class="highlight"><pre><span></span>psql -c <span class="s1">'COPY table1 TO STDOUT'</span> db1 <span class="p">|</span> gzip -c <span class="p">|</span> <span class="se">\</span>
ssh host2 <span class="s2">"gunzip -c | psql -c 'COPY table2 FROM STDIN' db2"</span>
</pre></div>
<p>Bonus: with <a href="http://www.ivarch.com/programs/pv.shtml">pv(1)</a>, you can see how quickly the data is flowing:</p><div class="highlight"><pre><span></span>psql -c <span class="s1">'COPY table1 TO STDOUT'</span> db1 <span class="p">|</span> pv <span class="p">|</span> <span class="se">\</span>
psql -c <span class="s1">'COPY table2 FROM STDIN'</span> db2
</pre></div>
https://alangrow.com/blog/measuring-the-measurersMeasuring the Measurers2011-06-10T00:00:00Alanalangrow+blog@gmail.com<p>"Projects A and B are your top priority now. Oh, and Project C can't be impacted."</p>
<p>Sound familiar?</p>
<p>It's a common complaint of the project-managed: everything can't be top priority. Something has to give. Resources allocated to Project A must be deallocated from elsewhere, either Project C, or some other project. Declaring everything "top priority" is not helpful.</p>
<p>If project management accomplishes one thing, it should help each of us answer the question, "What should I work on next?"</p>
<p>A friend of mine relates a story about a meeting between tech and client services. The tech team came prepared with a list of development tasks in loose priority order. As the meeting progressed, the client services team found more and more reasons to disagree with the priorities.</p>
<p>Eventually, in frustration, the tech lead said, "Here's the list. You order it."</p>
<p>The client services lead was taken aback and refused: "It all has to be done. As soon as possible."</p>
<p>This is not helpful.</p>
<p>While I do think there are better ways of scheduling work than imposing a single ordering -- which breaks down when multiple workers are able to proceed in parallel -- I also think the ability to see and maintain consistent priorities is an important thing to look for in a project manager. Or any manager, really.</p>
<p>Which is why I propose the following fun experiment. Present a manager with two randomly sampled work items from their team, side by side, and ask which is higher priority. Repeat until you've got a decent number of comparisons. Remember xkcd's <a href="http://thefunniest.info/">project to find the funniest image in the world</a>? Yeah. It's kinda like that.</p>
<p>Now that we've turned a human being into a comparison operator ;) we can ask how good that operator is. Does it define an ordering? For any reasonable sample size, probably not.</p>
<p>Forget about <a href="http://en.wikipedia.org/wiki/Sorting_algorithm#Stability">stable sort</a>. Viewed as a <a href="http://en.wikipedia.org/wiki/Directed_graph">directed graph</a>, there will probably be cycles, like A > B > C > A. In general, you can induce an acyclic digraph from a cyclic digraph by identifying the <a href="http://en.wikipedia.org/wiki/Strongly_connected_component">strongly connected components</a>. So one metric would be to compare the size of the induced acyclic graph to the original graph (<code>1/|V|</code> is the worst, <code>|V|/|V|=1.0</code> is the best). Another metric would be the height of the induced acyclic graph over the number of nodes (work items). A perfect comparison operator would produce a line of nodes in a well-defined order, and would score 1.0.</p>
<p>Another thing to measure would be the consistency of the ordering over time. Yes, priorities change, but resource re-allocation also has a cost.</p>
<p>Measuring the measurers seems like a good thing for a number of reasons. Among them, that it exposes the often subtle problems of <em>conflicting directives</em> and the even subtler problems of <em>competing directives</em>. Too often, only the people carrying out the directives are aware of them.</p>https://alangrow.com/blog/put-everything-in-vi-modePut *Everything* in vi Mode2011-05-17T00:00:00Alanalangrow+blog@gmail.com<p>If you're a vi user like me, try adding these two lines to your <code>~/.inputrc</code> file:</p>
<pre><code>set keymap vi
set editing-mode vi
</code></pre>
<p>Now, every program that uses the readline library for tty input (<code>perl -d</code>, the <code>python</code> REPL, <code>psql</code>, <code>gdb</code>, anything you run under <code>rlwrap</code>, etc.) has vi key bindings instead of the default emacs bindings.</p>
<p>In short, this means things like:</p>
<ul>
<li><code>0</code> and <code>$</code> for beginning and end of line</li>
<li><code>k</code> and <code>j</code> for navigating history forwards and backwards</li>
<li><code>b</code> and <code>e</code> for skipping words</li>
<li><code>u</code> for undo</li>
</ul>
<p>See this <a href="https://readline.kablamo.org/vi.html">readline vi mode cheatsheet</a> for a longer list.</p>
<p>I've been using this for years with bash, where one can do <code>set -o vi</code>. Apparently vi mode has been present since GNU readline 2.0, released in 1994, so I really have no excuse for this one!</p>https://alangrow.com/blog/how-i-lost-100-and-blamed-calHow I Lost $100 and Blamed It On cal(1)2011-03-22T00:00:00Alanalangrow+blog@gmail.com<style type="text/css">
@media screen and (max-width: 899px) {
pre {
font-size: 2vw;
white-space: nowrap;
}
}
</style>
<p>True story. Back in September 2008, I decided that this year, I would <strong>not</strong> wait until the last minute to book my Thanksgiving flight home.</p>
<p>What's the rule for Thanksgiving again? Oh right, fourth Thursday in November. So I busted out <a href="http://www.freebsd.org/cgi/man.cgi?query=cal&apropos=0&sektion=0&manpath=FreeBSD+8.2-RELEASE&format=html">cal(1)</a>:</p>
<pre><code>$ cal
September 2008
Su Mo Tu We Th Fr Sa
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
</code></pre>
<p>Whoops, it only shows the current month. So I passed it the year:</p>
<pre><code>$ cal 08
8
January February March
Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa
1 2 3 4 5 6 7 1 2 3 4 1 2 3
8 9 10 11 12 13 14 5 6 7 8 9 10 11 4 5 6 7 8 9 10
15 16 17 18 19 20 21 12 13 14 15 16 17 18 11 12 13 14 15 16 17
22 23 24 25 26 27 28 19 20 21 22 23 24 25 18 19 20 21 22 23 24
29 30 31 26 27 28 29 25 26 27 28 29 30 31
April May June
Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa
1 2 3 4 5 6 7 1 2 3 4 5 1 2
8 9 10 11 12 13 14 6 7 8 9 10 11 12 3 4 5 6 7 8 9
15 16 17 18 19 20 21 13 14 15 16 17 18 19 10 11 12 13 14 15 16
22 23 24 25 26 27 28 20 21 22 23 24 25 26 17 18 19 20 21 22 23
29 30 27 28 29 30 31 24 25 26 27 28 29 30
July August September
Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa
1 2 3 4 5 6 7 1 2 3 4 1
8 9 10 11 12 13 14 5 6 7 8 9 10 11 2 3 4 5 6 7 8
15 16 17 18 19 20 21 12 13 14 15 16 17 18 9 10 11 12 13 14 15
22 23 24 25 26 27 28 19 20 21 22 23 24 25 16 17 18 19 20 21 22
29 30 31 26 27 28 29 30 31 23 24 25 26 27 28 29
30
October November December
Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa
1 2 3 4 5 6 1 2 3 1
7 8 9 10 11 12 13 4 5 6 7 8 9 10 2 3 4 5 6 7 8
14 15 16 17 18 19 20 11 12 13 14 15 16 17 9 10 11 12 13 14 15
21 22 23 24 25 26 27 18 19 20 21 22 23 24 16 17 18 19 20 21 22
28 29 30 31 25 26 27 28 29 30 23 24 25 26 27 28 29
30 31
</code></pre>
<p>I booked my flight for Tuesday, November 20th, and forgot about it.</p>
<p>The day approached. I called home just to make sure someone could pick me up from the airport. That's when I discovered that Thanksgiving was actually the following week. <strong>I had booked my flight based on the calendar for the year 8 A.D.</strong></p>
<p>What I should have done was this:</p>
<pre><code>$ cal 2008
2008
January February March
Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa
1 2 3 4 5 1 2 1
6 7 8 9 10 11 12 3 4 5 6 7 8 9 2 3 4 5 6 7 8
13 14 15 16 17 18 19 10 11 12 13 14 15 16 9 10 11 12 13 14 15
20 21 22 23 24 25 26 17 18 19 20 21 22 23 16 17 18 19 20 21 22
27 28 29 30 31 24 25 26 27 28 29 23 24 25 26 27 28 29
30 31
April May June
Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa
1 2 3 4 5 1 2 3 1 2 3 4 5 6 7
6 7 8 9 10 11 12 4 5 6 7 8 9 10 8 9 10 11 12 13 14
13 14 15 16 17 18 19 11 12 13 14 15 16 17 15 16 17 18 19 20 21
20 21 22 23 24 25 26 18 19 20 21 22 23 24 22 23 24 25 26 27 28
27 28 29 30 25 26 27 28 29 30 31 29 30
July August September
Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa
1 2 3 4 5 1 2 1 2 3 4 5 6
6 7 8 9 10 11 12 3 4 5 6 7 8 9 7 8 9 10 11 12 13
13 14 15 16 17 18 19 10 11 12 13 14 15 16 14 15 16 17 18 19 20
20 21 22 23 24 25 26 17 18 19 20 21 22 23 21 22 23 24 25 26 27
27 28 29 30 31 24 25 26 27 28 29 30 28 29 30
31
October November December
Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa
1 2 3 4 1 1 2 3 4 5 6
5 6 7 8 9 10 11 2 3 4 5 6 7 8 7 8 9 10 11 12 13
12 13 14 15 16 17 18 9 10 11 12 13 14 15 14 15 16 17 18 19 20
19 20 21 22 23 24 25 16 17 18 19 20 21 22 21 22 23 24 25 26 27
26 27 28 29 30 31 23 24 25 26 27 28 29 28 29 30 31
30
</code></pre>
<p>When all was said and done -- with the change fee and the fare difference -- the mistake cost me $100. But it "inspired" me to actually learn a thing or two about cal(1).</p>
<p><strong>TL;DR</strong>: RTFM, or you will pay.</p>
<pre><code>CAL(1)
...
A single parameter specifies the year (1 - 5875706) to be displayed; note the year must be fully specified: “cal 89” will not display a calendar
for 1989.
</code></pre>https://alangrow.com/blog/teasing-out-a-new-repositoryTeasing Out a New Git Repository2011-03-02T00:00:00Alanalangrow+blog@gmail.com<p><em>The Ideal Git Law states that the documentation surrounding git(1) will expand to fill all available volume.</em></p>
<p>I'm building a suite of record processing tools. Up to now, the development has taken place inside the <a href="https://github.com/acg/lwpb">lwpb</a> git repository. But it doesn't really belong there, since other record formats besides protobuf are supported: the classic unix tab-separated text format, and soon json.</p>
<p>So how does one extract <em>part</em> of a git repository into a new repository, preserving history where possible?</p>
<p>All of the files I want to extract from the main repository live under the same subdirectory, which should become the top-level directory of the new repository. So a good place to start is this <a href="http://stackoverflow.com/questions/359424/detach-subdirectory-into-separate-git-repository">stack overflow thread</a> which explains <code>git filter-branch --subdirectory-filter subdir</code>. It goes something like this:</p><div class="highlight"><pre><span></span>mkdir newrepo
<span class="nb">cd</span> newrepo
git clone --no-hardlinks /oldrepo ./
git filter-branch --subdirectory-filter subdir HEAD
git reset --hard
git gc --aggressive
git prune
</pre></div>
<p>As a comment on the stackoverflow thread mentions, it's also a good idea to remove the old repo as a remote of the new repo, so you don't accidentally push changes back to it:</p><div class="highlight"><pre><span></span>git remote rm origin
</pre></div>
<p>So far so good. But I only want <em>some</em> of the files under this subdirectory in the new repo. The rest shouldn't be there. Can I rewrite the commit history again, this time file-wise?</p>
<p>Yes. For this I used <code>git filter-branch --tree-filter command</code>. This works by checking out each commit, running <code>$SHELL -c "$command"</code>, looking at what changes were made to the checkout, and then formulating a new commit. If the command removes a file in the checkout, it will be removed from the commit. If a command creates a file, it will be added to the commit.</p>
<p>In my case, I only want to remove certain files, so the filter command is a shell script that looks like this:</p><div class="highlight"><pre><span></span><span class="ch">#!/bin/sh</span>
find . -type f -not -path <span class="s2">"*/.git/*"</span> <span class="p">|</span>
sed -e <span class="s1">'s#^./##'</span> <span class="p">|</span>
grep -v -E <span class="s1">'^(pb.*\.py|flat\.py|percent.*)$'</span> <span class="p">|</span>
tr <span class="s1">'\n'</span> <span class="s1">'\0'</span> <span class="p">|</span>
xargs -0 rm -v
</pre></div>
<p>The <code>rm -v</code> lets me see all the deletions this script makes for each commit. I saved this as my-git-filter and ran</p><div class="highlight"><pre><span></span>git filter-branch -f --prune-empty --tree-filter my-git-filter HEAD
</pre></div>
<p>The <code>-f</code> option forces the operation even if there's already a backup of the original repo from a previous <code>git filter-branch</code> run.</p>
<p>Follow this up with the same cleanup procedure from the <code>--subdirectory-filter</code> example:</p><div class="highlight"><pre><span></span>git reset --hard
git gc --aggressive
git prune
</pre></div>
https://alangrow.com/blog/profiling-every-command-in-a-makefileProfiling every command in a Makefile2011-02-25T00:00:00Alanalangrow+blog@gmail.com<p>Here's the scenario. I've got a batch data processing pipeline implemented as a Makefile. (Hey! It's only a prototype! Trust me, I'm a make hater just like you!) There's already a lot of data, so an end-to-end full run can take about a day, with some of the individual stages taking hours.</p>
<p>Now I'm thinking, wouldn't it be nice to know how long each rule took? Even better, wouldn't it be nice to get a report of how much cpu it consumed, how much memory it used, how much I/O it performed, etc.? Armed with this information, I could start optimizing poorly performing stages.</p>
<p>So, let's suppose we cook up some wrapper program that runs a subordinate program, collects <a href="http://www.freebsd.org/cgi/man.cgi?query=getrusage&apropos=0&sektion=2&format=html">rusage</a> when it exits, and prints out the interesting info. Fortunately, such a wrapper program basically already exists.</p>
<p>I'd rather not go rewrite every rule in the Makefile, prefixing it with this wrapper program. That wouldn't even work if the rule was a pipeline: since <code>make(1)</code> executes rules by wrapping them with <code>$(SHELL) -c</code>, only the first command in the pipeline would actually run under the wrapper.</p>
<p>The solution is to <a href="http://www.gnu.org/software/make/manual/make.html#Choosing-the-Shell">set the shell</a> in your Makefile to:</p><div class="highlight"><pre><span></span><span class="nv">SHELL</span> <span class="o">=</span> rusage sh
</pre></div>
<p>Where <code>rusage</code> is a wrapper shell script that looks like this:</p><div class="highlight"><pre><span></span><span class="ch">#!/bin/sh</span>
<span class="nb">exec</span> <span class="nb">time</span> -f <span class="s1">'rc=%x elapsed=%e user=%U system=%S maxrss=%M avgrss=%t ins=%I outs=%O minflt=%R majflt=%F swaps=%W avgmem=%K avgdata=%D argv="%C"'</span> <span class="s2">"</span><span class="nv">$@</span><span class="s2">"</span>
</pre></div>
<p>Note that this uses <code>/usr/bin/time</code>, <strong>not to be confused</strong> with the bash builtin <code>time</code>, which is what you're using probably 90% of the time at the command line.</p>
<p>Note also, this unfortunately only works with GNU <code>time(1)</code>. The BSD (and probably Darwin, haven't actually checked) versions of <code>time(1)</code> don't support the <code>-f</code> argument to specify a format string. But on BSD derivatives, you should be able to at least get a human readable dump of the rusage structure by using <code>/usr/bin/time -l</code>. Which looks equivalent to the <code>/usr/bin/time -v</code> output from GNU time. (It's just not as convenient if you plan to analyze the logs later.)</p>https://alangrow.com/blog/bouncing-hopping-tunneling-with-tcpforwardBouncing, Hopping and Tunneling with tcpforward2011-02-07T00:00:00Alanalangrow+blog@gmail.com<p>This weekend I dusted off a little network utility of mine called <a href="https://github.com/acg/tcpforward">tcpforward</a>. It proved its worth once again, so instead of throwing it back into the rusty toolbox like I always do, here's why you might want to throw it into your very own rusty toolbox. ;)</p>
<ul class="toc">
<li><a href="#bouncing">Scenario: Remote Assistance, AKA "Bouncing Your Signal Off The Moon"</a></li>
<li><a href="#hopping">Scenario: Hopping Over the Middleman</a></li>
<li><a href="#tunneling">Scenario: Tunneling Through Corporate Firewalls </a></li>
<li><a href="#how-it-works">How it Works</a></li>
</ul>
<p><span id="bouncing"></span> </p>
<h3>Scenario: Remote Assistance, AKA "Bouncing Your Signal Off The Moon"</h3>
<p>Suppose you need to SSH to a friend's machine, but you're both behind NATs.</p>
<p>If your friend is savvy enough to compile it, and you've got time for that, you could use <a href="http://samy.pl/pwnat/">pwnat</a>. You could also have your friend configure port forwarding on his router -- again, only if your friend is savvy enough, and doesn't mind punching a hole in his firewall. Yet another option: give your friend an SSH account on a public machine, and go look up the SSH arguments for reverse port forwarding for the bazillionth time.</p>
<p>The lowest-hassle option I can think of is to use tcpforward. Suppose you and your friend can both reach a 3rd machine, a public server you own called <em>moon</em>.</p>
<p>Run the following on <em>moon</em>:</p><div class="highlight"><pre><span></span>tcpforward -v -N <span class="m">1</span> -l moon:9922 -l moon:9921
</pre></div>
<p>Arrange for your friend to run the following on his local machine:</p><div class="highlight"><pre><span></span>./tcpforward -v -N <span class="m">1</span> -c moon:9922 -c localhost:22
</pre></div>
<p>Now, on your machine, run:</p><div class="highlight"><pre><span></span>ssh -p <span class="m">9921</span> moon
</pre></div>
<p>And voila, your SSH connection is forwarded past your friend's NAT, to his machine. The <code>-N 1</code> option makes this a one-shot connection. The <code>-v</code> option gives him something to watch while you go to work -- some realtime transfer statistics.</p>
<p>(This example assumes port 9921 and 9922 are open on <em>moon</em>, and that your friend is running sshd).</p>
<p><span id="hopping"></span> </p>
<h3>Scenario: Hopping Over the Middleman</h3>
<p>Ever wanted to copy files to a machine you could only reach from an intermediate machine? For no particular reason, let's call these machines <em>production</em> and <em>gateway</em>. I bet you usually end up scp'ing or rsync'ing files to <em>gateway</em>, ssh'ing to <em>gateway</em>, then running scp or rsync again, then cleaning up the files, etc.</p>
<p>"There must be a better way!" I hear you scream.</p>
<p>Yes. First, ssh to <em>gateway</em> and run:</p><div class="highlight"><pre><span></span>tcpforward -v -k -l <span class="m">0</span>.0.0.0:9922 -c production:22
</pre></div>
<p>In another tty on your local machine, you can now run:</p><div class="highlight"><pre><span></span>scp -o <span class="nv">Port</span><span class="o">=</span><span class="m">9922</span> somefile gateway:somefile
</pre></div>
<p>Or, rsync:</p><div class="highlight"><pre><span></span>rsync -e <span class="s2">"ssh -p 9922"</span> -avzp somedir/ gateway:somedir/
</pre></div>
<p>Remember to kill the tcpforward session on <em>gateway</em>, or your sysadmin may get angry, annoyed, frightened, or all of the above.</p>
<p>(Once again, assumes port 9922 is open on <em>gateway</em>.)</p>
<p><span id="tunneling"></span> </p>
<h3>Scenario: Tunneling Through Corporate Firewalls</h3>
<p>Let's continue with the slightly subversive examples. Suppose you're behind a corporate firewall that doesn't allow SSH connections out, only web traffic. You've got a public server out there called <em>freedom</em>, and you want to log in once in a while.</p>
<p>You could run <code>hts</code> from <a href="http://www.nocrew.org/software/httptunnel.html">httptunnel</a> on <em>freedom</em>. That's a fair bit of C code to expose to the world though. ;)</p>
<p>Alternately, let's say you're not running anything on <em>freedom:443</em>. Most corporate firewalls will allow https out, and most of them don't do deep packet inspection to verify that the initial handshake actually conforms to the TLS protocol.</p>
<p>Before going off to work, run the following on <em>freedom</em>:</p><div class="highlight"><pre><span></span>tcpforward -v -k -l <span class="m">0</span>.0.0.0:443 -c localhost:22
</pre></div>
<p>From work:</p><div class="highlight"><pre><span></span>ssh -p <span class="m">443</span> freedom <span class="c1"># scream FREEEEEEDOOOOMMM!!! as you're doing this</span>
</pre></div>
<p><span id="how-it-works"></span> </p>
<h3>How it Works</h3>
<p>The time has come to pull back the curtain, revealing the wizened figure of a <a href="https://github.com/acg/tcpforward/blob/master/tcpforward">160 line Perl script</a>.</p>
<p>How does it work?</p>
<p>Well, you always run <code>tcpforward</code> with two arguments that specify a pair of TCP sockets to set up, then copy bytes between. Each socket argument is either a listen / accept socket -- if you specify the <code>-l</code> flag -- or a connect socket, if you specify the <code>-c</code> flag. Once both sockets of a pair are accepted or connected, a little async I/O copy loop runs until both sockets close for reading. If you pass the <code>-k</code> flag, the I/O copy loop runs in a forked process and another socket pair is immediately ready for setup.</p>
<p>There's more documentation in the <a href="https://github.com/acg/tcpforward/blob/master/README.md">POD</a>.</p>
<p>Happy connection hacking!</p>https://alangrow.com/blog/python-default-refsA Python Gotcha: References as Default Parameters2011-02-05T00:00:00Alanalangrow+blog@gmail.com<p>Suppose you're writing a Python function like <a href="https://github.com/acg/lwpb/blob/python/python/flat.py">this one</a> that unpacks data into a dictionary; optionally, an existing dictionary instead of an empty one.</p>
<p><em>Surprise</em>!</p><div class="highlight"><pre><span></span><span class="err">$</span> <span class="n">python</span>
<span class="n">Python</span> <span class="mf">2.6</span><span class="o">.</span><span class="mi">4</span>
<span class="p">[</span><span class="n">GCC</span> <span class="mf">4.4</span><span class="o">.</span><span class="mi">1</span><span class="p">]</span> <span class="n">on</span> <span class="n">linux2</span>
<span class="o">>>></span> <span class="k">def</span> <span class="nf">hashcopy</span><span class="p">(</span><span class="n">src</span><span class="p">,</span> <span class="n">dst</span><span class="o">=</span><span class="p">{}):</span>
<span class="o">...</span> <span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">src</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
<span class="o">...</span> <span class="n">dst</span><span class="p">[</span><span class="n">k</span><span class="p">]</span> <span class="o">=</span> <span class="n">v</span>
<span class="o">...</span> <span class="k">return</span> <span class="n">dst</span>
<span class="o">...</span>
<span class="o">>>></span> <span class="n">hashcopy</span><span class="p">({</span><span class="mi">1</span><span class="p">:</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">:</span><span class="mi">4</span><span class="p">})</span>
<span class="p">{</span><span class="mi">1</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">:</span> <span class="mi">4</span><span class="p">}</span>
<span class="o">>>></span> <span class="n">hashcopy</span><span class="p">({</span><span class="mi">5</span><span class="p">:</span><span class="mi">6</span><span class="p">,</span><span class="mi">7</span><span class="p">:</span><span class="mi">8</span><span class="p">})</span>
<span class="p">{</span><span class="mi">1</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">:</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">:</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">7</span><span class="p">:</span> <span class="mi">8</span><span class="p">}</span>
</pre></div>
<p>I haven't looked deeply into this, but it seems like default parameters must be bound to object instances at compile time.</p>
<p>In Perl 5 you typically only set default parameters at runtime, so the empty hashref you get is always the freshest in the land:</p><div class="highlight"><pre><span></span><span class="k">sub</span> <span class="nf">hashcopy</span>
<span class="p">{</span>
<span class="k">my</span> <span class="nv">$src</span> <span class="o">=</span> <span class="nb">shift</span><span class="p">;</span>
<span class="k">my</span> <span class="nv">$dst</span> <span class="o">=</span> <span class="nb">shift</span> <span class="o">||</span> <span class="p">{};</span>
<span class="nv">%$dst</span> <span class="o">=</span> <span class="p">(</span><span class="nv">%$dst</span><span class="p">,</span> <span class="nv">%$src</span><span class="p">);</span>
<span class="k">return</span> <span class="nv">$dst</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
<p>All other things equal, this is undoubtedly slower, but considerably less wtf-subtle.</p>https://alangrow.com/blog/thinkpad-key-replacementThinkpad T43 Key Removal, Assembly2007-02-18T00:00:00Alanalangrow+blog@gmail.com<p>Within a few days of the <a href="../05/lcd-smashed-so-ratpoison.html">destruction of my T40</a>, I got a T43 from a guy on craigslist. The left control key promptly broke so I swapped it for the right one. There's relatively little info out there about how to assemble and disassemble keys, so here's some info on the process. Before we begin, get out your jeweler's eyepiece...</p>
<p>You can pry off the key face gently as described <a href="http://dqd.com/~mayoff/notes/thinkpad/key/">here</a>, just push away from you and up with a flat object. The face snaps into a cage mechanism consisting of three parts: a top plate and two wickets which anchor it to from the north and south respectively. Each wicket has a bar that wraps over the top plate, and two legs with pegs that secure it to the keyboard bevel. Viewed from the east or west sides, the wickets cross over each other, making an X. There is enough play in the cage's anchoring that you can squish the whole thing down flat. The only thing that impedes you is a little rubber spring glued to the keyboard bevel. This spring is primarily responsible for that distinctive Thinkpad key feel.</p>
<p>By squishing the cage flat, you can hook or unhook the wickets. To reassemble and replace a key, I found it easiest to build the cage first. Start by crossing the wickets--they are fitted to each other. While pressing the X sides of the cage in, you can slip in the face plate. Don't put on the key face yet. Attach the cage to the keyboard bevel by putting it in place and hooking in the south wicket's legs first. Getting the north wicket in is a bit of a stretch. Flatten the cage by pressing down on it until the north legs slip in. Now you can attach the key face by setting it on top of the cage and applying gentle downward force. You should hear it snap.</p>https://alangrow.com/blog/tai64-for-all-timeTAI64 For All Time2006-09-14T00:00:00Alanalangrow+blog@gmail.com<p>From Bernstein's <a href="http://cr.yp.to/libtai/tai64.html#tai64">tai64 page</a>:</p>
<blockquote>
<p>"Integers 2^63 and larger are reserved for future extensions. Under many cosmological theories, the integers under 2^63 are adequate to cover the entire expected lifetime of the universe; in this case no extensions will be necessary."</p>
</blockquote>
<p>Phew!</p>
<p>Dealing with <a href="http://cr.yp.to/daemontools/multilog.html">multilog</a>'s TAI64 timestamps is always a bit annoying, but I suppose old djb may very well be laughing his head off in <a href="http://www.unixtimestamp.com/index.php">2038</a>. Still, the idea of writing software "for all time" has enough allure to the developer mind that it feels like a trap.</p>https://alangrow.com/blog/colorful-prompt-generatorColorful Bash Prompt Generator2004-12-30T00:00:00Alanalangrow+blog@gmail.com<p>(A very old post, but I've used this prompt ever since.)</p>
<p>Customizing a shell prompt often culminates in an impressive plumage display like</p><div class="highlight"><pre><span></span><span class="nb">export</span> <span class="nv">PS1</span><span class="o">=</span><span class="s1">'\[\e]0;\w\a\]\n\[\e[32m\]\u@\h \[\e[33m\]\w\n\[\e[0m\]$ '</span>
</pre></div>
<p>the idea being that lots of escape sequences = eliteness. Though, I'd guess most people just copy someone else's bash prompt and foist it off as their own, rather than learn ansi / xterm / bash escape sequences. Like me initially. :)</p>
<p>However, you can easily make your prompt setup readable by breaking it down.</p><div class="highlight"><pre><span></span><span class="c1"># ansi color escape sequences</span>
<span class="nv">prompt_black</span><span class="o">=</span><span class="s1">'\[\e[30m\]'</span>
<span class="nv">prompt_red</span><span class="o">=</span><span class="s1">'\[\e[31m\]'</span>
<span class="nv">prompt_green</span><span class="o">=</span><span class="s1">'\[\e[32m\]'</span>
<span class="nv">prompt_yellow</span><span class="o">=</span><span class="s1">'\[\e[33m\]'</span>
<span class="nv">prompt_blue</span><span class="o">=</span><span class="s1">'\[\e[34m\]'</span>
<span class="nv">prompt_magenta</span><span class="o">=</span><span class="s1">'\[\e[35m\]'</span>
<span class="nv">prompt_cyan</span><span class="o">=</span><span class="s1">'\[\e[36m\]'</span>
<span class="nv">prompt_white</span><span class="o">=</span><span class="s1">'\[\e[37m\]'</span>
<span class="nv">prompt_default_color</span><span class="o">=</span><span class="s1">'\[\e[0m\]'</span>
</pre></div>
<p>My motivation initially was to avoid beeping console prompts. The xterm escape sequence to set the window title contains a bell character, which was of course interpreted by xterm and friends, but not when I'd sit down at system consoles (where usually <code>TERM=cons25</code>). I needed to set <code>$PS1</code> according to <code>$TERM</code>.</p>
<p>In the course of things, I discovered the <code>\t</code> bash escape sequence, which gives you the current time in <code>hh:mm:ss</code> form. Nice. By incorporating this into the prompt you can now tell by inspection how long you've been sitting with your jaw open trying to remember what you were about to do. Or, how severe one's random spastic <code>ls</code>-ing has gotten.</p>
<div class="image">
<a href="../images/blog/bash-prompt-with-time.png">
<img src="../images/blog/bash-prompt-with-time-small.png" />
</a>
</div>
<p>For emergencies, there's also the no-color prompt.</p><div class="highlight"><pre><span></span><span class="nv">prompt_nocolor</span><span class="o">=</span><span class="s1">'\n\u@\h \w\n$ '</span>
</pre></div>
<p>For nostalgia (or out of masochism) there's the old dos prompt.</p><div class="highlight"><pre><span></span><span class="nv">prompt_dos</span><span class="o">=</span><span class="s1">'\n\w>'</span>
</pre></div>