Tuesday, January 21, 2014

A Perl Tidbit (aka Evil P3rl R Us)

Say you have Perl script A which is deployed in multiple sites, does its job well and thus should not be changed/refactored. In my case it takes a phone number on the command line and decides whether it's from Canada. The verdict is communicated via the exit code.

At one site one needs to write script B which does what A does but has a different interface incompatible with the one of A (in my case it must be an Asterisk AGI script).

Refactor? Nah, that's for wimps. Call A from B as a subshell? Suboptimal, for wimps only.

We need to go old skool and do. Never heard of perl-do? Not-for-the-faint-hearted.

Bad news: A calls exit(1) in multiple places. That bombs B as well. Two things to do: use $SIG{__DIE__} [doesn't work satisfactorily] or overload exit:


use strict;
use warnings;

use Asterisk::AGI;

push @INC => '/root'; # A.pl needs this :(

my $AGI = new Asterisk::AGI;

my %input = $AGI->ReadParse();

$ARGV[0] = $input{callerid}; # fake passing args using command line

my $canada;
eval {
   no warnings;
   local *CORE::GLOBAL::exit = sub {
     my $excode = shift;
     $AGI->noop("Exit code was $excode");
     $canada = !int($excode);
     goto out; # A.pl calls the overloaded exit twice w/o this (!!)

   -r '/root/A.pl' or $AGI->noop("Cannot read A.pl: $!");
   $AGI->noop("do() FAILED $@ - $!") unless do '/root/A.pl';
$AGI->set_variable('RESULT', $canada? 'YES': 'NO');


Sunday, January 5, 2014

How to Find the Process ID of this instance of CMD.EXE

Say you are writing a CMD.EXE script and would like to know the pid of the shell. Easy? No. CMD.EXE does not provide a $PID as Bash does so it takes a bit of contorting to get there (and you will mess up the window title):
@echo off

set r=%RANDOM%
for /f "usebackq tokens=2" %%a in (`" \
  tasklist /v /fi "imagename eq cmd.exe"| \
  %windir%\System32\find "SUPERSECRETTITLE %r%" \
  "`) do set PID=%%a

echo PID is %PID%
Note: Blogspot has a funny way to place text into columns. I broke the "for" command line into multiple lines using "\" -- you need to make it back a superlong command line for this to work.

Bonus (continuation): Find out if this CMD.exe was started with "/K" so you know to do "exit 0" or "exit /b 0":
wmic process where (name="cmd.exe" and processid=%PID%)\
  get commandline /value | \
  %windir%\system32\find " /K " >nul
The point of all this is that one does not need PowerShell to do useful Win32 scripting.