Mimsy Were the Borogoves

Hacks: Articles about programming in Python, Perl, PHP, and whatever else I happen to feel like hacking at.

GeekTool, Perl, and ANSI color codes

Jerry Stratton, October 7, 2010

GeekTool’s “shell” Geeklet is a great way of displaying the results of command-line scripts on your desktop. I currently have the following shell Geeklets on my computer for monitoring my sites:

  • /usr/bin/curl http://www.example.com/server-status?auto
  • /usr/bin/ssh monitor@web /usr/bin/uptime
  • /usr/bin/ssh monitor@web "/bin/ps -axe | /usr/bin/grep httpd | /usr/bin/wc -l | /usr/bin/sed -E 's/ +//;'"
  • /usr/bin/ssh monitor@web "/usr/bin/tail -40 /var/log/apache2/error_log"
  • /usr/bin/ssh monitor@web bin/freemem
  • /usr/bin/ssh monitor@web bin/hightimes

The first one is just grabbing the text version of Apache’s server status page. The second one displays the uptime and current load. The third one counts up the number of httpd processes currently running. And the fourth one displays the last 40 lines of Apache’s error log.

GeekTool system monitors

Looking at system stats is as simple as using Expose to pull all windows aside and reveal the desktop. “The memory’s green, the system is clean.”

The final two are a little more complex: freemem and hightimes are Perl scripts I’ve written to display color-coded warnings. It’s easy to add ANSI escape codes using Perl. The escape character is ‘\e’. Follow it by an open bracket, the code, and the letter m1. Look for the codes on Wikipedia’s ANSI escape code.

[toggle code]

  • #!/usr/bin/perl
  • $critical="\e[31m"; #red
  • $warning="\e[35m"; #magenta
  • $notice="\e[33m"; #yellow
  • $clear="\e[0m";
  • @processes = `/bin/ps -u _www -o time,pid | /usr/bin/sort -nr`;
  • for $process (@processes) {
    • chomp $process;
    • if ($process =~ m/^ +([0-9:.]+) +([0-9]+)/) {
      • ($time, $processId) = ($1, $2);
      • if ($time =~ m/([0-9]+):([0-9]+)\.([0-9]+)/) {
        • ($hours, $minutes, $minuteFraction) = ($1, $2, $3);
        • if ($hours) {
          • print "$critical$process$clear\n";
        • } elsif ($minutes > 10) {
          • print "$warning$process$clear\n";
        • } elsif ($minutes > 5) {
          • print "$notice$process$clear\n";
        • } else {
          • #at the end of cputime-intensive processes
          • exit;
        • }
      • } else {
        • print "${critical}UNKNOWN TIME FORMAT: $time$clear\n";
      • }
    • }
  • }

This runs the ‘ps’ command, getting only processes owned by _www, and displaying only the time and pid columns. The results of ps get piped through sort so that the most time-consuming processes are at the top.

Processes owned by _www that take up a lot of cpu time are displayed in red; those that might be about to take up a lot of cpu time are displayed magenta; and those that are probably just dipping over the line temporarily are displayed in yellow. Otherwise, it doesn’t display at all.

[toggle code]

  • #!/usr/bin/perl
  • $okay="\e[32m"; #green
  • $critical="\e[31m"; #red
  • $problem="\e[33m"; #yellow
  • $clear="\e[0m";
  • $usage = `/usr/bin/vm_stat`;
  • $warning = $critical;
  • if ($usage =~ m/page size of ([0-9]+) bytes/) {
    • $pageSize = $1;
    • if ($usage =~ m/Pages free: *([0-9]+)/) {
      • $free = $1;
      • $free = int($free*$pageSize/(1024*1024));
      • if ($free > 8000) {
        • $warning = $okay;
      • } elsif ($free > 6000) {
        • $warning = $problem;
      • }
      • $message = "$free megabytes free";
    • } else {
      • $message = "Unable to find free pages";
    • }
  • } else {
    • $message = "Unable to find page size";
  • }
  • print $warning, $message, $clear, "\n";
  • if ($warning eq $critical) {
    • exit(1);
  • }

This parses the output of vm_stat to get the current free memory. It also will adjust the green response dot you see in some of the screenshot’s geeklets. The green dot normally means only that the script did okay. For direct commands, you can’t make it be anything else. For your own scripts, however, if you want to change the color of that button you can exit with an error. In Perl, this is as simple as “exit(1)”.

That’s why the free memory script checks to see if the warning is critical before exiting. If the free memory is low enough to justify a critical color code, the script exits with an error code. GeekTool responds to this by turning the normally green dot red.

The hightimes script doesn’t bother with an exit code because it doesn’t display anything at all if there’s nothing out of the ordinary.

Obviously, this isn’t a replacement for a good system monitoring tool like Zenoss2. But for keeping an eye on something command-line oriented through both good and bad, GeekTool is a great little tool.

Note: I’m guessing what the time format is for ps in the first script. I don’t understand what the “ps.1p” means in %l:ps.1p, in the man page description. That’s why I have an error condition for unknown time formats.

February 10, 2011: Using Term::ANSIColor with GeekTool

I was looking for something else this morning and ran across Term::ANSIColor. Instead of having to look up the color codes in a table and output them exactly right, Term::ANSIColor makes manipulating ANSI codes much easier—and thus less likely to error.

There may be some cases where I don’t want to introduce any dependencies or overhead, but for the most part, Term::ANSIColor should be a much better choice.

In each of the two scripts, it’s just a matter of replacing the variables. Instead of:

  • $critical="\e[31m"; #red
  • $warning="\e[35m"; #magenta
  • $notice="\e[33m"; #yellow
  • $clear="\e[0m";

Import Term::ANSIColor:

  • use Term::ANSIColor;
  • $critical = color 'red on_black';
  • $warning = color 'magenta on_black';
  • $notice = color 'yellow on_black';
  • $clear = color 'reset';

Everything else remains the same.

And for the vm_stat script:

  • use Term::ANSIColor;
  • $okay = color 'green';
  • $critical = color 'red';
  • $problem = color 'yellow';
  • $clear = color 'reset';

One odd issue I ran into is that GeekTool appears to need the ‘reset’ to be followed by a carriage return. Running this Whammy Burger script directly on the command line will act normally—print the text in green—but running it in GeekTool will display an “[0m” at the end of the green text.

  • #!/usr/bin/perl
  • use Term::ANSIColor;
  • print color 'green';
  • print "Whammy Burger!\n";
  • print color 'reset';

The solution is to move the final ‘\n’ below the reset.

  1. I’m not sure what the ‘m’ means. Monitor?

  2. Which is what provides the graphs in the screenshot. They’re created using Zenoss and displayed via a public URL, using Geektool’s image geeklet.

  1. <- minidom and script tags
  2. GeekTool and TaskPaper ->