Showing posts with label Perl. Show all posts
Showing posts with label Perl. Show all posts

2010-01-12

rcs ci -l results in "Unable to create temp directory: No such file or directory"

Suddenly, out of the blue, when I was checking in changes via rcs (using ci), I was getting an error.

me@mybox:~/sandbox
$ ci -l example
RCS/example,v  <--  example
Unable to create temp directory: No such file or directory
In my RCS directory I would see remnants of the failed check-in. I would see the example,v as usual, but there would also be a file named ,example, there, too.
me@mybox:~/sandbox
$ ls -l RCS
total 14
-r--r--r-- 1 me me   239 Feb 24 14:41 ,example,
-r--r--r-- 1 me me 11400 Feb 24 14:41 example,v
Subsequent check-in attempts would result in "file is in use" error messages.
me@mybox:~/sandbox
$ ci -l example
ci: RCS file RCS/example,v is in use
If I removed the ,example, file in the RCS directory, then the problem would start all over again when I did another check-in.
me@mybox:~/sandbox
$ rm RCS/,example,
rm: remove write-protected regular file `RCS/,example,'? y
me@mybox:~/sandbox
$ ci -l example
RCS/example,v  <--  example
Unable to create temp directory: No such file or directory
me@mybox:~/sandbox
$ ls RCS
1.conf,v  1.pl,v  1.sh,v  ,example,  example,v
me@mybox:~/sandbox
$ ci -l example
ci: RCS file RCS/example,v is in use
So what was the cause of this problem? Some system admin screwed up the TMP environment variable. It was set to a bad value.
me@mybox:~/sandbox
$ set | grep ^TMP=
TMP=Invalid
Brilliant. Unsetting that variable fixed the problem, after I removed the ,example, file from the RCS directory, of course.
me@mybox:~/sandbox
$ unset TMP
me@mybox:~/sandbox
$ rm RCS/,example,
rm: remove write-protected regular file `RCS/,example,'? y
me@mybox:~/sandbox
$ ci -l example
RCS/example,v  <--  example
new revision: 1.2; previous revision: 1.1
enter log message, terminated with single '.' or end of file:
>> It works!
>> .
done

2009-11-08

Apache access log quick & dirty busy report (from awk to Perl).

This is my second awk snippet that I've clumsily rewritten in Perl in my attempt to improve my Perl chops. I'll refactor for more elegant Perl later.

What this script does is spits out a report of the number of requests from Apache access logs (default common LogFormat) broken down by day and hour.

Here is what a line from Apache httpd access logs looks like.

127.0.0.1 - frank [10/Oct/2000:13:55:36 -0700] "GET /apache_pb.gif HTTP/1.0" 200 2326


Here is the awk script.

#!/bin/gawk -f

BEGIN {
        months["Jan"] = 01
        months["Feb"] = 02
        months["Mar"] = 03
        months["Apr"] = 04
        months["May"] = 05
        months["Jun"] = 06
        months["Jul"] = 07
        months["Aug"] = 08
        months["Sep"] = 09
        months["Oct"] = 10
        months["Nov"] = 11
        months["Dec"] = 12
}

{
        key = substr($4, 2, 14)

        if (key in totals) {
                totals[key]++
        }
        else {
                totals[key] = 1
        }
}

END {

        printf("| *date* | *hour* | *total* | *req/min* |\n")

        sort = "sort -k1,2 -t'|'"
        for (indx in totals) {

                hour = substr(indx, 13, 2)
                day = substr(indx, 1, 2)
                month_word = substr(indx, 4, 3)
                month = months[month_word]
                year = substr(indx, 8, 4)
                rate = totals[indx] / 60

                printf("| %d-%02d-%02d | %02d | %d | %.0f |\n", year, month, day, hour, totals[indx], rate) | sort
        }
        close(sort)
}

Even my awk code has extraneous sytax. I did not need to test the associative array key before incrementing its value. Oh, well. My dirty laundry is here for all to see.

Here is an example of what the script output looks like.

| *date* | *hour* | *total* | *req/min* |
| 2009-11-06 | 09 | 188 | 3 |
| 2009-11-06 | 10 | 9 | 0 |
| 2009-11-06 | 11 | 29 | 0 |

You'll notice that is comes out formatted as TWiki – ahemFoswiki syntax for easy pasting. The format is also close enough to CSV that importation into spreadsheets and databases is not a challenge.

Here is my Perl equivalent.

#!/usr/bin/perl -anw

use warnings;
use strict;

my %month = (
             "Jan" => 1,
             "Feb" => 2,
             "Mar" => 3,
             "Apr" => 4,
             "May" => 5,
             "Jun" => 6,
             "Jul" => 7,
             "Aug" => 8,
             "Sep" => 9,
             "Oct" => 10,
             "Nov" => 11,
             "Dec" => 12,
);

our %total;
# Each outer hash key is a date stamp.
# Each outer hash value is an hash reference.
# Each inner hash has a key for reported hours of that day.
# The value at each inner key is the tally of the corresponding access requests.
$total{substr($F[3], 1, 11)}->{substr($F[3], 13, 2)}++;

END {

    print "| *date* | *hour* | *total* | *req/min* |\n";

    for my $t (sort(keys %total)) {
        my $day = substr $t, 0, 2;
        my $month_word = substr $t, 3, 3;
        my $month = $month{$month_word};
        my $year = substr $t, 7, 4;

        for my $hour (sort(keys %{$total{$t}})) {
            my $rate = $total{$t}->{$hour} / 60;
            printf "| %d-%02d-%02d | %02d | %d | %.0f |\n", ($year, $month, $day, $hour, $total{$t}->{$hour}, $rate);
        }
    }
}

Even my rough draft is a wee bit more elegant than the awk syntax. I did not rely on an external program to performing the sorting, thanks to Perl's sort.

Also, the total data structure is a bit different. Instead of using the full string "06/Nov/2009:09"as an array index like I did in awk, I broke the data down into a hash of hashes. In the Perl version, "06/Nov/2009" was the key to the outer hash, and "09" was the key to the inner hash.  This made sorting during output a lot easier.

At first, I tried using an array instead a hash to hold the hours of the day, but this turned out to be problematic. I think the problem had something to do with "09" being treated as an illegal octal digit in the array index. Treating the "09" as a string in the hash key was just easier and more flexible.

2009-10-27

My set complement Perl script.

Currently, my awk-vs-perl knowledge slider is about like this.

awk <--O-------> Perl

I am trying to move it more like this.

awk <-----O---> Perl

To that end, I'll start by translating one of my handy-dandy mini-awk scripts to perl. Have you ever needed to find the elements in one list that were not in another list?

For example, suppose you have a list of all files in a directory. You also have a list of all files in a package, and some of those files in the package list are the ones in the directory list. Now you want to find out what files in the directory are not part of the package.

Here is the awk code:

#!/usr/bin/awk -f

# ** Purpose **
#
# Exclude items in lists A, B, ... from list Z.
#
# ** Usage **
#
# ./exclude_list.awk -v x="listA listB" listZ
#
# The list of files assigned to x is the list of files which contain the lists
# of items to exclude.  The last argument (the file awk processes line by
# line) is the list of items to exclude things from.

BEGIN {
    split(x, ex);
    for (f in ex) {
        while ((getline line < ex[f]) > 0) {
            exclude[line] = 1;
        }
    close(ex[f]);
    }
}

{
    if (exclude[$0] != 1) {
        print;
    }
}

Here is what I just wrote up in Perl.

#!/usr/bin/perl -w

my $set1 = shift;
my %set2=();

while (<>) {
    $set2{$_}++;
}

open my $fh, '<', $set1 or die "Can't open $set1";
while (<$fh>) {
   print $_ if ! exists $set2{$_};
}
close $fh;

To make the source code length comparison fair, I'll have to add POD to the Perl source.

Later on, I'll seek to expand the functionality a little bit, and also see if any Perl built-ins can do this job quicker than I've done here.

2009-05-29

AJP ping in Perl

For use with Tomcat AJP connectors. I had to use this to see which was misbehaving, mod_jk or Tomcat.

This is my first working draft.


#!/usr/bin/perl -w

use warnings;
use strict;

use Socket;

my ($remote, $port) = split /:/, shift @ARGV, 2;

if (! $remote) {
$remote = 'localhost';
}
print "remote = $remote\n";

if (! $port) {
$port = 8009;
}
print "port = $port\n";

my ($iaddr, $paddr, $proto);

# If the port has anything other than numbers, we're assuming it is an
# /etc/services name.
if ($port =~ /\D/) {
$port = getservbyname $port, 'tcp' ;
}

die "Bad port, stopped" unless $port;
print "port = $port\n";

$iaddr = inet_aton($remote) || die "No host: $remote, stopped";
print "iaddr = $iaddr\n";

$paddr = sockaddr_in($port, $iaddr) || die "sockaddr: $!, stopped";
print "paddr = $paddr\n";

# Grab the number for TCP out of /etc/protocols.
$proto = getprotobyname 'tcp' ;
print "proto = $proto\n";

my $sock;
# PF_INET and SOCK_STREAM are constants imported by the Socket module. They
# are the same as what is defined in sys/socket.h.
socket $sock, PF_INET, SOCK_STREAM, $proto || die "socket: $!, stopped";
print "sock = $sock\n";

print "BEFORE CONNECT\n";
connect $sock, $paddr || die "connect: $!, stopped";
print "AFTER CONNECT\n";

# This is the ping packet. For detailed documentation, see
# http://tomcat.apache.org/connectors-doc/ajp/ajpv13a.html
# I stole the exact byte sequence from
# http://sourceforge.net/project/shownotes.php?group_id=128058&release_id=438456
# instead of fully understanding the packet structure.
my $ping = pack 'C5' # Format template.
, 0x12, 0x34 # Magic number for server->container packets.
, 0x00, 0x01 # 2 byte int length of payload.
, 0x0A # Type of packet. 10 = CPing.
;

my @ping_values = unpack 'C5', $ping;
print "ping_values = " , join ' ', @ping_values , "\n";

# This is the expected pong packet. That is, this is what Tomcat sends back
# to indicate that it is operating OK.
my $expected = pack 'C5' # Format template.
, 0x41, 0x42 # Magic number for container->server packets.
, 0x00, 0x01 # 2 byte int length of payload.
, 0x09 # Type of packet. 9 = CPong reply.
;

syswrite $sock, $ping || die "syswrite: $!, stopped";

my $pong;
$pong = 'empty';
print "BEFORE READ\n";
sysread $sock, $pong, 5 || die "read: $!, stopped";
print "AFTER READ\n";

my @pong_values = unpack 'C5', $pong;
print "pong_values = " , join ' ', @pong_values , "\n";

close $sock || die "close: $!, stopped";

exit 0;