2006-01-26
A Unix annoyance
Unix: where you have to escape command line arguments twice, once from the shell and once from the program.
I dearly love Unix, but I am forced to admit that every so often it
has what I can only describe as 'robot logic'; things that are
perfectly logical, but not to normal humans. For example, consider the
fun of trying to remove a file called something like "-Brad Foo".
$ rm -Brad Foo rm: invalid option -- B $ rm "-Brad Foo" rm: invalid option -- B $ rm ./-Brad Foo rm: cannot remove `./-Brad': No such file or directory rm: cannot remove `Foo': No such file or directory
(and so on.)
Of course this is happening because the arguments are processed twice;
once by the shell to determine what's a single argument and what gets
split up, and then again by rm itself to find its options. So you
have to escape the filename in the shell because it has a space in it,
and that's entirely separate from how rm normally thinks anything
starting with a dash is a switch, not a filename to be removed. (And
programs can be wildly inconsistent in how to escape their arguments.)
I've used Unix sufficiently long and deeply that that I can see and explain all of this in my sleep. It all makes perfect sense and it's completely consistent and logical (once you understand Unix's logic).
But let's be honest here: it's robot logic, not human logic.
(This entry is inspired by the travails of a perfectly technically
adept Unix person in a similar situation, as recounted
here. He had a
simpler example that didn't need being escaped from the shell, just
from rm.)
2006-01-16
The danger of specific errno values
One of the things I've learned the hard way in writing Unix programs is that you should almost never expect to know all of the errno values that system calls can return. In theory it's documented in the manpages, but in practice a manpage's list is never comprehensive. The safest thing to do is to assume that any system call can return almost any errno value.
The example I remember most vividly is that our SMTP server daemon
used to carefully check the result of accept(). If there was an
error and it was anything besides EAGAIN, the daemon decided that
something was undoubtedly broken and therefor it should die.
Unfortunately, it turns out that on some systems accept() can also
return ECONNRESET, because people can initiate a TCP connection and
then terminate it before the SMTP daemon gets a chance to accept()
the new connection.
Until we caught this and fixed it, the result was that every so often (usually under periods of high load), the daemon would die mysteriously. Whoops.
This also points out another danger with checking specific errno values: they can change over time. So even code that was perfect and correct when it was written can drift into having problems over time and new systems, unless you recheck and revise it all the time.
(Our SMTP server daemon's code was probably perfectly correct for BSD systems of the early 1990s, for example.)