Monday, February 11, 2008

Bit Shifting In Perl To Get Errno Values From The System Function

Hey there,

My two main reasons for putting together this little Perl script are:

1. To account for the unlikely event when mail queue monitoring commands don't work correctly, or you suspect that your system is so munged up that your OS commands aren't giving you correct information. Most Linux and Unix setups are far more robust than Windows, etc, and can take a severe beating before they go overboard. But, just like anything else, if pushed way too hard, they'll all eventually crack. Common symptoms of a total meltdown include obviously incorrect memory, swap and cpu statistics.

2. To demonstrate how to get back the actual errno result from a system command in Perl without using backticks.

This Perl script can, basically, be used on any directory and I set it up so that it takes a directory name as its only argument (so that it will work with any mail software no matter where it keeps its mail queue). You can call it simply, like this:

host # ./mailkue.sh /var/spool/clientmqueue <--- Substitute your mail queue location

We're making use of Perl's stat() and localtime() functions to parse a time-sorted directory listing. This should help you determine if there really are tons of old expired emails in your mail queue that you may or may not want to delete.

Also, probably more notably, we're using Perl's bit shift operator to extract the correct errno value from the system function. Normally, the system function only returns a scalar value indicating whether or not it executed the subshell sucessfully. If you run a program within the system function, like so:

host # $return = system("touch a");

The return value from that system command would be the numerical value of the success or failure of system's ability to invoke the shell.

If you did the same thing with backticks, your return value would be the output from that command. So you could do something as easy as this to get the real errno value:

host # $return = `touch a;echo $?`;

If you're not interested in the output, as we're clearly not in the above examples, but just want the proper return value from the command executed by the system function (as opposed to the return value of the system function... getting confusing and convoluted, here ;), you can get that by changing the above example, like so:

host # system("touch a");
host # $return = $? >> 8


And you'll know whether or not your touch was successful, not just if the system function worked. And it's guaranteed to work on Linux, Unix or any *nix system that Perl will run on.

Of course, backticks are generally simpler to use, but, under certain circumstances the above trick can come in very handy. Here's hoping it helps you out with your Perl scripting in the future!

Cheers,


Creative Commons License


This work is licensed under a
Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License

#!/usr/bin/perl

#
# mailkue.sh - check your mail
# queue - or anywhere else :)
#
# 2008 - Mike Golvach - eggi@comcast.net
#
# Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License
#

if ( $#ARGV < 0 ) {
print "Usage: $0 directory\n";
exit(1);
}

%months = qw(0 Jan 1 Feb 2 Mar 3 Apr 4 May 5 Jun 6 Jul 7 Aug 8 Sep 9 Oct 10 Nov 11 Dec);
chdir $ARGV[0];
system("ls -t >/dev/null 2>&1");
$status = $? >> 8;
if ( ! $status ) {
@nfiles = `ls -t`;
foreach $nfile (@nfiles) {
chomp($nfile);
$count = 1;
print "FILE: $nfile\n";
@files = `/usr/bin/find $ARGV[0]/$nfile -print|xargs -I {} ls -d1 "{}"`;
foreach $file (@files) {
chomp($file);
($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks) = stat($file);
($csec,$cmin,$chour,$cmday,$cmon,$cyear,$cwday,$cyday,$cisdst) = localtime($mtime);
$year = $cyear + 1900;
printf("%-2d: %-45s\t%10d %-3s %2d, %-4d\n", $count, $file, $size, $months{$cmon}, $cmday, $year);
$count++;
}
}
} else {
print "No mail in queue\n";
}


, Mike