Category Things I’ve Learned

Bourne Shell Introspection

So I was thinking about how to refactor our custom Linux and Solaris init scripts at work. The way FreeBSD does it is to have the scripts in /etc/rc.d define variables with the commands to execute, e.g.,

start_cmd='/usr/sbin/foobard'
stop_cmd='kill `cat /var/run/foobar.pid`'

run_rc_command "$1"

where $1 is “start”, “stop”, or whatever, and run_rc_command is a function loaded from an external file. It can check whether $stop_cmd is defined, and if not, take some default action.

This is great and all, but I was wondering whether it would be possible to check whether a given shell function exists. That way, a common file could implement a generic structure for starting and stopping daemons, and the daemon-specific file could just set the specifics by defining do_start and do_stop functions.

The way to do this in Perl is to iterate over the symbol table of the package you’re looking for, and seeing whether each entry is a function. The symbol table for Foo::Bar is %Foo::Bar::; for the main package, it’s %::. Thus:

while (my ($k, $v) = each %::)
{
	if (defined())
	{
		print "$k is a functionn";
	}
}

sub test_x() {}
sub test_y() {}
sub test_z() {}

But I didn’t know how to do it in the Bourne shell.

Enter type, which tells you exactly that:

#!/bin/sh

# List of all known commands
STD_CMDS="start stop restart status verify"
MORE_CMDS="graceful something_incredibly_daemon_specific"

do_start="This is a string, not a function"

do_restart() {
	echo "I ought to restart something"
}

do_graceful() {
	echo "I am so fucking graceful"
}

for cmd in ${STD_CMDS} ${MORE_CMDS}; do
	if type "do_$cmd" >/dev/null 2>&1; then
		echo "* do_$cmd is defined"
	else
		echo "- do_$cmd is not defined"
	fi
done

And yes, this works not just in bash, but in the traditional, bourne-just-once shell, on every platform that I care about.

So yay, it turns out that the Bourne shell has more introspection than
I thought.

Walking in A Winter Wonderland

The snow that was pelting Atlanta earlier has finally made it up here. It’s still at the “ooh, isn’t that pretty!” stage, rather than the “oh, no, not more goddamn snow” stage, so I went for a walk in it.

Yes, the world shrouded in snow and silence was beautiful. But what struck me is that I unconsciously started walking differently.

I usually walk differently in the snow; that part isn’t surprising. What I found surprising is that apparently I’ve internalized my snow gait to the point where I didn’t need to consciously turn it on. Huh.

Normally, when I walk on dry ground, my heel hits the ground first, followed by the ball. My legs don’t bend much.

But when I’m walking on snow, my whole foot hits the ground at the same time. I also bend my knees so that my foot comes down straight, rather than at an angle. It’s a bit like an elephant or a Star Wars AT-AT, where the thigh moves the most, and the shin hangs vertically down from there. As you can imagine, my steps are shorter and I don’t walk as quickly.

It all has to do with coefficients of friction. Plural. For those who have forgotten High School physics, there are two coefficients of friction between two given surfaces: the static one, and the dynamic one. Dynamic friction refers to how hard it is to rub the two surfaces against each other. Static friction refers to when the surfaces are motionless, and how hard it is to get one of them moving.

If you’ve ever received a box of equipment (say, a new computer) and tried pushing it across the floor, you may have noticed that pushing it along the floor is easier than getting it to move in the first place. That’s because when it’s just lying there, you have to overcome static friction to get it to move. When it’s already moving, and you want it to keep moving, you have to overcome dynamic friction. And the dynamic friction coefficient is typically lower than the static one.

Normally, when you’re walking, this doesn’t matter a lot: when your sole hits dry pavement, both the static and dynamic coefficients of friction are high enough that you can trust them to hold your foot in place and allow you to push against the ground for the next step. But on snow or ice, there’s a very real danger of slipping, falling into traffic, and having your head squished by a passing car.

In this light, I think my snow gait makes sense: hitting the ground with my entire foot at once means there’s more surface to take advantage of what little friction there is (though on the other hand (foot?), my body weight is distributed over a larger area, so there’s less friction per square centimeter; I don’t know how this affects things).

Putting my foot straight down means that it’s not moving with respect to the ground when it hits, which in turn means that I’m taking advantage of the static friction coefficient, which is higher than the dynamic one, to keep from slipping. And the “elephant walk” bit is just so that I can put my foot down vertically.

[youtube http://www.youtube.com/watch?v=4B_8DY30xDg&fs=1&hl=en_US]

So now that you know how to safely walk on slippery surfaces, go out there and enjoy the snow.

Pre-Compressing Web Content

This was definitely a “D’oh!” type of problem.

One thing I’d been meaning to figure out for a while was how to send gzip-compressed files to a browser. That is, if I have a large HTML file, it’d be nice if the server could compress it to save bandwith and transmission time. Yes, Apache has mod_deflate which takes foo.html and gzips it on the fly, setting all the appropriate HTTP headers. But for static content, I should just be able to compress the file in advance. If the browser asked for foo.html, I wanted Apache to see that there’s a foo.html.gz and send that instead, with headers saying that it’s a text/html file that happens to be compressed.

mod_mime seemed like just the thing: just add

AddEncoding x-gzip .gz

to .htaccess. But every time I did that, Apache sent back “Content-Type: application/x-gzip“, so my browser treated it as a random file of unknown type that happened to be compressed.

Then I noticed that my vanilla-ish site-wide Apache config had

AddType application/x-gzip .gz .tgz

so that when Apache saw foo.html.gz, it ignored the .html extension, and saw only the .gz one.

The fix was to add RemoveType to my .htaccess:

RemoveType .gz
AddEncoding x-gzip .gz

And voilà! .gz stops being a file type and becomes an encoding, allowing .html to shine through.

I’ll add that this plays nice with AddLanguage as well. In my test setup, I have foo.html.en.gz, for which Apache returns the headers

Content-Type: text/html
Content-Encoding: x-gzip
Content-Language: en

I.e., it’s an HTML file, it’s gzip-encoded, and it’s in English.

Just as importantly, this works with other file types (e.g., CSS files and JavaScript scripts), and XMLHttpRequest does the Right Thing with them on all of the browsers I care about.

dirvish + MySQL

I’ve been using dirvish to do backups at home. One problem I’ve always had is how to back up MySQL databases, since the backup takes a snapshot of the binary files, so if it happens at the wrong time (e.g., in the middle of a transaction), the backup might wind up being in an unusable, inconsistent state. Much better to use mysqldump to export the database to a file (one which, by the way, can also be manipulated with standard tools like perl and emacs, in case I need to repair anything).

The obvious way to do this was to use the pre-client dirvish directive to export to a file, then let that file be backed up. But I could never get it to work.

Cut to the chase: It turns out that for this to work, the pre-client directive (and post-client, if you want to clean up afterward) needs an extra semicolon:

pre-client: ; /usr/local/bin/mysqldump -a -A -e > $DIRVISH_SRC/mysqldump.%Y-%m-%d.sql
post-client: ; /bin/rm $DIRVISH_SRC/mysqldump.%Y-%m-%d.sql

Without those semicolons, things don’t run correctly. My guess is that the semicolon tricks dirvish into thinking that the command consists of multiple commands, which must therefore be run inside a shell, rather than a single command to be executed with fork()/exec().

Another Useful Keybinding

I just found out that the mutt mailreader accepts ~Lexpr in searches. This matches messages where expr appears either as a sender or as a recipient.

Every so often, I need to look for a mail exchange I’ve had with a particular person, or where two people have corresponded and Cc-ed me. So I’d limit to

~farensb ~C(pat|chris) || ~f(pat|chris) ~Carensb

(messages (from arensb and (to or cc-ed) to (pat or chris)) or from (pat or chris) and (to or cc-ed) to arensb, for those who don’t speak mutt-regex).

Now that I know about ~L, the above can be simplified down to

~Larensb ~L(pat|chris)

(messages (from or to or cc-ed to) arensb and (from or to or cc-ed to) (pat or chris)).

My tunnelled carpals thank you, Mutt developers!

Firefox: Reopen Last Tab

Since M. didn’t know about this the other day, I thought I’d mention it, in case it helps someone else: Firefox has a massively-useful feature I’ve been using all the time: reopen the last tab that was closed.

You can find it under History → Recently closed tabs. There’s a Recently closed windows list to go with it.

Reopen last tab is bound to Alt-Shift-T for me, though your mileage may vary, since I’ve adjusted Firefox’s bindings to suit my modifier keys (what’s a Meta for?).

I should also add that Alt-1 is bound to “Go to 1st tab”, Alt-2 to “Go to 2nd tab”, and so forth. I sometimes find this to be quicker than moving my hand off of the keyboard to click on a tab.

Monthly Reports with Org-Mode

Like a lot of people, I have to submit a monthly “bullet” report, listing the things I’ve done in the previous month.

Since I use Org-Mode for planning, scheduling, and organizing tool (or rather: I tend to throw a bunch of notes into a file and tell this love child of a day planner and a wiki to tell me what I should do next), I figured I should use that.

I could use the timeline feature (C-c a L), but that only works for the current buffer, and I want a report that covers all buffers, just like the agenda.

What I’ve done in the past is to use C-c a a to get the agenda view, go back a month, toggle displaying completed/archived/whatever items, and go through that to make my bullet list.

But I finally got around to encapsulating that into a single M-x bullet command:

; Make it easier to generate bullets for $BOSS
(defvar bullet-entry-types
  '(:closed)
  "Org-mode agenda types that we want to see in the monthly bullet report
See `org-agenda-entry-types'."
  )

(defun bullets ()
  "Show a list of achievements for the past month, for monthly reports.
Uses `org-agenda'.
"
  (interactive)
  (require 'org-agenda)
  ; All we're doing here, really, is calling `org-agenda' with
  ; arguments giving a start date and a number of days. But to do
  ; that, we need to figure out
  ; - the date of the first of last month
  ; - the number of days in last month
  (let* ((now (current-time))
	 ; Figure out when last month was. Assuming that I run this
	 ; close to the beginning of a month, then `now' minus two
	 ; weeks was some time in the previous month. We can use that
	 ; to extract the year and month that we're interested in.
	 (2weeks-ago
	  (time-subtract now
			 (days-to-time 14)))
	 ; We'll also need to know when the first of this month was,
	 ; to find out how long last month was. If today is the 12th
	 ; of the month, then the first of the month was `now' minus
	 ; 11 days.
	 (1st-of-this-month
	  (time-subtract now
			 (days-to-time
			  (- (nth 3 (decode-time now))
			     1))))
	 ; Ditto to find the first of last month.
	 (1st-of-last-month
	  (time-subtract 2weeks-ago
			 (days-to-time
			  (- (nth 3 (decode-time 2weeks-ago))
			     1))))
	 ; The length of last month is the difference (in days)
	 ; between the first of last month, and the first of this
	 ; month.
	 (len-last-month
	  (time-to-number-of-days
	   (time-subtract 1st-of-this-month
			  1st-of-last-month)))
	 (start-date (decode-time 1st-of-last-month))
	 (start-year (nth 5 start-date))	; Year number
	 (start-mon (nth 4 start-date))		; Month number
	 ; Restrict the agenda to only those types of entries we're
	 ; interested in. I think this takes advantage of dynamic
	 ; scoping, which is normally an abomination unto the lord,
	 ; but is useful here.
	 (org-agenda-entry-types bullet-entry-types)
	 )
    ; Create an agenda with the stuff we've prepared above
    (org-agenda-list nil
		     (format "%04d-%02d-01"
			     start-year
			     start-mon)
		     len-last-month)
    ))

I hope this proves useful to someone.

iPhone Keyboard Trick

I’d noticed a while back that if you hold down a key on the iPhone keyboard, such as the ‘E’, for a second or two, you get a pop-up menu with variations on the ‘E’ theme, like ‘é’, ‘è’, ‘ê’, and so on.

But what I hadn’t noticed until just now is that the “.com” key, which appears when you’re expected to type in a URL, exhibits the same behavior: if you hold it down, you get a popup menu with “.net”, “.edu”, “.org”, and “.com”.

In addition, since I have the French keyboard installed, the popup contains “.fr”.

In the email application, when you’re entering an address, there’s no “.com” button, just a “.” (period) button. However, it also has the domain popup, with the same TLDs as the “.com” button.

I’ve gotta say: it’s little touches like this that help the interface get the hell out of the way of whatever it is you’re trying to do.

Sing Like You Mean It

What with it being late December and all, I’ve been listening to a lot of Christmas music lately. And one thing I’ve decided is that I really need to separate my collection into “Christmas music (straight)” and “Christmas music (ironic)”. It hurts my brain when I put the MP3 player on shuffle and it goes from Bing Crosby’s Silver Bells to William Hung’s version.

Another thing is that I need to purge my collection of such schmalzy glurge as Christmas at the Dentist’s and Stuck in an Elevator for Christmas.

Which brings me to my main point: that playing or singing like you mean it counts for a lot. I just listened to Etta Jones’s Have Yourself a Merry Little Christmas. I can’t think of a single objective criterion (tempo, syncopation, inflection, etc.) by which it should be excluded from An Uncool Square’s Treasury of Easy-Listening Christmas Favorites, but I like it. There’s something I can’t define, but basically she sings like she means it, like it gives her joy to be singing this song, rather than singing like “hey, it’s a gig.” (And yes, I hear a lot of the same thing in gospel music, which is why it goes on my list of genres that I respect, even if I don’t enjoy the music itself.)

Mannheim Steamroller could easily have been a novelty act: A Synthpop Christmas. But I think there’s a joy that comes through in his playing, a feeling that he actually likes those songs, and wanted to do them justice in his own style. Ditto Bing Crosby and Frank Sinatra, though Dean Martin could get a bit too schmalzy for me.

Of course, singing like you mean it isn’t everything. Wing is quite earnest, as is William Hung. But that doesn’t mean I want to listen to more of them than I absolutely have to.

I completely understand people who hate shopping in December because all the malls are playing the same twelve goddamned songs for a solid month. I too put on my iPod to drown out the Extruded Music Product. But if you look, you can find performances of those same twelve songs (and a lot more) that actually sound good. To a large extent, I think it’s simply because the musicians have genuine love for the material.

(PS: for the people who are tired of hearing the same twelve songs over and over, every year I make it a point to listen to Navidades Radioactivas, a Spanish punk Christmas compilation. It’s definitely… different.)

Previewing bit.ly Links

What I learned today: if you want to see where a bit.ly link points, without actually looking at the page, append a plus sign to the URL. That is, if someone points you at http://bit.ly/abcde, then http://bit.ly/abcde+ will give you the long URL.