Happy 808 Day!

August 8th, 2008

Hey, look at the date! Happy 08/08/08!

I wrote a patch for WordPress 2.5 that adds a pager to the Manage Pages tab. For a site I’m working on with over 500 pages, this produces a 10x speedup. I submitted the patch to the WordPress bug tracker.

Here’s a template script for writing a daemontools service in PHP. It handles HUP signals, so you can make the service reload its configuration file without restarting.

<?php

// Don’t forget this line or signal handling won’t work:
declare(ticks = 1);

// This global flag is used to trigger a configuration file reload.
$load_config = true;

// The SIGHUP handler doesn’t do any actual work; it just sets the flag.
function handle_hup($signal) {
global $load_config;
$load_config = true;
pcntl_signal(SIGHUP, ‘handle_hup’, false);
}

// Register the signal handler.
pcntl_signal(SIGHUP, ‘handle_hup’, false);

// Start the service.
echo “myserver: starting service\n”;
while (true) {
// Load the config file if necessary.
if ($load_config) {
$load_config = false;
echo “myserver: loading configuration\n”;
include ‘config.php’;
}

// Do something useful.
// …

// Pause for a configurable amount of time.
echo “myserver: sleeping for $SLEEP_TIME seconds…\n”;
sleep($SLEEP_TIME);
}

?>

And in config.php, you can start with something like this, to make the sleep time configurable:

<?php

// Sleep time in seconds.

$SLEEP_TIME = 60;

?>

Some random thoughts

May 6th, 2008
  • Slashes are to URLs what dots are to objects
  • HTML is assembly language
  • The spec for HTML tables is practically frozen
  • Programming languages encapsulate philosophies
  • Across the spectrum of languages, some properties are nearly universal, others divisive
  • Blogging is like Usenet with bigger egos (if that is possible)

To get an Apache-style access log, complete with referrers and user-agents, I created a directory called /var/log/tomcat writable by the Tomcat process, and I added the following to tomcat/conf/context.xml:

<Valve className=”org.apache.catalina.valves.FastCommonAccessLogValve”
pattern=”combined”
directory=”/var/log/tomcat”
prefix=”access.log”
rotatable=”false” />

This will create a file called /var/log/tomcat/access.log and start logging requests to it. I’m turning off Tomcat’s date stamping and log rotation, since I prefer to use logrotate.

I’m a big fan of the daemontools server framework for quite a few reasons. For one, it’s incredibly stable. So stable, I use it to watch Apache and restart it when it crashes because daemontools never crashes. “Depend in the direction of stability” is my mantra.

Your server might crash, but daemontools will ruthlessly restart it, and since it runs from inittab, the OS will restart daemontools if it ever crashes. But, like I said, daemontools never crashes.

Another reason I love daemontools is that it does automatic logging and log rotation, so you can create as many servers you need and you don’t have to worry that the one last server you threw in there in a hurry won’t get its logs rotated, causing your hard drive to fill up.

Tomcat, on the other hand, has never been very friendly to the system administrator. It creates many different log files at once and rotates them by renaming them with date stamps; this makes it really annoying when you’re trying to tail the log files. Log messages tend to be large and span multiple lines, so it’s very hard to see what’s going on with all the noise. Killing Tomcat requires running a shell script that sends a shutdown command to a socket, completely breaking UNIX convention for no good reason.

I decided to bite the bullet and get Tomcat to run under daemontools instead, and I’ve never been happier (well, given that I’m still talking about Java here, let’s just say I’ve never been less unhappy…). Here’s how I did it:

My run script looks like this:

#!/bin/sh
exec 2>&1
exec envdir ./env setuidgid tomcat /usr/local/tomcat/bin/catalina.sh run

In my env directory I have two files that set up my environment: JAVA_HOME and CLASSPATH. JAVA_HOME contains the following:

/usr

CLASSPATH contains this (I’ll explain why in a bit):

/usr/local/tomcat/bin/tiny-formatter.jar

I had to comment out the following line at the top of tomcat/bin/setclasspath.sh to keep it from clobbering the CLASSPATH environment variable:

# First clear out the user classpath
# CLASSPATH=

Now, tiny-formatter.jar is a little hack that makes Tomcat’s logger use only one line per log message and removes the datestamp, since multilog adds one already. It contains a class file, TinyFormatter.class, generated by compiling the following source file, TinyFormatter.java:

import java.io.PrintWriter;
import java.io.StringWriter;

import java.util.logging.Formatter;
import java.util.logging.LogRecord;

public class TinyFormatter extends Formatter
{
static final String lineSep = System.getProperty(”line.separator”);

public String format(LogRecord record)
{
StringBuffer buf = new StringBuffer(180);
buf.append(record.getLevel());
buf.append(”: “);
buf.append(formatMessage(record));
buf.append(” (”);
buf.append(record.getSourceClassName());
buf.append(’.');
buf.append(record.getSourceMethodName());
buf.append(’)');
buf.append(lineSep);

Throwable throwable = record.getThrown();
if (throwable != null) {
StringWriter sink = new StringWriter();
throwable.printStackTrace(new PrintWriter(sink, true));
buf.append(sink.toString());
}

return buf.toString();
}
}

Okay, maybe it’s not so tiny. That’s Java for ‘ya.

To install this custom formatter, I edited tomcat/conf/logging.properties and replaced its contents with the following:

handlers = java.util.logging.ConsoleHandler
.handlers = java.util.logging.ConsoleHandler

java.util.logging.ConsoleHandler.level = FINE
java.util.logging.ConsoleHandler.formatter = TinyFormatter

The run script for the daemontools logger is pretty standard:

#!/bin/sh
exec setuidgid tomcat multilog t /var/multilog/tomcat

I can now start and stop Tomcat with “svc -u /service/tomcat” and “svc -d /service/tomcat”, and restarting is the usual “svc -t /service/tomcat”. To learn all the details I’ve left out, I highly recommend reading the djb way. I’m out of time. ;)

PMA Scanbots

March 19th, 2008

I can’t think of any good reason why you’d want to put your phpMyAdmin installation in any of the following locations:

  1. /MYADMIN/
  2. /MYadmin/
  3. /MyAdmin/
  4. /PHPMYADMIN/
  5. /PHPMYadmin/
  6. /PHPmyadmin/
  7. /PMA/
  8. /PhPmYaDmIn/
  9. /admin/
  10. /admin/mysql/
  11. /admin/phpmyadmin/
  12. /admin/pma/
  13. /db/
  14. /dbadmin/
  15. /myADMIN/
  16. /myadmin/
  17. /mysql-admin/
  18. /mysql/
  19. /mysqladmin/
  20. /pHpMyAdMiN/
  21. /phpMYadmin/
  22. /phpMyAdmin-2.2.0/
  23. /phpMyAdmin-2.2.3/
  24. /phpMyAdmin-2.2.6/
  25. /phpMyAdmin-2.2.7-pl1/
  26. /phpMyAdmin-2.2.7/
  27. /phpMyAdmin-2.5.1/
  28. /phpMyAdmin-2.5.4/
  29. /phpMyAdmin-2.5.6/
  30. /phpMyAdmin-2.6.4-pl4/
  31. /phpMyAdmin-2.6.4/
  32. /phpMyAdmin-2.7.0-pl2/
  33. /phpMyAdmin-2.7.0/
  34. /phpMyAdmin-2.8.1/
  35. /phpMyAdmin-2.8.2.1/
  36. /phpMyAdmin-2.8.2.2/
  37. /phpMyAdmin-2.8.2.4/
  38. /phpMyAdmin-2.9.0.1/
  39. /phpMyAdmin-2.9.0.2/
  40. /phpMyAdmin-2.9.0/
  41. /phpMyAdmin-2.9.1/
  42. /phpMyAdmin/
  43. /phpmyADMIN/
  44. /phpmyadmin/
  45. /phpmyadmin2/
  46. /pma/
  47. /pmamy/
  48. /web/phpMyAdmin/

It’s a jungle out there.

I did my taxes with MySQL

March 9th, 2008

For the second year now, I used MySQL to do my taxes. I find that even with GainsKeeper, it’s tedious to do investment taxes with TurboTax because it takes a considerable amount of research (even with a small portfolio like mine) to provided the needed information to calculate the cost basis: When did I buy this stock or fund? Did I buy multiple lots? Were there reinvested dividends? Was this the first sale? If not, when else did I sell?

As geeky as it sounds, SQL is a powerful tool to answer these kinds of ad-hoc questions. It can transform investment taxes from a several-week project into something you can do in half a day (which I did, by the way). Here are the tables I use, which are pretty close to E*Trade’s CSV export format:

CREATE TABLE `security` (
`cusip` varchar(32) NOT NULL,
`symbol` varchar(32) NOT NULL,
`description` varchar(255) NOT NULL,
PRIMARY KEY (`cusip`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

CREATE TABLE `trade` (
`trade_date` date NOT NULL,
`order_type` enum(’BUY’,'SELL’) NOT NULL,
`cusip` varchar(32) NOT NULL,
`description` varchar(255) NOT NULL,
`quantity` decimal(16,8) NOT NULL,
`executed` decimal(16,8) NOT NULL,
`commision` decimal(16,8) NOT NULL,
`net_amount` decimal(16,8) NOT NULL,
PRIMARY KEY (`trade_date`,`order_type`,`cusip`,`quantity`,`executed`),
KEY `cusip` (`cusip`),
CONSTRAINT `trade_ibfk_1` FOREIGN KEY (`cusip`) REFERENCES `security` (`cusip`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

I also have a couple of views that show just buys or sells for convenience:

CREATE VIEW `buys` AS select `t`.`trade_date` AS `trade_date`,`t`.`order_type` AS `order_type`,`t`.`cusip` AS `cusip`,`t`.`description` AS `description`,`t`.`quantity` AS `quantity`,`t`.`executed` AS `executed`,`t`.`commision` AS `commision`,`t`.`net_amount` AS `net_amount`,`s`.`symbol` AS `symbol`,`s`.`description` AS `symbol_description` from (`trade` `t` join `security` `s` on((`t`.`cusip` = `s`.`cusip`))) where (`t`.`order_type` = ‘BUY’)

CREATE VIEW `sells` AS select `t`.`trade_date` AS `trade_date`,`t`.`order_type` AS `order_type`,`t`.`cusip` AS `cusip`,`t`.`description` AS `description`,`t`.`quantity` AS `quantity`,`t`.`executed` AS `executed`,`t`.`commision` AS `commision`,`t`.`net_amount` AS `net_amount`,`s`.`symbol` AS `symbol`,`s`.`description` AS `symbol_description` from (`trade` `t` join `security` `s` on((`t`.`cusip` = `s`.`cusip`))) where (`t`.`order_type` = ‘SELL’)

Finding all the purchases I made for a particular stock is as simple as this:

mysql> select * from buys where cusip = ‘007903107′ order by trade_date \G
*************************** 1. row ***************************
trade_date: 2005-12-06
order_type: BUY
cusip: 007903107
description: ADV MICRO DEVICES
quantity: 5.00000000
executed: 27.59000000
commision: 12.99000000
net_amount: 150.94000000
symbol: AMD
symbol_description: ADV MICRO DEVICES
1 row in set (0.00 sec)

I decided to use CUSIP numbers as primary keys for securities since they are more stable and work for bonds as well as stocks. This makes things a little bit annoying because I always have to look the CUSIP up from the security table. That’s what I get for trying to “do the right thing” I guess. =)

Posted in MySQL | No Comments »

…will be Firefox 3 for Mac OS X. Mark my words.

Disclosure: Though I use a Mac often, I am a Linux user at heart.

  1. Standard, cross-language, typeful serialization of data
  2. User-defined error codes and messages
  3. “Boxcarring” of requests to reduce overhead
  4. Serialization of binary content
  5. Serialization of date-time values
  6. Standardized parameter passing
  7. Introspection allowing for straightforward code generation
  8. High-level APIs for just about every language
  9. No manual parsing of XML, ever
  10. Only three lines to call a function in Python and several other languages:

>>> import xmlrpclib
>>> s = xmlrpclib.Server(’http://localhost/xmlrpc’)
>>> s.demo.addTwoNumbers(3, 4)
7

Not that the REST doesn’t have its benefits, but someone ought to be saying this. XML-RPC isn’t complicated like SOAP, it runs just about everywhere, and it lets you get on with your work rather than arguing about semicolons versus slashes or XML versus JSON or countless other things. Besides, when your goal is to support as many languages as possible, you want to minimize the amount of code you write for each language. As far as I’ve seen, nothing else accomplishes literally no-code binding like XML-RPC.