Chris Adams

The iPhone's new Exchange support works really well: bidirectional pushes take only a second or two as promised, the contact app has seamless searching of our corporate directory and so on, exactly as demoed. Unfortunately, there's a major drawback: once you enable Exchange, the iPhone inexplicably disables the local calendar and address book support. Initially I had assumed that this was an unfortunate compromise to get it out the door by July 11th, along with other notable omissions like tasks and attendee information but it's actually inexcusable: the mobile iCal has full support for multiple calendars if you pay Apple $100/year for Mobile Me. ActiveSync isn't the only trick Apple learned from Microsoft…

The bash exec function has an underutilized feature allowing you to redirect stdin or stderr for the entire script, avoiding the need to make sure that the user always runs myscript.sh > output.log:

exec > example.`date +%Y-%m-%d`.log

Unfortunately, when the documentation says "If FILE is not specified, the redirections take effect in this shell" there's a key omission: you can't simply use a normal pipe to have your output go to mail, logger, etc. or in non-interactive environments like grid / batch schedulers which don't allow you to specify a pipe for job output.

Googling won't turn up much useful other than some hacks which redirect to a temporary file/pipe or re-exec the script and I wanted something cleaner. The solution is to use the bash process substitution feature which allows you to create a file handle for a given command and use that where you might normally use a filename. This is normally demoed for cool input hacks such as diff-ing the output of two commands but it's also useful for our purposes:

#!/bin/bash
exec > >(mail -s "$0: normal job output" user@example.edu)
exec 2> >(mail -s "$0: error job output" syadmin@example.edu)

... commands which will be executed normally ...

Here's a simple example of how to use the Python ctypes module to load the Security framework on OS X and use it to delete an item from the keychain: keychain-delete.py

Inspired by a talk, I'm getting around to posting a script I've been using for a bit over a year to track down locking performance issues on OS X, particularly with network filesystems:

#!/usr/sbin/dtrace -s
/*
  Traces system locking activity and attempts to print the process and filename for each lock request

  Accounts for locks resulting from the following calls:
  flock()
  fcntl(SETLK|SETLKW)
  open(..., O_SHLOCK|O_EXLOCK)
*/

#pragma D option quiet

BEGIN {
  start = timestamp;
}

ERROR {
  printf("dtrace error in %s[%d] error on probe ID %d action #%d at DIF offset %d: %d : %x\n", execname, pid, arg1, arg2, arg3, arg4, arg5)
}

syscall::flock:entry
/ (arg1 & 1) || (arg1 & 2) /
{
  @locks[execname, pid, probefunc, arg1 & 1 ? "LOCK_SH" : "LOCK_EX", fds[arg0].fi_pathname ] = count();
} 

syscall::fcntl:entry
/ (arg1 == 8) || (arg1 == 9)/
{
  /* F_SETLK / F_SETLKW */
  @locks[execname, pid, probefunc, arg1 == 8 ? "F_SETLK" : "F_SETLKW", fds[arg0].fi_pathname ] = count();
} 

syscall::open:entry
/ (arg1 & O_SHLOCK) || (arg1 & O_EXLOCK) /
{
  self->locktype = arg1
} 

syscall::open:return
/ arg0 >= 0 /
{
  @locks[execname, pid, probefunc, self->locktype & O_SHLOCK ? "O_SHLOCK" : "O_EXLOCK", fds[arg0].fi_pathname ] = count();
}

tick-$1 {
  normalize(@locks, (timestamp - start) / 1000000000);
  printa("%16s[%u]\t%8s %8s %s\n", @locks);
  trunc(@locks);
  printf("----- %Y\n", walltimestamp);
}

If you spend a lot of time working on public networks or are worried about an unethical ISP injecting ads into your web pages, here's an easy way to keep your traffic intact and a bit more secure. OpenSSH has a handy DynamicProxy mode which allows it to provide a local SOCKS proxy: enable it and your traffic will be secure until it leaves your remote server. Besides thwarting a malicious network this is also a handy way to access intranet pages or things like scientific journals which restrict access to work/campus network addresses.

The only drawback to using this is that it requires you to keep an ssh session open all the time - this is where launchd and OS X 10.5's built-in SSH agent support come in handy. Once you've setup public-key authentication you won't be prompted each time it restarts, so there are only two steps for seamless remote working:

  1. Add this to your ~/.ssh/config file to enable keepalives, ensuring that ssh will be restarted quickly when your system resumes from sleep:
    TCPKeepAlive yes
    ServerAliveInterval 30
    
  2. Create a Launch Agent by storing this in ~/Library/LaunchAgents/org.openssh.dynamic-proxy.plist:
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
    <dict>
        <key>KeepAlive</key>
        <true/>
        <key>Label</key>
        <string>org.openssh.dynamic-proxy</string>
        <key>LimitLoadToSessionType</key>
        <string>Aqua</string>
        <key>OnDemand</key>
        <false/>
        <key>ProgramArguments</key>
        <array>
            <string>/usr/bin/ssh</string>
            <string>-D1080</string>
            <string>-Nn</string>
            <string>-n</string>
            <string>-C</string>
            <string>shell.example.org</string>
        </array>
        <key>RunAtLoad</key>
        <true/>
    </dict>
    </plist>
    
  3. Tell launchd to load the agent (it will keep it loaded in the future):
    launchctl load -w -S Aqua ~/Library/LaunchAgents/org.openssh.dynamic-proxy.plist
  4. Open the advanced section of your network preferences and enable a SOCKS proxy using 127.0.0.1 port 1080: 2512311692_4cbed5a480.jpg?v=1211401576
  5. If you use Firefox, you'll need to configure it to use the SOCKS proxy as it doesn't use the system settings.
  6. Visit whatismyip.com to confirm that your traffic appears to originate from your remote server's address

If you use the standard Python logging library, the syslog handler doesn't work by default on OS X 10.5. The problem is that SyslogHandler assumes that it can send to localhost:514. In 10.5 Apple disabled network syslog by default, which is a good security measure but it means that previously working code will no longer work (rdar://5871746). The solution is simple - if you used to do logging.handlers.SysLogHandler(address) you now need something like this:

if sys.platform == "darwin":
	# Apple made 10.5 more secure by disabling network syslog:
	address = "/var/run/syslog"
else:
	address = ('localhost', 514)
				
syslog = logging.handlers.SysLogHandler(address)
2008-4-17 08:16

A great example of how bad customer service works: Comcast cut David Winer's service for exceeding a secret transfer limit. He calls and is immediately confronted with a hostile legal department convinced that customers should pay for service they're not allowed to use. Hilarity ensues.

The interesting question: will it be too late by the time the cable companies realize they don't have a monopoly any more? Bandwidth already has competition and now more and enough companies are starting to offer their content on iTunes, Hulu, etc. that the cable company's $60/mo is starting to look rather excessive…

2008-4-11 09:11

Finally, a blog-meme I can related to. Here's what I do all day at home and at work - numbers which I'm sure have changed dramatically since I started living in TextMate a few years ago:

chris@home:~ $ history|awk '{print $2}'|sort|uniq -c|sort -rn|head
 166 python
  52 ls
  47 cd
  37 ssh
  34 svn
  16 pydoc
  16 mv
  16 essh
  15 vim
  12 cat
cadams@work:~ $ history|awk '{print $2}'|sort|uniq -c|sort -rn|head
 93 sudo
 56 ls
 53 cat
 42 man
 41 cd
 25 svn
 16 vim
 14 grep
  9 top
  8 rm

Older Entries